summaryrefslogtreecommitdiff
path: root/drivers/net/wireless
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/wireless')
-rw-r--r--drivers/net/wireless/ath/ar5523/ar5523.c4
-rw-r--r--drivers/net/wireless/ath/ath10k/ahb.c2
-rw-r--r--drivers/net/wireless/ath/ath10k/bmi.c6
-rw-r--r--drivers/net/wireless/ath/ath10k/ce.c32
-rw-r--r--drivers/net/wireless/ath/ath10k/core.c17
-rw-r--r--drivers/net/wireless/ath/ath10k/debug.c2
-rw-r--r--drivers/net/wireless/ath/ath10k/htc.c6
-rw-r--r--drivers/net/wireless/ath/ath10k/htt_rx.c4
-rw-r--r--drivers/net/wireless/ath/ath10k/hw.c62
-rw-r--r--drivers/net/wireless/ath/ath10k/hw.h34
-rw-r--r--drivers/net/wireless/ath/ath10k/mac.c22
-rw-r--r--drivers/net/wireless/ath/ath10k/pci.c6
-rw-r--r--drivers/net/wireless/ath/ath10k/sdio.c4
-rw-r--r--drivers/net/wireless/ath/ath10k/snoc.c2
-rw-r--r--drivers/net/wireless/ath/ath10k/testmode.c4
-rw-r--r--drivers/net/wireless/ath/ath10k/txrx.c2
-rw-r--r--drivers/net/wireless/ath/ath10k/usb.c4
-rw-r--r--drivers/net/wireless/ath/ath10k/wmi.c4
-rw-r--r--drivers/net/wireless/ath/ath11k/Makefile1
-rw-r--r--drivers/net/wireless/ath/ath11k/ahb.c10
-rw-r--r--drivers/net/wireless/ath/ath11k/ce.c11
-rw-r--r--drivers/net/wireless/ath/ath11k/core.c313
-rw-r--r--drivers/net/wireless/ath/ath11k/core.h29
-rw-r--r--drivers/net/wireless/ath/ath11k/coredump.c52
-rw-r--r--drivers/net/wireless/ath/ath11k/coredump.h79
-rw-r--r--drivers/net/wireless/ath/ath11k/dp.c39
-rw-r--r--drivers/net/wireless/ath/ath11k/dp.h6
-rw-r--r--drivers/net/wireless/ath/ath11k/dp_rx.c166
-rw-r--r--drivers/net/wireless/ath/ath11k/fw.c3
-rw-r--r--drivers/net/wireless/ath/ath11k/hal.c4
-rw-r--r--drivers/net/wireless/ath/ath11k/hif.h21
-rw-r--r--drivers/net/wireless/ath/ath11k/mac.c193
-rw-r--r--drivers/net/wireless/ath/ath11k/mhi.c19
-rw-r--r--drivers/net/wireless/ath/ath11k/mhi.h5
-rw-r--r--drivers/net/wireless/ath/ath11k/pci.c245
-rw-r--r--drivers/net/wireless/ath/ath11k/qmi.c30
-rw-r--r--drivers/net/wireless/ath/ath11k/qmi.h10
-rw-r--r--drivers/net/wireless/ath/ath11k/reg.c107
-rw-r--r--drivers/net/wireless/ath/ath11k/reg.h3
-rw-r--r--drivers/net/wireless/ath/ath11k/testmode.c82
-rw-r--r--drivers/net/wireless/ath/ath11k/wmi.c11
-rw-r--r--drivers/net/wireless/ath/ath11k/wmi.h11
-rw-r--r--drivers/net/wireless/ath/ath12k/Kconfig8
-rw-r--r--drivers/net/wireless/ath/ath12k/Makefile4
-rw-r--r--drivers/net/wireless/ath/ath12k/acpi.c202
-rw-r--r--drivers/net/wireless/ath/ath12k/acpi.h40
-rw-r--r--drivers/net/wireless/ath/ath12k/ahb.c1155
-rw-r--r--drivers/net/wireless/ath/ath12k/ahb.h80
-rw-r--r--drivers/net/wireless/ath/ath12k/ce.c103
-rw-r--r--drivers/net/wireless/ath/ath12k/ce.h18
-rw-r--r--drivers/net/wireless/ath/ath12k/core.c428
-rw-r--r--drivers/net/wireless/ath/ath12k/core.h308
-rw-r--r--drivers/net/wireless/ath/ath12k/debug.c6
-rw-r--r--drivers/net/wireless/ath/ath12k/debug.h10
-rw-r--r--drivers/net/wireless/ath/ath12k/debugfs.c1468
-rw-r--r--drivers/net/wireless/ath/ath12k/debugfs.h122
-rw-r--r--drivers/net/wireless/ath/ath12k/debugfs_htt_stats.c1241
-rw-r--r--drivers/net/wireless/ath/ath12k/debugfs_htt_stats.h453
-rw-r--r--drivers/net/wireless/ath/ath12k/debugfs_sta.c337
-rw-r--r--drivers/net/wireless/ath/ath12k/debugfs_sta.h24
-rw-r--r--drivers/net/wireless/ath/ath12k/dp.c159
-rw-r--r--drivers/net/wireless/ath/ath12k/dp.h135
-rw-r--r--drivers/net/wireless/ath/ath12k/dp_mon.c2494
-rw-r--r--drivers/net/wireless/ath/ath12k/dp_mon.h17
-rw-r--r--drivers/net/wireless/ath/ath12k/dp_rx.c660
-rw-r--r--drivers/net/wireless/ath/ath12k/dp_rx.h49
-rw-r--r--drivers/net/wireless/ath/ath12k/dp_tx.c450
-rw-r--r--drivers/net/wireless/ath/ath12k/dp_tx.h5
-rw-r--r--drivers/net/wireless/ath/ath12k/fw.c9
-rw-r--r--drivers/net/wireless/ath/ath12k/fw.h3
-rw-r--r--drivers/net/wireless/ath/ath12k/hal.c153
-rw-r--r--drivers/net/wireless/ath/ath12k/hal.h80
-rw-r--r--drivers/net/wireless/ath/ath12k/hal_desc.h18
-rw-r--r--drivers/net/wireless/ath/ath12k/hal_rx.c121
-rw-r--r--drivers/net/wireless/ath/ath12k/hal_rx.h469
-rw-r--r--drivers/net/wireless/ath/ath12k/hal_tx.h10
-rw-r--r--drivers/net/wireless/ath/ath12k/hw.c519
-rw-r--r--drivers/net/wireless/ath/ath12k/hw.h30
-rw-r--r--drivers/net/wireless/ath/ath12k/mac.c2171
-rw-r--r--drivers/net/wireless/ath/ath12k/mac.h66
-rw-r--r--drivers/net/wireless/ath/ath12k/mhi.c9
-rw-r--r--drivers/net/wireless/ath/ath12k/pci.c88
-rw-r--r--drivers/net/wireless/ath/ath12k/pci.h5
-rw-r--r--drivers/net/wireless/ath/ath12k/peer.c5
-rw-r--r--drivers/net/wireless/ath/ath12k/peer.h3
-rw-r--r--drivers/net/wireless/ath/ath12k/qmi.c257
-rw-r--r--drivers/net/wireless/ath/ath12k/qmi.h5
-rw-r--r--drivers/net/wireless/ath/ath12k/reg.c526
-rw-r--r--drivers/net/wireless/ath/ath12k/reg.h25
-rw-r--r--drivers/net/wireless/ath/ath12k/rx_desc.h12
-rw-r--r--drivers/net/wireless/ath/ath12k/testmode.c395
-rw-r--r--drivers/net/wireless/ath/ath12k/testmode.h40
-rw-r--r--drivers/net/wireless/ath/ath12k/wmi.c1692
-rw-r--r--drivers/net/wireless/ath/ath12k/wmi.h409
-rw-r--r--drivers/net/wireless/ath/ath12k/wow.c3
-rw-r--r--drivers/net/wireless/ath/ath6kl/cfg80211.c6
-rw-r--r--drivers/net/wireless/ath/ath6kl/init.c2
-rw-r--r--drivers/net/wireless/ath/ath6kl/main.c2
-rw-r--r--drivers/net/wireless/ath/ath6kl/recovery.c4
-rw-r--r--drivers/net/wireless/ath/ath6kl/txrx.c2
-rw-r--r--drivers/net/wireless/ath/ath9k/ahb.c13
-rw-r--r--drivers/net/wireless/ath/ath9k/ath9k.h3
-rw-r--r--drivers/net/wireless/ath/ath9k/channel.c2
-rw-r--r--drivers/net/wireless/ath/ath9k/common-spectral.c4
-rw-r--r--drivers/net/wireless/ath/ath9k/gpio.c8
-rw-r--r--drivers/net/wireless/ath/ath9k/htc_drv_beacon.c3
-rw-r--r--drivers/net/wireless/ath/ath9k/htc_drv_main.c6
-rw-r--r--drivers/net/wireless/ath/ath9k/init.c6
-rw-r--r--drivers/net/wireless/ath/ath9k/link.c2
-rw-r--r--drivers/net/wireless/ath/ath9k/main.c10
-rw-r--r--drivers/net/wireless/ath/ath9k/pci.c2
-rw-r--r--drivers/net/wireless/ath/ath9k/xmit.c9
-rw-r--r--drivers/net/wireless/ath/carl9170/fw.c2
-rw-r--r--drivers/net/wireless/ath/carl9170/tx.c3
-rw-r--r--drivers/net/wireless/ath/testmode_i.h (renamed from drivers/net/wireless/ath/ath11k/testmode_i.h)54
-rw-r--r--drivers/net/wireless/ath/wcn36xx/dxe.c4
-rw-r--r--drivers/net/wireless/ath/wcn36xx/testmode.c2
-rw-r--r--drivers/net/wireless/ath/wil6210/cfg80211.c2
-rw-r--r--drivers/net/wireless/ath/wil6210/main.c6
-rw-r--r--drivers/net/wireless/ath/wil6210/netdev.c6
-rw-r--r--drivers/net/wireless/ath/wil6210/p2p.c2
-rw-r--r--drivers/net/wireless/ath/wil6210/txrx.h4
-rw-r--r--drivers/net/wireless/ath/wil6210/wmi.c6
-rw-r--r--drivers/net/wireless/atmel/at76c50x-usb.c4
-rw-r--r--drivers/net/wireless/broadcom/b43/main.c2
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c20
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/btcoex.c4
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c44
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.h25
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c5
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c2
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/cyw/core.c308
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/cyw/fwil_types.h87
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.c3
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.h4
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/fweh.c7
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/fweh.h8
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwvid.h29
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c2
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c2
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c29
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmsmac/aiutils.c6
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmsmac/aiutils.h2
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmsmac/ampdu.c4
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmsmac/channel.c4
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmsmac/dma.c2
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmsmac/mac80211_if.c4
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmsmac/main.c4
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmsmac/main.h2
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmsmac/pmu.c2
-rw-r--r--drivers/net/wireless/intel/ipw2x00/ipw2200.c2
-rw-r--r--drivers/net/wireless/intel/ipw2x00/libipw.h2
-rw-r--r--drivers/net/wireless/intel/ipw2x00/libipw_crypto.c2
-rw-r--r--drivers/net/wireless/intel/ipw2x00/libipw_rx.c91
-rw-r--r--drivers/net/wireless/intel/iwlegacy/3945-mac.c2
-rw-r--r--drivers/net/wireless/intel/iwlegacy/3945-rs.c2
-rw-r--r--drivers/net/wireless/intel/iwlegacy/4965-mac.c4
-rw-r--r--drivers/net/wireless/intel/iwlegacy/4965-rs.c18
-rw-r--r--drivers/net/wireless/intel/iwlegacy/common.c2
-rw-r--r--drivers/net/wireless/intel/iwlegacy/common.h2
-rw-r--r--drivers/net/wireless/intel/iwlwifi/Kconfig15
-rw-r--r--drivers/net/wireless/intel/iwlwifi/Makefile22
-rw-r--r--drivers/net/wireless/intel/iwlwifi/cfg/1000.c51
-rw-r--r--drivers/net/wireless/intel/iwlwifi/cfg/2000.c90
-rw-r--r--drivers/net/wireless/intel/iwlwifi/cfg/22000.c379
-rw-r--r--drivers/net/wireless/intel/iwlwifi/cfg/5000.c83
-rw-r--r--drivers/net/wireless/intel/iwlwifi/cfg/6000.c227
-rw-r--r--drivers/net/wireless/intel/iwlwifi/cfg/7000.c173
-rw-r--r--drivers/net/wireless/intel/iwlwifi/cfg/8000.c92
-rw-r--r--drivers/net/wireless/intel/iwlwifi/cfg/9000.c168
-rw-r--r--drivers/net/wireless/intel/iwlwifi/cfg/ax210.c229
-rw-r--r--drivers/net/wireless/intel/iwlwifi/cfg/bz.c162
-rw-r--r--drivers/net/wireless/intel/iwlwifi/cfg/dr.c174
-rw-r--r--drivers/net/wireless/intel/iwlwifi/cfg/rf-fm.c51
-rw-r--r--drivers/net/wireless/intel/iwlwifi/cfg/rf-gf.c42
-rw-r--r--drivers/net/wireless/intel/iwlwifi/cfg/rf-hr.c42
-rw-r--r--drivers/net/wireless/intel/iwlwifi/cfg/rf-jf.c84
-rw-r--r--drivers/net/wireless/intel/iwlwifi/cfg/rf-pe.c16
-rw-r--r--drivers/net/wireless/intel/iwlwifi/cfg/rf-wh.c15
-rw-r--r--drivers/net/wireless/intel/iwlwifi/cfg/sc.c156
-rw-r--r--drivers/net/wireless/intel/iwlwifi/dvm/agn.h4
-rw-r--r--drivers/net/wireless/intel/iwlwifi/dvm/commands.h154
-rw-r--r--drivers/net/wireless/intel/iwlwifi/dvm/debugfs.c10
-rw-r--r--drivers/net/wireless/intel/iwlwifi/dvm/dev.h3
-rw-r--r--drivers/net/wireless/intel/iwlwifi/dvm/devices.c4
-rw-r--r--drivers/net/wireless/intel/iwlwifi/dvm/eeprom.c53
-rw-r--r--drivers/net/wireless/intel/iwlwifi/dvm/led.c6
-rw-r--r--drivers/net/wireless/intel/iwlwifi/dvm/mac80211.c10
-rw-r--r--drivers/net/wireless/intel/iwlwifi/dvm/main.c81
-rw-r--r--drivers/net/wireless/intel/iwlwifi/dvm/power.c34
-rw-r--r--drivers/net/wireless/intel/iwlwifi/dvm/rx.c8
-rw-r--r--drivers/net/wireless/intel/iwlwifi/dvm/rxon.c8
-rw-r--r--drivers/net/wireless/intel/iwlwifi/dvm/tt.c21
-rw-r--r--drivers/net/wireless/intel/iwlwifi/dvm/tt.h1
-rw-r--r--drivers/net/wireless/intel/iwlwifi/dvm/tx.c6
-rw-r--r--drivers/net/wireless/intel/iwlwifi/dvm/ucode.c10
-rw-r--r--drivers/net/wireless/intel/iwlwifi/fw/acpi.c28
-rw-r--r--drivers/net/wireless/intel/iwlwifi/fw/acpi.h11
-rw-r--r--drivers/net/wireless/intel/iwlwifi/fw/api/alive.h14
-rw-r--r--drivers/net/wireless/intel/iwlwifi/fw/api/commands.h13
-rw-r--r--drivers/net/wireless/intel/iwlwifi/fw/api/context.h6
-rw-r--r--drivers/net/wireless/intel/iwlwifi/fw/api/d3.h2
-rw-r--r--drivers/net/wireless/intel/iwlwifi/fw/api/datapath.h19
-rw-r--r--drivers/net/wireless/intel/iwlwifi/fw/api/dbg-tlv.h9
-rw-r--r--drivers/net/wireless/intel/iwlwifi/fw/api/debug.h42
-rw-r--r--drivers/net/wireless/intel/iwlwifi/fw/api/dhc.h226
-rw-r--r--drivers/net/wireless/intel/iwlwifi/fw/api/location.h149
-rw-r--r--drivers/net/wireless/intel/iwlwifi/fw/api/mac-cfg.h283
-rw-r--r--drivers/net/wireless/intel/iwlwifi/fw/api/mac.h6
-rw-r--r--drivers/net/wireless/intel/iwlwifi/fw/api/phy-ctxt.h9
-rw-r--r--drivers/net/wireless/intel/iwlwifi/fw/api/phy.h22
-rw-r--r--drivers/net/wireless/intel/iwlwifi/fw/api/power.h47
-rw-r--r--drivers/net/wireless/intel/iwlwifi/fw/api/rs.h135
-rw-r--r--drivers/net/wireless/intel/iwlwifi/fw/api/rx.h32
-rw-r--r--drivers/net/wireless/intel/iwlwifi/fw/api/sta.h1
-rw-r--r--drivers/net/wireless/intel/iwlwifi/fw/api/stats.h5
-rw-r--r--drivers/net/wireless/intel/iwlwifi/fw/api/tdls.h6
-rw-r--r--drivers/net/wireless/intel/iwlwifi/fw/api/time-event.h42
-rw-r--r--drivers/net/wireless/intel/iwlwifi/fw/api/tx.h40
-rw-r--r--drivers/net/wireless/intel/iwlwifi/fw/dbg.c310
-rw-r--r--drivers/net/wireless/intel/iwlwifi/fw/dbg.h9
-rw-r--r--drivers/net/wireless/intel/iwlwifi/fw/debugfs.c2
-rw-r--r--drivers/net/wireless/intel/iwlwifi/fw/dhc-utils.h75
-rw-r--r--drivers/net/wireless/intel/iwlwifi/fw/dump.c15
-rw-r--r--drivers/net/wireless/intel/iwlwifi/fw/error-dump.h7
-rw-r--r--drivers/net/wireless/intel/iwlwifi/fw/file.h28
-rw-r--r--drivers/net/wireless/intel/iwlwifi/fw/init.c20
-rw-r--r--drivers/net/wireless/intel/iwlwifi/fw/paging.c4
-rw-r--r--drivers/net/wireless/intel/iwlwifi/fw/pnvm.c61
-rw-r--r--drivers/net/wireless/intel/iwlwifi/fw/pnvm.h5
-rw-r--r--drivers/net/wireless/intel/iwlwifi/fw/regulatory.c101
-rw-r--r--drivers/net/wireless/intel/iwlwifi/fw/regulatory.h27
-rw-r--r--drivers/net/wireless/intel/iwlwifi/fw/rs.c137
-rw-r--r--drivers/net/wireless/intel/iwlwifi/fw/runtime.h11
-rw-r--r--drivers/net/wireless/intel/iwlwifi/fw/smem.c4
-rw-r--r--drivers/net/wireless/intel/iwlwifi/fw/uefi.c87
-rw-r--r--drivers/net/wireless/intel/iwlwifi/fw/uefi.h41
-rw-r--r--drivers/net/wireless/intel/iwlwifi/iwl-config.h504
-rw-r--r--drivers/net/wireless/intel/iwlwifi/iwl-context-info-v2.h (renamed from drivers/net/wireless/intel/iwlwifi/iwl-context-info-gen3.h)67
-rw-r--r--drivers/net/wireless/intel/iwlwifi/iwl-context-info.h14
-rw-r--r--drivers/net/wireless/intel/iwlwifi/iwl-csr.h30
-rw-r--r--drivers/net/wireless/intel/iwlwifi/iwl-dbg-tlv.c18
-rw-r--r--drivers/net/wireless/intel/iwlwifi/iwl-debug.h9
-rw-r--r--drivers/net/wireless/intel/iwlwifi/iwl-devtrace.h9
-rw-r--r--drivers/net/wireless/intel/iwlwifi/iwl-drv.c190
-rw-r--r--drivers/net/wireless/intel/iwlwifi/iwl-drv.h4
-rw-r--r--drivers/net/wireless/intel/iwlwifi/iwl-fh.h30
-rw-r--r--drivers/net/wireless/intel/iwlwifi/iwl-io.c24
-rw-r--r--drivers/net/wireless/intel/iwlwifi/iwl-io.h14
-rw-r--r--drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c136
-rw-r--r--drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.h16
-rw-r--r--drivers/net/wireless/intel/iwlwifi/iwl-nvm-utils.c17
-rw-r--r--drivers/net/wireless/intel/iwlwifi/iwl-op-mode.h17
-rw-r--r--drivers/net/wireless/intel/iwlwifi/iwl-trans.c273
-rw-r--r--drivers/net/wireless/intel/iwlwifi/iwl-trans.h246
-rw-r--r--drivers/net/wireless/intel/iwlwifi/iwl-utils.c1
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/Makefile16
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/agg.c672
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/agg.h127
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/ap.c353
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/ap.h45
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/coex.c36
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/coex.h15
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/constants.h88
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/d3.c1998
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/d3.h51
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/debugfs.c1104
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/debugfs.h244
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/ftm-initiator.c451
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/ftm-initiator.h29
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/fw.c554
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/hcmd.h56
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/iface.c692
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/iface.h238
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/key.c358
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/key.h39
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/led.c100
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/led.h29
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/link.c1214
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/link.h153
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/low_latency.c339
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/low_latency.h68
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/mac80211.c2712
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/mac80211.h13
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/mcc.c329
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/mcc.h17
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/mld.c748
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/mld.h586
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/mlo.c1241
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/mlo.h173
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/notif.c722
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/notif.h35
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/phy.c198
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/phy.h60
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/power.c399
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/power.h33
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/ptp.c321
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/ptp.h45
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/regulatory.c393
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/regulatory.h23
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/roc.c265
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/roc.h20
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/rx.c2071
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/rx.h72
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/scan.c2011
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/scan.h136
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/session-protect.c222
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/session-protect.h102
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/sta.c1321
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/sta.h273
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/stats.c520
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/stats.h22
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/tests/Makefile5
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/tests/agg.c663
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/tests/emlsr_with_bt.c140
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/tests/hcmd.c62
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/tests/link-selection.c345
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/tests/link.c110
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/tests/module.c11
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/tests/rx.c353
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/tests/utils.c503
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/tests/utils.h140
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/thermal.c467
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/thermal.h36
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/time_sync.c240
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/time_sync.h26
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/tlc.c702
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/tlc.h23
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/tx.c1394
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/tx.h77
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/coex.c6
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/d3.c113
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c49
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/ftm-initiator.c123
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/ftm-responder.c86
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/fw.c143
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/led.c4
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c42
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c177
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/mld-mac.c46
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/mld-mac80211.c18
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/mld-sta.c12
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/mvm.h73
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/nvm.c42
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/ops.c128
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/power.c5
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/quota.c43
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/rs-fw.c23
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/rs.c206
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/rs.h5
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/rx.c6
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c91
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/sta.c95
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/sta.h11
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/tests/Makefile2
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/tests/hcmd.c38
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/time-event.c12
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/tt.c97
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/tx.c194
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/utils.c24
-rw-r--r--drivers/net/wireless/intel/iwlwifi/pcie/ctxt-info-v2.c (renamed from drivers/net/wireless/intel/iwlwifi/pcie/ctxt-info-gen3.c)203
-rw-r--r--drivers/net/wireless/intel/iwlwifi/pcie/ctxt-info.c20
-rw-r--r--drivers/net/wireless/intel/iwlwifi/pcie/drv.c2337
-rw-r--r--drivers/net/wireless/intel/iwlwifi/pcie/internal.h128
-rw-r--r--drivers/net/wireless/intel/iwlwifi/pcie/rx.c189
-rw-r--r--drivers/net/wireless/intel/iwlwifi/pcie/trans-gen2.c154
-rw-r--r--drivers/net/wireless/intel/iwlwifi/pcie/trans.c308
-rw-r--r--drivers/net/wireless/intel/iwlwifi/pcie/tx-gen2.c84
-rw-r--r--drivers/net/wireless/intel/iwlwifi/pcie/tx.c257
-rw-r--r--drivers/net/wireless/intel/iwlwifi/tests/devinfo.c174
-rw-r--r--drivers/net/wireless/intersil/p54/fwio.c2
-rw-r--r--drivers/net/wireless/intersil/p54/p54.h1
-rw-r--r--drivers/net/wireless/intersil/p54/txrx.c13
-rw-r--r--drivers/net/wireless/marvell/libertas/cmd.c143
-rw-r--r--drivers/net/wireless/marvell/libertas/cmd.h10
-rw-r--r--drivers/net/wireless/marvell/libertas/cmdresp.c3
-rw-r--r--drivers/net/wireless/marvell/libertas/decl.h4
-rw-r--r--drivers/net/wireless/marvell/libertas/dev.h4
-rw-r--r--drivers/net/wireless/marvell/libertas/if_usb.c2
-rw-r--r--drivers/net/wireless/marvell/libertas/main.c100
-rw-r--r--drivers/net/wireless/marvell/libertas_tf/cmd.c2
-rw-r--r--drivers/net/wireless/marvell/libertas_tf/if_usb.c2
-rw-r--r--drivers/net/wireless/marvell/libertas_tf/main.c4
-rw-r--r--drivers/net/wireless/marvell/mwifiex/11n.c6
-rw-r--r--drivers/net/wireless/marvell/mwifiex/11n_rxreorder.c2
-rw-r--r--drivers/net/wireless/marvell/mwifiex/cfg80211.c42
-rw-r--r--drivers/net/wireless/marvell/mwifiex/cfp.c6
-rw-r--r--drivers/net/wireless/marvell/mwifiex/cmdevt.c76
-rw-r--r--drivers/net/wireless/marvell/mwifiex/fw.h14
-rw-r--r--drivers/net/wireless/marvell/mwifiex/init.c25
-rw-r--r--drivers/net/wireless/marvell/mwifiex/main.c54
-rw-r--r--drivers/net/wireless/marvell/mwifiex/main.h17
-rw-r--r--drivers/net/wireless/marvell/mwifiex/pcie.c10
-rw-r--r--drivers/net/wireless/marvell/mwifiex/sdio.c4
-rw-r--r--drivers/net/wireless/marvell/mwifiex/sta_cmd.c69
-rw-r--r--drivers/net/wireless/marvell/mwifiex/sta_event.c4
-rw-r--r--drivers/net/wireless/marvell/mwifiex/tdls.c2
-rw-r--r--drivers/net/wireless/marvell/mwifiex/txrx.c3
-rw-r--r--drivers/net/wireless/marvell/mwifiex/uap_event.c16
-rw-r--r--drivers/net/wireless/marvell/mwifiex/usb.c10
-rw-r--r--drivers/net/wireless/marvell/mwifiex/util.c20
-rw-r--r--drivers/net/wireless/marvell/mwifiex/wmm.c12
-rw-r--r--drivers/net/wireless/mediatek/mt76/channel.c5
-rw-r--r--drivers/net/wireless/mediatek/mt76/dma.c1
-rw-r--r--drivers/net/wireless/mediatek/mt76/eeprom.c4
-rw-r--r--drivers/net/wireless/mediatek/mt76/mac80211.c24
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76.h28
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7615/init.c2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7615/main.c4
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7615/mcu.c6
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7615/pci_mac.c4
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7615/usb.c2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76_connac.h7
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76_connac3_mac.h4
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c45
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h14
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76x0/pci.c3
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76x0/usb.c3
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76x02_mmio.c1
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76x02_usb_core.c4
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76x2/pci.c3
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76x2/usb.c6
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76x2/usb_init.c13
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7915/debugfs.c81
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7915/eeprom.c33
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7915/eeprom.h1
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7915/init.c14
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7915/mac.c60
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7915/mcu.c52
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7915/mcu.h14
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7915/mmio.c6
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h25
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7921/main.c12
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7925/Makefile1
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7925/init.c102
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7925/main.c196
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7925/mcu.c430
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7925/mcu.h88
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7925/mt7925.h42
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7925/pci.c7
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7925/regs.h2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7925/testmode.c201
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt792x.h19
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt792x_acpi_sar.c123
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt792x_acpi_sar.h18
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt792x_core.c5
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7996/coredump.c4
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7996/debugfs.c73
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7996/dma.c196
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7996/eeprom.c42
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7996/init.c41
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7996/mac.c428
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7996/main.c995
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7996/mcu.c750
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7996/mcu.h59
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7996/mmio.c200
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h161
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7996/pci.c21
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7996/regs.h51
-rw-r--r--drivers/net/wireless/mediatek/mt76/scan.c21
-rw-r--r--drivers/net/wireless/mediatek/mt76/tx.c3
-rw-r--r--drivers/net/wireless/microchip/wilc1000/hif.c18
-rw-r--r--drivers/net/wireless/purelifi/plfxlc/mac.c1
-rw-r--r--drivers/net/wireless/purelifi/plfxlc/usb.c8
-rw-r--r--drivers/net/wireless/quantenna/qtnfmac/qlink.h21
-rw-r--r--drivers/net/wireless/ralink/rt2x00/rt2800lib.c2
-rw-r--r--drivers/net/wireless/ralink/rt2x00/rt2800mmio.c2
-rw-r--r--drivers/net/wireless/ralink/rt2x00/rt2800usb.c2
-rw-r--r--drivers/net/wireless/ralink/rt2x00/rt2x00dev.c4
-rw-r--r--drivers/net/wireless/realtek/rtl8xxxu/8192c.c2
-rw-r--r--drivers/net/wireless/realtek/rtl8xxxu/core.c17
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/base.c2
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/core.c11
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/core.h1
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/pci.c10
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/rtl8188ee/sw.c4
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/rtl8192de/phy.c11
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/rtl8192de/phy.h1
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/rtl8192du/phy.c5
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/rtl8192du/phy.h1
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/rtl8192ee/phy.c4
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/rtl8192ee/phy.h1
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/rtl8821ae/phy.c4
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/rtl8821ae/phy.h1
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/usb.c12
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/usb.h2
-rw-r--r--drivers/net/wireless/realtek/rtw88/Kconfig25
-rw-r--r--drivers/net/wireless/realtek/rtw88/Makefile9
-rw-r--r--drivers/net/wireless/realtek/rtw88/coex.c2
-rw-r--r--drivers/net/wireless/realtek/rtw88/debug.c57
-rw-r--r--drivers/net/wireless/realtek/rtw88/fw.c23
-rw-r--r--drivers/net/wireless/realtek/rtw88/fw.h1
-rw-r--r--drivers/net/wireless/realtek/rtw88/hci.h8
-rw-r--r--drivers/net/wireless/realtek/rtw88/mac.c18
-rw-r--r--drivers/net/wireless/realtek/rtw88/mac.h2
-rw-r--r--drivers/net/wireless/realtek/rtw88/mac80211.c2
-rw-r--r--drivers/net/wireless/realtek/rtw88/main.c93
-rw-r--r--drivers/net/wireless/realtek/rtw88/main.h49
-rw-r--r--drivers/net/wireless/realtek/rtw88/pci.c6
-rw-r--r--drivers/net/wireless/realtek/rtw88/phy.c215
-rw-r--r--drivers/net/wireless/realtek/rtw88/phy.h20
-rw-r--r--drivers/net/wireless/realtek/rtw88/reg.h69
-rw-r--r--drivers/net/wireless/realtek/rtw88/rtw8703b.c61
-rw-r--r--drivers/net/wireless/realtek/rtw88/rtw8723cs.c2
-rw-r--r--drivers/net/wireless/realtek/rtw88/rtw8723d.c5
-rw-r--r--drivers/net/wireless/realtek/rtw88/rtw8723de.c2
-rw-r--r--drivers/net/wireless/realtek/rtw88/rtw8723ds.c2
-rw-r--r--drivers/net/wireless/realtek/rtw88/rtw8723du.c2
-rw-r--r--drivers/net/wireless/realtek/rtw88/rtw8723x.c59
-rw-r--r--drivers/net/wireless/realtek/rtw88/rtw8812a.c2
-rw-r--r--drivers/net/wireless/realtek/rtw88/rtw8812au.c2
-rw-r--r--drivers/net/wireless/realtek/rtw88/rtw8814a.c2269
-rw-r--r--drivers/net/wireless/realtek/rtw88/rtw8814a.h62
-rw-r--r--drivers/net/wireless/realtek/rtw88/rtw8814a_table.c23930
-rw-r--r--drivers/net/wireless/realtek/rtw88/rtw8814a_table.h40
-rw-r--r--drivers/net/wireless/realtek/rtw88/rtw8814ae.c31
-rw-r--r--drivers/net/wireless/realtek/rtw88/rtw8814au.c54
-rw-r--r--drivers/net/wireless/realtek/rtw88/rtw8821a.c2
-rw-r--r--drivers/net/wireless/realtek/rtw88/rtw8821au.c2
-rw-r--r--drivers/net/wireless/realtek/rtw88/rtw8821c.c18
-rw-r--r--drivers/net/wireless/realtek/rtw88/rtw8821ce.c2
-rw-r--r--drivers/net/wireless/realtek/rtw88/rtw8821cs.c2
-rw-r--r--drivers/net/wireless/realtek/rtw88/rtw8821cu.c2
-rw-r--r--drivers/net/wireless/realtek/rtw88/rtw8822b.c18
-rw-r--r--drivers/net/wireless/realtek/rtw88/rtw8822be.c2
-rw-r--r--drivers/net/wireless/realtek/rtw88/rtw8822bs.c2
-rw-r--r--drivers/net/wireless/realtek/rtw88/rtw8822bu.c8
-rw-r--r--drivers/net/wireless/realtek/rtw88/rtw8822c.c7
-rw-r--r--drivers/net/wireless/realtek/rtw88/rtw8822ce.c2
-rw-r--r--drivers/net/wireless/realtek/rtw88/rtw8822cs.c2
-rw-r--r--drivers/net/wireless/realtek/rtw88/rtw8822cu.c2
-rw-r--r--drivers/net/wireless/realtek/rtw88/rtw88xxa.c2
-rw-r--r--drivers/net/wireless/realtek/rtw88/rx.c6
-rw-r--r--drivers/net/wireless/realtek/rtw88/sar.c2
-rw-r--r--drivers/net/wireless/realtek/rtw88/sdio.c29
-rw-r--r--drivers/net/wireless/realtek/rtw88/usb.c65
-rw-r--r--drivers/net/wireless/realtek/rtw88/util.c3
-rw-r--r--drivers/net/wireless/realtek/rtw89/Kconfig2
-rw-r--r--drivers/net/wireless/realtek/rtw89/acpi.c1037
-rw-r--r--drivers/net/wireless/realtek/rtw89/acpi.h190
-rw-r--r--drivers/net/wireless/realtek/rtw89/cam.c13
-rw-r--r--drivers/net/wireless/realtek/rtw89/chan.c456
-rw-r--r--drivers/net/wireless/realtek/rtw89/chan.h19
-rw-r--r--drivers/net/wireless/realtek/rtw89/coex.c2959
-rw-r--r--drivers/net/wireless/realtek/rtw89/coex.h18
-rw-r--r--drivers/net/wireless/realtek/rtw89/core.c733
-rw-r--r--drivers/net/wireless/realtek/rtw89/core.h352
-rw-r--r--drivers/net/wireless/realtek/rtw89/debug.c2217
-rw-r--r--drivers/net/wireless/realtek/rtw89/fw.c847
-rw-r--r--drivers/net/wireless/realtek/rtw89/fw.h197
-rw-r--r--drivers/net/wireless/realtek/rtw89/mac.c138
-rw-r--r--drivers/net/wireless/realtek/rtw89/mac.h24
-rw-r--r--drivers/net/wireless/realtek/rtw89/mac80211.c320
-rw-r--r--drivers/net/wireless/realtek/rtw89/mac_be.c11
-rw-r--r--drivers/net/wireless/realtek/rtw89/pci.c36
-rw-r--r--drivers/net/wireless/realtek/rtw89/pci.h56
-rw-r--r--drivers/net/wireless/realtek/rtw89/pci_be.c2
-rw-r--r--drivers/net/wireless/realtek/rtw89/phy.c904
-rw-r--r--drivers/net/wireless/realtek/rtw89/phy.h37
-rw-r--r--drivers/net/wireless/realtek/rtw89/phy_be.c2
-rw-r--r--drivers/net/wireless/realtek/rtw89/ps.c153
-rw-r--r--drivers/net/wireless/realtek/rtw89/ps.h3
-rw-r--r--drivers/net/wireless/realtek/rtw89/reg.h59
-rw-r--r--drivers/net/wireless/realtek/rtw89/regd.c645
-rw-r--r--drivers/net/wireless/realtek/rtw89/rtw8851b.c28
-rw-r--r--drivers/net/wireless/realtek/rtw89/rtw8852a.c28
-rw-r--r--drivers/net/wireless/realtek/rtw89/rtw8852b.c28
-rw-r--r--drivers/net/wireless/realtek/rtw89/rtw8852b_common.c30
-rw-r--r--drivers/net/wireless/realtek/rtw89/rtw8852b_rfk.c13
-rw-r--r--drivers/net/wireless/realtek/rtw89/rtw8852bt.c28
-rw-r--r--drivers/net/wireless/realtek/rtw89/rtw8852bt_rfk.c13
-rw-r--r--drivers/net/wireless/realtek/rtw89/rtw8852c.c58
-rw-r--r--drivers/net/wireless/realtek/rtw89/rtw8922a.c106
-rw-r--r--drivers/net/wireless/realtek/rtw89/rtw8922a_rfk.c5
-rw-r--r--drivers/net/wireless/realtek/rtw89/sar.c678
-rw-r--r--drivers/net/wireless/realtek/rtw89/sar.h27
-rw-r--r--drivers/net/wireless/realtek/rtw89/ser.c20
-rw-r--r--drivers/net/wireless/realtek/rtw89/txrx.h31
-rw-r--r--drivers/net/wireless/realtek/rtw89/util.c220
-rw-r--r--drivers/net/wireless/realtek/rtw89/util.h13
-rw-r--r--drivers/net/wireless/realtek/rtw89/wow.c10
-rw-r--r--drivers/net/wireless/rsi/rsi_91x_hal.c4
-rw-r--r--drivers/net/wireless/rsi/rsi_91x_mac80211.c6
-rw-r--r--drivers/net/wireless/silabs/wfx/bus.h1
-rw-r--r--drivers/net/wireless/silabs/wfx/bus_sdio.c54
-rw-r--r--drivers/net/wireless/silabs/wfx/bus_spi.c45
-rw-r--r--drivers/net/wireless/silabs/wfx/main.c14
-rw-r--r--drivers/net/wireless/silabs/wfx/sta.c25
-rw-r--r--drivers/net/wireless/silabs/wfx/sta.h3
-rw-r--r--drivers/net/wireless/st/cw1200/main.c2
-rw-r--r--drivers/net/wireless/st/cw1200/pm.c2
-rw-r--r--drivers/net/wireless/st/cw1200/queue.c2
-rw-r--r--drivers/net/wireless/st/cw1200/sta.c6
-rw-r--r--drivers/net/wireless/ti/wl1251/tx.c4
-rw-r--r--drivers/net/wireless/ti/wlcore/main.c4
-rw-r--r--drivers/net/wireless/virtual/mac80211_hwsim.c33
-rw-r--r--drivers/net/wireless/virtual/virt_wifi.c10
-rw-r--r--drivers/net/wireless/zydas/zd1211rw/zd_mac.c2
598 files changed, 92435 insertions, 14731 deletions
diff --git a/drivers/net/wireless/ath/ar5523/ar5523.c b/drivers/net/wireless/ath/ar5523/ar5523.c
index 156f3650c006..96dc2778022a 100644
--- a/drivers/net/wireless/ath/ar5523/ar5523.c
+++ b/drivers/net/wireless/ath/ar5523/ar5523.c
@@ -733,7 +733,7 @@ static void ar5523_data_tx_pkt_put(struct ar5523 *ar)
{
atomic_dec(&ar->tx_nr_total);
if (!atomic_dec_return(&ar->tx_nr_pending)) {
- del_timer(&ar->tx_wd_timer);
+ timer_delete(&ar->tx_wd_timer);
wake_up(&ar->tx_flush_waitq);
}
@@ -1076,7 +1076,7 @@ static void ar5523_stop(struct ieee80211_hw *hw, bool suspend)
ar5523_cmd_write(ar, WDCMSG_TARGET_STOP, NULL, 0, 0);
- del_timer_sync(&ar->tx_wd_timer);
+ timer_delete_sync(&ar->tx_wd_timer);
cancel_work_sync(&ar->tx_wd_work);
cancel_work_sync(&ar->rx_refill_work);
ar5523_cancel_rx_bufs(ar);
diff --git a/drivers/net/wireless/ath/ath10k/ahb.c b/drivers/net/wireless/ath/ath10k/ahb.c
index db9f9ebcb62d..eb8b35b6224d 100644
--- a/drivers/net/wireless/ath/ath10k/ahb.c
+++ b/drivers/net/wireless/ath/ath10k/ahb.c
@@ -497,7 +497,7 @@ static int ath10k_ahb_resource_init(struct ath10k *ar)
ath10k_dbg(ar, ATH10K_DBG_BOOT, "irq: %d\n", ar_ahb->irq);
- ath10k_dbg(ar, ATH10K_DBG_BOOT, "mem: 0x%pK mem_len: %lu gcc mem: 0x%pK tcsr_mem: 0x%pK\n",
+ ath10k_dbg(ar, ATH10K_DBG_BOOT, "mem: 0x%p mem_len: %lu gcc mem: 0x%p tcsr_mem: 0x%p\n",
ar_ahb->mem, ar_ahb->mem_len,
ar_ahb->gcc_mem, ar_ahb->tcsr_mem);
return 0;
diff --git a/drivers/net/wireless/ath/ath10k/bmi.c b/drivers/net/wireless/ath/ath10k/bmi.c
index 9a4f8e815412..48efdc71d54d 100644
--- a/drivers/net/wireless/ath/ath10k/bmi.c
+++ b/drivers/net/wireless/ath/ath10k/bmi.c
@@ -349,7 +349,7 @@ static int ath10k_bmi_lz_data_large(struct ath10k *ar, const void *buffer, u32 l
int ret;
size_t buf_len;
- ath10k_dbg(ar, ATH10K_DBG_BMI, "large bmi lz data buffer 0x%pK length %d\n",
+ ath10k_dbg(ar, ATH10K_DBG_BMI, "large bmi lz data buffer 0x%p length %d\n",
buffer, length);
if (ar->bmi.done_sent) {
@@ -395,7 +395,7 @@ int ath10k_bmi_lz_data(struct ath10k *ar, const void *buffer, u32 length)
u32 txlen;
int ret;
- ath10k_dbg(ar, ATH10K_DBG_BMI, "bmi lz data buffer 0x%pK length %d\n",
+ ath10k_dbg(ar, ATH10K_DBG_BMI, "bmi lz data buffer 0x%p length %d\n",
buffer, length);
if (ar->bmi.done_sent) {
@@ -461,7 +461,7 @@ int ath10k_bmi_fast_download(struct ath10k *ar,
int ret;
ath10k_dbg(ar, ATH10K_DBG_BMI,
- "bmi fast download address 0x%x buffer 0x%pK length %d\n",
+ "bmi fast download address 0x%x buffer 0x%p length %d\n",
address, buffer, length);
ret = ath10k_bmi_lz_stream_start(ar, address);
diff --git a/drivers/net/wireless/ath/ath10k/ce.c b/drivers/net/wireless/ath/ath10k/ce.c
index afae4a8027f8..a89a7491a76c 100644
--- a/drivers/net/wireless/ath/ath10k/ce.c
+++ b/drivers/net/wireless/ath/ath10k/ce.c
@@ -80,7 +80,7 @@ static inline u32 shadow_sr_wr_ind_addr(struct ath10k *ar,
static inline unsigned int
ath10k_set_ring_byte(unsigned int offset,
- struct ath10k_hw_ce_regs_addr_map *addr_map)
+ const struct ath10k_hw_ce_regs_addr_map *addr_map)
{
return ((offset << addr_map->lsb) & addr_map->mask);
}
@@ -203,7 +203,7 @@ static inline void ath10k_ce_src_ring_dmax_set(struct ath10k *ar,
u32 ce_ctrl_addr,
unsigned int n)
{
- struct ath10k_hw_ce_ctrl1 *ctrl_regs = ar->hw_ce_regs->ctrl1_regs;
+ const struct ath10k_hw_ce_ctrl1 *ctrl_regs = ar->hw_ce_regs->ctrl1_regs;
u32 ctrl1_addr = ath10k_ce_read32(ar, ce_ctrl_addr +
ctrl_regs->addr);
@@ -217,7 +217,7 @@ static inline void ath10k_ce_src_ring_byte_swap_set(struct ath10k *ar,
u32 ce_ctrl_addr,
unsigned int n)
{
- struct ath10k_hw_ce_ctrl1 *ctrl_regs = ar->hw_ce_regs->ctrl1_regs;
+ const struct ath10k_hw_ce_ctrl1 *ctrl_regs = ar->hw_ce_regs->ctrl1_regs;
u32 ctrl1_addr = ath10k_ce_read32(ar, ce_ctrl_addr +
ctrl_regs->addr);
@@ -231,7 +231,7 @@ static inline void ath10k_ce_dest_ring_byte_swap_set(struct ath10k *ar,
u32 ce_ctrl_addr,
unsigned int n)
{
- struct ath10k_hw_ce_ctrl1 *ctrl_regs = ar->hw_ce_regs->ctrl1_regs;
+ const struct ath10k_hw_ce_ctrl1 *ctrl_regs = ar->hw_ce_regs->ctrl1_regs;
u32 ctrl1_addr = ath10k_ce_read32(ar, ce_ctrl_addr +
ctrl_regs->addr);
@@ -313,7 +313,7 @@ static inline void ath10k_ce_src_ring_highmark_set(struct ath10k *ar,
u32 ce_ctrl_addr,
unsigned int n)
{
- struct ath10k_hw_ce_dst_src_wm_regs *srcr_wm = ar->hw_ce_regs->wm_srcr;
+ const struct ath10k_hw_ce_dst_src_wm_regs *srcr_wm = ar->hw_ce_regs->wm_srcr;
u32 addr = ath10k_ce_read32(ar, ce_ctrl_addr + srcr_wm->addr);
ath10k_ce_write32(ar, ce_ctrl_addr + srcr_wm->addr,
@@ -325,7 +325,7 @@ static inline void ath10k_ce_src_ring_lowmark_set(struct ath10k *ar,
u32 ce_ctrl_addr,
unsigned int n)
{
- struct ath10k_hw_ce_dst_src_wm_regs *srcr_wm = ar->hw_ce_regs->wm_srcr;
+ const struct ath10k_hw_ce_dst_src_wm_regs *srcr_wm = ar->hw_ce_regs->wm_srcr;
u32 addr = ath10k_ce_read32(ar, ce_ctrl_addr + srcr_wm->addr);
ath10k_ce_write32(ar, ce_ctrl_addr + srcr_wm->addr,
@@ -337,7 +337,7 @@ static inline void ath10k_ce_dest_ring_highmark_set(struct ath10k *ar,
u32 ce_ctrl_addr,
unsigned int n)
{
- struct ath10k_hw_ce_dst_src_wm_regs *dstr_wm = ar->hw_ce_regs->wm_dstr;
+ const struct ath10k_hw_ce_dst_src_wm_regs *dstr_wm = ar->hw_ce_regs->wm_dstr;
u32 addr = ath10k_ce_read32(ar, ce_ctrl_addr + dstr_wm->addr);
ath10k_ce_write32(ar, ce_ctrl_addr + dstr_wm->addr,
@@ -349,7 +349,7 @@ static inline void ath10k_ce_dest_ring_lowmark_set(struct ath10k *ar,
u32 ce_ctrl_addr,
unsigned int n)
{
- struct ath10k_hw_ce_dst_src_wm_regs *dstr_wm = ar->hw_ce_regs->wm_dstr;
+ const struct ath10k_hw_ce_dst_src_wm_regs *dstr_wm = ar->hw_ce_regs->wm_dstr;
u32 addr = ath10k_ce_read32(ar, ce_ctrl_addr + dstr_wm->addr);
ath10k_ce_write32(ar, ce_ctrl_addr + dstr_wm->addr,
@@ -360,7 +360,7 @@ static inline void ath10k_ce_dest_ring_lowmark_set(struct ath10k *ar,
static inline void ath10k_ce_copy_complete_inter_enable(struct ath10k *ar,
u32 ce_ctrl_addr)
{
- struct ath10k_hw_ce_host_ie *host_ie = ar->hw_ce_regs->host_ie;
+ const struct ath10k_hw_ce_host_ie *host_ie = ar->hw_ce_regs->host_ie;
u32 host_ie_addr = ath10k_ce_read32(ar, ce_ctrl_addr +
ar->hw_ce_regs->host_ie_addr);
@@ -372,7 +372,7 @@ static inline void ath10k_ce_copy_complete_inter_enable(struct ath10k *ar,
static inline void ath10k_ce_copy_complete_intr_disable(struct ath10k *ar,
u32 ce_ctrl_addr)
{
- struct ath10k_hw_ce_host_ie *host_ie = ar->hw_ce_regs->host_ie;
+ const struct ath10k_hw_ce_host_ie *host_ie = ar->hw_ce_regs->host_ie;
u32 host_ie_addr = ath10k_ce_read32(ar, ce_ctrl_addr +
ar->hw_ce_regs->host_ie_addr);
@@ -384,7 +384,7 @@ static inline void ath10k_ce_copy_complete_intr_disable(struct ath10k *ar,
static inline void ath10k_ce_watermark_intr_disable(struct ath10k *ar,
u32 ce_ctrl_addr)
{
- struct ath10k_hw_ce_host_wm_regs *wm_regs = ar->hw_ce_regs->wm_regs;
+ const struct ath10k_hw_ce_host_wm_regs *wm_regs = ar->hw_ce_regs->wm_regs;
u32 host_ie_addr = ath10k_ce_read32(ar, ce_ctrl_addr +
ar->hw_ce_regs->host_ie_addr);
@@ -396,7 +396,7 @@ static inline void ath10k_ce_watermark_intr_disable(struct ath10k *ar,
static inline void ath10k_ce_error_intr_disable(struct ath10k *ar,
u32 ce_ctrl_addr)
{
- struct ath10k_hw_ce_misc_regs *misc_regs = ar->hw_ce_regs->misc_regs;
+ const struct ath10k_hw_ce_misc_regs *misc_regs = ar->hw_ce_regs->misc_regs;
u32 misc_ie_addr = ath10k_ce_read32(ar,
ce_ctrl_addr + ar->hw_ce_regs->misc_ie_addr);
@@ -410,7 +410,7 @@ static inline void ath10k_ce_engine_int_status_clear(struct ath10k *ar,
u32 ce_ctrl_addr,
unsigned int mask)
{
- struct ath10k_hw_ce_host_wm_regs *wm_regs = ar->hw_ce_regs->wm_regs;
+ const struct ath10k_hw_ce_host_wm_regs *wm_regs = ar->hw_ce_regs->wm_regs;
ath10k_ce_write32(ar, ce_ctrl_addr + wm_regs->addr, mask);
}
@@ -1230,7 +1230,7 @@ void ath10k_ce_per_engine_service(struct ath10k *ar, unsigned int ce_id)
{
struct ath10k_ce *ce = ath10k_ce_priv(ar);
struct ath10k_ce_pipe *ce_state = &ce->ce_states[ce_id];
- struct ath10k_hw_ce_host_wm_regs *wm_regs = ar->hw_ce_regs->wm_regs;
+ const struct ath10k_hw_ce_host_wm_regs *wm_regs = ar->hw_ce_regs->wm_regs;
u32 ctrl_addr = ce_state->ctrl_addr;
/*
@@ -1388,7 +1388,7 @@ static int ath10k_ce_init_src_ring(struct ath10k *ar,
ath10k_ce_src_ring_highmark_set(ar, ctrl_addr, nentries);
ath10k_dbg(ar, ATH10K_DBG_BOOT,
- "boot init ce src ring id %d entries %d base_addr %pK\n",
+ "boot init ce src ring id %d entries %d base_addr %p\n",
ce_id, nentries, src_ring->base_addr_owner_space);
return 0;
@@ -1426,7 +1426,7 @@ static int ath10k_ce_init_dest_ring(struct ath10k *ar,
ath10k_ce_dest_ring_highmark_set(ar, ctrl_addr, nentries);
ath10k_dbg(ar, ATH10K_DBG_BOOT,
- "boot ce dest ring id %d entries %d base_addr %pK\n",
+ "boot ce dest ring id %d entries %d base_addr %p\n",
ce_id, nentries, dest_ring->base_addr_owner_space);
return 0;
diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c
index b3294287bce1..fe3a8f4a1cc1 100644
--- a/drivers/net/wireless/ath/ath10k/core.c
+++ b/drivers/net/wireless/ath/ath10k/core.c
@@ -1163,9 +1163,12 @@ int ath10k_core_check_dt(struct ath10k *ar)
if (!node)
return -ENOENT;
- of_property_read_string(node, "qcom,ath10k-calibration-variant",
+ of_property_read_string(node, "qcom,calibration-variant",
&variant);
if (!variant)
+ of_property_read_string(node, "qcom,ath10k-calibration-variant",
+ &variant);
+ if (!variant)
return -ENODATA;
if (strscpy(ar->id.bdf_ext, variant, sizeof(ar->id.bdf_ext)) < 0)
@@ -1197,7 +1200,7 @@ static int ath10k_download_fw(struct ath10k *ar)
}
ath10k_dbg(ar, ATH10K_DBG_BOOT,
- "boot uploading firmware image %pK len %d\n",
+ "boot uploading firmware image %p len %d\n",
data, data_len);
/* Check if device supports to download firmware via
@@ -1823,7 +1826,7 @@ static int ath10k_download_and_run_otp(struct ath10k *ar)
if (!ar->running_fw->fw_file.otp_data ||
!ar->running_fw->fw_file.otp_len) {
- ath10k_warn(ar, "Not running otp, calibration will be incorrect (otp-data %pK otp_len %zd)!\n",
+ ath10k_warn(ar, "Not running otp, calibration will be incorrect (otp-data %p otp_len %zd)!\n",
ar->running_fw->fw_file.otp_data,
ar->running_fw->fw_file.otp_len);
return 0;
@@ -2259,7 +2262,9 @@ static int ath10k_core_pre_cal_download(struct ath10k *ar)
"boot did not find a pre calibration file, try DT next: %d\n",
ret);
- ret = ath10k_download_cal_dt(ar, "qcom,ath10k-pre-calibration-data");
+ ret = ath10k_download_cal_dt(ar, "qcom,pre-calibration-data");
+ if (ret == -ENOENT)
+ ret = ath10k_download_cal_dt(ar, "qcom,ath10k-pre-calibration-data");
if (ret) {
ath10k_dbg(ar, ATH10K_DBG_BOOT,
"unable to load pre cal data from DT: %d\n", ret);
@@ -2337,7 +2342,9 @@ static int ath10k_download_cal_data(struct ath10k *ar)
"boot did not find a calibration file, try DT next: %d\n",
ret);
- ret = ath10k_download_cal_dt(ar, "qcom,ath10k-calibration-data");
+ ret = ath10k_download_cal_dt(ar, "qcom,calibration-data");
+ if (ret == -ENOENT)
+ ret = ath10k_download_cal_dt(ar, "qcom,ath10k-calibration-data");
if (ret == 0) {
ar->cal_mode = ATH10K_CAL_MODE_DT;
goto done;
diff --git a/drivers/net/wireless/ath/ath10k/debug.c b/drivers/net/wireless/ath/ath10k/debug.c
index 35bfe7232e95..a0c1afeda4dd 100644
--- a/drivers/net/wireless/ath/ath10k/debug.c
+++ b/drivers/net/wireless/ath/ath10k/debug.c
@@ -1751,7 +1751,7 @@ void ath10k_debug_stop(struct ath10k *ar)
/* Must not use _sync to avoid deadlock, we do that in
* ath10k_debug_destroy(). The check for htt_stats_mask is to avoid
- * warning from del_timer().
+ * warning from timer_delete().
*/
if (ar->debug.htt_stats_mask != 0)
cancel_delayed_work(&ar->debug.htt_stats_dwork);
diff --git a/drivers/net/wireless/ath/ath10k/htc.c b/drivers/net/wireless/ath/ath10k/htc.c
index a6e21ce90bad..2da08dfebd3e 100644
--- a/drivers/net/wireless/ath/ath10k/htc.c
+++ b/drivers/net/wireless/ath/ath10k/htc.c
@@ -34,7 +34,7 @@ static struct sk_buff *ath10k_htc_build_tx_ctrl_skb(void *ar)
skb_cb = ATH10K_SKB_CB(skb);
memset(skb_cb, 0, sizeof(*skb_cb));
- ath10k_dbg(ar, ATH10K_DBG_HTC, "%s: skb %pK\n", __func__, skb);
+ ath10k_dbg(ar, ATH10K_DBG_HTC, "%s: skb %p\n", __func__, skb);
return skb;
}
@@ -54,7 +54,7 @@ void ath10k_htc_notify_tx_completion(struct ath10k_htc_ep *ep,
struct ath10k *ar = ep->htc->ar;
struct ath10k_htc_hdr *hdr;
- ath10k_dbg(ar, ATH10K_DBG_HTC, "%s: ep %d skb %pK\n", __func__,
+ ath10k_dbg(ar, ATH10K_DBG_HTC, "%s: ep %d skb %p\n", __func__,
ep->eid, skb);
/* A corner case where the copy completion is reaching to host but still
@@ -515,7 +515,7 @@ void ath10k_htc_rx_completion_handler(struct ath10k *ar, struct sk_buff *skb)
/* zero length packet with trailer data, just drop these */
goto out;
- ath10k_dbg(ar, ATH10K_DBG_HTC, "htc rx completion ep %d skb %pK\n",
+ ath10k_dbg(ar, ATH10K_DBG_HTC, "htc rx completion ep %d skb %p\n",
eid, skb);
ep->ep_ops.ep_rx_complete(ar, skb);
diff --git a/drivers/net/wireless/ath/ath10k/htt_rx.c b/drivers/net/wireless/ath/ath10k/htt_rx.c
index 7d28ae5453cf..52981052e211 100644
--- a/drivers/net/wireless/ath/ath10k/htt_rx.c
+++ b/drivers/net/wireless/ath/ath10k/htt_rx.c
@@ -287,7 +287,7 @@ void ath10k_htt_rx_free(struct ath10k_htt *htt)
if (htt->ar->bus_param.dev_type == ATH10K_DEV_TYPE_HL)
return;
- del_timer_sync(&htt->rx_ring.refill_retry_timer);
+ timer_delete_sync(&htt->rx_ring.refill_retry_timer);
skb_queue_purge(&htt->rx_msdus_q);
skb_queue_purge(&htt->rx_in_ord_compl_q);
@@ -1373,7 +1373,7 @@ static void ath10k_process_rx(struct ath10k *ar, struct sk_buff *skb)
}
ath10k_dbg(ar, ATH10K_DBG_DATA,
- "rx skb %pK len %u peer %pM %s %s sn %u %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 %p len %u peer %pM %s %s sn %u %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",
skb,
skb->len,
ieee80211_get_SA(hdr),
diff --git a/drivers/net/wireless/ath/ath10k/hw.c b/drivers/net/wireless/ath/ath10k/hw.c
index 8fafe096adff..84b35a22fc23 100644
--- a/drivers/net/wireless/ath/ath10k/hw.c
+++ b/drivers/net/wireless/ath/ath10k/hw.c
@@ -212,40 +212,40 @@ const struct ath10k_hw_regs wcn3990_regs = {
.pcie_intr_fw_mask = 0x00100000,
};
-static struct ath10k_hw_ce_regs_addr_map wcn3990_src_ring = {
+static const struct ath10k_hw_ce_regs_addr_map wcn3990_src_ring = {
.msb = 0x00000010,
.lsb = 0x00000010,
.mask = GENMASK(17, 17),
};
-static struct ath10k_hw_ce_regs_addr_map wcn3990_dst_ring = {
+static const struct ath10k_hw_ce_regs_addr_map wcn3990_dst_ring = {
.msb = 0x00000012,
.lsb = 0x00000012,
.mask = GENMASK(18, 18),
};
-static struct ath10k_hw_ce_regs_addr_map wcn3990_dmax = {
+static const struct ath10k_hw_ce_regs_addr_map wcn3990_dmax = {
.msb = 0x00000000,
.lsb = 0x00000000,
.mask = GENMASK(15, 0),
};
-static struct ath10k_hw_ce_ctrl1 wcn3990_ctrl1 = {
+static const struct ath10k_hw_ce_ctrl1 wcn3990_ctrl1 = {
.addr = 0x00000018,
.src_ring = &wcn3990_src_ring,
.dst_ring = &wcn3990_dst_ring,
.dmax = &wcn3990_dmax,
};
-static struct ath10k_hw_ce_regs_addr_map wcn3990_host_ie_cc = {
+static const struct ath10k_hw_ce_regs_addr_map wcn3990_host_ie_cc = {
.mask = GENMASK(0, 0),
};
-static struct ath10k_hw_ce_host_ie wcn3990_host_ie = {
+static const struct ath10k_hw_ce_host_ie wcn3990_host_ie = {
.copy_complete = &wcn3990_host_ie_cc,
};
-static struct ath10k_hw_ce_host_wm_regs wcn3990_wm_reg = {
+static const struct ath10k_hw_ce_host_wm_regs wcn3990_wm_reg = {
.dstr_lmask = 0x00000010,
.dstr_hmask = 0x00000008,
.srcr_lmask = 0x00000004,
@@ -255,7 +255,7 @@ static struct ath10k_hw_ce_host_wm_regs wcn3990_wm_reg = {
.addr = 0x00000030,
};
-static struct ath10k_hw_ce_misc_regs wcn3990_misc_reg = {
+static const struct ath10k_hw_ce_misc_regs wcn3990_misc_reg = {
.axi_err = 0x00000100,
.dstr_add_err = 0x00000200,
.srcr_len_err = 0x00000100,
@@ -266,19 +266,19 @@ static struct ath10k_hw_ce_misc_regs wcn3990_misc_reg = {
.addr = 0x00000038,
};
-static struct ath10k_hw_ce_regs_addr_map wcn3990_src_wm_low = {
+static const struct ath10k_hw_ce_regs_addr_map wcn3990_src_wm_low = {
.msb = 0x00000000,
.lsb = 0x00000010,
.mask = GENMASK(31, 16),
};
-static struct ath10k_hw_ce_regs_addr_map wcn3990_src_wm_high = {
+static const struct ath10k_hw_ce_regs_addr_map wcn3990_src_wm_high = {
.msb = 0x0000000f,
.lsb = 0x00000000,
.mask = GENMASK(15, 0),
};
-static struct ath10k_hw_ce_dst_src_wm_regs wcn3990_wm_src_ring = {
+static const struct ath10k_hw_ce_dst_src_wm_regs wcn3990_wm_src_ring = {
.addr = 0x0000004c,
.low_rst = 0x00000000,
.high_rst = 0x00000000,
@@ -286,18 +286,18 @@ static struct ath10k_hw_ce_dst_src_wm_regs wcn3990_wm_src_ring = {
.wm_high = &wcn3990_src_wm_high,
};
-static struct ath10k_hw_ce_regs_addr_map wcn3990_dst_wm_low = {
+static const struct ath10k_hw_ce_regs_addr_map wcn3990_dst_wm_low = {
.lsb = 0x00000010,
.mask = GENMASK(31, 16),
};
-static struct ath10k_hw_ce_regs_addr_map wcn3990_dst_wm_high = {
+static const struct ath10k_hw_ce_regs_addr_map wcn3990_dst_wm_high = {
.msb = 0x0000000f,
.lsb = 0x00000000,
.mask = GENMASK(15, 0),
};
-static struct ath10k_hw_ce_dst_src_wm_regs wcn3990_wm_dst_ring = {
+static const struct ath10k_hw_ce_dst_src_wm_regs wcn3990_wm_dst_ring = {
.addr = 0x00000050,
.low_rst = 0x00000000,
.high_rst = 0x00000000,
@@ -305,7 +305,7 @@ static struct ath10k_hw_ce_dst_src_wm_regs wcn3990_wm_dst_ring = {
.wm_high = &wcn3990_dst_wm_high,
};
-static struct ath10k_hw_ce_ctrl1_upd wcn3990_ctrl1_upd = {
+static const struct ath10k_hw_ce_ctrl1_upd wcn3990_ctrl1_upd = {
.shift = 19,
.mask = 0x00080000,
.enable = 0x00000000,
@@ -344,25 +344,25 @@ const struct ath10k_hw_values wcn3990_values = {
.ce_desc_meta_data_lsb = 4,
};
-static struct ath10k_hw_ce_regs_addr_map qcax_src_ring = {
+static const struct ath10k_hw_ce_regs_addr_map qcax_src_ring = {
.msb = 0x00000010,
.lsb = 0x00000010,
.mask = GENMASK(16, 16),
};
-static struct ath10k_hw_ce_regs_addr_map qcax_dst_ring = {
+static const struct ath10k_hw_ce_regs_addr_map qcax_dst_ring = {
.msb = 0x00000011,
.lsb = 0x00000011,
.mask = GENMASK(17, 17),
};
-static struct ath10k_hw_ce_regs_addr_map qcax_dmax = {
+static const struct ath10k_hw_ce_regs_addr_map qcax_dmax = {
.msb = 0x0000000f,
.lsb = 0x00000000,
.mask = GENMASK(15, 0),
};
-static struct ath10k_hw_ce_ctrl1 qcax_ctrl1 = {
+static const struct ath10k_hw_ce_ctrl1 qcax_ctrl1 = {
.addr = 0x00000010,
.hw_mask = 0x0007ffff,
.sw_mask = 0x0007ffff,
@@ -375,31 +375,31 @@ static struct ath10k_hw_ce_ctrl1 qcax_ctrl1 = {
.dmax = &qcax_dmax,
};
-static struct ath10k_hw_ce_regs_addr_map qcax_cmd_halt_status = {
+static const struct ath10k_hw_ce_regs_addr_map qcax_cmd_halt_status = {
.msb = 0x00000003,
.lsb = 0x00000003,
.mask = GENMASK(3, 3),
};
-static struct ath10k_hw_ce_cmd_halt qcax_cmd_halt = {
+static const struct ath10k_hw_ce_cmd_halt qcax_cmd_halt = {
.msb = 0x00000000,
.mask = GENMASK(0, 0),
.status_reset = 0x00000000,
.status = &qcax_cmd_halt_status,
};
-static struct ath10k_hw_ce_regs_addr_map qcax_host_ie_cc = {
+static const struct ath10k_hw_ce_regs_addr_map qcax_host_ie_cc = {
.msb = 0x00000000,
.lsb = 0x00000000,
.mask = GENMASK(0, 0),
};
-static struct ath10k_hw_ce_host_ie qcax_host_ie = {
+static const struct ath10k_hw_ce_host_ie qcax_host_ie = {
.copy_complete_reset = 0x00000000,
.copy_complete = &qcax_host_ie_cc,
};
-static struct ath10k_hw_ce_host_wm_regs qcax_wm_reg = {
+static const struct ath10k_hw_ce_host_wm_regs qcax_wm_reg = {
.dstr_lmask = 0x00000010,
.dstr_hmask = 0x00000008,
.srcr_lmask = 0x00000004,
@@ -409,7 +409,7 @@ static struct ath10k_hw_ce_host_wm_regs qcax_wm_reg = {
.addr = 0x00000030,
};
-static struct ath10k_hw_ce_misc_regs qcax_misc_reg = {
+static const struct ath10k_hw_ce_misc_regs qcax_misc_reg = {
.axi_err = 0x00000400,
.dstr_add_err = 0x00000200,
.srcr_len_err = 0x00000100,
@@ -420,19 +420,19 @@ static struct ath10k_hw_ce_misc_regs qcax_misc_reg = {
.addr = 0x00000038,
};
-static struct ath10k_hw_ce_regs_addr_map qcax_src_wm_low = {
+static const struct ath10k_hw_ce_regs_addr_map qcax_src_wm_low = {
.msb = 0x0000001f,
.lsb = 0x00000010,
.mask = GENMASK(31, 16),
};
-static struct ath10k_hw_ce_regs_addr_map qcax_src_wm_high = {
+static const struct ath10k_hw_ce_regs_addr_map qcax_src_wm_high = {
.msb = 0x0000000f,
.lsb = 0x00000000,
.mask = GENMASK(15, 0),
};
-static struct ath10k_hw_ce_dst_src_wm_regs qcax_wm_src_ring = {
+static const struct ath10k_hw_ce_dst_src_wm_regs qcax_wm_src_ring = {
.addr = 0x0000004c,
.low_rst = 0x00000000,
.high_rst = 0x00000000,
@@ -440,18 +440,18 @@ static struct ath10k_hw_ce_dst_src_wm_regs qcax_wm_src_ring = {
.wm_high = &qcax_src_wm_high,
};
-static struct ath10k_hw_ce_regs_addr_map qcax_dst_wm_low = {
+static const struct ath10k_hw_ce_regs_addr_map qcax_dst_wm_low = {
.lsb = 0x00000010,
.mask = GENMASK(31, 16),
};
-static struct ath10k_hw_ce_regs_addr_map qcax_dst_wm_high = {
+static const struct ath10k_hw_ce_regs_addr_map qcax_dst_wm_high = {
.msb = 0x0000000f,
.lsb = 0x00000000,
.mask = GENMASK(15, 0),
};
-static struct ath10k_hw_ce_dst_src_wm_regs qcax_wm_dst_ring = {
+static const struct ath10k_hw_ce_dst_src_wm_regs qcax_wm_dst_ring = {
.addr = 0x00000050,
.low_rst = 0x00000000,
.high_rst = 0x00000000,
diff --git a/drivers/net/wireless/ath/ath10k/hw.h b/drivers/net/wireless/ath/ath10k/hw.h
index 442091c6dfd2..7ffa1fbe2874 100644
--- a/drivers/net/wireless/ath/ath10k/hw.h
+++ b/drivers/net/wireless/ath/ath10k/hw.h
@@ -289,19 +289,22 @@ struct ath10k_hw_ce_ctrl1 {
u32 sw_wr_mask;
u32 reset_mask;
u32 reset;
- struct ath10k_hw_ce_regs_addr_map *src_ring;
- struct ath10k_hw_ce_regs_addr_map *dst_ring;
- struct ath10k_hw_ce_regs_addr_map *dmax; };
+ const struct ath10k_hw_ce_regs_addr_map *src_ring;
+ const struct ath10k_hw_ce_regs_addr_map *dst_ring;
+ const struct ath10k_hw_ce_regs_addr_map *dmax;
+};
struct ath10k_hw_ce_cmd_halt {
u32 status_reset;
u32 msb;
u32 mask;
- struct ath10k_hw_ce_regs_addr_map *status; };
+ const struct ath10k_hw_ce_regs_addr_map *status;
+};
struct ath10k_hw_ce_host_ie {
u32 copy_complete_reset;
- struct ath10k_hw_ce_regs_addr_map *copy_complete; };
+ const struct ath10k_hw_ce_regs_addr_map *copy_complete;
+};
struct ath10k_hw_ce_host_wm_regs {
u32 dstr_lmask;
@@ -328,8 +331,9 @@ struct ath10k_hw_ce_dst_src_wm_regs {
u32 addr;
u32 low_rst;
u32 high_rst;
- struct ath10k_hw_ce_regs_addr_map *wm_low;
- struct ath10k_hw_ce_regs_addr_map *wm_high; };
+ const struct ath10k_hw_ce_regs_addr_map *wm_low;
+ const struct ath10k_hw_ce_regs_addr_map *wm_high;
+};
struct ath10k_hw_ce_ctrl1_upd {
u32 shift;
@@ -355,14 +359,14 @@ struct ath10k_hw_ce_regs {
u32 ce_rri_low;
u32 ce_rri_high;
u32 host_ie_addr;
- struct ath10k_hw_ce_host_wm_regs *wm_regs;
- struct ath10k_hw_ce_misc_regs *misc_regs;
- struct ath10k_hw_ce_ctrl1 *ctrl1_regs;
- struct ath10k_hw_ce_cmd_halt *cmd_halt;
- struct ath10k_hw_ce_host_ie *host_ie;
- struct ath10k_hw_ce_dst_src_wm_regs *wm_srcr;
- struct ath10k_hw_ce_dst_src_wm_regs *wm_dstr;
- struct ath10k_hw_ce_ctrl1_upd *upd;
+ const struct ath10k_hw_ce_host_wm_regs *wm_regs;
+ const struct ath10k_hw_ce_misc_regs *misc_regs;
+ const struct ath10k_hw_ce_ctrl1 *ctrl1_regs;
+ const struct ath10k_hw_ce_cmd_halt *cmd_halt;
+ const struct ath10k_hw_ce_host_ie *host_ie;
+ const struct ath10k_hw_ce_dst_src_wm_regs *wm_srcr;
+ const struct ath10k_hw_ce_dst_src_wm_regs *wm_dstr;
+ const struct ath10k_hw_ce_ctrl1_upd *upd;
};
struct ath10k_hw_values {
diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c
index c61b95a928da..8c7ffea0fa44 100644
--- a/drivers/net/wireless/ath/ath10k/mac.c
+++ b/drivers/net/wireless/ath/ath10k/mac.c
@@ -875,7 +875,7 @@ static void ath10k_peer_map_cleanup(struct ath10k *ar, struct ath10k_peer *peer)
*/
for (i = 0; i < ARRAY_SIZE(ar->peer_map); i++) {
if (ar->peer_map[i] == peer) {
- ath10k_warn(ar, "removing stale peer_map entry for %pM (ptr %pK idx %d)\n",
+ ath10k_warn(ar, "removing stale peer_map entry for %pM (ptr %p idx %d)\n",
peer->addr, peer, i);
ar->peer_map[i] = NULL;
}
@@ -4063,7 +4063,7 @@ static int ath10k_mac_tx(struct ath10k *ar,
if (!noque_offchan && info->flags & IEEE80211_TX_CTL_TX_OFFCHAN) {
if (!ath10k_mac_tx_frm_has_freq(ar)) {
- ath10k_dbg(ar, ATH10K_DBG_MAC, "mac queued offchannel skb %pK len %d\n",
+ ath10k_dbg(ar, ATH10K_DBG_MAC, "mac queued offchannel skb %p len %d\n",
skb, skb->len);
skb_queue_tail(&ar->offchan_tx_queue, skb);
@@ -4126,7 +4126,7 @@ void ath10k_offchan_tx_work(struct work_struct *work)
mutex_lock(&ar->conf_mutex);
- ath10k_dbg(ar, ATH10K_DBG_MAC, "mac offchannel skb %pK len %d\n",
+ ath10k_dbg(ar, ATH10K_DBG_MAC, "mac offchannel skb %p len %d\n",
skb, skb->len);
hdr = (struct ieee80211_hdr *)skb->data;
@@ -4181,7 +4181,7 @@ void ath10k_offchan_tx_work(struct work_struct *work)
time_left =
wait_for_completion_timeout(&ar->offchan_tx_completed, 3 * HZ);
if (time_left == 0)
- ath10k_warn(ar, "timed out waiting for offchannel skb %pK, len: %d\n",
+ ath10k_warn(ar, "timed out waiting for offchannel skb %p, len: %d\n",
skb, skb->len);
if (!peer && tmp_peer_created) {
@@ -7604,7 +7604,7 @@ static int ath10k_sta_state(struct ieee80211_hw *hw,
* Existing station deletion.
*/
ath10k_dbg(ar, ATH10K_DBG_STA,
- "mac vdev %d peer delete %pM sta %pK (sta gone)\n",
+ "mac vdev %d peer delete %pM sta %p (sta gone)\n",
arvif->vdev_id, sta->addr, sta);
if (sta->tdls) {
@@ -7631,7 +7631,7 @@ static int ath10k_sta_state(struct ieee80211_hw *hw,
continue;
if (peer->sta == sta) {
- ath10k_warn(ar, "found sta peer %pM (ptr %pK id %d) entry on vdev %i after it was supposedly removed\n",
+ ath10k_warn(ar, "found sta peer %pM (ptr %p id %d) entry on vdev %i after it was supposedly removed\n",
sta->addr, peer, i, arvif->vdev_id);
peer->sta = NULL;
@@ -8811,7 +8811,7 @@ ath10k_mac_op_add_chanctx(struct ieee80211_hw *hw,
struct ath10k *ar = hw->priv;
ath10k_dbg(ar, ATH10K_DBG_MAC,
- "mac chanctx add freq %u width %d ptr %pK\n",
+ "mac chanctx add freq %u width %d ptr %p\n",
ctx->def.chan->center_freq, ctx->def.width, ctx);
mutex_lock(&ar->conf_mutex);
@@ -8835,7 +8835,7 @@ ath10k_mac_op_remove_chanctx(struct ieee80211_hw *hw,
struct ath10k *ar = hw->priv;
ath10k_dbg(ar, ATH10K_DBG_MAC,
- "mac chanctx remove freq %u width %d ptr %pK\n",
+ "mac chanctx remove freq %u width %d ptr %p\n",
ctx->def.chan->center_freq, ctx->def.width, ctx);
mutex_lock(&ar->conf_mutex);
@@ -8900,7 +8900,7 @@ ath10k_mac_op_change_chanctx(struct ieee80211_hw *hw,
mutex_lock(&ar->conf_mutex);
ath10k_dbg(ar, ATH10K_DBG_MAC,
- "mac chanctx change freq %u width %d ptr %pK changed %x\n",
+ "mac chanctx change freq %u width %d ptr %p changed %x\n",
ctx->def.chan->center_freq, ctx->def.width, ctx, changed);
/* This shouldn't really happen because channel switching should use
@@ -8959,7 +8959,7 @@ ath10k_mac_op_assign_vif_chanctx(struct ieee80211_hw *hw,
mutex_lock(&ar->conf_mutex);
ath10k_dbg(ar, ATH10K_DBG_MAC,
- "mac chanctx assign ptr %pK vdev_id %i\n",
+ "mac chanctx assign ptr %p vdev_id %i\n",
ctx, arvif->vdev_id);
if (WARN_ON(arvif->is_started)) {
@@ -9039,7 +9039,7 @@ ath10k_mac_op_unassign_vif_chanctx(struct ieee80211_hw *hw,
mutex_lock(&ar->conf_mutex);
ath10k_dbg(ar, ATH10K_DBG_MAC,
- "mac chanctx unassign ptr %pK vdev_id %i\n",
+ "mac chanctx unassign ptr %p vdev_id %i\n",
ctx, arvif->vdev_id);
WARN_ON(!arvif->is_started);
diff --git a/drivers/net/wireless/ath/ath10k/pci.c b/drivers/net/wireless/ath/ath10k/pci.c
index c52a16f8078f..20ec0a6d0f71 100644
--- a/drivers/net/wireless/ath/ath10k/pci.c
+++ b/drivers/net/wireless/ath/ath10k/pci.c
@@ -619,7 +619,7 @@ static void ath10k_pci_sleep_sync(struct ath10k *ar)
return;
}
- del_timer_sync(&ar_pci->ps_timer);
+ timer_delete_sync(&ar_pci->ps_timer);
spin_lock_irqsave(&ar_pci->ps_lock, flags);
WARN_ON(ar_pci->ps_wake_refcount > 0);
@@ -1817,7 +1817,7 @@ static void ath10k_pci_rx_retry_sync(struct ath10k *ar)
{
struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
- del_timer_sync(&ar_pci->rx_post_retry);
+ timer_delete_sync(&ar_pci->rx_post_retry);
}
int ath10k_pci_hif_map_service_to_pipe(struct ath10k *ar, u16 service_id,
@@ -3411,7 +3411,7 @@ static int ath10k_pci_claim(struct ath10k *ar)
goto err_region;
}
- ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot pci_mem 0x%pK\n", ar_pci->mem);
+ ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot pci_mem 0x%p\n", ar_pci->mem);
return 0;
err_region:
diff --git a/drivers/net/wireless/ath/ath10k/sdio.c b/drivers/net/wireless/ath/ath10k/sdio.c
index 6805357ee29e..f3212eab56a1 100644
--- a/drivers/net/wireless/ath/ath10k/sdio.c
+++ b/drivers/net/wireless/ath/ath10k/sdio.c
@@ -1621,7 +1621,7 @@ static void ath10k_sdio_hif_power_down(struct ath10k *ar)
ath10k_dbg(ar, ATH10K_DBG_BOOT, "sdio power off\n");
- del_timer_sync(&ar_sdio->sleep_timer);
+ timer_delete_sync(&ar_sdio->sleep_timer);
ath10k_sdio_set_mbox_sleep(ar, true);
/* Disable the card */
@@ -1844,7 +1844,7 @@ static int ath10k_sdio_get_htt_tx_complete(struct ath10k *ar)
ret = ath10k_sdio_diag_read32(ar, addr, &val);
if (ret) {
ath10k_warn(ar,
- "unable to read hi_acs_flags for htt tx comple : %d\n", ret);
+ "unable to read hi_acs_flags for htt tx complete: %d\n", ret);
return ret;
}
diff --git a/drivers/net/wireless/ath/ath10k/snoc.c b/drivers/net/wireless/ath/ath10k/snoc.c
index d436a874cd5a..866bad2db334 100644
--- a/drivers/net/wireless/ath/ath10k/snoc.c
+++ b/drivers/net/wireless/ath/ath10k/snoc.c
@@ -911,7 +911,7 @@ static void ath10k_snoc_buffer_cleanup(struct ath10k *ar)
struct ath10k_snoc_pipe *pipe_info;
int pipe_num;
- del_timer_sync(&ar_snoc->rx_post_retry);
+ timer_delete_sync(&ar_snoc->rx_post_retry);
for (pipe_num = 0; pipe_num < CE_COUNT; pipe_num++) {
pipe_info = &ar_snoc->pipe_info[pipe_num];
ath10k_snoc_rx_pipe_cleanup(pipe_info);
diff --git a/drivers/net/wireless/ath/ath10k/testmode.c b/drivers/net/wireless/ath/ath10k/testmode.c
index 7a9b9bbcdbfc..3fcefc55b74f 100644
--- a/drivers/net/wireless/ath/ath10k/testmode.c
+++ b/drivers/net/wireless/ath/ath10k/testmode.c
@@ -35,7 +35,7 @@ bool ath10k_tm_event_wmi(struct ath10k *ar, u32 cmd_id, struct sk_buff *skb)
int ret;
ath10k_dbg(ar, ATH10K_DBG_TESTMODE,
- "testmode event wmi cmd_id %d skb %pK skb->len %d\n",
+ "testmode event wmi cmd_id %d skb %p skb->len %d\n",
cmd_id, skb, skb->len);
ath10k_dbg_dump(ar, ATH10K_DBG_TESTMODE, NULL, "", skb->data, skb->len);
@@ -397,7 +397,7 @@ static int ath10k_tm_cmd_wmi(struct ath10k *ar, struct nlattr *tb[])
cmd_id = nla_get_u32(tb[ATH10K_TM_ATTR_WMI_CMDID]);
ath10k_dbg(ar, ATH10K_DBG_TESTMODE,
- "testmode cmd wmi cmd_id %d buf %pK buf_len %d\n",
+ "testmode cmd wmi cmd_id %d buf %p buf_len %d\n",
cmd_id, buf, buf_len);
ath10k_dbg_dump(ar, ATH10K_DBG_TESTMODE, NULL, "", buf, buf_len);
diff --git a/drivers/net/wireless/ath/ath10k/txrx.c b/drivers/net/wireless/ath/ath10k/txrx.c
index da3bc35e41aa..493bfb410aff 100644
--- a/drivers/net/wireless/ath/ath10k/txrx.c
+++ b/drivers/net/wireless/ath/ath10k/txrx.c
@@ -35,7 +35,7 @@ static void ath10k_report_offchan_tx(struct ath10k *ar, struct sk_buff *skb)
complete(&ar->offchan_tx_completed);
ar->offchan_tx_skb = NULL; /* just for sanity */
- ath10k_dbg(ar, ATH10K_DBG_HTT, "completed offchannel skb %pK\n", skb);
+ ath10k_dbg(ar, ATH10K_DBG_HTT, "completed offchannel skb %p\n", skb);
out:
spin_unlock_bh(&ar->data_lock);
}
diff --git a/drivers/net/wireless/ath/ath10k/usb.c b/drivers/net/wireless/ath/ath10k/usb.c
index 3b51b7f52130..1732a4f98418 100644
--- a/drivers/net/wireless/ath/ath10k/usb.c
+++ b/drivers/net/wireless/ath/ath10k/usb.c
@@ -131,7 +131,7 @@ static void ath10k_usb_recv_complete(struct urb *urb)
int status = 0;
ath10k_dbg(ar, ATH10K_DBG_USB_BULK,
- "usb recv pipe %d stat %d len %d urb 0x%pK\n",
+ "usb recv pipe %d stat %d len %d urb 0x%p\n",
pipe->logical_pipe_num, urb->status, urb->actual_length,
urb);
@@ -230,7 +230,7 @@ static void ath10k_usb_post_recv_transfers(struct ath10k *ar,
ath10k_usb_recv_complete, urb_context);
ath10k_dbg(ar, ATH10K_DBG_USB_BULK,
- "usb bulk recv submit %d 0x%x ep 0x%2.2x len %d buf 0x%pK\n",
+ "usb bulk recv submit %d 0x%x ep 0x%2.2x len %d buf 0x%p\n",
recv_pipe->logical_pipe_num,
recv_pipe->usb_pipe_handle, recv_pipe->ep_address,
ATH10K_USB_RX_BUFFER_SIZE, urb_context->skb);
diff --git a/drivers/net/wireless/ath/ath10k/wmi.c b/drivers/net/wireless/ath/ath10k/wmi.c
index 5e061f7525a6..df6a24f8f8d5 100644
--- a/drivers/net/wireless/ath/ath10k/wmi.c
+++ b/drivers/net/wireless/ath/ath10k/wmi.c
@@ -2029,7 +2029,7 @@ ath10k_wmi_op_gen_mgmt_tx(struct ath10k *ar, struct sk_buff *msdu)
ether_addr_copy(cmd->hdr.peer_macaddr.addr, ieee80211_get_DA(hdr));
memcpy(cmd->buf, msdu->data, msdu->len);
- ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi mgmt tx skb %pK len %d ftype %02x stype %02x\n",
+ ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi mgmt tx skb %p len %d ftype %02x stype %02x\n",
msdu, skb->len, fc & IEEE80211_FCTL_FTYPE,
fc & IEEE80211_FCTL_STYPE);
trace_ath10k_tx_hdr(ar, skb->data, skb->len);
@@ -2637,7 +2637,7 @@ int ath10k_wmi_event_mgmt_rx(struct ath10k *ar, struct sk_buff *skb)
status->boottime_ns = ktime_get_boottime_ns();
ath10k_dbg(ar, ATH10K_DBG_MGMT,
- "event mgmt rx skb %pK len %d ftype %02x stype %02x\n",
+ "event mgmt rx skb %p len %d ftype %02x stype %02x\n",
skb, skb->len,
fc & IEEE80211_FCTL_FTYPE, fc & IEEE80211_FCTL_STYPE);
diff --git a/drivers/net/wireless/ath/ath11k/Makefile b/drivers/net/wireless/ath/ath11k/Makefile
index 43d2d8ddcdc0..d9092414b362 100644
--- a/drivers/net/wireless/ath/ath11k/Makefile
+++ b/drivers/net/wireless/ath/ath11k/Makefile
@@ -27,6 +27,7 @@ ath11k-$(CONFIG_ATH11K_TRACING) += trace.o
ath11k-$(CONFIG_THERMAL) += thermal.o
ath11k-$(CONFIG_ATH11K_SPECTRAL) += spectral.o
ath11k-$(CONFIG_PM) += wow.o
+ath11k-$(CONFIG_DEV_COREDUMP) += coredump.o
obj-$(CONFIG_ATH11K_AHB) += ath11k_ahb.o
ath11k_ahb-y += ahb.o
diff --git a/drivers/net/wireless/ath/ath11k/ahb.c b/drivers/net/wireless/ath/ath11k/ahb.c
index f2fc04596d48..fde1ce43c499 100644
--- a/drivers/net/wireless/ath/ath11k/ahb.c
+++ b/drivers/net/wireless/ath/ath11k/ahb.c
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: BSD-3-Clause-Clear
/*
* Copyright (c) 2018-2019 The Linux Foundation. All rights reserved.
- * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) 2022-2025 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#include <linux/module.h>
@@ -397,7 +397,7 @@ static void ath11k_ahb_stop(struct ath11k_base *ab)
ath11k_ahb_ce_irqs_disable(ab);
ath11k_ahb_sync_ce_irqs(ab);
ath11k_ahb_kill_tasklets(ab);
- del_timer_sync(&ab->rx_replenish_retry);
+ timer_delete_sync(&ab->rx_replenish_retry);
ath11k_ce_cleanup_pipes(ab);
}
@@ -413,7 +413,7 @@ static int ath11k_ahb_power_up(struct ath11k_base *ab)
return ret;
}
-static void ath11k_ahb_power_down(struct ath11k_base *ab)
+static void ath11k_ahb_power_down(struct ath11k_base *ab, bool is_suspend)
{
struct ath11k_ahb *ab_ahb = ath11k_ahb_priv(ab);
@@ -1280,7 +1280,7 @@ static void ath11k_ahb_remove(struct platform_device *pdev)
struct ath11k_base *ab = platform_get_drvdata(pdev);
if (test_bit(ATH11K_FLAG_QMI_FAIL, &ab->dev_flags)) {
- ath11k_ahb_power_down(ab);
+ ath11k_ahb_power_down(ab, false);
ath11k_debugfs_soc_destroy(ab);
ath11k_qmi_deinit_service(ab);
goto qmi_fail;
@@ -1290,6 +1290,7 @@ static void ath11k_ahb_remove(struct platform_device *pdev)
ath11k_core_deinit(ab);
qmi_fail:
+ ath11k_fw_destroy(ab);
ath11k_ahb_free_resources(ab);
}
@@ -1309,6 +1310,7 @@ static void ath11k_ahb_shutdown(struct platform_device *pdev)
ath11k_core_deinit(ab);
free_resources:
+ ath11k_fw_destroy(ab);
ath11k_ahb_free_resources(ab);
}
diff --git a/drivers/net/wireless/ath/ath11k/ce.c b/drivers/net/wireless/ath/ath11k/ce.c
index e66e86bdec20..9d8efec46508 100644
--- a/drivers/net/wireless/ath/ath11k/ce.c
+++ b/drivers/net/wireless/ath/ath11k/ce.c
@@ -393,11 +393,10 @@ static int ath11k_ce_completed_recv_next(struct ath11k_ce_pipe *pipe,
goto err;
}
+ /* Make sure descriptor is read after the head pointer. */
+ dma_rmb();
+
*nbytes = ath11k_hal_ce_dst_status_get_length(desc);
- if (*nbytes == 0) {
- ret = -EIO;
- goto err;
- }
*skb = pipe->dest_ring->skb[sw_index];
pipe->dest_ring->skb[sw_index] = NULL;
@@ -430,8 +429,8 @@ static void ath11k_ce_recv_process_cb(struct ath11k_ce_pipe *pipe)
dma_unmap_single(ab->dev, ATH11K_SKB_RXCB(skb)->paddr,
max_nbytes, DMA_FROM_DEVICE);
- if (unlikely(max_nbytes < nbytes)) {
- ath11k_warn(ab, "rxed more than expected (nbytes %d, max %d)",
+ if (unlikely(max_nbytes < nbytes || nbytes == 0)) {
+ ath11k_warn(ab, "unexpected rx length (nbytes %d, max %d)",
nbytes, max_nbytes);
dev_kfree_skb_any(skb);
continue;
diff --git a/drivers/net/wireless/ath/ath11k/core.c b/drivers/net/wireless/ath/ath11k/core.c
index c576bbba52bf..2e9f8a5e61e4 100644
--- 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-2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#include <linux/module.h>
@@ -907,12 +907,51 @@ static const struct ath11k_hw_params ath11k_hw_params[] = {
},
};
-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];
-}
+static const struct dmi_system_id ath11k_pm_quirk_table[] = {
+ {
+ .driver_data = (void *)ATH11K_PM_WOW,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "21J4"),
+ },
+ },
+ {
+ .driver_data = (void *)ATH11K_PM_WOW,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "21K4"),
+ },
+ },
+ {
+ .driver_data = (void *)ATH11K_PM_WOW,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "21K6"),
+ },
+ },
+ {
+ .driver_data = (void *)ATH11K_PM_WOW,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "21K8"),
+ },
+ },
+ {
+ .driver_data = (void *)ATH11K_PM_WOW,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "21KA"),
+ },
+ },
+ {
+ .driver_data = (void *)ATH11K_PM_WOW,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "21F9"),
+ },
+ },
+ {}
+};
void ath11k_fw_stats_pdevs_free(struct list_head *head)
{
@@ -972,23 +1011,33 @@ bool ath11k_core_coldboot_cal_support(struct ath11k_base *ab)
return ab->hw_params.coldboot_cal_mm;
}
-int ath11k_core_suspend(struct ath11k_base *ab)
+/* Check if we need to continue with suspend/resume operation.
+ * Return:
+ * a negative value: error happens and don't continue.
+ * 0: no error but don't continue.
+ * positive value: no error and do continue.
+ */
+static int ath11k_core_continue_suspend_resume(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.
+ * so pass 0 as a dummy pdev_id here.
*/
- pdev = ath11k_core_get_single_pdev(ab);
- ar = pdev->ar;
+ ar = ab->pdevs[0].ar;
if (!ar || ar->state != ATH11K_STATE_OFF)
return 0;
+ return 1;
+}
+
+static int ath11k_core_suspend_wow(struct ath11k_base *ab)
+{
+ int ret;
+
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",
@@ -996,7 +1045,10 @@ int ath11k_core_suspend(struct ath11k_base *ab)
return ret;
}
- ret = ath11k_mac_wait_tx_complete(ar);
+ /* So far only single_pdev_only devices can reach here,
+ * so it is valid to handle the first, and the only, pdev.
+ */
+ ret = ath11k_mac_wait_tx_complete(ab->pdevs[0].ar);
if (ret) {
ath11k_warn(ab, "failed to wait tx complete: %d\n", ret);
return ret;
@@ -1029,24 +1081,146 @@ int ath11k_core_suspend(struct ath11k_base *ab)
return 0;
}
+
+static int ath11k_core_suspend_default(struct ath11k_base *ab)
+{
+ int ret;
+
+ 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",
+ ret);
+ return ret;
+ }
+
+ /* So far only single_pdev_only devices can reach here,
+ * so it is valid to handle the first, and the only, pdev.
+ */
+ ret = ath11k_mac_wait_tx_complete(ab->pdevs[0].ar);
+ if (ret) {
+ ath11k_warn(ab, "failed to wait tx complete: %d\n", ret);
+ return ret;
+ }
+
+ ret = ath11k_dp_rx_pktlog_stop(ab, false);
+ if (ret) {
+ ath11k_warn(ab, "failed to stop dp rx pktlog during suspend: %d\n",
+ ret);
+ return ret;
+ }
+
+ ath11k_ce_stop_shadow_timers(ab);
+ ath11k_dp_stop_shadow_timers(ab);
+
+ /* PM framework skips suspend_late/resume_early callbacks
+ * if other devices report errors in their suspend callbacks.
+ * However ath11k_core_resume() would still be called because
+ * here we return success thus kernel put us on dpm_suspended_list.
+ * Since we won't go through a power down/up cycle, there is
+ * no chance to call complete(&ab->restart_completed) in
+ * ath11k_core_restart(), making ath11k_core_resume() timeout.
+ * So call it here to avoid this issue. This also works in case
+ * no error happens thus suspend_late/resume_early get called,
+ * because it will be reinitialized in ath11k_core_resume_early().
+ */
+ complete(&ab->restart_completed);
+
+ return 0;
+}
+
+int ath11k_core_suspend(struct ath11k_base *ab)
+{
+ int ret;
+
+ ret = ath11k_core_continue_suspend_resume(ab);
+ if (ret <= 0)
+ return ret;
+
+ if (ab->actual_pm_policy == ATH11K_PM_WOW)
+ return ath11k_core_suspend_wow(ab);
+
+ return ath11k_core_suspend_default(ab);
+}
EXPORT_SYMBOL(ath11k_core_suspend);
-int ath11k_core_resume(struct ath11k_base *ab)
+int ath11k_core_suspend_late(struct ath11k_base *ab)
{
int ret;
- struct ath11k_pdev *pdev;
+
+ ret = ath11k_core_continue_suspend_resume(ab);
+ if (ret <= 0)
+ return ret;
+
+ if (ab->actual_pm_policy == ATH11K_PM_WOW)
+ return 0;
+
+ ath11k_hif_irq_disable(ab);
+ ath11k_hif_ce_irq_disable(ab);
+
+ ath11k_hif_power_down(ab, true);
+
+ return 0;
+}
+EXPORT_SYMBOL(ath11k_core_suspend_late);
+
+int ath11k_core_resume_early(struct ath11k_base *ab)
+{
+ int ret;
+
+ ret = ath11k_core_continue_suspend_resume(ab);
+ if (ret <= 0)
+ return ret;
+
+ if (ab->actual_pm_policy == ATH11K_PM_WOW)
+ return 0;
+
+ reinit_completion(&ab->restart_completed);
+ ret = ath11k_hif_power_up(ab);
+ if (ret)
+ ath11k_warn(ab, "failed to power up hif during resume: %d\n", ret);
+
+ return ret;
+}
+EXPORT_SYMBOL(ath11k_core_resume_early);
+
+static int ath11k_core_resume_default(struct ath11k_base *ab)
+{
struct ath11k *ar;
+ long time_left;
+ int ret;
- if (!ab->hw_params.supports_suspend)
- return -EOPNOTSUPP;
+ time_left = wait_for_completion_timeout(&ab->restart_completed,
+ ATH11K_RESET_TIMEOUT_HZ);
+ if (time_left == 0) {
+ ath11k_warn(ab, "timeout while waiting for restart complete");
+ return -ETIMEDOUT;
+ }
- /* so far signle_pdev_only chips have supports_suspend as true
- * and only the first pdev is valid.
+ /* So far only single_pdev_only devices can reach here,
+ * so it is valid to handle the first, and the only, pdev.
*/
- pdev = ath11k_core_get_single_pdev(ab);
- ar = pdev->ar;
- if (!ar || ar->state != ATH11K_STATE_OFF)
- return 0;
+ ar = ab->pdevs[0].ar;
+ if (ab->hw_params.current_cc_support &&
+ ar->alpha2[0] != 0 && ar->alpha2[1] != 0) {
+ ret = ath11k_reg_set_cc(ar);
+ if (ret) {
+ ath11k_warn(ab, "failed to set country code during resume: %d\n",
+ ret);
+ return ret;
+ }
+ }
+
+ ret = ath11k_dp_rx_pktlog_start(ab);
+ if (ret)
+ ath11k_warn(ab, "failed to start rx pktlog during resume: %d\n",
+ ret);
+
+ return ret;
+}
+
+static int ath11k_core_resume_wow(struct ath11k_base *ab)
+{
+ int ret;
ret = ath11k_hif_resume(ab);
if (ret) {
@@ -1072,6 +1246,20 @@ int ath11k_core_resume(struct ath11k_base *ab)
return 0;
}
+
+int ath11k_core_resume(struct ath11k_base *ab)
+{
+ int ret;
+
+ ret = ath11k_core_continue_suspend_resume(ab);
+ if (ret <= 0)
+ return ret;
+
+ if (ab->actual_pm_policy == ATH11K_PM_WOW)
+ return ath11k_core_resume_wow(ab);
+
+ return ath11k_core_resume_default(ab);
+}
EXPORT_SYMBOL(ath11k_core_resume);
static void ath11k_core_check_cc_code_bdfext(const struct dmi_header *hdr, void *data)
@@ -1175,9 +1363,12 @@ int ath11k_core_check_dt(struct ath11k_base *ab)
if (!node)
return -ENOENT;
- of_property_read_string(node, "qcom,ath11k-calibration-variant",
+ of_property_read_string(node, "qcom,calibration-variant",
&variant);
if (!variant)
+ of_property_read_string(node, "qcom,ath11k-calibration-variant",
+ &variant);
+ if (!variant)
return -ENODATA;
if (strscpy(ab->qmi.target.bdf_ext, variant, max_len) < 0)
@@ -2047,6 +2238,7 @@ err_hal_srng_deinit:
void ath11k_core_halt(struct ath11k *ar)
{
struct ath11k_base *ab = ar->ab;
+ struct list_head *pos, *n;
lockdep_assert_held(&ar->conf_mutex);
@@ -2056,12 +2248,18 @@ void ath11k_core_halt(struct ath11k *ar)
ath11k_mac_scan_finish(ar);
ath11k_mac_peer_cleanup_all(ar);
cancel_delayed_work_sync(&ar->scan.timeout);
+ cancel_work_sync(&ar->channel_update_work);
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();
- INIT_LIST_HEAD(&ar->arvifs);
+
+ spin_lock_bh(&ar->data_lock);
+ list_for_each_safe(pos, n, &ar->arvifs)
+ list_del_init(pos);
+ spin_unlock_bh(&ar->data_lock);
+
idr_init(&ar->txmgmt_idr);
}
@@ -2201,6 +2399,8 @@ static void ath11k_core_restart(struct work_struct *work)
if (!ab->is_reset)
ath11k_core_post_reconfigure_recovery(ab);
+
+ complete(&ab->restart_completed);
}
static void ath11k_core_reset(struct work_struct *work)
@@ -2257,6 +2457,7 @@ static void ath11k_core_reset(struct work_struct *work)
reinit_completion(&ab->recovery_start);
atomic_set(&ab->recovery_start_count, 0);
+ ath11k_coredump_collect(ab);
ath11k_core_pre_reconfigure_recovery(ab);
reinit_completion(&ab->reconfigure_complete);
@@ -2270,7 +2471,7 @@ static void ath11k_core_reset(struct work_struct *work)
ath11k_hif_irq_disable(ab);
ath11k_hif_ce_irq_disable(ab);
- ath11k_hif_power_down(ab);
+ ath11k_hif_power_down(ab, false);
ath11k_hif_power_up(ab);
ath11k_dbg(ab, ATH11K_DBG_BOOT, "reset started\n");
@@ -2320,10 +2521,62 @@ int ath11k_core_pre_init(struct ath11k_base *ab)
}
EXPORT_SYMBOL(ath11k_core_pre_init);
+static int ath11k_core_pm_notify(struct notifier_block *nb,
+ unsigned long action, void *nouse)
+{
+ struct ath11k_base *ab = container_of(nb, struct ath11k_base,
+ pm_nb);
+
+ switch (action) {
+ case PM_SUSPEND_PREPARE:
+ ab->actual_pm_policy = ab->pm_policy;
+ break;
+ case PM_HIBERNATION_PREPARE:
+ ab->actual_pm_policy = ATH11K_PM_DEFAULT;
+ break;
+ default:
+ break;
+ }
+
+ return NOTIFY_OK;
+}
+
+static int ath11k_core_pm_notifier_register(struct ath11k_base *ab)
+{
+ ab->pm_nb.notifier_call = ath11k_core_pm_notify;
+ return register_pm_notifier(&ab->pm_nb);
+}
+
+void ath11k_core_pm_notifier_unregister(struct ath11k_base *ab)
+{
+ int ret;
+
+ ret = unregister_pm_notifier(&ab->pm_nb);
+ if (ret)
+ /* just warn here, there is nothing can be done in fail case */
+ ath11k_warn(ab, "failed to unregister PM notifier %d\n", ret);
+}
+EXPORT_SYMBOL(ath11k_core_pm_notifier_unregister);
+
int ath11k_core_init(struct ath11k_base *ab)
{
+ const struct dmi_system_id *dmi_id;
int ret;
+ dmi_id = dmi_first_match(ath11k_pm_quirk_table);
+ if (dmi_id)
+ ab->pm_policy = (kernel_ulong_t)dmi_id->driver_data;
+ else
+ ab->pm_policy = ATH11K_PM_DEFAULT;
+
+ ath11k_dbg(ab, ATH11K_DBG_BOOT, "pm policy %u\n", ab->pm_policy);
+
+ ret = ath11k_core_pm_notifier_register(ab);
+ if (ret) {
+ ath11k_err(ab, "failed to register PM notifier: %d\n", ret);
+ return ret;
+ }
+
ret = ath11k_core_soc_create(ab);
if (ret) {
ath11k_err(ab, "failed to create soc core: %d\n", ret);
@@ -2343,10 +2596,10 @@ void ath11k_core_deinit(struct ath11k_base *ab)
mutex_unlock(&ab->core_lock);
- ath11k_hif_power_down(ab);
+ ath11k_hif_power_down(ab, false);
ath11k_mac_destroy(ab);
ath11k_core_soc_destroy(ab);
- ath11k_fw_destroy(ab);
+ ath11k_core_pm_notifier_unregister(ab);
}
EXPORT_SYMBOL(ath11k_core_deinit);
@@ -2393,9 +2646,11 @@ struct ath11k_base *ath11k_core_alloc(struct device *dev, size_t priv_size,
INIT_WORK(&ab->restart_work, ath11k_core_restart);
INIT_WORK(&ab->update_11d_work, ath11k_update_11d);
INIT_WORK(&ab->reset_work, ath11k_core_reset);
+ INIT_WORK(&ab->dump_work, ath11k_coredump_upload);
timer_setup(&ab->rx_replenish_retry, ath11k_ce_rx_replenish_retry, 0);
init_completion(&ab->htc_suspend);
init_completion(&ab->wow.wakeup_completed);
+ init_completion(&ab->restart_completed);
ab->dev = dev;
ab->hif.bus = bus;
diff --git a/drivers/net/wireless/ath/ath11k/core.h b/drivers/net/wireless/ath/ath11k/core.h
index a9dc7fe7765a..339d4fca1ed5 100644
--- a/drivers/net/wireless/ath/ath11k/core.h
+++ b/drivers/net/wireless/ath/ath11k/core.h
@@ -1,7 +1,7 @@
/* SPDX-License-Identifier: BSD-3-Clause-Clear */
/*
* Copyright (c) 2018-2019 The Linux Foundation. All rights reserved.
- * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#ifndef ATH11K_CORE_H
@@ -16,6 +16,7 @@
#include <linux/rhashtable.h>
#include <linux/average.h>
#include <linux/firmware.h>
+#include <linux/suspend.h>
#include "qmi.h"
#include "htc.h"
@@ -32,6 +33,7 @@
#include "spectral.h"
#include "wow.h"
#include "fw.h"
+#include "coredump.h"
#define SM(_v, _f) (((_v) << _f##_LSB) & _f##_MASK)
@@ -370,6 +372,7 @@ struct ath11k_vif {
struct ieee80211_vif *vif;
struct wmi_wmm_params_all_arg wmm_params;
+ struct wmi_wmm_params_all_arg muedca_params;
struct list_head list;
union {
struct {
@@ -685,7 +688,7 @@ struct ath11k {
struct mutex conf_mutex;
/* protects the radio specific data like debug stats, ppdu_stats_info stats,
* vdev_stop_status info, scan data, ath11k_sta info, ath11k_vif info,
- * channel context data, survey info, test mode data.
+ * channel context data, survey info, test mode data, channel_update_queue.
*/
spinlock_t data_lock;
@@ -743,6 +746,9 @@ struct ath11k {
struct completion bss_survey_done;
struct work_struct regd_update_work;
+ struct work_struct channel_update_work;
+ /* protected with data_lock */
+ struct list_head channel_update_queue;
struct work_struct wmi_mgmt_tx_work;
struct sk_buff_head wmi_mgmt_tx_queue;
@@ -887,6 +893,11 @@ struct ath11k_msi_config {
u16 hw_rev;
};
+enum ath11k_pm_policy {
+ ATH11K_PM_DEFAULT,
+ ATH11K_PM_WOW,
+};
+
/* Master structure to hold the hw data which may be used in core module */
struct ath11k_base {
enum ath11k_hw_rev hw_rev;
@@ -900,6 +911,10 @@ struct ath11k_base {
/* HW channel counters frequency value in hertz common to all MACs */
u32 cc_freq_hz;
+ struct ath11k_dump_file_data *dump_data;
+ size_t ath11k_coredump_len;
+ struct work_struct dump_work;
+
struct ath11k_htc htc;
struct ath11k_dp dp;
@@ -1041,6 +1056,8 @@ struct ath11k_base {
DECLARE_BITMAP(fw_features, ATH11K_FW_FEATURE_COUNT);
} fw;
+ struct completion restart_completed;
+
#ifdef CONFIG_NL80211_TESTMODE
struct {
u32 data_pos;
@@ -1049,6 +1066,10 @@ struct ath11k_base {
} testmode;
#endif
+ enum ath11k_pm_policy pm_policy;
+ enum ath11k_pm_policy actual_pm_policy;
+ struct notifier_block pm_nb;
+
/* must be last */
u8 drv_priv[] __aligned(sizeof(void *));
};
@@ -1240,8 +1261,10 @@ 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_early(struct ath11k_base *ab);
int ath11k_core_resume(struct ath11k_base *ab);
int ath11k_core_suspend(struct ath11k_base *ab);
+int ath11k_core_suspend_late(struct ath11k_base *ab);
void ath11k_core_pre_reconfigure_recovery(struct ath11k_base *ab);
bool ath11k_core_coldboot_cal_support(struct ath11k_base *ab);
@@ -1313,4 +1336,6 @@ static inline const char *ath11k_bus_str(enum ath11k_bus bus)
return "unknown";
}
+void ath11k_core_pm_notifier_unregister(struct ath11k_base *ab);
+
#endif /* _CORE_H_ */
diff --git a/drivers/net/wireless/ath/ath11k/coredump.c b/drivers/net/wireless/ath/ath11k/coredump.c
new file mode 100644
index 000000000000..b8bad358cebe
--- /dev/null
+++ b/drivers/net/wireless/ath/ath11k/coredump.c
@@ -0,0 +1,52 @@
+// SPDX-License-Identifier: BSD-3-Clause-Clear
+/*
+ * Copyright (c) 2020 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+#include <linux/devcoredump.h>
+#include "hif.h"
+#include "coredump.h"
+#include "debug.h"
+
+enum
+ath11k_fw_crash_dump_type ath11k_coredump_get_dump_type(int type)
+{
+ enum ath11k_fw_crash_dump_type dump_type;
+
+ switch (type) {
+ case HOST_DDR_REGION_TYPE:
+ dump_type = FW_CRASH_DUMP_REMOTE_MEM_DATA;
+ break;
+ case M3_DUMP_REGION_TYPE:
+ dump_type = FW_CRASH_DUMP_M3_DUMP;
+ break;
+ case PAGEABLE_MEM_REGION_TYPE:
+ dump_type = FW_CRASH_DUMP_PAGEABLE_DATA;
+ break;
+ case BDF_MEM_REGION_TYPE:
+ case CALDB_MEM_REGION_TYPE:
+ dump_type = FW_CRASH_DUMP_NONE;
+ break;
+ default:
+ dump_type = FW_CRASH_DUMP_TYPE_MAX;
+ break;
+ }
+
+ return dump_type;
+}
+EXPORT_SYMBOL(ath11k_coredump_get_dump_type);
+
+void ath11k_coredump_upload(struct work_struct *work)
+{
+ struct ath11k_base *ab = container_of(work, struct ath11k_base, dump_work);
+
+ ath11k_info(ab, "Uploading coredump\n");
+ /* dev_coredumpv() takes ownership of the buffer */
+ dev_coredumpv(ab->dev, ab->dump_data, ab->ath11k_coredump_len, GFP_KERNEL);
+ ab->dump_data = NULL;
+}
+
+void ath11k_coredump_collect(struct ath11k_base *ab)
+{
+ ath11k_hif_coredump_download(ab);
+}
diff --git a/drivers/net/wireless/ath/ath11k/coredump.h b/drivers/net/wireless/ath/ath11k/coredump.h
new file mode 100644
index 000000000000..3960d9385261
--- /dev/null
+++ b/drivers/net/wireless/ath/ath11k/coredump.h
@@ -0,0 +1,79 @@
+/* SPDX-License-Identifier: BSD-3-Clause-Clear */
+/*
+ * Copyright (c) 2020 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+#ifndef _ATH11K_COREDUMP_H_
+#define _ATH11K_COREDUMP_H_
+
+#define ATH11K_FW_CRASH_DUMP_V2 2
+
+enum ath11k_fw_crash_dump_type {
+ FW_CRASH_DUMP_PAGING_DATA,
+ FW_CRASH_DUMP_RDDM_DATA,
+ FW_CRASH_DUMP_REMOTE_MEM_DATA,
+ FW_CRASH_DUMP_PAGEABLE_DATA,
+ FW_CRASH_DUMP_M3_DUMP,
+ FW_CRASH_DUMP_NONE,
+
+ /* keep last */
+ FW_CRASH_DUMP_TYPE_MAX,
+};
+
+#define COREDUMP_TLV_HDR_SIZE 8
+
+struct ath11k_tlv_dump_data {
+ /* see ath11k_fw_crash_dump_type above */
+ __le32 type;
+
+ /* in bytes */
+ __le32 tlv_len;
+
+ /* pad to 32-bit boundaries as needed */
+ u8 tlv_data[];
+} __packed;
+
+struct ath11k_dump_file_data {
+ /* "ATH11K-FW-DUMP" */
+ char df_magic[16];
+ /* total dump len in bytes */
+ __le32 len;
+ /* file dump version */
+ __le32 version;
+ /* pci device id */
+ __le32 chip_id;
+ /* qrtr instance id */
+ __le32 qrtr_id;
+ /* pci domain id */
+ __le32 bus_id;
+ guid_t guid;
+ /* time-of-day stamp */
+ __le64 tv_sec;
+ /* time-of-day stamp, nano-seconds */
+ __le64 tv_nsec;
+ /* room for growth w/out changing binary format */
+ u8 unused[128];
+ u8 data[];
+} __packed;
+
+#ifdef CONFIG_DEV_COREDUMP
+enum ath11k_fw_crash_dump_type ath11k_coredump_get_dump_type(int type);
+void ath11k_coredump_upload(struct work_struct *work);
+void ath11k_coredump_collect(struct ath11k_base *ab);
+#else
+static inline enum
+ath11k_fw_crash_dump_type ath11k_coredump_get_dump_type(int type)
+{
+ return FW_CRASH_DUMP_TYPE_MAX;
+}
+
+static inline void ath11k_coredump_upload(struct work_struct *work)
+{
+}
+
+static inline void ath11k_coredump_collect(struct ath11k_base *ab)
+{
+}
+#endif
+
+#endif
diff --git a/drivers/net/wireless/ath/ath11k/dp.c b/drivers/net/wireless/ath/ath11k/dp.c
index fbf666d0ecf1..3a544e5fefca 100644
--- a/drivers/net/wireless/ath/ath11k/dp.c
+++ b/drivers/net/wireless/ath/ath11k/dp.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-2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#include <crypto/hash.h>
@@ -104,14 +104,12 @@ void ath11k_dp_srng_cleanup(struct ath11k_base *ab, struct dp_srng *ring)
if (!ring->vaddr_unaligned)
return;
- if (ring->cached) {
- dma_unmap_single(ab->dev, ring->paddr_unaligned, ring->size,
- DMA_FROM_DEVICE);
- kfree(ring->vaddr_unaligned);
- } else {
+ if (ring->cached)
+ dma_free_noncoherent(ab->dev, ring->size, ring->vaddr_unaligned,
+ ring->paddr_unaligned, DMA_FROM_DEVICE);
+ else
dma_free_coherent(ab->dev, ring->size, ring->vaddr_unaligned,
ring->paddr_unaligned);
- }
ring->vaddr_unaligned = NULL;
}
@@ -249,25 +247,14 @@ int ath11k_dp_srng_setup(struct ath11k_base *ab, struct dp_srng *ring,
default:
cached = false;
}
-
- if (cached) {
- ring->vaddr_unaligned = kzalloc(ring->size, GFP_KERNEL);
- if (!ring->vaddr_unaligned)
- return -ENOMEM;
-
- ring->paddr_unaligned = dma_map_single(ab->dev,
- ring->vaddr_unaligned,
- ring->size,
- DMA_FROM_DEVICE);
- if (dma_mapping_error(ab->dev, ring->paddr_unaligned)) {
- kfree(ring->vaddr_unaligned);
- ring->vaddr_unaligned = NULL;
- return -ENOMEM;
- }
- }
}
- if (!cached)
+ if (cached)
+ ring->vaddr_unaligned = dma_alloc_noncoherent(ab->dev, ring->size,
+ &ring->paddr_unaligned,
+ DMA_FROM_DEVICE,
+ GFP_KERNEL);
+ else
ring->vaddr_unaligned = dma_alloc_coherent(ab->dev, ring->size,
&ring->paddr_unaligned,
GFP_KERNEL);
@@ -888,7 +875,7 @@ void ath11k_dp_pdev_free(struct ath11k_base *ab)
struct ath11k *ar;
int i;
- del_timer_sync(&ab->mon_reap_timer);
+ timer_delete_sync(&ab->mon_reap_timer);
for (i = 0; i < ab->num_radios; i++) {
ar = ab->pdevs[i].ar;
@@ -1183,7 +1170,7 @@ void ath11k_dp_shadow_stop_timer(struct ath11k_base *ab,
if (!update_timer->init)
return;
- del_timer_sync(&update_timer->timer);
+ timer_delete_sync(&update_timer->timer);
}
void ath11k_dp_shadow_init_timer(struct ath11k_base *ab,
diff --git a/drivers/net/wireless/ath/ath11k/dp.h b/drivers/net/wireless/ath/ath11k/dp.h
index f777314db8b3..7a55afd33be8 100644
--- a/drivers/net/wireless/ath/ath11k/dp.h
+++ b/drivers/net/wireless/ath/ath11k/dp.h
@@ -1,7 +1,7 @@
/* SPDX-License-Identifier: BSD-3-Clause-Clear */
/*
* Copyright (c) 2018-2019 The Linux Foundation. All rights reserved.
- * Copyright (c) 2021-2023 Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) 2021-2023, 2025 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#ifndef ATH11K_DP_H
@@ -20,7 +20,6 @@ struct ath11k_ext_irq_grp;
struct dp_rx_tid {
u8 tid;
- u32 *vaddr;
dma_addr_t paddr;
u32 size;
u32 ba_win_sz;
@@ -37,6 +36,9 @@ struct dp_rx_tid {
/* Timer info related to fragments */
struct timer_list frag_timer;
struct ath11k_base *ab;
+ u32 *vaddr_unaligned;
+ dma_addr_t paddr_unaligned;
+ u32 unaligned_size;
};
#define DP_REO_DESC_FREE_THRESHOLD 64
diff --git a/drivers/net/wireless/ath/ath11k/dp_rx.c b/drivers/net/wireless/ath/ath11k/dp_rx.c
index 029ecf51c9ef..ea2959305dec 100644
--- a/drivers/net/wireless/ath/ath11k/dp_rx.c
+++ b/drivers/net/wireless/ath/ath11k/dp_rx.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-2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#include <linux/ieee80211.h>
@@ -675,11 +675,11 @@ void ath11k_dp_reo_cmd_list_cleanup(struct ath11k_base *ab)
list_for_each_entry_safe(cmd, tmp, &dp->reo_cmd_list, list) {
list_del(&cmd->list);
rx_tid = &cmd->data;
- if (rx_tid->vaddr) {
- dma_unmap_single(ab->dev, rx_tid->paddr,
- rx_tid->size, DMA_BIDIRECTIONAL);
- kfree(rx_tid->vaddr);
- rx_tid->vaddr = NULL;
+ if (rx_tid->vaddr_unaligned) {
+ dma_free_noncoherent(ab->dev, rx_tid->unaligned_size,
+ rx_tid->vaddr_unaligned,
+ rx_tid->paddr_unaligned, DMA_BIDIRECTIONAL);
+ rx_tid->vaddr_unaligned = NULL;
}
kfree(cmd);
}
@@ -689,11 +689,11 @@ void ath11k_dp_reo_cmd_list_cleanup(struct ath11k_base *ab)
list_del(&cmd_cache->list);
dp->reo_cmd_cache_flush_count--;
rx_tid = &cmd_cache->data;
- if (rx_tid->vaddr) {
- dma_unmap_single(ab->dev, rx_tid->paddr,
- rx_tid->size, DMA_BIDIRECTIONAL);
- kfree(rx_tid->vaddr);
- rx_tid->vaddr = NULL;
+ if (rx_tid->vaddr_unaligned) {
+ dma_free_noncoherent(ab->dev, rx_tid->unaligned_size,
+ rx_tid->vaddr_unaligned,
+ rx_tid->paddr_unaligned, DMA_BIDIRECTIONAL);
+ rx_tid->vaddr_unaligned = NULL;
}
kfree(cmd_cache);
}
@@ -708,11 +708,11 @@ static void ath11k_dp_reo_cmd_free(struct ath11k_dp *dp, void *ctx,
if (status != HAL_REO_CMD_SUCCESS)
ath11k_warn(dp->ab, "failed to flush rx tid hw desc, tid %d status %d\n",
rx_tid->tid, status);
- if (rx_tid->vaddr) {
- dma_unmap_single(dp->ab->dev, rx_tid->paddr, rx_tid->size,
- DMA_BIDIRECTIONAL);
- kfree(rx_tid->vaddr);
- rx_tid->vaddr = NULL;
+ if (rx_tid->vaddr_unaligned) {
+ dma_free_noncoherent(dp->ab->dev, rx_tid->unaligned_size,
+ rx_tid->vaddr_unaligned,
+ rx_tid->paddr_unaligned, DMA_BIDIRECTIONAL);
+ rx_tid->vaddr_unaligned = NULL;
}
}
@@ -749,10 +749,10 @@ static void ath11k_dp_reo_cache_flush(struct ath11k_base *ab,
if (ret) {
ath11k_err(ab, "failed to send HAL_REO_CMD_FLUSH_CACHE cmd, tid %d (%d)\n",
rx_tid->tid, ret);
- dma_unmap_single(ab->dev, rx_tid->paddr, rx_tid->size,
- DMA_BIDIRECTIONAL);
- kfree(rx_tid->vaddr);
- rx_tid->vaddr = NULL;
+ dma_free_noncoherent(ab->dev, rx_tid->unaligned_size,
+ rx_tid->vaddr_unaligned,
+ rx_tid->paddr_unaligned, DMA_BIDIRECTIONAL);
+ rx_tid->vaddr_unaligned = NULL;
}
}
@@ -802,10 +802,10 @@ static void ath11k_dp_rx_tid_del_func(struct ath11k_dp *dp, void *ctx,
return;
free_desc:
- dma_unmap_single(ab->dev, rx_tid->paddr, rx_tid->size,
- DMA_BIDIRECTIONAL);
- kfree(rx_tid->vaddr);
- rx_tid->vaddr = NULL;
+ dma_free_noncoherent(ab->dev, rx_tid->unaligned_size,
+ rx_tid->vaddr_unaligned,
+ rx_tid->paddr_unaligned, DMA_BIDIRECTIONAL);
+ rx_tid->vaddr_unaligned = NULL;
}
void ath11k_peer_rx_tid_delete(struct ath11k *ar,
@@ -831,14 +831,16 @@ void ath11k_peer_rx_tid_delete(struct ath11k *ar,
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);
- rx_tid->vaddr = NULL;
+ dma_free_noncoherent(ar->ab->dev, rx_tid->unaligned_size,
+ rx_tid->vaddr_unaligned,
+ rx_tid->paddr_unaligned, DMA_BIDIRECTIONAL);
+ rx_tid->vaddr_unaligned = NULL;
}
rx_tid->paddr = 0;
+ rx_tid->paddr_unaligned = 0;
rx_tid->size = 0;
+ rx_tid->unaligned_size = 0;
}
static int ath11k_dp_rx_link_desc_return(struct ath11k_base *ab,
@@ -904,7 +906,7 @@ void ath11k_peer_frags_flush(struct ath11k *ar, struct ath11k_peer *peer)
rx_tid = &peer->rx_tid[i];
spin_unlock_bh(&ar->ab->base_lock);
- del_timer_sync(&rx_tid->frag_timer);
+ timer_delete_sync(&rx_tid->frag_timer);
spin_lock_bh(&ar->ab->base_lock);
ath11k_dp_rx_frags_cleanup(rx_tid, true);
@@ -925,7 +927,7 @@ void ath11k_peer_rx_tid_cleanup(struct ath11k *ar, struct ath11k_peer *peer)
ath11k_dp_rx_frags_cleanup(rx_tid, true);
spin_unlock_bh(&ar->ab->base_lock);
- del_timer_sync(&rx_tid->frag_timer);
+ timer_delete_sync(&rx_tid->frag_timer);
spin_lock_bh(&ar->ab->base_lock);
}
}
@@ -982,10 +984,9 @@ static void ath11k_dp_rx_tid_mem_free(struct ath11k_base *ab,
if (!rx_tid->active)
goto unlock_exit;
- dma_unmap_single(ab->dev, rx_tid->paddr, rx_tid->size,
- DMA_BIDIRECTIONAL);
- kfree(rx_tid->vaddr);
- rx_tid->vaddr = NULL;
+ dma_free_noncoherent(ab->dev, rx_tid->unaligned_size, rx_tid->vaddr_unaligned,
+ rx_tid->paddr_unaligned, DMA_BIDIRECTIONAL);
+ rx_tid->vaddr_unaligned = NULL;
rx_tid->active = false;
@@ -1000,9 +1001,8 @@ int ath11k_peer_rx_tid_setup(struct ath11k *ar, const u8 *peer_mac, int vdev_id,
struct ath11k_base *ab = ar->ab;
struct ath11k_peer *peer;
struct dp_rx_tid *rx_tid;
- u32 hw_desc_sz;
- u32 *addr_aligned;
- void *vaddr;
+ u32 hw_desc_sz, *vaddr;
+ void *vaddr_unaligned;
dma_addr_t paddr;
int ret;
@@ -1050,37 +1050,34 @@ int ath11k_peer_rx_tid_setup(struct ath11k *ar, const u8 *peer_mac, int vdev_id,
else
hw_desc_sz = ath11k_hal_reo_qdesc_size(DP_BA_WIN_SZ_MAX, tid);
- vaddr = kzalloc(hw_desc_sz + HAL_LINK_DESC_ALIGN - 1, GFP_ATOMIC);
- if (!vaddr) {
+ rx_tid->unaligned_size = hw_desc_sz + HAL_LINK_DESC_ALIGN - 1;
+ vaddr_unaligned = dma_alloc_noncoherent(ab->dev, rx_tid->unaligned_size, &paddr,
+ DMA_BIDIRECTIONAL, GFP_ATOMIC);
+ if (!vaddr_unaligned) {
spin_unlock_bh(&ab->base_lock);
return -ENOMEM;
}
- addr_aligned = PTR_ALIGN(vaddr, HAL_LINK_DESC_ALIGN);
-
- ath11k_hal_reo_qdesc_setup(addr_aligned, tid, ba_win_sz,
- ssn, pn_type);
-
- paddr = dma_map_single(ab->dev, addr_aligned, hw_desc_sz,
- DMA_BIDIRECTIONAL);
-
- ret = dma_mapping_error(ab->dev, paddr);
- if (ret) {
- spin_unlock_bh(&ab->base_lock);
- ath11k_warn(ab, "failed to setup dma map for peer %pM rx tid %d: %d\n",
- peer_mac, tid, ret);
- goto err_mem_free;
- }
-
- rx_tid->vaddr = vaddr;
- rx_tid->paddr = paddr;
+ rx_tid->vaddr_unaligned = vaddr_unaligned;
+ vaddr = PTR_ALIGN(vaddr_unaligned, HAL_LINK_DESC_ALIGN);
+ rx_tid->paddr_unaligned = paddr;
+ rx_tid->paddr = rx_tid->paddr_unaligned + ((unsigned long)vaddr -
+ (unsigned long)rx_tid->vaddr_unaligned);
+ ath11k_hal_reo_qdesc_setup(vaddr, tid, ba_win_sz, ssn, pn_type);
rx_tid->size = hw_desc_sz;
rx_tid->active = true;
+ /* After dma_alloc_noncoherent, vaddr is being modified for reo qdesc setup.
+ * Since these changes are not reflected in the device, driver now needs to
+ * explicitly call dma_sync_single_for_device.
+ */
+ dma_sync_single_for_device(ab->dev, rx_tid->paddr,
+ rx_tid->size,
+ DMA_TO_DEVICE);
spin_unlock_bh(&ab->base_lock);
- ret = ath11k_wmi_peer_rx_reorder_queue_setup(ar, vdev_id, peer_mac,
- paddr, tid, 1, ba_win_sz);
+ ret = ath11k_wmi_peer_rx_reorder_queue_setup(ar, vdev_id, peer_mac, rx_tid->paddr,
+ tid, 1, ba_win_sz);
if (ret) {
ath11k_warn(ar->ab, "failed to setup rx reorder queue for peer %pM tid %d: %d\n",
peer_mac, tid, ret);
@@ -1088,12 +1085,6 @@ int ath11k_peer_rx_tid_setup(struct ath11k *ar, const u8 *peer_mac, int vdev_id,
}
return ret;
-
-err_mem_free:
- kfree(rx_tid->vaddr);
- rx_tid->vaddr = NULL;
-
- return ret;
}
int ath11k_dp_rx_ampdu_start(struct ath11k *ar,
@@ -2646,7 +2637,7 @@ int ath11k_dp_process_rx(struct ath11k_base *ab, int ring_id,
struct ath11k *ar;
struct hal_reo_dest_ring *desc;
enum hal_reo_dest_ring_push_reason push_reason;
- u32 cookie;
+ u32 cookie, info0, rx_msdu_info0, rx_mpdu_info0;
int i;
for (i = 0; i < MAX_RADIOS; i++)
@@ -2659,11 +2650,14 @@ int ath11k_dp_process_rx(struct ath11k_base *ab, int ring_id,
try_again:
ath11k_hal_srng_access_begin(ab, srng);
+ /* Make sure descriptor is read after the head pointer. */
+ dma_rmb();
+
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);
+ READ_ONCE(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);
@@ -2692,8 +2686,9 @@ try_again:
num_buffs_reaped[mac_id]++;
+ info0 = READ_ONCE(desc->info0);
push_reason = FIELD_GET(HAL_REO_DEST_RING_INFO0_PUSH_REASON,
- desc->info0);
+ info0);
if (unlikely(push_reason !=
HAL_REO_DEST_RING_PUSH_REASON_ROUTING_INSTRUCTION)) {
dev_kfree_skb_any(msdu);
@@ -2701,18 +2696,21 @@ try_again:
continue;
}
- rxcb->is_first_msdu = !!(desc->rx_msdu_info.info0 &
+ rx_msdu_info0 = READ_ONCE(desc->rx_msdu_info.info0);
+ rx_mpdu_info0 = READ_ONCE(desc->rx_mpdu_info.info0);
+
+ rxcb->is_first_msdu = !!(rx_msdu_info0 &
RX_MSDU_DESC_INFO0_FIRST_MSDU_IN_MPDU);
- rxcb->is_last_msdu = !!(desc->rx_msdu_info.info0 &
+ rxcb->is_last_msdu = !!(rx_msdu_info0 &
RX_MSDU_DESC_INFO0_LAST_MSDU_IN_MPDU);
- rxcb->is_continuation = !!(desc->rx_msdu_info.info0 &
+ rxcb->is_continuation = !!(rx_msdu_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);
+ READ_ONCE(desc->rx_mpdu_info.meta_data));
rxcb->seq_no = FIELD_GET(RX_MPDU_DESC_INFO0_SEQ_NUM,
- desc->rx_mpdu_info.info0);
+ rx_mpdu_info0);
rxcb->tid = FIELD_GET(HAL_REO_DEST_RING_INFO0_RX_QUEUE_NUM,
- desc->info0);
+ info0);
rxcb->mac_id = mac_id;
__skb_queue_tail(&msdu_list[mac_id], msdu);
@@ -2831,8 +2829,6 @@ static void ath11k_dp_rx_update_peer_stats(struct ath11k_sta *arsta,
rx_stats->dcm_count += ppdu_info->dcm;
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));
@@ -3721,7 +3717,7 @@ static int ath11k_dp_rx_frag_h_mpdu(struct ath11k *ar,
}
spin_unlock_bh(&ab->base_lock);
- del_timer_sync(&rx_tid->frag_timer);
+ timer_delete_sync(&rx_tid->frag_timer);
spin_lock_bh(&ab->base_lock);
peer = ath11k_peer_find_by_id(ab, peer_id);
@@ -4783,7 +4779,7 @@ u32 ath11k_dp_rx_mon_mpdu_pop(struct ath11k *ar, int mac_id,
if (!msdu) {
ath11k_dbg(ar->ab, ATH11K_DBG_DATA,
"msdu_pop: invalid buf_id %d\n", buf_id);
- break;
+ goto next_msdu;
}
rxcb = ATH11K_SKB_RXCB(msdu);
if (!rxcb->unmapped) {
@@ -5148,7 +5144,7 @@ static void ath11k_dp_rx_mon_dest_process(struct ath11k *ar, int mac_id,
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;
+ struct hal_srng *mon_dst_srng;
u32 ppdu_id;
u32 rx_bufs_used;
u32 ring_id;
@@ -5165,6 +5161,7 @@ static void ath11k_dp_rx_mon_dest_process(struct ath11k *ar, int mac_id,
spin_lock_bh(&pmon->mon_lock);
+ spin_lock_bh(&mon_dst_srng->lock);
ath11k_hal_srng_access_begin(ar->ab, mon_dst_srng);
ppdu_id = pmon->mon_ppdu_info.ppdu_id;
@@ -5223,6 +5220,7 @@ static void ath11k_dp_rx_mon_dest_process(struct ath11k *ar, int mac_id,
mon_dst_srng);
}
ath11k_hal_srng_access_end(ar->ab, mon_dst_srng);
+ spin_unlock_bh(&mon_dst_srng->lock);
spin_unlock_bh(&pmon->mon_lock);
@@ -5410,7 +5408,7 @@ ath11k_dp_rx_full_mon_mpdu_pop(struct ath11k *ar,
"full mon msdu_pop: invalid buf_id %d\n",
buf_id);
spin_unlock_bh(&rx_ring->idr_lock);
- break;
+ goto next_msdu;
}
idr_remove(&rx_ring->bufs_idr, buf_id);
spin_unlock_bh(&rx_ring->idr_lock);
@@ -5612,7 +5610,7 @@ static int ath11k_dp_full_mon_process_rx(struct ath11k_base *ab, int mac_id,
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];
+ struct hal_srng *mon_dst_srng;
void *ring_entry;
u32 rx_bufs_used = 0, mpdu_rx_bufs_used;
int quota = 0, ret;
@@ -5628,6 +5626,9 @@ static int ath11k_dp_full_mon_process_rx(struct ath11k_base *ab, int mac_id,
goto reap_status_ring;
}
+ mon_dst_srng = &ar->ab->hal.srng_list[dp->rxdma_mon_dst_ring.ring_id];
+ spin_lock_bh(&mon_dst_srng->lock);
+
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;
@@ -5671,6 +5672,7 @@ next_entry:
}
ath11k_hal_srng_access_end(ar->ab, mon_dst_srng);
+ spin_unlock_bh(&mon_dst_srng->lock);
spin_unlock_bh(&pmon->mon_lock);
if (rx_bufs_used) {
@@ -5786,7 +5788,7 @@ int ath11k_dp_rx_pktlog_stop(struct ath11k_base *ab, bool stop_timer)
int ret;
if (stop_timer)
- del_timer_sync(&ab->mon_reap_timer);
+ timer_delete_sync(&ab->mon_reap_timer);
/* reap all the monitor related rings */
ret = ath11k_dp_purge_mon_ring(ab);
diff --git a/drivers/net/wireless/ath/ath11k/fw.c b/drivers/net/wireless/ath/ath11k/fw.c
index 4e36292a79db..cbbd8e57119f 100644
--- a/drivers/net/wireless/ath/ath11k/fw.c
+++ b/drivers/net/wireless/ath/ath11k/fw.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: BSD-3-Clause-Clear
/*
- * Copyright (c) 2022-2023, Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) 2022-2025 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#include "core.h"
@@ -166,3 +166,4 @@ void ath11k_fw_destroy(struct ath11k_base *ab)
{
release_firmware(ab->fw.fw);
}
+EXPORT_SYMBOL(ath11k_fw_destroy);
diff --git a/drivers/net/wireless/ath/ath11k/hal.c b/drivers/net/wireless/ath/ath11k/hal.c
index 61f4b6dd5380..8cb1505a5a0c 100644
--- a/drivers/net/wireless/ath/ath11k/hal.c
+++ b/drivers/net/wireless/ath/ath11k/hal.c
@@ -599,7 +599,7 @@ u32 ath11k_hal_ce_dst_status_get_length(void *buf)
struct hal_ce_srng_dst_status_desc *desc = buf;
u32 len;
- len = FIELD_GET(HAL_CE_DST_STATUS_DESC_FLAGS_LEN, desc->flags);
+ len = FIELD_GET(HAL_CE_DST_STATUS_DESC_FLAGS_LEN, READ_ONCE(desc->flags));
desc->flags &= ~HAL_CE_DST_STATUS_DESC_FLAGS_LEN;
return len;
@@ -829,7 +829,7 @@ void ath11k_hal_srng_access_begin(struct ath11k_base *ab, struct hal_srng *srng)
srng->u.src_ring.cached_tp =
*(volatile u32 *)srng->u.src_ring.tp_addr;
} else {
- srng->u.dst_ring.cached_hp = *srng->u.dst_ring.hp_addr;
+ srng->u.dst_ring.cached_hp = READ_ONCE(*srng->u.dst_ring.hp_addr);
/* Try to prefetch the next descriptor in the ring */
if (srng->flags & HAL_SRNG_FLAGS_CACHED)
diff --git a/drivers/net/wireless/ath/ath11k/hif.h b/drivers/net/wireless/ath/ath11k/hif.h
index 674ff772b181..cd9c4b838246 100644
--- a/drivers/net/wireless/ath/ath11k/hif.h
+++ b/drivers/net/wireless/ath/ath11k/hif.h
@@ -1,7 +1,7 @@
/* SPDX-License-Identifier: BSD-3-Clause-Clear */
/*
* Copyright (c) 2019-2020 The Linux Foundation. All rights reserved.
- * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) 2022-2025 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#ifndef _HIF_H_
@@ -18,7 +18,7 @@ struct ath11k_hif_ops {
int (*start)(struct ath11k_base *ab);
void (*stop)(struct ath11k_base *ab);
int (*power_up)(struct ath11k_base *ab);
- void (*power_down)(struct ath11k_base *ab);
+ void (*power_down)(struct ath11k_base *ab, bool is_suspend);
int (*suspend)(struct ath11k_base *ab);
int (*resume)(struct ath11k_base *ab);
int (*map_service_to_pipe)(struct ath11k_base *ab, u16 service_id,
@@ -31,6 +31,7 @@ struct ath11k_hif_ops {
void (*ce_irq_enable)(struct ath11k_base *ab);
void (*ce_irq_disable)(struct ath11k_base *ab);
void (*get_ce_msi_idx)(struct ath11k_base *ab, u32 ce_id, u32 *msi_idx);
+ void (*coredump_download)(struct ath11k_base *ab);
};
static inline void ath11k_hif_ce_irq_enable(struct ath11k_base *ab)
@@ -67,12 +68,18 @@ static inline void ath11k_hif_irq_disable(struct ath11k_base *ab)
static inline int ath11k_hif_power_up(struct ath11k_base *ab)
{
+ if (!ab->hif.ops->power_up)
+ return -EOPNOTSUPP;
+
return ab->hif.ops->power_up(ab);
}
-static inline void ath11k_hif_power_down(struct ath11k_base *ab)
+static inline void ath11k_hif_power_down(struct ath11k_base *ab, bool is_suspend)
{
- ab->hif.ops->power_down(ab);
+ if (!ab->hif.ops->power_down)
+ return;
+
+ ab->hif.ops->power_down(ab, is_suspend);
}
static inline int ath11k_hif_suspend(struct ath11k_base *ab)
@@ -146,4 +153,10 @@ static inline void ath11k_get_ce_msi_idx(struct ath11k_base *ab, u32 ce_id,
*msi_data_idx = ce_id;
}
+static inline void ath11k_hif_coredump_download(struct ath11k_base *ab)
+{
+ if (ab->hif.ops->coredump_download)
+ ab->hif.ops->coredump_download(ab);
+}
+
#endif /* _HIF_H_ */
diff --git a/drivers/net/wireless/ath/ath11k/mac.c b/drivers/net/wireless/ath/ath11k/mac.c
index 1556392f7ad4..08d7b136851f 100644
--- 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-2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#include <net/mac80211.h>
@@ -1529,17 +1529,29 @@ static int ath11k_mac_set_vif_params(struct ath11k_vif *arvif,
return ret;
}
-static int ath11k_mac_setup_bcn_tmpl_ema(struct ath11k_vif *arvif)
+static struct ath11k_vif *ath11k_mac_get_tx_arvif(struct ath11k_vif *arvif)
+{
+ struct ieee80211_bss_conf *link_conf, *tx_bss_conf;
+
+ lockdep_assert_wiphy(arvif->ar->hw->wiphy);
+
+ link_conf = &arvif->vif->bss_conf;
+ tx_bss_conf = wiphy_dereference(arvif->ar->hw->wiphy, link_conf->tx_bss_conf);
+ if (tx_bss_conf)
+ return ath11k_vif_to_arvif(tx_bss_conf->vif);
+
+ return NULL;
+}
+
+static int ath11k_mac_setup_bcn_tmpl_ema(struct ath11k_vif *arvif,
+ struct ath11k_vif *tx_arvif)
{
- struct ath11k_vif *tx_arvif;
struct ieee80211_ema_beacons *beacons;
int ret = 0;
bool nontx_vif_params_set = false;
u32 params = 0;
u8 i = 0;
- tx_arvif = ath11k_vif_to_arvif(arvif->vif->mbssid_tx_vif);
-
beacons = ieee80211_beacon_get_template_ema_list(tx_arvif->ar->hw,
tx_arvif->vif, 0);
if (!beacons || !beacons->cnt) {
@@ -1585,25 +1597,22 @@ static int ath11k_mac_setup_bcn_tmpl_ema(struct ath11k_vif *arvif)
return ret;
}
-static int ath11k_mac_setup_bcn_tmpl_mbssid(struct ath11k_vif *arvif)
+static int ath11k_mac_setup_bcn_tmpl_mbssid(struct ath11k_vif *arvif,
+ struct ath11k_vif *tx_arvif)
{
struct ath11k *ar = arvif->ar;
struct ath11k_base *ab = ar->ab;
- struct ath11k_vif *tx_arvif = arvif;
struct ieee80211_hw *hw = ar->hw;
struct ieee80211_vif *vif = arvif->vif;
struct ieee80211_mutable_offsets offs = {};
struct sk_buff *bcn;
int ret;
- if (vif->mbssid_tx_vif) {
- tx_arvif = ath11k_vif_to_arvif(vif->mbssid_tx_vif);
- if (tx_arvif != arvif) {
- ar = tx_arvif->ar;
- ab = ar->ab;
- hw = ar->hw;
- vif = tx_arvif->vif;
- }
+ if (tx_arvif != arvif) {
+ ar = tx_arvif->ar;
+ ab = ar->ab;
+ hw = ar->hw;
+ vif = tx_arvif->vif;
}
bcn = ieee80211_beacon_get_template(hw, vif, &offs, 0);
@@ -1632,6 +1641,7 @@ static int ath11k_mac_setup_bcn_tmpl_mbssid(struct ath11k_vif *arvif)
static int ath11k_mac_setup_bcn_tmpl(struct ath11k_vif *arvif)
{
struct ieee80211_vif *vif = arvif->vif;
+ struct ath11k_vif *tx_arvif;
if (arvif->vdev_type != WMI_VDEV_TYPE_AP)
return 0;
@@ -1639,14 +1649,18 @@ static int ath11k_mac_setup_bcn_tmpl(struct ath11k_vif *arvif)
/* Target does not expect beacon templates for the already up
* non-transmitting interfaces, and results in a crash if sent.
*/
- if (vif->mbssid_tx_vif &&
- arvif != ath11k_vif_to_arvif(vif->mbssid_tx_vif) && arvif->is_up)
- return 0;
+ tx_arvif = ath11k_mac_get_tx_arvif(arvif);
+ if (tx_arvif) {
+ if (arvif != tx_arvif && arvif->is_up)
+ return 0;
- if (vif->bss_conf.ema_ap && vif->mbssid_tx_vif)
- return ath11k_mac_setup_bcn_tmpl_ema(arvif);
+ if (vif->bss_conf.ema_ap)
+ return ath11k_mac_setup_bcn_tmpl_ema(arvif, tx_arvif);
+ } else {
+ tx_arvif = arvif;
+ }
- return ath11k_mac_setup_bcn_tmpl_mbssid(arvif);
+ return ath11k_mac_setup_bcn_tmpl_mbssid(arvif, tx_arvif);
}
void ath11k_mac_bcn_tx_event(struct ath11k_vif *arvif)
@@ -1674,7 +1688,7 @@ static void ath11k_control_beaconing(struct ath11k_vif *arvif,
struct ieee80211_bss_conf *info)
{
struct ath11k *ar = arvif->ar;
- struct ath11k_vif *tx_arvif = NULL;
+ struct ath11k_vif *tx_arvif;
int ret = 0;
lockdep_assert_held(&arvif->ar->conf_mutex);
@@ -1701,9 +1715,7 @@ static void ath11k_control_beaconing(struct ath11k_vif *arvif,
ether_addr_copy(arvif->bssid, info->bssid);
- if (arvif->vif->mbssid_tx_vif)
- tx_arvif = ath11k_vif_to_arvif(arvif->vif->mbssid_tx_vif);
-
+ tx_arvif = ath11k_mac_get_tx_arvif(arvif);
ret = ath11k_wmi_vdev_up(arvif->ar, arvif->vdev_id, arvif->aid,
arvif->bssid,
tx_arvif ? tx_arvif->bssid : NULL,
@@ -5204,6 +5216,45 @@ exit:
return ret;
}
+static int ath11k_mac_op_conf_tx_mu_edca(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ unsigned int link_id, u16 ac,
+ const struct ieee80211_tx_queue_params *params)
+{
+ struct ath11k_vif *arvif = ath11k_vif_to_arvif(vif);
+ struct ath11k *ar = hw->priv;
+ struct wmi_wmm_params_arg *p;
+ int ret;
+
+ switch (ac) {
+ case IEEE80211_AC_VO:
+ p = &arvif->muedca_params.ac_vo;
+ break;
+ case IEEE80211_AC_VI:
+ p = &arvif->muedca_params.ac_vi;
+ break;
+ case IEEE80211_AC_BE:
+ p = &arvif->muedca_params.ac_be;
+ break;
+ case IEEE80211_AC_BK:
+ p = &arvif->muedca_params.ac_bk;
+ break;
+ default:
+ ath11k_warn(ar->ab, "error ac: %d", ac);
+ return -EINVAL;
+ }
+
+ p->cwmin = u8_get_bits(params->mu_edca_param_rec.ecw_min_max, GENMASK(3, 0));
+ p->cwmax = u8_get_bits(params->mu_edca_param_rec.ecw_min_max, GENMASK(7, 4));
+ p->aifs = u8_get_bits(params->mu_edca_param_rec.aifsn, GENMASK(3, 0));
+ p->txop = params->mu_edca_param_rec.mu_edca_timer;
+
+ ret = ath11k_wmi_send_wmm_update_cmd_tlv(ar, arvif->vdev_id,
+ &arvif->muedca_params,
+ WMI_WMM_PARAM_TYPE_11AX_MU_EDCA);
+ return ret;
+}
+
static int ath11k_mac_op_conf_tx(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
unsigned int link_id, u16 ac,
@@ -5242,12 +5293,22 @@ static int ath11k_mac_op_conf_tx(struct ieee80211_hw *hw,
p->txop = params->txop;
ret = ath11k_wmi_send_wmm_update_cmd_tlv(ar, arvif->vdev_id,
- &arvif->wmm_params);
+ &arvif->wmm_params,
+ WMI_WMM_PARAM_TYPE_LEGACY);
if (ret) {
ath11k_warn(ar->ab, "failed to set wmm params: %d\n", ret);
goto exit;
}
+ if (params->mu_edca) {
+ ret = ath11k_mac_op_conf_tx_mu_edca(hw, vif, link_id, ac,
+ params);
+ if (ret) {
+ ath11k_warn(ar->ab, "failed to set mu_edca params: %d\n", ret);
+ goto exit;
+ }
+ }
+
ret = ath11k_conf_tx_uapsd(ar, vif, ac, params->uapsd);
if (ret)
@@ -5336,8 +5397,6 @@ static int ath11k_mac_set_txbf_conf(struct ath11k_vif *arvif)
if (vht_cap & (IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE)) {
nsts = vht_cap & IEEE80211_VHT_CAP_BEAMFORMEE_STS_MASK;
nsts >>= IEEE80211_VHT_CAP_BEAMFORMEE_STS_SHIFT;
- if (nsts > (ar->num_rx_chains - 1))
- nsts = ar->num_rx_chains - 1;
value |= SM(nsts, WMI_TXBF_STS_CAP_OFFSET);
}
@@ -5421,9 +5480,6 @@ static void ath11k_set_vht_txbf_cap(struct ath11k *ar, u32 *vht_cap)
/* Enable Beamformee STS Field only if SU BF is enabled */
if (subfee) {
- if (nsts > (ar->num_rx_chains - 1))
- nsts = ar->num_rx_chains - 1;
-
nsts <<= IEEE80211_VHT_CAP_BEAMFORMEE_STS_SHIFT;
nsts &= IEEE80211_VHT_CAP_BEAMFORMEE_STS_MASK;
*vht_cap |= nsts;
@@ -6288,6 +6344,7 @@ static void ath11k_mac_op_stop(struct ieee80211_hw *hw, bool suspend)
{
struct ath11k *ar = hw->priv;
struct htt_ppdu_stats_info *ppdu_stats, *tmp;
+ struct scan_chan_list_params *params;
int ret;
ath11k_mac_drain_tx(ar);
@@ -6303,6 +6360,7 @@ static void ath11k_mac_op_stop(struct ieee80211_hw *hw, bool suspend)
mutex_unlock(&ar->conf_mutex);
cancel_delayed_work_sync(&ar->scan.timeout);
+ cancel_work_sync(&ar->channel_update_work);
cancel_work_sync(&ar->regd_update_work);
cancel_work_sync(&ar->ab->update_11d_work);
@@ -6312,10 +6370,19 @@ static void ath11k_mac_op_stop(struct ieee80211_hw *hw, bool suspend)
}
spin_lock_bh(&ar->data_lock);
+
list_for_each_entry_safe(ppdu_stats, tmp, &ar->ppdu_stats_info, list) {
list_del(&ppdu_stats->list);
kfree(ppdu_stats);
}
+
+ while ((params = list_first_entry_or_null(&ar->channel_update_queue,
+ struct scan_chan_list_params,
+ list))) {
+ list_del(&params->list);
+ kfree(params);
+ }
+
spin_unlock_bh(&ar->data_lock);
rcu_assign_pointer(ar->ab->pdevs_active[ar->pdev_idx], NULL);
@@ -6330,23 +6397,20 @@ static int ath11k_mac_setup_vdev_params_mbssid(struct ath11k_vif *arvif,
{
struct ath11k *ar = arvif->ar;
struct ath11k_vif *tx_arvif;
- struct ieee80211_vif *tx_vif;
*tx_vdev_id = 0;
- tx_vif = arvif->vif->mbssid_tx_vif;
- if (!tx_vif) {
+ tx_arvif = ath11k_mac_get_tx_arvif(arvif);
+ if (!tx_arvif) {
*flags = WMI_HOST_VDEV_FLAGS_NON_MBSSID_AP;
return 0;
}
- tx_arvif = ath11k_vif_to_arvif(tx_vif);
-
if (arvif->vif->bss_conf.nontransmitted) {
- if (ar->hw->wiphy != ieee80211_vif_to_wdev(tx_vif)->wiphy)
+ if (ar->hw->wiphy != tx_arvif->ar->hw->wiphy)
return -EINVAL;
*flags = WMI_HOST_VDEV_FLAGS_NON_TRANSMIT_AP;
- *tx_vdev_id = ath11k_vif_to_arvif(tx_vif)->vdev_id;
+ *tx_vdev_id = tx_arvif->vdev_id;
} else if (tx_arvif == arvif) {
*flags = WMI_HOST_VDEV_FLAGS_TRANSMIT_AP;
} else {
@@ -7306,8 +7370,7 @@ ath11k_mac_update_vif_chan(struct ath11k *ar,
int n_vifs)
{
struct ath11k_base *ab = ar->ab;
- struct ath11k_vif *arvif, *tx_arvif = NULL;
- struct ieee80211_vif *mbssid_tx_vif;
+ struct ath11k_vif *arvif, *tx_arvif;
int ret;
int i;
bool monitor_vif = false;
@@ -7361,10 +7424,7 @@ ath11k_mac_update_vif_chan(struct ath11k *ar,
ath11k_warn(ab, "failed to update bcn tmpl during csa: %d\n",
ret);
- mbssid_tx_vif = arvif->vif->mbssid_tx_vif;
- if (mbssid_tx_vif)
- tx_arvif = ath11k_vif_to_arvif(mbssid_tx_vif);
-
+ tx_arvif = ath11k_mac_get_tx_arvif(arvif);
ret = ath11k_wmi_vdev_up(arvif->ar, arvif->vdev_id, arvif->aid,
arvif->bssid,
tx_arvif ? tx_arvif->bssid : NULL,
@@ -9912,12 +9972,17 @@ static int ath11k_mac_setup_iface_combinations(struct ath11k *ar)
struct ath11k_base *ab = ar->ab;
struct ieee80211_iface_combination *combinations;
struct ieee80211_iface_limit *limits;
- int n_limits;
+ int n_limits, n_combos;
bool p2p;
p2p = ab->hw_params.interface_modes & BIT(NL80211_IFTYPE_P2P_DEVICE);
- combinations = kzalloc(sizeof(*combinations), GFP_KERNEL);
+ if (ab->hw_params.support_dual_stations)
+ n_combos = 2;
+ else
+ n_combos = 1;
+
+ combinations = kcalloc(n_combos, sizeof(*combinations), GFP_KERNEL);
if (!combinations)
return -ENOMEM;
@@ -9932,7 +9997,9 @@ static int ath11k_mac_setup_iface_combinations(struct ath11k *ar)
return -ENOMEM;
}
+ limits[0].max = 1;
limits[0].types |= BIT(NL80211_IFTYPE_STATION);
+ limits[1].max = 16;
limits[1].types |= BIT(NL80211_IFTYPE_AP);
if (IS_ENABLED(CONFIG_MAC80211_MESH) &&
ab->hw_params.interface_modes & BIT(NL80211_IFTYPE_MESH_POINT))
@@ -9942,25 +10009,24 @@ static int ath11k_mac_setup_iface_combinations(struct ath11k *ar)
combinations[0].n_limits = n_limits;
combinations[0].beacon_int_infra_match = true;
combinations[0].beacon_int_min_gcd = 100;
+ combinations[0].max_interfaces = 16;
+ combinations[0].num_different_channels = 1;
+ 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_80P80) |
+ BIT(NL80211_CHAN_WIDTH_160);
if (ab->hw_params.support_dual_stations) {
limits[0].max = 2;
- limits[1].max = 1;
-
- combinations[0].max_interfaces = ab->hw_params.num_vdevs;
- combinations[0].num_different_channels = 2;
- } else {
- limits[0].max = 1;
- limits[1].max = 16;
- combinations[0].max_interfaces = 16;
- combinations[0].num_different_channels = 1;
- 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_80P80) |
- BIT(NL80211_CHAN_WIDTH_160);
+ combinations[1].limits = limits;
+ combinations[1].n_limits = n_limits;
+ combinations[1].beacon_int_infra_match = true;
+ combinations[1].beacon_int_min_gcd = 100;
+ combinations[1].max_interfaces = ab->hw_params.num_vdevs;
+ combinations[1].num_different_channels = 2;
}
if (p2p) {
@@ -9971,7 +10037,7 @@ static int ath11k_mac_setup_iface_combinations(struct ath11k *ar)
}
ar->hw->wiphy->iface_combinations = combinations;
- ar->hw->wiphy->n_iface_combinations = 1;
+ ar->hw->wiphy->n_iface_combinations = n_combos;
return 0;
}
@@ -10019,6 +10085,7 @@ static const struct wiphy_iftype_ext_capab ath11k_iftypes_ext_capa[] = {
static void __ath11k_mac_unregister(struct ath11k *ar)
{
+ cancel_work_sync(&ar->channel_update_work);
cancel_work_sync(&ar->regd_update_work);
ieee80211_unregister_hw(ar->hw);
@@ -10418,6 +10485,8 @@ int ath11k_mac_allocate(struct ath11k_base *ab)
init_completion(&ar->thermal.wmi_sync);
INIT_DELAYED_WORK(&ar->scan.timeout, ath11k_scan_timeout_work);
+ INIT_WORK(&ar->channel_update_work, ath11k_regd_update_chan_list_work);
+ INIT_LIST_HEAD(&ar->channel_update_queue);
INIT_WORK(&ar->regd_update_work, ath11k_regd_update_work);
INIT_WORK(&ar->wmi_mgmt_tx_work, ath11k_mgmt_over_wmi_tx_work);
diff --git a/drivers/net/wireless/ath/ath11k/mhi.c b/drivers/net/wireless/ath/ath11k/mhi.c
index 6e45f464a429..acd76e9392d3 100644
--- a/drivers/net/wireless/ath/ath11k/mhi.c
+++ b/drivers/net/wireless/ath/ath11k/mhi.c
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: BSD-3-Clause-Clear
/*
* Copyright (c) 2020 The Linux Foundation. All rights reserved.
- * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#include <linux/msi.h>
@@ -454,9 +454,17 @@ int ath11k_mhi_start(struct ath11k_pci *ab_pci)
return 0;
}
-void ath11k_mhi_stop(struct ath11k_pci *ab_pci)
+void ath11k_mhi_stop(struct ath11k_pci *ab_pci, bool is_suspend)
{
- mhi_power_down(ab_pci->mhi_ctrl, true);
+ /* During suspend we need to use mhi_power_down_keep_dev()
+ * workaround, otherwise ath11k_core_resume() will timeout
+ * during resume.
+ */
+ if (is_suspend)
+ mhi_power_down_keep_dev(ab_pci->mhi_ctrl, true);
+ else
+ mhi_power_down(ab_pci->mhi_ctrl, true);
+
mhi_unprepare_after_power_down(ab_pci->mhi_ctrl);
}
@@ -491,3 +499,8 @@ int ath11k_mhi_resume(struct ath11k_pci *ab_pci)
return 0;
}
+
+void ath11k_mhi_coredump(struct mhi_controller *mhi_ctrl, bool in_panic)
+{
+ mhi_download_rddm_image(mhi_ctrl, in_panic);
+}
diff --git a/drivers/net/wireless/ath/ath11k/mhi.h b/drivers/net/wireless/ath/ath11k/mhi.h
index a682aad52fc5..5c5c2b03c81f 100644
--- a/drivers/net/wireless/ath/ath11k/mhi.h
+++ b/drivers/net/wireless/ath/ath11k/mhi.h
@@ -1,7 +1,7 @@
/* SPDX-License-Identifier: BSD-3-Clause-Clear */
/*
* Copyright (c) 2020 The Linux Foundation. All rights reserved.
- * Copyright (c) 2022, 2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) 2022, 2024-2025 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#ifndef _ATH11K_MHI_H
#define _ATH11K_MHI_H
@@ -18,7 +18,7 @@
#define MHICTRL_RESET_MASK 0x2
int ath11k_mhi_start(struct ath11k_pci *ar_pci);
-void ath11k_mhi_stop(struct ath11k_pci *ar_pci);
+void ath11k_mhi_stop(struct ath11k_pci *ar_pci, bool is_suspend);
int ath11k_mhi_register(struct ath11k_pci *ar_pci);
void ath11k_mhi_unregister(struct ath11k_pci *ar_pci);
void ath11k_mhi_set_mhictrl_reset(struct ath11k_base *ab);
@@ -26,5 +26,6 @@ void ath11k_mhi_clear_vector(struct ath11k_base *ab);
int ath11k_mhi_suspend(struct ath11k_pci *ar_pci);
int ath11k_mhi_resume(struct ath11k_pci *ar_pci);
+void ath11k_mhi_coredump(struct mhi_controller *mhi_ctrl, bool in_panic);
#endif
diff --git a/drivers/net/wireless/ath/ath11k/pci.c b/drivers/net/wireless/ath/ath11k/pci.c
index b93f04973ad7..78444f8ea153 100644
--- a/drivers/net/wireless/ath/ath11k/pci.c
+++ b/drivers/net/wireless/ath/ath11k/pci.c
@@ -1,13 +1,15 @@
// SPDX-License-Identifier: BSD-3-Clause-Clear
/*
* Copyright (c) 2019-2020 The Linux Foundation. All rights reserved.
- * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#include <linux/module.h>
#include <linux/msi.h>
#include <linux/pci.h>
#include <linux/of.h>
+#include <linux/time.h>
+#include <linux/vmalloc.h>
#include "pci.h"
#include "core.h"
@@ -610,6 +612,187 @@ static void ath11k_pci_aspm_restore(struct ath11k_pci *ab_pci)
PCI_EXP_LNKCTL_ASPMC);
}
+#ifdef CONFIG_DEV_COREDUMP
+static int ath11k_pci_coredump_calculate_size(struct ath11k_base *ab, u32 *dump_seg_sz)
+{
+ struct ath11k_pci *ab_pci = ath11k_pci_priv(ab);
+ struct mhi_controller *mhi_ctrl = ab_pci->mhi_ctrl;
+ struct image_info *rddm_img, *fw_img;
+ struct ath11k_tlv_dump_data *dump_tlv;
+ enum ath11k_fw_crash_dump_type mem_type;
+ u32 len = 0, rddm_tlv_sz = 0, paging_tlv_sz = 0;
+ struct ath11k_dump_file_data *file_data;
+ int i;
+
+ rddm_img = mhi_ctrl->rddm_image;
+ if (!rddm_img) {
+ ath11k_err(ab, "No RDDM dump found\n");
+ return 0;
+ }
+
+ fw_img = mhi_ctrl->fbc_image;
+
+ for (i = 0; i < fw_img->entries ; i++) {
+ if (!fw_img->mhi_buf[i].buf)
+ continue;
+
+ paging_tlv_sz += fw_img->mhi_buf[i].len;
+ }
+ dump_seg_sz[FW_CRASH_DUMP_PAGING_DATA] = paging_tlv_sz;
+
+ for (i = 0; i < rddm_img->entries; i++) {
+ if (!rddm_img->mhi_buf[i].buf)
+ continue;
+
+ rddm_tlv_sz += rddm_img->mhi_buf[i].len;
+ }
+ dump_seg_sz[FW_CRASH_DUMP_RDDM_DATA] = rddm_tlv_sz;
+
+ for (i = 0; i < ab->qmi.mem_seg_count; i++) {
+ mem_type = ath11k_coredump_get_dump_type(ab->qmi.target_mem[i].type);
+
+ if (mem_type == FW_CRASH_DUMP_NONE)
+ continue;
+
+ if (mem_type == FW_CRASH_DUMP_TYPE_MAX) {
+ ath11k_dbg(ab, ATH11K_DBG_PCI,
+ "target mem region type %d not supported",
+ ab->qmi.target_mem[i].type);
+ continue;
+ }
+
+ if (!ab->qmi.target_mem[i].anyaddr)
+ continue;
+
+ dump_seg_sz[mem_type] += ab->qmi.target_mem[i].size;
+ }
+
+ for (i = 0; i < FW_CRASH_DUMP_TYPE_MAX; i++) {
+ if (!dump_seg_sz[i])
+ continue;
+
+ len += sizeof(*dump_tlv) + dump_seg_sz[i];
+ }
+
+ if (len)
+ len += sizeof(*file_data);
+
+ return len;
+}
+
+static void ath11k_pci_coredump_download(struct ath11k_base *ab)
+{
+ struct ath11k_pci *ab_pci = ath11k_pci_priv(ab);
+ struct mhi_controller *mhi_ctrl = ab_pci->mhi_ctrl;
+ struct image_info *rddm_img, *fw_img;
+ struct timespec64 timestamp;
+ int i, len, mem_idx;
+ enum ath11k_fw_crash_dump_type mem_type;
+ struct ath11k_dump_file_data *file_data;
+ struct ath11k_tlv_dump_data *dump_tlv;
+ size_t hdr_len = sizeof(*file_data);
+ void *buf;
+ u32 dump_seg_sz[FW_CRASH_DUMP_TYPE_MAX] = { 0 };
+
+ ath11k_mhi_coredump(mhi_ctrl, false);
+
+ len = ath11k_pci_coredump_calculate_size(ab, dump_seg_sz);
+ if (!len) {
+ ath11k_warn(ab, "No crash dump data found for devcoredump");
+ return;
+ }
+
+ rddm_img = mhi_ctrl->rddm_image;
+ fw_img = mhi_ctrl->fbc_image;
+
+ /* dev_coredumpv() requires vmalloc data */
+ buf = vzalloc(len);
+ if (!buf)
+ return;
+
+ ab->dump_data = buf;
+ ab->ath11k_coredump_len = len;
+ file_data = ab->dump_data;
+ strscpy(file_data->df_magic, "ATH11K-FW-DUMP", sizeof(file_data->df_magic));
+ file_data->len = cpu_to_le32(len);
+ file_data->version = cpu_to_le32(ATH11K_FW_CRASH_DUMP_V2);
+ file_data->chip_id = cpu_to_le32(ab_pci->dev_id);
+ file_data->qrtr_id = cpu_to_le32(ab_pci->ab->qmi.service_ins_id);
+ file_data->bus_id = cpu_to_le32(pci_domain_nr(ab_pci->pdev->bus));
+ guid_gen(&file_data->guid);
+ ktime_get_real_ts64(&timestamp);
+ file_data->tv_sec = cpu_to_le64(timestamp.tv_sec);
+ file_data->tv_nsec = cpu_to_le64(timestamp.tv_nsec);
+ buf += hdr_len;
+ dump_tlv = buf;
+ dump_tlv->type = cpu_to_le32(FW_CRASH_DUMP_PAGING_DATA);
+ dump_tlv->tlv_len = cpu_to_le32(dump_seg_sz[FW_CRASH_DUMP_PAGING_DATA]);
+ buf += COREDUMP_TLV_HDR_SIZE;
+
+ /* append all segments together as they are all part of a single contiguous
+ * block of memory
+ */
+ for (i = 0; i < fw_img->entries ; i++) {
+ if (!fw_img->mhi_buf[i].buf)
+ continue;
+
+ memcpy_fromio(buf, (void const __iomem *)fw_img->mhi_buf[i].buf,
+ fw_img->mhi_buf[i].len);
+ buf += fw_img->mhi_buf[i].len;
+ }
+
+ dump_tlv = buf;
+ dump_tlv->type = cpu_to_le32(FW_CRASH_DUMP_RDDM_DATA);
+ dump_tlv->tlv_len = cpu_to_le32(dump_seg_sz[FW_CRASH_DUMP_RDDM_DATA]);
+ buf += COREDUMP_TLV_HDR_SIZE;
+
+ /* append all segments together as they are all part of a single contiguous
+ * block of memory
+ */
+ for (i = 0; i < rddm_img->entries; i++) {
+ if (!rddm_img->mhi_buf[i].buf)
+ continue;
+
+ memcpy_fromio(buf, (void const __iomem *)rddm_img->mhi_buf[i].buf,
+ rddm_img->mhi_buf[i].len);
+ buf += rddm_img->mhi_buf[i].len;
+ }
+
+ mem_idx = FW_CRASH_DUMP_REMOTE_MEM_DATA;
+ for (; mem_idx < FW_CRASH_DUMP_TYPE_MAX; mem_idx++) {
+ if (mem_idx == FW_CRASH_DUMP_NONE)
+ continue;
+
+ for (i = 0; i < ab->qmi.mem_seg_count; i++) {
+ mem_type = ath11k_coredump_get_dump_type
+ (ab->qmi.target_mem[i].type);
+
+ if (mem_type != mem_idx)
+ continue;
+
+ if (!ab->qmi.target_mem[i].anyaddr) {
+ ath11k_dbg(ab, ATH11K_DBG_PCI,
+ "Skipping mem region type %d",
+ ab->qmi.target_mem[i].type);
+ continue;
+ }
+
+ dump_tlv = buf;
+ dump_tlv->type = cpu_to_le32(mem_idx);
+ dump_tlv->tlv_len = cpu_to_le32(dump_seg_sz[mem_idx]);
+ buf += COREDUMP_TLV_HDR_SIZE;
+
+ memcpy_fromio(buf, ab->qmi.target_mem[i].iaddr,
+ ab->qmi.target_mem[i].size);
+
+ buf += ab->qmi.target_mem[i].size;
+ }
+ }
+
+ queue_work(ab->workqueue, &ab->dump_work);
+}
+#endif
+
static int ath11k_pci_power_up(struct ath11k_base *ab)
{
struct ath11k_pci *ab_pci = ath11k_pci_priv(ab);
@@ -638,7 +821,7 @@ static int ath11k_pci_power_up(struct ath11k_base *ab)
return 0;
}
-static void ath11k_pci_power_down(struct ath11k_base *ab)
+static void ath11k_pci_power_down(struct ath11k_base *ab, bool is_suspend)
{
struct ath11k_pci *ab_pci = ath11k_pci_priv(ab);
@@ -649,7 +832,7 @@ static void ath11k_pci_power_down(struct ath11k_base *ab)
ath11k_pci_msi_disable(ab_pci);
- ath11k_mhi_stop(ab_pci);
+ ath11k_mhi_stop(ab_pci, is_suspend);
clear_bit(ATH11K_FLAG_DEVICE_INIT_DONE, &ab->dev_flags);
ath11k_pci_sw_reset(ab_pci->ab, false);
}
@@ -713,6 +896,9 @@ static const struct ath11k_hif_ops ath11k_pci_hif_ops = {
.ce_irq_enable = ath11k_pci_hif_ce_irq_enable,
.ce_irq_disable = ath11k_pci_hif_ce_irq_disable,
.get_ce_msi_idx = ath11k_pcic_get_ce_msi_idx,
+#ifdef CONFIG_DEV_COREDUMP
+ .coredump_download = ath11k_pci_coredump_download,
+#endif
};
static void ath11k_pci_read_hw_version(struct ath11k_base *ab, u32 *major, u32 *minor)
@@ -735,7 +921,7 @@ static int ath11k_pci_set_irq_affinity_hint(struct ath11k_pci *ab_pci,
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);
+ return irq_set_affinity_and_hint(ab_pci->pdev->irq, m);
}
static int ath11k_pci_probe(struct pci_dev *pdev,
@@ -743,7 +929,7 @@ static int ath11k_pci_probe(struct pci_dev *pdev,
{
struct ath11k_base *ab;
struct ath11k_pci *ab_pci;
- u32 soc_hw_version_major, soc_hw_version_minor, addr;
+ u32 soc_hw_version_major, soc_hw_version_minor;
int ret;
u32 sub_version;
@@ -769,8 +955,7 @@ static int ath11k_pci_probe(struct pci_dev *pdev,
* 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)
+ if (of_property_present(ab->dev->of_node, "memory-region"))
set_bit(ATH11K_FLAG_FIXED_MEM_RGN, &ab->dev_flags);
ret = ath11k_pci_claim(ab_pci, pdev);
@@ -939,6 +1124,8 @@ unsupported_wcn6855_soc:
return 0;
err_free_irq:
+ /* __free_irq() expects the caller to have cleared the affinity hint */
+ ath11k_pci_set_irq_affinity_hint(ab_pci, NULL);
ath11k_pcic_free_irq(ab);
err_ce_free:
@@ -973,17 +1160,21 @@ static void ath11k_pci_remove(struct pci_dev *pdev)
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_pci_power_down(ab, false);
ath11k_debugfs_soc_destroy(ab);
ath11k_qmi_deinit_service(ab);
+ ath11k_core_pm_notifier_unregister(ab);
goto qmi_fail;
}
set_bit(ATH11K_FLAG_UNREGISTERING, &ab->dev_flags);
+ cancel_work_sync(&ab->reset_work);
+ cancel_work_sync(&ab->dump_work);
ath11k_core_deinit(ab);
qmi_fail:
+ ath11k_fw_destroy(ab);
ath11k_mhi_unregister(ab_pci);
ath11k_pcic_free_irq(ab);
@@ -1001,7 +1192,7 @@ static void ath11k_pci_shutdown(struct pci_dev *pdev)
struct ath11k_pci *ab_pci = ath11k_pci_priv(ab);
ath11k_pci_set_irq_affinity_hint(ab_pci, NULL);
- ath11k_pci_power_down(ab);
+ ath11k_pci_power_down(ab, false);
}
static __maybe_unused int ath11k_pci_pm_suspend(struct device *dev)
@@ -1038,9 +1229,39 @@ static __maybe_unused int ath11k_pci_pm_resume(struct device *dev)
return ret;
}
-static SIMPLE_DEV_PM_OPS(ath11k_pci_pm_ops,
- ath11k_pci_pm_suspend,
- ath11k_pci_pm_resume);
+static __maybe_unused int ath11k_pci_pm_suspend_late(struct device *dev)
+{
+ struct ath11k_base *ab = dev_get_drvdata(dev);
+ int ret;
+
+ ret = ath11k_core_suspend_late(ab);
+ if (ret)
+ ath11k_warn(ab, "failed to late suspend core: %d\n", ret);
+
+ /* Similar to ath11k_pci_pm_suspend(), we return success here
+ * even error happens, to allow system suspend/hibernation survive.
+ */
+ return 0;
+}
+
+static __maybe_unused int ath11k_pci_pm_resume_early(struct device *dev)
+{
+ struct ath11k_base *ab = dev_get_drvdata(dev);
+ int ret;
+
+ ret = ath11k_core_resume_early(ab);
+ if (ret)
+ ath11k_warn(ab, "failed to early resume core: %d\n", ret);
+
+ return ret;
+}
+
+static const struct dev_pm_ops __maybe_unused ath11k_pci_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(ath11k_pci_pm_suspend,
+ ath11k_pci_pm_resume)
+ SET_LATE_SYSTEM_SLEEP_PM_OPS(ath11k_pci_pm_suspend_late,
+ ath11k_pci_pm_resume_early)
+};
static struct pci_driver ath11k_pci_driver = {
.name = "ath11k_pci",
diff --git a/drivers/net/wireless/ath/ath11k/qmi.c b/drivers/net/wireless/ath/ath11k/qmi.c
index 5759fc521316..2782f4723e41 100644
--- a/drivers/net/wireless/ath/ath11k/qmi.c
+++ b/drivers/net/wireless/ath/ath11k/qmi.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-2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#include <linux/elf.h>
@@ -1957,13 +1957,15 @@ 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->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);
+ if (!ab->qmi.target_mem[i].anyaddr)
+ continue;
- if (!ab->qmi.target_mem[i].vaddr)
+ if (ab->hw_params.fixed_mem_region ||
+ test_bit(ATH11K_FLAG_FIXED_MEM_RGN, &ab->dev_flags)) {
+ iounmap(ab->qmi.target_mem[i].iaddr);
+ ab->qmi.target_mem[i].iaddr = NULL;
continue;
+ }
dma_free_coherent(ab->dev,
ab->qmi.target_mem[i].prev_size,
@@ -1991,6 +1993,15 @@ static int ath11k_qmi_alloc_target_mem_chunk(struct ath11k_base *ab)
chunk->prev_size == chunk->size)
continue;
+ if (ab->qmi.mem_seg_count <= ATH11K_QMI_FW_MEM_REQ_SEGMENT_CNT) {
+ ath11k_dbg(ab, ATH11K_DBG_QMI,
+ "size/type mismatch (current %d %u) (prev %d %u), try later with small size\n",
+ chunk->size, chunk->type,
+ chunk->prev_size, chunk->prev_type);
+ ab->qmi.target_mem_delayed = true;
+ return 0;
+ }
+
/* cannot reuse the existing chunk */
dma_free_coherent(ab->dev, chunk->prev_size,
chunk->vaddr, chunk->paddr);
@@ -2070,7 +2081,7 @@ static int ath11k_qmi_assign_target_mem_chunk(struct ath11k_base *ab)
break;
case BDF_MEM_REGION_TYPE:
ab->qmi.target_mem[idx].paddr = ab->hw_params.bdf_addr;
- ab->qmi.target_mem[idx].vaddr = NULL;
+ ab->qmi.target_mem[idx].iaddr = NULL;
ab->qmi.target_mem[idx].size = ab->qmi.target_mem[i].size;
ab->qmi.target_mem[idx].type = ab->qmi.target_mem[i].type;
idx++;
@@ -2093,10 +2104,11 @@ static int ath11k_qmi_assign_target_mem_chunk(struct ath11k_base *ab)
} else {
ab->qmi.target_mem[idx].paddr =
ATH11K_QMI_CALDB_ADDRESS;
+ ab->qmi.target_mem[idx].iaddr = NULL;
}
} else {
ab->qmi.target_mem[idx].paddr = 0;
- ab->qmi.target_mem[idx].vaddr = NULL;
+ ab->qmi.target_mem[idx].iaddr = NULL;
}
ab->qmi.target_mem[idx].size = ab->qmi.target_mem[i].size;
ab->qmi.target_mem[idx].type = ab->qmi.target_mem[i].type;
@@ -2884,7 +2896,7 @@ int ath11k_qmi_fwreset_from_cold_boot(struct ath11k_base *ab)
}
/* reset the firmware */
- ath11k_hif_power_down(ab);
+ ath11k_hif_power_down(ab, false);
ath11k_hif_power_up(ab);
ath11k_dbg(ab, ATH11K_DBG_QMI, "exit wait for cold boot done\n");
return 0;
diff --git a/drivers/net/wireless/ath/ath11k/qmi.h b/drivers/net/wireless/ath/ath11k/qmi.h
index 7e06d100af57..7968ab122b65 100644
--- a/drivers/net/wireless/ath/ath11k/qmi.h
+++ b/drivers/net/wireless/ath/ath11k/qmi.h
@@ -1,7 +1,7 @@
/* SPDX-License-Identifier: BSD-3-Clause-Clear */
/*
* Copyright (c) 2018-2019 The Linux Foundation. All rights reserved.
- * Copyright (c) 2021-2023 Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#ifndef ATH11K_QMI_H
@@ -102,8 +102,11 @@ struct target_mem_chunk {
u32 prev_size;
u32 prev_type;
dma_addr_t paddr;
- u32 *vaddr;
- void __iomem *iaddr;
+ union {
+ u32 *vaddr;
+ void __iomem *iaddr;
+ void *anyaddr;
+ };
};
struct target_info {
@@ -154,6 +157,7 @@ struct ath11k_qmi {
#define BDF_MEM_REGION_TYPE 0x2
#define M3_DUMP_REGION_TYPE 0x3
#define CALDB_MEM_REGION_TYPE 0x4
+#define PAGEABLE_MEM_REGION_TYPE 0x9
struct qmi_wlanfw_host_cap_req_msg_v01 {
u8 num_clients_valid;
diff --git a/drivers/net/wireless/ath/ath11k/reg.c b/drivers/net/wireless/ath/ath11k/reg.c
index b0f289784dd3..d62a2014315a 100644
--- a/drivers/net/wireless/ath/ath11k/reg.c
+++ b/drivers/net/wireless/ath/ath11k/reg.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-2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#include <linux/rtnetlink.h>
@@ -55,6 +55,19 @@ ath11k_reg_notifier(struct wiphy *wiphy, struct regulatory_request *request)
ath11k_dbg(ar->ab, ATH11K_DBG_REG,
"Regulatory Notification received for %s\n", wiphy_name(wiphy));
+ if (request->initiator == NL80211_REGDOM_SET_BY_DRIVER) {
+ ath11k_dbg(ar->ab, ATH11K_DBG_REG,
+ "driver initiated regd update\n");
+ if (ar->state != ATH11K_STATE_ON)
+ return;
+
+ ret = ath11k_reg_update_chan_list(ar, true);
+ if (ret)
+ ath11k_warn(ar->ab, "failed to update channel list: %d\n", ret);
+
+ return;
+ }
+
/* Currently supporting only General User Hints. Cell base user
* hints to be handled later.
* Hints from other sources like Core, Beacons are not expected for
@@ -111,32 +124,7 @@ int ath11k_reg_update_chan_list(struct ath11k *ar, bool wait)
struct channel_param *ch;
enum nl80211_band band;
int num_channels = 0;
- 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,
- "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,
- "hw scan wait left time %d\n", left);
- }
+ int i, ret = 0;
if (ar->state == ATH11K_STATE_RESTARTING)
return 0;
@@ -218,6 +206,16 @@ int ath11k_reg_update_chan_list(struct ath11k *ar, bool wait)
}
}
+ if (wait) {
+ spin_lock_bh(&ar->data_lock);
+ list_add_tail(&params->list, &ar->channel_update_queue);
+ spin_unlock_bh(&ar->data_lock);
+
+ queue_work(ar->ab->workqueue, &ar->channel_update_work);
+
+ return 0;
+ }
+
ret = ath11k_wmi_send_scan_chan_list_cmd(ar, params);
kfree(params);
@@ -293,12 +291,6 @@ int ath11k_regd_update(struct ath11k *ar)
if (ret)
goto err;
- if (ar->state == ATH11K_STATE_ON) {
- ret = ath11k_reg_update_chan_list(ar, true);
- if (ret)
- goto err;
- }
-
return 0;
err:
ath11k_warn(ab, "failed to perform regd update : %d\n", ret);
@@ -804,6 +796,54 @@ ret:
return new_regd;
}
+void ath11k_regd_update_chan_list_work(struct work_struct *work)
+{
+ struct ath11k *ar = container_of(work, struct ath11k,
+ channel_update_work);
+ struct scan_chan_list_params *params;
+ struct list_head local_update_list;
+ int left;
+
+ INIT_LIST_HEAD(&local_update_list);
+
+ spin_lock_bh(&ar->data_lock);
+ list_splice_tail_init(&ar->channel_update_queue, &local_update_list);
+ spin_unlock_bh(&ar->data_lock);
+
+ while ((params = list_first_entry_or_null(&local_update_list,
+ struct scan_chan_list_params,
+ list))) {
+ if (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 ((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);
+ }
+
+ ath11k_wmi_send_scan_chan_list_cmd(ar, params);
+ list_del(&params->list);
+ kfree(params);
+ }
+}
+
static bool ath11k_reg_is_world_alpha(char *alpha)
{
if (alpha[0] == '0' && alpha[1] == '0')
@@ -977,6 +1017,7 @@ void ath11k_regd_update_work(struct work_struct *work)
void ath11k_reg_init(struct ath11k *ar)
{
ar->hw->wiphy->regulatory_flags = REGULATORY_WIPHY_SELF_MANAGED;
+ ar->hw->wiphy->flags |= WIPHY_FLAG_NOTIFY_REGDOM_BY_DRIVER;
ar->hw->wiphy->reg_notifier = ath11k_reg_notifier;
}
diff --git a/drivers/net/wireless/ath/ath11k/reg.h b/drivers/net/wireless/ath/ath11k/reg.h
index 263ea9061948..72b483594015 100644
--- a/drivers/net/wireless/ath/ath11k/reg.h
+++ b/drivers/net/wireless/ath/ath11k/reg.h
@@ -1,7 +1,7 @@
/* SPDX-License-Identifier: BSD-3-Clause-Clear */
/*
* Copyright (c) 2019 The Linux Foundation. All rights reserved.
- * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) 2022-2025 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#ifndef ATH11K_REG_H
@@ -33,6 +33,7 @@ void ath11k_reg_init(struct ath11k *ar);
void ath11k_reg_reset_info(struct cur_regulatory_info *reg_info);
void ath11k_reg_free(struct ath11k_base *ab);
void ath11k_regd_update_work(struct work_struct *work);
+void ath11k_regd_update_chan_list_work(struct work_struct *work);
struct ieee80211_regdomain *
ath11k_reg_build_regd(struct ath11k_base *ab,
struct cur_regulatory_info *reg_info, bool intersect,
diff --git a/drivers/net/wireless/ath/ath11k/testmode.c b/drivers/net/wireless/ath/ath11k/testmode.c
index 302d66092b97..a9751ea2a0b7 100644
--- a/drivers/net/wireless/ath/ath11k/testmode.c
+++ b/drivers/net/wireless/ath/ath11k/testmode.c
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: BSD-3-Clause-Clear
/*
* Copyright (c) 2018-2019 The Linux Foundation. All rights reserved.
- * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) 2023-2025 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#include "testmode.h"
@@ -10,18 +10,18 @@
#include "wmi.h"
#include "hw.h"
#include "core.h"
-#include "testmode_i.h"
+#include "../testmode_i.h"
#define ATH11K_FTM_SEGHDR_CURRENT_SEQ GENMASK(3, 0)
#define ATH11K_FTM_SEGHDR_TOTAL_SEGMENTS GENMASK(7, 4)
-static const struct nla_policy ath11k_tm_policy[ATH11K_TM_ATTR_MAX + 1] = {
- [ATH11K_TM_ATTR_CMD] = { .type = NLA_U32 },
- [ATH11K_TM_ATTR_DATA] = { .type = NLA_BINARY,
- .len = ATH11K_TM_DATA_MAX_LEN },
- [ATH11K_TM_ATTR_WMI_CMDID] = { .type = NLA_U32 },
- [ATH11K_TM_ATTR_VERSION_MAJOR] = { .type = NLA_U32 },
- [ATH11K_TM_ATTR_VERSION_MINOR] = { .type = NLA_U32 },
+static const struct nla_policy ath11k_tm_policy[ATH_TM_ATTR_MAX + 1] = {
+ [ATH_TM_ATTR_CMD] = { .type = NLA_U32 },
+ [ATH_TM_ATTR_DATA] = { .type = NLA_BINARY,
+ .len = ATH_TM_DATA_MAX_LEN },
+ [ATH_TM_ATTR_WMI_CMDID] = { .type = NLA_U32 },
+ [ATH_TM_ATTR_VERSION_MAJOR] = { .type = NLA_U32 },
+ [ATH_TM_ATTR_VERSION_MINOR] = { .type = NLA_U32 },
};
static struct ath11k *ath11k_tm_get_ar(struct ath11k_base *ab)
@@ -73,9 +73,9 @@ static void ath11k_tm_wmi_event_unsegmented(struct ath11k_base *ab, u32 cmd_id,
goto out;
}
- if (nla_put_u32(nl_skb, ATH11K_TM_ATTR_CMD, ATH11K_TM_CMD_WMI) ||
- nla_put_u32(nl_skb, ATH11K_TM_ATTR_WMI_CMDID, cmd_id) ||
- nla_put(nl_skb, ATH11K_TM_ATTR_DATA, skb->len, skb->data)) {
+ if (nla_put_u32(nl_skb, ATH_TM_ATTR_CMD, ATH_TM_CMD_WMI) ||
+ nla_put_u32(nl_skb, ATH_TM_ATTR_WMI_CMDID, cmd_id) ||
+ nla_put(nl_skb, ATH_TM_ATTR_DATA, skb->len, skb->data)) {
ath11k_warn(ab, "failed to populate testmode unsegmented event\n");
kfree_skb(nl_skb);
goto out;
@@ -107,7 +107,7 @@ static int ath11k_tm_process_event(struct ath11k_base *ab, u32 cmd_id,
u32 pdev_id;
ath11k_dbg(ab, ATH11K_DBG_TESTMODE,
- "event wmi cmd_id %d ftm event msg %pK datalen %d\n",
+ "event wmi cmd_id %d ftm event msg %p datalen %d\n",
cmd_id, ftm_msg, length);
ath11k_dbg_dump(ab, ATH11K_DBG_TESTMODE, NULL, "", ftm_msg, length);
pdev_id = DP_HW2SW_MACID(ftm_msg->seg_hdr.pdev_id);
@@ -140,7 +140,7 @@ static int ath11k_tm_process_event(struct ath11k_base *ab, u32 cmd_id,
data_pos = ab->testmode.data_pos;
- if ((data_pos + datalen) > ATH11K_FTM_EVENT_MAX_BUF_LENGTH) {
+ if ((data_pos + datalen) > ATH_FTM_EVENT_MAX_BUF_LENGTH) {
ath11k_warn(ab, "Invalid ftm event length at %d: %d\n",
data_pos, datalen);
ret = -EINVAL;
@@ -172,10 +172,10 @@ static int ath11k_tm_process_event(struct ath11k_base *ab, u32 cmd_id,
goto out;
}
- if (nla_put_u32(nl_skb, ATH11K_TM_ATTR_CMD,
- ATH11K_TM_CMD_WMI_FTM) ||
- nla_put_u32(nl_skb, ATH11K_TM_ATTR_WMI_CMDID, cmd_id) ||
- nla_put(nl_skb, ATH11K_TM_ATTR_DATA, data_pos,
+ if (nla_put_u32(nl_skb, ATH_TM_ATTR_CMD,
+ ATH_TM_CMD_WMI_FTM) ||
+ nla_put_u32(nl_skb, ATH_TM_ATTR_WMI_CMDID, cmd_id) ||
+ nla_put(nl_skb, ATH_TM_ATTR_DATA, data_pos,
&ab->testmode.eventdata[0])) {
ath11k_warn(ab, "failed to populate segmented testmode event");
kfree_skb(nl_skb);
@@ -235,23 +235,23 @@ static int ath11k_tm_cmd_get_version(struct ath11k *ar, struct nlattr *tb[])
ath11k_dbg(ar->ab, ATH11K_DBG_TESTMODE,
"cmd get version_major %d version_minor %d\n",
- ATH11K_TESTMODE_VERSION_MAJOR,
- ATH11K_TESTMODE_VERSION_MINOR);
+ ATH_TESTMODE_VERSION_MAJOR,
+ ATH_TESTMODE_VERSION_MINOR);
skb = cfg80211_testmode_alloc_reply_skb(ar->hw->wiphy,
nla_total_size(sizeof(u32)));
if (!skb)
return -ENOMEM;
- ret = nla_put_u32(skb, ATH11K_TM_ATTR_VERSION_MAJOR,
- ATH11K_TESTMODE_VERSION_MAJOR);
+ ret = nla_put_u32(skb, ATH_TM_ATTR_VERSION_MAJOR,
+ ATH_TESTMODE_VERSION_MAJOR);
if (ret) {
kfree_skb(skb);
return ret;
}
- ret = nla_put_u32(skb, ATH11K_TM_ATTR_VERSION_MINOR,
- ATH11K_TESTMODE_VERSION_MINOR);
+ ret = nla_put_u32(skb, ATH_TM_ATTR_VERSION_MINOR,
+ ATH_TESTMODE_VERSION_MINOR);
if (ret) {
kfree_skb(skb);
return ret;
@@ -277,7 +277,7 @@ static int ath11k_tm_cmd_testmode_start(struct ath11k *ar, struct nlattr *tb[])
goto err;
}
- ar->ab->testmode.eventdata = kzalloc(ATH11K_FTM_EVENT_MAX_BUF_LENGTH,
+ ar->ab->testmode.eventdata = kzalloc(ATH_FTM_EVENT_MAX_BUF_LENGTH,
GFP_KERNEL);
if (!ar->ab->testmode.eventdata) {
ret = -ENOMEM;
@@ -310,25 +310,25 @@ static int ath11k_tm_cmd_wmi(struct ath11k *ar, struct nlattr *tb[],
mutex_lock(&ar->conf_mutex);
- if (!tb[ATH11K_TM_ATTR_DATA]) {
+ if (!tb[ATH_TM_ATTR_DATA]) {
ret = -EINVAL;
goto out;
}
- if (!tb[ATH11K_TM_ATTR_WMI_CMDID]) {
+ if (!tb[ATH_TM_ATTR_WMI_CMDID]) {
ret = -EINVAL;
goto out;
}
- buf = nla_data(tb[ATH11K_TM_ATTR_DATA]);
- buf_len = nla_len(tb[ATH11K_TM_ATTR_DATA]);
+ buf = nla_data(tb[ATH_TM_ATTR_DATA]);
+ buf_len = nla_len(tb[ATH_TM_ATTR_DATA]);
if (!buf_len) {
ath11k_warn(ar->ab, "No data present in testmode wmi command\n");
ret = -EINVAL;
goto out;
}
- cmd_id = nla_get_u32(tb[ATH11K_TM_ATTR_WMI_CMDID]);
+ cmd_id = nla_get_u32(tb[ATH_TM_ATTR_WMI_CMDID]);
/* Make sure that the buffer length is long enough to
* hold TLV and pdev/vdev id.
@@ -409,13 +409,13 @@ static int ath11k_tm_cmd_wmi_ftm(struct ath11k *ar, struct nlattr *tb[])
goto out;
}
- if (!tb[ATH11K_TM_ATTR_DATA]) {
+ if (!tb[ATH_TM_ATTR_DATA]) {
ret = -EINVAL;
goto out;
}
- buf = nla_data(tb[ATH11K_TM_ATTR_DATA]);
- buf_len = nla_len(tb[ATH11K_TM_ATTR_DATA]);
+ buf = nla_data(tb[ATH_TM_ATTR_DATA]);
+ buf_len = nla_len(tb[ATH_TM_ATTR_DATA]);
cmd_id = WMI_PDEV_UTF_CMDID;
ath11k_dbg(ar->ab, ATH11K_DBG_TESTMODE,
@@ -476,25 +476,25 @@ int ath11k_tm_cmd(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
void *data, int len)
{
struct ath11k *ar = hw->priv;
- struct nlattr *tb[ATH11K_TM_ATTR_MAX + 1];
+ struct nlattr *tb[ATH_TM_ATTR_MAX + 1];
int ret;
- ret = nla_parse(tb, ATH11K_TM_ATTR_MAX, data, len, ath11k_tm_policy,
+ ret = nla_parse(tb, ATH_TM_ATTR_MAX, data, len, ath11k_tm_policy,
NULL);
if (ret)
return ret;
- if (!tb[ATH11K_TM_ATTR_CMD])
+ if (!tb[ATH_TM_ATTR_CMD])
return -EINVAL;
- switch (nla_get_u32(tb[ATH11K_TM_ATTR_CMD])) {
- case ATH11K_TM_CMD_GET_VERSION:
+ switch (nla_get_u32(tb[ATH_TM_ATTR_CMD])) {
+ case ATH_TM_CMD_GET_VERSION:
return ath11k_tm_cmd_get_version(ar, tb);
- case ATH11K_TM_CMD_WMI:
+ case ATH_TM_CMD_WMI:
return ath11k_tm_cmd_wmi(ar, tb, vif);
- case ATH11K_TM_CMD_TESTMODE_START:
+ case ATH_TM_CMD_TESTMODE_START:
return ath11k_tm_cmd_testmode_start(ar, tb);
- case ATH11K_TM_CMD_WMI_FTM:
+ case ATH_TM_CMD_WMI_FTM:
return ath11k_tm_cmd_wmi_ftm(ar, tb);
default:
return -EOPNOTSUPP;
diff --git a/drivers/net/wireless/ath/ath11k/wmi.c b/drivers/net/wireless/ath/ath11k/wmi.c
index 87abfa547529..d7f852bebf4a 100644
--- a/drivers/net/wireless/ath/ath11k/wmi.c
+++ b/drivers/net/wireless/ath/ath11k/wmi.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-2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#include <linux/skbuff.h>
#include <linux/ctype.h>
@@ -2662,7 +2662,8 @@ int ath11k_wmi_send_scan_chan_list_cmd(struct ath11k *ar,
}
int ath11k_wmi_send_wmm_update_cmd_tlv(struct ath11k *ar, u32 vdev_id,
- struct wmi_wmm_params_all_arg *param)
+ struct wmi_wmm_params_all_arg *param,
+ enum wmi_wmm_params_type wmm_param_type)
{
struct ath11k_pdev_wmi *wmi = ar->wmi;
struct wmi_vdev_set_wmm_params_cmd *cmd;
@@ -2681,7 +2682,7 @@ int ath11k_wmi_send_wmm_update_cmd_tlv(struct ath11k *ar, u32 vdev_id,
FIELD_PREP(WMI_TLV_LEN, sizeof(*cmd) - TLV_HDR_SIZE);
cmd->vdev_id = vdev_id;
- cmd->wmm_param_type = 0;
+ cmd->wmm_param_type = wmm_param_type;
for (ac = 0; ac < WME_NUM_AC; ac++) {
switch (ac) {
@@ -2714,8 +2715,8 @@ int ath11k_wmi_send_wmm_update_cmd_tlv(struct ath11k *ar, u32 vdev_id,
wmm_param->no_ack = wmi_wmm_arg->no_ack;
ath11k_dbg(ar->ab, ATH11K_DBG_WMI,
- "wmm set ac %d aifs %d cwmin %d cwmax %d txop %d acm %d no_ack %d\n",
- ac, wmm_param->aifs, wmm_param->cwmin,
+ "wmm set type %d ac %d aifs %d cwmin %d cwmax %d txop %d acm %d no_ack %d\n",
+ wmm_param_type, ac, wmm_param->aifs, wmm_param->cwmin,
wmm_param->cwmax, wmm_param->txoplimit,
wmm_param->acm, wmm_param->no_ack);
}
diff --git a/drivers/net/wireless/ath/ath11k/wmi.h b/drivers/net/wireless/ath/ath11k/wmi.h
index 8982b909c821..9fcffaa2f383 100644
--- a/drivers/net/wireless/ath/ath11k/wmi.h
+++ b/drivers/net/wireless/ath/ath11k/wmi.h
@@ -1,7 +1,7 @@
/* SPDX-License-Identifier: BSD-3-Clause-Clear */
/*
* Copyright (c) 2018-2019 The Linux Foundation. All rights reserved.
- * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#ifndef ATH11K_WMI_H
@@ -3817,6 +3817,7 @@ struct wmi_stop_scan_cmd {
};
struct scan_chan_list_params {
+ struct list_head list;
u32 pdev_id;
u16 nallchans;
struct channel_param ch_param[];
@@ -6346,6 +6347,11 @@ enum wmi_sta_keepalive_method {
#define WMI_STA_KEEPALIVE_INTERVAL_DEFAULT 30
#define WMI_STA_KEEPALIVE_INTERVAL_DISABLE 0
+enum wmi_wmm_params_type {
+ WMI_WMM_PARAM_TYPE_LEGACY = 0,
+ WMI_WMM_PARAM_TYPE_11AX_MU_EDCA = 1,
+};
+
const void **ath11k_wmi_tlv_parse_alloc(struct ath11k_base *ab,
struct sk_buff *skb, gfp_t gfp);
int ath11k_wmi_cmd_send(struct ath11k_pdev_wmi *wmi, struct sk_buff *skb,
@@ -6402,7 +6408,8 @@ int ath11k_wmi_send_scan_start_cmd(struct ath11k *ar,
int ath11k_wmi_send_scan_stop_cmd(struct ath11k *ar,
struct scan_cancel_param *param);
int ath11k_wmi_send_wmm_update_cmd_tlv(struct ath11k *ar, u32 vdev_id,
- struct wmi_wmm_params_all_arg *param);
+ struct wmi_wmm_params_all_arg *param,
+ enum wmi_wmm_params_type wmm_param_type);
int ath11k_wmi_pdev_suspend(struct ath11k *ar, u32 suspend_opt,
u32 pdev_id);
int ath11k_wmi_pdev_resume(struct ath11k *ar, u32 pdev_id);
diff --git a/drivers/net/wireless/ath/ath12k/Kconfig b/drivers/net/wireless/ath/ath12k/Kconfig
index 52a1bb19e3da..b3b15e1eb282 100644
--- a/drivers/net/wireless/ath/ath12k/Kconfig
+++ b/drivers/net/wireless/ath/ath12k/Kconfig
@@ -15,6 +15,14 @@ config ATH12K
If you choose to build a module, it'll be called ath12k.
+config ATH12K_AHB
+ bool "QTI ath12k AHB support"
+ depends on ATH12K && REMOTEPROC
+ select QCOM_MDT_LOADER
+ select QCOM_SCM
+ help
+ Enable support for Ath12k AHB bus chipsets, example IPQ5332.
+
config ATH12K_DEBUG
bool "ath12k debugging"
depends on ATH12K
diff --git a/drivers/net/wireless/ath/ath12k/Makefile b/drivers/net/wireless/ath/ath12k/Makefile
index b5bb3e2599cd..d95ee525a6cd 100644
--- a/drivers/net/wireless/ath/ath12k/Makefile
+++ b/drivers/net/wireless/ath/ath12k/Makefile
@@ -23,11 +23,13 @@ ath12k-y += core.o \
fw.o \
p2p.o
-ath12k-$(CONFIG_ATH12K_DEBUGFS) += debugfs.o debugfs_htt_stats.o
+ath12k-$(CONFIG_ATH12K_AHB) += ahb.o
+ath12k-$(CONFIG_ATH12K_DEBUGFS) += debugfs.o debugfs_htt_stats.o debugfs_sta.o
ath12k-$(CONFIG_ACPI) += acpi.o
ath12k-$(CONFIG_ATH12K_TRACING) += trace.o
ath12k-$(CONFIG_PM) += wow.o
ath12k-$(CONFIG_ATH12K_COREDUMP) += coredump.o
+ath12k-$(CONFIG_NL80211_TESTMODE) += testmode.o
# for tracing framework to find trace.h
CFLAGS_trace.o := -I$(src)
diff --git a/drivers/net/wireless/ath/ath12k/acpi.c b/drivers/net/wireless/ath/ath12k/acpi.c
index 0555d35aab47..d81367ce6929 100644
--- a/drivers/net/wireless/ath/ath12k/acpi.c
+++ b/drivers/net/wireless/ath/ath12k/acpi.c
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: BSD-3-Clause-Clear
/*
* Copyright (c) 2018-2021 The Linux Foundation. All rights reserved.
- * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#include "core.h"
@@ -12,7 +12,7 @@ static int ath12k_acpi_dsm_get_data(struct ath12k_base *ab, int func)
{
union acpi_object *obj;
acpi_handle root_handle;
- int ret;
+ int ret, i;
root_handle = ACPI_HANDLE(ab->dev);
if (!root_handle) {
@@ -29,9 +29,48 @@ static int ath12k_acpi_dsm_get_data(struct ath12k_base *ab, int func)
}
if (obj->type == ACPI_TYPE_INTEGER) {
- ab->acpi.func_bit = obj->integer.value;
+ switch (func) {
+ case ATH12K_ACPI_DSM_FUNC_SUPPORT_FUNCS:
+ ab->acpi.func_bit = obj->integer.value;
+ break;
+ case ATH12K_ACPI_DSM_FUNC_DISABLE_FLAG:
+ ab->acpi.bit_flag = obj->integer.value;
+ break;
+ }
+ } else if (obj->type == ACPI_TYPE_STRING) {
+ switch (func) {
+ case ATH12K_ACPI_DSM_FUNC_BDF_EXT:
+ if (obj->string.length <= ATH12K_ACPI_BDF_ANCHOR_STRING_LEN ||
+ obj->string.length > ATH12K_ACPI_BDF_MAX_LEN ||
+ memcmp(obj->string.pointer, ATH12K_ACPI_BDF_ANCHOR_STRING,
+ ATH12K_ACPI_BDF_ANCHOR_STRING_LEN)) {
+ ath12k_warn(ab, "invalid ACPI DSM BDF size: %d\n",
+ obj->string.length);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ memcpy(ab->acpi.bdf_string, obj->string.pointer,
+ obj->buffer.length);
+
+ break;
+ }
} else if (obj->type == ACPI_TYPE_BUFFER) {
switch (func) {
+ case ATH12K_ACPI_DSM_FUNC_SUPPORT_FUNCS:
+ if (obj->buffer.length < ATH12K_ACPI_DSM_FUNC_MIN_BITMAP_SIZE ||
+ obj->buffer.length > ATH12K_ACPI_DSM_FUNC_MAX_BITMAP_SIZE) {
+ ath12k_warn(ab, "invalid ACPI DSM func size: %d\n",
+ obj->buffer.length);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ ab->acpi.func_bit = 0;
+ for (i = 0; i < obj->buffer.length; i++)
+ ab->acpi.func_bit += obj->buffer.pointer[i] << (i * 8);
+
+ break;
case ATH12K_ACPI_DSM_FUNC_TAS_CFG:
if (obj->buffer.length != ATH12K_ACPI_DSM_TAS_CFG_SIZE) {
ath12k_warn(ab, "invalid ACPI DSM TAS config size: %d\n",
@@ -247,24 +286,118 @@ static int ath12k_acpi_set_tas_params(struct ath12k_base *ab)
return 0;
}
+bool ath12k_acpi_get_disable_rfkill(struct ath12k_base *ab)
+{
+ return ab->acpi.acpi_disable_rfkill;
+}
+
+bool ath12k_acpi_get_disable_11be(struct ath12k_base *ab)
+{
+ return ab->acpi.acpi_disable_11be;
+}
+
+void ath12k_acpi_set_dsm_func(struct ath12k_base *ab)
+{
+ int ret;
+ u8 *buf;
+
+ if (!ab->hw_params->acpi_guid)
+ /* not supported with this hardware */
+ return;
+
+ if (ab->acpi.acpi_tas_enable) {
+ ret = ath12k_acpi_set_tas_params(ab);
+ if (ret) {
+ ath12k_warn(ab, "failed to send ACPI TAS parameters: %d\n", ret);
+ return;
+ }
+ }
+
+ if (ab->acpi.acpi_bios_sar_enable) {
+ ret = ath12k_acpi_set_bios_sar_params(ab);
+ if (ret) {
+ ath12k_warn(ab, "failed to send ACPI BIOS SAR: %d\n", ret);
+ return;
+ }
+ }
+
+ if (ab->acpi.acpi_cca_enable) {
+ buf = ab->acpi.cca_data + ATH12K_ACPI_CCA_THR_OFFSET_DATA_OFFSET;
+ ret = ath12k_wmi_set_bios_cmd(ab,
+ WMI_BIOS_PARAM_CCA_THRESHOLD_TYPE,
+ buf,
+ ATH12K_ACPI_CCA_THR_OFFSET_LEN);
+ if (ret) {
+ ath12k_warn(ab, "failed to set ACPI DSM CCA threshold: %d\n",
+ ret);
+ return;
+ }
+ }
+
+ if (ab->acpi.acpi_band_edge_enable) {
+ ret = ath12k_wmi_set_bios_cmd(ab,
+ WMI_BIOS_PARAM_TYPE_BANDEDGE,
+ ab->acpi.band_edge_power,
+ sizeof(ab->acpi.band_edge_power));
+ if (ret) {
+ ath12k_warn(ab,
+ "failed to set ACPI DSM band edge channel power: %d\n",
+ ret);
+ return;
+ }
+ }
+}
+
int ath12k_acpi_start(struct ath12k_base *ab)
{
acpi_status status;
- u8 *buf;
int ret;
+ ab->acpi.acpi_tas_enable = false;
+ ab->acpi.acpi_disable_11be = false;
+ ab->acpi.acpi_disable_rfkill = false;
+ ab->acpi.acpi_bios_sar_enable = false;
+ ab->acpi.acpi_cca_enable = false;
+ ab->acpi.acpi_band_edge_enable = false;
+ ab->acpi.acpi_enable_bdf = false;
+ ab->acpi.bdf_string[0] = '\0';
+
if (!ab->hw_params->acpi_guid)
/* not supported with this hardware */
return 0;
- ab->acpi.acpi_tas_enable = false;
-
ret = ath12k_acpi_dsm_get_data(ab, ATH12K_ACPI_DSM_FUNC_SUPPORT_FUNCS);
if (ret) {
ath12k_dbg(ab, ATH12K_DBG_BOOT, "failed to get ACPI DSM data: %d\n", ret);
return ret;
}
+ if (ATH12K_ACPI_FUNC_BIT_VALID(ab->acpi, ATH12K_ACPI_FUNC_BIT_DISABLE_FLAG)) {
+ ret = ath12k_acpi_dsm_get_data(ab, ATH12K_ACPI_DSM_FUNC_DISABLE_FLAG);
+ if (ret) {
+ ath12k_warn(ab, "failed to get ACPI DISABLE FLAG: %d\n", ret);
+ return ret;
+ }
+
+ if (ATH12K_ACPI_CHEK_BIT_VALID(ab->acpi,
+ ATH12K_ACPI_DSM_DISABLE_11BE_BIT))
+ ab->acpi.acpi_disable_11be = true;
+
+ if (!ATH12K_ACPI_CHEK_BIT_VALID(ab->acpi,
+ ATH12K_ACPI_DSM_DISABLE_RFKILL_BIT))
+ ab->acpi.acpi_disable_rfkill = true;
+ }
+
+ if (ATH12K_ACPI_FUNC_BIT_VALID(ab->acpi, ATH12K_ACPI_FUNC_BIT_BDF_EXT)) {
+ ret = ath12k_acpi_dsm_get_data(ab, ATH12K_ACPI_DSM_FUNC_BDF_EXT);
+ if (ret || ab->acpi.bdf_string[0] == '\0') {
+ ath12k_warn(ab, "failed to get ACPI BDF EXT: %d\n", ret);
+ return ret;
+ }
+
+ ab->acpi.acpi_enable_bdf = true;
+ }
+
if (ATH12K_ACPI_FUNC_BIT_VALID(ab->acpi, ATH12K_ACPI_FUNC_BIT_TAS_CFG)) {
ret = ath12k_acpi_dsm_get_data(ab, ATH12K_ACPI_DSM_FUNC_TAS_CFG);
if (ret) {
@@ -308,20 +441,6 @@ int ath12k_acpi_start(struct ath12k_base *ab)
ab->acpi.acpi_bios_sar_enable = true;
}
- if (ab->acpi.acpi_tas_enable) {
- ret = ath12k_acpi_set_tas_params(ab);
- if (ret) {
- ath12k_warn(ab, "failed to send ACPI parameters: %d\n", ret);
- return ret;
- }
- }
-
- if (ab->acpi.acpi_bios_sar_enable) {
- ret = ath12k_acpi_set_bios_sar_params(ab);
- if (ret)
- return ret;
- }
-
if (ATH12K_ACPI_FUNC_BIT_VALID(ab->acpi, ATH12K_ACPI_FUNC_BIT_CCA)) {
ret = ath12k_acpi_dsm_get_data(ab, ATH12K_ACPI_DSM_FUNC_INDEX_CCA);
if (ret) {
@@ -332,18 +451,8 @@ int ath12k_acpi_start(struct ath12k_base *ab)
if (ab->acpi.cca_data[0] == ATH12K_ACPI_CCA_THR_VERSION &&
ab->acpi.cca_data[ATH12K_ACPI_CCA_THR_OFFSET_DATA_OFFSET] ==
- ATH12K_ACPI_CCA_THR_ENABLE_FLAG) {
- buf = ab->acpi.cca_data + ATH12K_ACPI_CCA_THR_OFFSET_DATA_OFFSET;
- ret = ath12k_wmi_set_bios_cmd(ab,
- WMI_BIOS_PARAM_CCA_THRESHOLD_TYPE,
- buf,
- ATH12K_ACPI_CCA_THR_OFFSET_LEN);
- if (ret) {
- ath12k_warn(ab, "failed to set ACPI DSM CCA threshold: %d\n",
- ret);
- return ret;
- }
- }
+ ATH12K_ACPI_CCA_THR_ENABLE_FLAG)
+ ab->acpi.acpi_cca_enable = true;
}
if (ATH12K_ACPI_FUNC_BIT_VALID(ab->acpi,
@@ -356,18 +465,8 @@ int ath12k_acpi_start(struct ath12k_base *ab)
}
if (ab->acpi.band_edge_power[0] == ATH12K_ACPI_BAND_EDGE_VERSION &&
- ab->acpi.band_edge_power[1] == ATH12K_ACPI_BAND_EDGE_ENABLE_FLAG) {
- ret = ath12k_wmi_set_bios_cmd(ab,
- WMI_BIOS_PARAM_TYPE_BANDEDGE,
- ab->acpi.band_edge_power,
- sizeof(ab->acpi.band_edge_power));
- if (ret) {
- ath12k_warn(ab,
- "failed to set ACPI DSM band edge channel power: %d\n",
- ret);
- return ret;
- }
- }
+ ab->acpi.band_edge_power[1] == ATH12K_ACPI_BAND_EDGE_ENABLE_FLAG)
+ ab->acpi.acpi_band_edge_enable = true;
}
status = acpi_install_notify_handler(ACPI_HANDLE(ab->dev),
@@ -383,6 +482,21 @@ int ath12k_acpi_start(struct ath12k_base *ab)
return 0;
}
+int ath12k_acpi_check_bdf_variant_name(struct ath12k_base *ab)
+{
+ size_t max_len = sizeof(ab->qmi.target.bdf_ext);
+
+ if (!ab->acpi.acpi_enable_bdf)
+ return -ENODATA;
+
+ if (strscpy(ab->qmi.target.bdf_ext, ab->acpi.bdf_string + 4, max_len) < 0)
+ ath12k_dbg(ab, ATH12K_DBG_BOOT,
+ "acpi bdf variant longer than the buffer (variant: %s)\n",
+ ab->acpi.bdf_string);
+
+ return 0;
+}
+
void ath12k_acpi_stop(struct ath12k_base *ab)
{
if (!ab->acpi.started)
diff --git a/drivers/net/wireless/ath/ath12k/acpi.h b/drivers/net/wireless/ath/ath12k/acpi.h
index 39e003d86a48..3a26fea6af1a 100644
--- a/drivers/net/wireless/ath/ath12k/acpi.h
+++ b/drivers/net/wireless/ath/ath12k/acpi.h
@@ -1,7 +1,7 @@
/* SPDX-License-Identifier: BSD-3-Clause-Clear */
/*
* Copyright (c) 2018-2021 The Linux Foundation. All rights reserved.
- * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#ifndef ATH12K_ACPI_H
#define ATH12K_ACPI_H
@@ -9,6 +9,8 @@
#include <linux/acpi.h>
#define ATH12K_ACPI_DSM_FUNC_SUPPORT_FUNCS 0
+#define ATH12K_ACPI_DSM_FUNC_DISABLE_FLAG 2
+#define ATH12K_ACPI_DSM_FUNC_BDF_EXT 3
#define ATH12K_ACPI_DSM_FUNC_BIOS_SAR 4
#define ATH12K_ACPI_DSM_FUNC_GEO_OFFSET 5
#define ATH12K_ACPI_DSM_FUNC_INDEX_CCA 6
@@ -16,6 +18,8 @@
#define ATH12K_ACPI_DSM_FUNC_TAS_DATA 9
#define ATH12K_ACPI_DSM_FUNC_INDEX_BAND_EDGE 10
+#define ATH12K_ACPI_FUNC_BIT_DISABLE_FLAG BIT(1)
+#define ATH12K_ACPI_FUNC_BIT_BDF_EXT BIT(2)
#define ATH12K_ACPI_FUNC_BIT_BIOS_SAR BIT(3)
#define ATH12K_ACPI_FUNC_BIT_GEO_OFFSET BIT(4)
#define ATH12K_ACPI_FUNC_BIT_CCA BIT(5)
@@ -25,6 +29,7 @@
#define ATH12K_ACPI_NOTIFY_EVENT 0x86
#define ATH12K_ACPI_FUNC_BIT_VALID(_acdata, _func) (((_acdata).func_bit) & (_func))
+#define ATH12K_ACPI_CHEK_BIT_VALID(_acdata, _func) (((_acdata).bit_flag) & (_func))
#define ATH12K_ACPI_TAS_DATA_VERSION 0x1
#define ATH12K_ACPI_TAS_DATA_ENABLE 0x1
@@ -48,6 +53,16 @@
#define ATH12K_ACPI_DSM_BAND_EDGE_DATA_SIZE 100
#define ATH12K_ACPI_DSM_TAS_CFG_SIZE 108
+#define ATH12K_ACPI_DSM_FUNC_MIN_BITMAP_SIZE 1
+#define ATH12K_ACPI_DSM_FUNC_MAX_BITMAP_SIZE 4
+
+#define ATH12K_ACPI_DSM_DISABLE_11BE_BIT BIT(0)
+#define ATH12K_ACPI_DSM_DISABLE_RFKILL_BIT BIT(2)
+
+#define ATH12K_ACPI_BDF_ANCHOR_STRING_LEN 3
+#define ATH12K_ACPI_BDF_ANCHOR_STRING "BDF"
+#define ATH12K_ACPI_BDF_MAX_LEN 100
+
#define ATH12K_ACPI_DSM_GEO_OFFSET_DATA_SIZE (ATH12K_ACPI_GEO_OFFSET_DATA_OFFSET + \
ATH12K_ACPI_BIOS_SAR_GEO_OFFSET_LEN)
#define ATH12K_ACPI_DSM_BIOS_SAR_DATA_SIZE (ATH12K_ACPI_POWER_LIMIT_DATA_OFFSET + \
@@ -59,6 +74,10 @@
int ath12k_acpi_start(struct ath12k_base *ab);
void ath12k_acpi_stop(struct ath12k_base *ab);
+bool ath12k_acpi_get_disable_rfkill(struct ath12k_base *ab);
+bool ath12k_acpi_get_disable_11be(struct ath12k_base *ab);
+void ath12k_acpi_set_dsm_func(struct ath12k_base *ab);
+int ath12k_acpi_check_bdf_variant_name(struct ath12k_base *ab);
#else
@@ -71,6 +90,25 @@ static inline void ath12k_acpi_stop(struct ath12k_base *ab)
{
}
+static inline bool ath12k_acpi_get_disable_rfkill(struct ath12k_base *ab)
+{
+ return false;
+}
+
+static inline bool ath12k_acpi_get_disable_11be(struct ath12k_base *ab)
+{
+ return false;
+}
+
+static inline void ath12k_acpi_set_dsm_func(struct ath12k_base *ab)
+{
+}
+
+static inline int ath12k_acpi_check_bdf_variant_name(struct ath12k_base *ab)
+{
+ return 0;
+}
+
#endif /* CONFIG_ACPI */
#endif /* ATH12K_ACPI_H */
diff --git a/drivers/net/wireless/ath/ath12k/ahb.c b/drivers/net/wireless/ath/ath12k/ahb.c
new file mode 100644
index 000000000000..8d1a86e420a4
--- /dev/null
+++ b/drivers/net/wireless/ath/ath12k/ahb.c
@@ -0,0 +1,1155 @@
+// SPDX-License-Identifier: BSD-3-Clause-Clear
+/*
+ * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2022-2025 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#include <linux/dma-mapping.h>
+#include <linux/firmware/qcom/qcom_scm.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/remoteproc.h>
+#include <linux/soc/qcom/mdt_loader.h>
+#include <linux/soc/qcom/smem_state.h>
+#include "ahb.h"
+#include "debug.h"
+#include "hif.h"
+
+static const struct of_device_id ath12k_ahb_of_match[] = {
+ { .compatible = "qcom,ipq5332-wifi",
+ .data = (void *)ATH12K_HW_IPQ5332_HW10,
+ },
+ { }
+};
+
+MODULE_DEVICE_TABLE(of, ath12k_ahb_of_match);
+
+#define ATH12K_IRQ_CE0_OFFSET 4
+#define ATH12K_MAX_UPDS 1
+#define ATH12K_UPD_IRQ_WRD_LEN 18
+static const char ath12k_userpd_irq[][9] = {"spawn",
+ "ready",
+ "stop-ack"};
+
+static const char *irq_name[ATH12K_IRQ_NUM_MAX] = {
+ "misc-pulse1",
+ "misc-latch",
+ "sw-exception",
+ "watchdog",
+ "ce0",
+ "ce1",
+ "ce2",
+ "ce3",
+ "ce4",
+ "ce5",
+ "ce6",
+ "ce7",
+ "ce8",
+ "ce9",
+ "ce10",
+ "ce11",
+ "host2wbm-desc-feed",
+ "host2reo-re-injection",
+ "host2reo-command",
+ "host2rxdma-monitor-ring3",
+ "host2rxdma-monitor-ring2",
+ "host2rxdma-monitor-ring1",
+ "reo2ost-exception",
+ "wbm2host-rx-release",
+ "reo2host-status",
+ "reo2host-destination-ring4",
+ "reo2host-destination-ring3",
+ "reo2host-destination-ring2",
+ "reo2host-destination-ring1",
+ "rxdma2host-monitor-destination-mac3",
+ "rxdma2host-monitor-destination-mac2",
+ "rxdma2host-monitor-destination-mac1",
+ "ppdu-end-interrupts-mac3",
+ "ppdu-end-interrupts-mac2",
+ "ppdu-end-interrupts-mac1",
+ "rxdma2host-monitor-status-ring-mac3",
+ "rxdma2host-monitor-status-ring-mac2",
+ "rxdma2host-monitor-status-ring-mac1",
+ "host2rxdma-host-buf-ring-mac3",
+ "host2rxdma-host-buf-ring-mac2",
+ "host2rxdma-host-buf-ring-mac1",
+ "rxdma2host-destination-ring-mac3",
+ "rxdma2host-destination-ring-mac2",
+ "rxdma2host-destination-ring-mac1",
+ "host2tcl-input-ring4",
+ "host2tcl-input-ring3",
+ "host2tcl-input-ring2",
+ "host2tcl-input-ring1",
+ "wbm2host-tx-completions-ring4",
+ "wbm2host-tx-completions-ring3",
+ "wbm2host-tx-completions-ring2",
+ "wbm2host-tx-completions-ring1",
+ "tcl2host-status-ring",
+};
+
+enum ext_irq_num {
+ host2wbm_desc_feed = 16,
+ host2reo_re_injection,
+ host2reo_command,
+ host2rxdma_monitor_ring3,
+ host2rxdma_monitor_ring2,
+ host2rxdma_monitor_ring1,
+ reo2host_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_ring4,
+ wbm2host_tx_completions_ring3,
+ wbm2host_tx_completions_ring2,
+ wbm2host_tx_completions_ring1,
+ tcl2host_status_ring,
+};
+
+static u32 ath12k_ahb_read32(struct ath12k_base *ab, u32 offset)
+{
+ if (ab->ce_remap && offset < HAL_SEQ_WCSS_CMEM_OFFSET)
+ return ioread32(ab->mem_ce + offset);
+ return ioread32(ab->mem + offset);
+}
+
+static void ath12k_ahb_write32(struct ath12k_base *ab, u32 offset,
+ u32 value)
+{
+ if (ab->ce_remap && offset < HAL_SEQ_WCSS_CMEM_OFFSET)
+ iowrite32(value, ab->mem_ce + offset);
+ else
+ iowrite32(value, ab->mem + offset);
+}
+
+static void ath12k_ahb_cancel_workqueue(struct ath12k_base *ab)
+{
+ int i;
+
+ for (i = 0; i < ab->hw_params->ce_count; i++) {
+ struct ath12k_ce_pipe *ce_pipe = &ab->ce.ce_pipe[i];
+
+ if (ath12k_ce_get_attr_flags(ab, i) & CE_ATTR_DIS_INTR)
+ continue;
+
+ cancel_work_sync(&ce_pipe->intr_wq);
+ }
+}
+
+static void ath12k_ahb_ext_grp_disable(struct ath12k_ext_irq_grp *irq_grp)
+{
+ int i;
+
+ for (i = 0; i < irq_grp->num_irq; i++)
+ disable_irq_nosync(irq_grp->ab->irq_num[irq_grp->irqs[i]]);
+}
+
+static void __ath12k_ahb_ext_irq_disable(struct ath12k_base *ab)
+{
+ int i;
+
+ for (i = 0; i < ATH12K_EXT_IRQ_GRP_NUM_MAX; i++) {
+ struct ath12k_ext_irq_grp *irq_grp = &ab->ext_irq_grp[i];
+
+ ath12k_ahb_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 ath12k_ahb_ext_grp_enable(struct ath12k_ext_irq_grp *irq_grp)
+{
+ int i;
+
+ for (i = 0; i < irq_grp->num_irq; i++)
+ enable_irq(irq_grp->ab->irq_num[irq_grp->irqs[i]]);
+}
+
+static void ath12k_ahb_setbit32(struct ath12k_base *ab, u8 bit, u32 offset)
+{
+ u32 val;
+
+ val = ath12k_ahb_read32(ab, offset);
+ ath12k_ahb_write32(ab, offset, val | BIT(bit));
+}
+
+static void ath12k_ahb_clearbit32(struct ath12k_base *ab, u8 bit, u32 offset)
+{
+ u32 val;
+
+ val = ath12k_ahb_read32(ab, offset);
+ ath12k_ahb_write32(ab, offset, val & ~BIT(bit));
+}
+
+static void ath12k_ahb_ce_irq_enable(struct ath12k_base *ab, u16 ce_id)
+{
+ const struct ce_attr *ce_attr;
+ const struct ce_ie_addr *ce_ie_addr = ab->hw_params->ce_ie_addr;
+ u32 ie1_reg_addr, ie2_reg_addr, ie3_reg_addr;
+
+ ie1_reg_addr = ce_ie_addr->ie1_reg_addr;
+ ie2_reg_addr = ce_ie_addr->ie2_reg_addr;
+ ie3_reg_addr = ce_ie_addr->ie3_reg_addr;
+
+ ce_attr = &ab->hw_params->host_ce_config[ce_id];
+ if (ce_attr->src_nentries)
+ ath12k_ahb_setbit32(ab, ce_id, ie1_reg_addr);
+
+ if (ce_attr->dest_nentries) {
+ ath12k_ahb_setbit32(ab, ce_id, ie2_reg_addr);
+ ath12k_ahb_setbit32(ab, ce_id + CE_HOST_IE_3_SHIFT,
+ ie3_reg_addr);
+ }
+}
+
+static void ath12k_ahb_ce_irq_disable(struct ath12k_base *ab, u16 ce_id)
+{
+ const struct ce_attr *ce_attr;
+ const struct ce_ie_addr *ce_ie_addr = ab->hw_params->ce_ie_addr;
+ u32 ie1_reg_addr, ie2_reg_addr, ie3_reg_addr;
+
+ ie1_reg_addr = ce_ie_addr->ie1_reg_addr;
+ ie2_reg_addr = ce_ie_addr->ie2_reg_addr;
+ ie3_reg_addr = ce_ie_addr->ie3_reg_addr;
+
+ ce_attr = &ab->hw_params->host_ce_config[ce_id];
+ if (ce_attr->src_nentries)
+ ath12k_ahb_clearbit32(ab, ce_id, ie1_reg_addr);
+
+ if (ce_attr->dest_nentries) {
+ ath12k_ahb_clearbit32(ab, ce_id, ie2_reg_addr);
+ ath12k_ahb_clearbit32(ab, ce_id + CE_HOST_IE_3_SHIFT,
+ ie3_reg_addr);
+ }
+}
+
+static void ath12k_ahb_sync_ce_irqs(struct ath12k_base *ab)
+{
+ int i;
+ int irq_idx;
+
+ for (i = 0; i < ab->hw_params->ce_count; i++) {
+ if (ath12k_ce_get_attr_flags(ab, i) & CE_ATTR_DIS_INTR)
+ continue;
+
+ irq_idx = ATH12K_IRQ_CE0_OFFSET + i;
+ synchronize_irq(ab->irq_num[irq_idx]);
+ }
+}
+
+static void ath12k_ahb_sync_ext_irqs(struct ath12k_base *ab)
+{
+ int i, j;
+ int irq_idx;
+
+ for (i = 0; i < ATH12K_EXT_IRQ_GRP_NUM_MAX; i++) {
+ struct ath12k_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]);
+ }
+ }
+}
+
+static void ath12k_ahb_ce_irqs_enable(struct ath12k_base *ab)
+{
+ int i;
+
+ for (i = 0; i < ab->hw_params->ce_count; i++) {
+ if (ath12k_ce_get_attr_flags(ab, i) & CE_ATTR_DIS_INTR)
+ continue;
+ ath12k_ahb_ce_irq_enable(ab, i);
+ }
+}
+
+static void ath12k_ahb_ce_irqs_disable(struct ath12k_base *ab)
+{
+ int i;
+
+ for (i = 0; i < ab->hw_params->ce_count; i++) {
+ if (ath12k_ce_get_attr_flags(ab, i) & CE_ATTR_DIS_INTR)
+ continue;
+ ath12k_ahb_ce_irq_disable(ab, i);
+ }
+}
+
+static int ath12k_ahb_start(struct ath12k_base *ab)
+{
+ ath12k_ahb_ce_irqs_enable(ab);
+ ath12k_ce_rx_post_buf(ab);
+
+ return 0;
+}
+
+static void ath12k_ahb_ext_irq_enable(struct ath12k_base *ab)
+{
+ struct ath12k_ext_irq_grp *irq_grp;
+ int i;
+
+ for (i = 0; i < ATH12K_EXT_IRQ_GRP_NUM_MAX; i++) {
+ irq_grp = &ab->ext_irq_grp[i];
+ if (!irq_grp->napi_enabled) {
+ napi_enable(&irq_grp->napi);
+ irq_grp->napi_enabled = true;
+ }
+ ath12k_ahb_ext_grp_enable(irq_grp);
+ }
+}
+
+static void ath12k_ahb_ext_irq_disable(struct ath12k_base *ab)
+{
+ __ath12k_ahb_ext_irq_disable(ab);
+ ath12k_ahb_sync_ext_irqs(ab);
+}
+
+static void ath12k_ahb_stop(struct ath12k_base *ab)
+{
+ if (!test_bit(ATH12K_FLAG_CRASH_FLUSH, &ab->dev_flags))
+ ath12k_ahb_ce_irqs_disable(ab);
+ ath12k_ahb_sync_ce_irqs(ab);
+ ath12k_ahb_cancel_workqueue(ab);
+ timer_delete_sync(&ab->rx_replenish_retry);
+ ath12k_ce_cleanup_pipes(ab);
+}
+
+static int ath12k_ahb_power_up(struct ath12k_base *ab)
+{
+ struct ath12k_ahb *ab_ahb = ath12k_ab_to_ahb(ab);
+ char fw_name[ATH12K_USERPD_FW_NAME_LEN];
+ char fw2_name[ATH12K_USERPD_FW_NAME_LEN];
+ struct device *dev = ab->dev;
+ const struct firmware *fw, *fw2;
+ struct reserved_mem *rmem = NULL;
+ unsigned long time_left;
+ phys_addr_t mem_phys;
+ void *mem_region;
+ size_t mem_size;
+ u32 pasid;
+ int ret;
+
+ rmem = ath12k_core_get_reserved_mem(ab, 0);
+ if (!rmem)
+ return -ENODEV;
+
+ mem_phys = rmem->base;
+ mem_size = rmem->size;
+ mem_region = devm_memremap(dev, mem_phys, mem_size, MEMREMAP_WC);
+ if (IS_ERR(mem_region)) {
+ ath12k_err(ab, "unable to map memory region: %pa+%pa\n",
+ &rmem->base, &rmem->size);
+ return PTR_ERR(mem_region);
+ }
+
+ snprintf(fw_name, sizeof(fw_name), "%s/%s/%s%d%s", ATH12K_FW_DIR,
+ ab->hw_params->fw.dir, ATH12K_AHB_FW_PREFIX, ab_ahb->userpd_id,
+ ATH12K_AHB_FW_SUFFIX);
+
+ ret = request_firmware(&fw, fw_name, dev);
+ if (ret < 0) {
+ ath12k_err(ab, "request_firmware failed\n");
+ return ret;
+ }
+
+ ath12k_dbg(ab, ATH12K_DBG_AHB, "Booting fw image %s, size %zd\n", fw_name,
+ fw->size);
+
+ if (!fw->size) {
+ ath12k_err(ab, "Invalid firmware size\n");
+ ret = -EINVAL;
+ goto err_fw;
+ }
+
+ pasid = (u32_encode_bits(ab_ahb->userpd_id, ATH12K_USERPD_ID_MASK)) |
+ ATH12K_AHB_UPD_SWID;
+
+ /* Load FW image to a reserved memory location */
+ ret = qcom_mdt_load(dev, fw, fw_name, pasid, mem_region, mem_phys, mem_size,
+ &mem_phys);
+ if (ret) {
+ ath12k_err(ab, "Failed to load MDT segments: %d\n", ret);
+ goto err_fw;
+ }
+
+ snprintf(fw2_name, sizeof(fw2_name), "%s/%s/%s", ATH12K_FW_DIR,
+ ab->hw_params->fw.dir, ATH12K_AHB_FW2);
+
+ ret = request_firmware(&fw2, fw2_name, dev);
+ if (ret < 0) {
+ ath12k_err(ab, "request_firmware failed\n");
+ goto err_fw;
+ }
+
+ ath12k_dbg(ab, ATH12K_DBG_AHB, "Booting fw image %s, size %zd\n", fw2_name,
+ fw2->size);
+
+ if (!fw2->size) {
+ ath12k_err(ab, "Invalid firmware size\n");
+ ret = -EINVAL;
+ goto err_fw2;
+ }
+
+ ret = qcom_mdt_load_no_init(dev, fw2, fw2_name, pasid, mem_region, mem_phys,
+ mem_size, &mem_phys);
+ if (ret) {
+ ath12k_err(ab, "Failed to load MDT segments: %d\n", ret);
+ goto err_fw2;
+ }
+
+ /* Authenticate FW image using peripheral ID */
+ ret = qcom_scm_pas_auth_and_reset(pasid);
+ if (ret) {
+ ath12k_err(ab, "failed to boot the remote processor %d\n", ret);
+ goto err_fw2;
+ }
+
+ /* Instruct Q6 to spawn userPD thread */
+ ret = qcom_smem_state_update_bits(ab_ahb->spawn_state, BIT(ab_ahb->spawn_bit),
+ BIT(ab_ahb->spawn_bit));
+ if (ret) {
+ ath12k_err(ab, "Failed to update spawn state %d\n", ret);
+ goto err_fw2;
+ }
+
+ time_left = wait_for_completion_timeout(&ab_ahb->userpd_spawned,
+ ATH12K_USERPD_SPAWN_TIMEOUT);
+ if (!time_left) {
+ ath12k_err(ab, "UserPD spawn wait timed out\n");
+ ret = -ETIMEDOUT;
+ goto err_fw2;
+ }
+
+ time_left = wait_for_completion_timeout(&ab_ahb->userpd_ready,
+ ATH12K_USERPD_READY_TIMEOUT);
+ if (!time_left) {
+ ath12k_err(ab, "UserPD ready wait timed out\n");
+ ret = -ETIMEDOUT;
+ goto err_fw2;
+ }
+
+ qcom_smem_state_update_bits(ab_ahb->spawn_state, BIT(ab_ahb->spawn_bit), 0);
+
+ ath12k_dbg(ab, ATH12K_DBG_AHB, "UserPD%d is now UP\n", ab_ahb->userpd_id);
+
+err_fw2:
+ release_firmware(fw2);
+err_fw:
+ release_firmware(fw);
+ return ret;
+}
+
+static void ath12k_ahb_power_down(struct ath12k_base *ab, bool is_suspend)
+{
+ struct ath12k_ahb *ab_ahb = ath12k_ab_to_ahb(ab);
+ unsigned long time_left;
+ u32 pasid;
+ int ret;
+
+ qcom_smem_state_update_bits(ab_ahb->stop_state, BIT(ab_ahb->stop_bit),
+ BIT(ab_ahb->stop_bit));
+
+ time_left = wait_for_completion_timeout(&ab_ahb->userpd_stopped,
+ ATH12K_USERPD_STOP_TIMEOUT);
+ if (!time_left) {
+ ath12k_err(ab, "UserPD stop wait timed out\n");
+ return;
+ }
+
+ qcom_smem_state_update_bits(ab_ahb->stop_state, BIT(ab_ahb->stop_bit), 0);
+
+ pasid = (u32_encode_bits(ab_ahb->userpd_id, ATH12K_USERPD_ID_MASK)) |
+ ATH12K_AHB_UPD_SWID;
+ /* Release the firmware */
+ ret = qcom_scm_pas_shutdown(pasid);
+ if (ret)
+ ath12k_err(ab, "scm pas shutdown failed for userPD%d: %d\n",
+ ab_ahb->userpd_id, ret);
+}
+
+static void ath12k_ahb_init_qmi_ce_config(struct ath12k_base *ab)
+{
+ struct ath12k_qmi_ce_cfg *cfg = &ab->qmi.ce_cfg;
+
+ cfg->tgt_ce_len = ab->hw_params->target_ce_count;
+ cfg->tgt_ce = ab->hw_params->target_ce_config;
+ cfg->svc_to_ce_map_len = ab->hw_params->svc_to_ce_map_len;
+ cfg->svc_to_ce_map = ab->hw_params->svc_to_ce_map;
+ ab->qmi.service_ins_id = ab->hw_params->qmi_service_ins_id;
+}
+
+static void ath12k_ahb_ce_workqueue(struct work_struct *work)
+{
+ struct ath12k_ce_pipe *ce_pipe = from_work(ce_pipe, work, intr_wq);
+
+ ath12k_ce_per_engine_service(ce_pipe->ab, ce_pipe->pipe_num);
+
+ ath12k_ahb_ce_irq_enable(ce_pipe->ab, ce_pipe->pipe_num);
+}
+
+static irqreturn_t ath12k_ahb_ce_interrupt_handler(int irq, void *arg)
+{
+ struct ath12k_ce_pipe *ce_pipe = arg;
+
+ /* last interrupt received for this CE */
+ ce_pipe->timestamp = jiffies;
+
+ ath12k_ahb_ce_irq_disable(ce_pipe->ab, ce_pipe->pipe_num);
+
+ queue_work(system_bh_wq, &ce_pipe->intr_wq);
+
+ return IRQ_HANDLED;
+}
+
+static int ath12k_ahb_ext_grp_napi_poll(struct napi_struct *napi, int budget)
+{
+ struct ath12k_ext_irq_grp *irq_grp = container_of(napi,
+ struct ath12k_ext_irq_grp,
+ napi);
+ struct ath12k_base *ab = irq_grp->ab;
+ int work_done;
+
+ work_done = ath12k_dp_service_srng(ab, irq_grp, budget);
+ if (work_done < budget) {
+ napi_complete_done(napi, work_done);
+ ath12k_ahb_ext_grp_enable(irq_grp);
+ }
+
+ if (work_done > budget)
+ work_done = budget;
+
+ return work_done;
+}
+
+static irqreturn_t ath12k_ahb_ext_interrupt_handler(int irq, void *arg)
+{
+ struct ath12k_ext_irq_grp *irq_grp = arg;
+
+ /* last interrupt received for this group */
+ irq_grp->timestamp = jiffies;
+
+ ath12k_ahb_ext_grp_disable(irq_grp);
+
+ napi_schedule(&irq_grp->napi);
+
+ return IRQ_HANDLED;
+}
+
+static int ath12k_ahb_config_ext_irq(struct ath12k_base *ab)
+{
+ const struct ath12k_hw_ring_mask *ring_mask;
+ struct ath12k_ext_irq_grp *irq_grp;
+ const struct hal_ops *hal_ops;
+ int i, j, irq, irq_idx, ret;
+ u32 num_irq;
+
+ ring_mask = ab->hw_params->ring_mask;
+ hal_ops = ab->hw_params->hal_ops;
+ for (i = 0; i < ATH12K_EXT_IRQ_GRP_NUM_MAX; i++) {
+ irq_grp = &ab->ext_irq_grp[i];
+ num_irq = 0;
+
+ irq_grp->ab = ab;
+ irq_grp->grp_id = i;
+
+ irq_grp->napi_ndev = alloc_netdev_dummy(0);
+ if (!irq_grp->napi_ndev)
+ return -ENOMEM;
+
+ netif_napi_add(irq_grp->napi_ndev, &irq_grp->napi,
+ ath12k_ahb_ext_grp_napi_poll);
+
+ for (j = 0; j < ATH12K_EXT_IRQ_NUM_MAX; j++) {
+ /* For TX ring, ensure that the ring mask and the
+ * tcl_to_wbm_rbm_map point to the same ring number.
+ */
+ if (ring_mask->tx[i] &
+ BIT(hal_ops->tcl_to_wbm_rbm_map[j].wbm_ring_num)) {
+ irq_grp->irqs[num_irq++] =
+ wbm2host_tx_completions_ring1 - j;
+ }
+
+ if (ring_mask->rx[i] & BIT(j)) {
+ irq_grp->irqs[num_irq++] =
+ reo2host_destination_ring1 - j;
+ }
+
+ if (ring_mask->rx_err[i] & BIT(j))
+ irq_grp->irqs[num_irq++] = reo2host_exception;
+
+ if (ring_mask->rx_wbm_rel[i] & BIT(j))
+ irq_grp->irqs[num_irq++] = wbm2host_rx_release;
+
+ if (ring_mask->reo_status[i] & BIT(j))
+ irq_grp->irqs[num_irq++] = reo2host_status;
+
+ if (ring_mask->rx_mon_dest[i] & BIT(j))
+ irq_grp->irqs[num_irq++] =
+ rxdma2host_monitor_destination_mac1;
+ }
+
+ irq_grp->num_irq = num_irq;
+
+ for (j = 0; j < irq_grp->num_irq; j++) {
+ irq_idx = irq_grp->irqs[j];
+
+ irq = platform_get_irq_byname(ab->pdev,
+ irq_name[irq_idx]);
+ ab->irq_num[irq_idx] = irq;
+ irq_set_status_flags(irq, IRQ_NOAUTOEN | IRQ_DISABLE_UNLAZY);
+ ret = devm_request_irq(ab->dev, irq,
+ ath12k_ahb_ext_interrupt_handler,
+ IRQF_TRIGGER_RISING,
+ irq_name[irq_idx], irq_grp);
+ if (ret)
+ ath12k_warn(ab, "failed request_irq for %d\n", irq);
+ }
+ }
+
+ return 0;
+}
+
+static int ath12k_ahb_config_irq(struct ath12k_base *ab)
+{
+ int irq, irq_idx, i;
+ int ret;
+
+ /* Configure CE irqs */
+ for (i = 0; i < ab->hw_params->ce_count; i++) {
+ struct ath12k_ce_pipe *ce_pipe = &ab->ce.ce_pipe[i];
+
+ if (ath12k_ce_get_attr_flags(ab, i) & CE_ATTR_DIS_INTR)
+ continue;
+
+ irq_idx = ATH12K_IRQ_CE0_OFFSET + i;
+
+ INIT_WORK(&ce_pipe->intr_wq, ath12k_ahb_ce_workqueue);
+ irq = platform_get_irq_byname(ab->pdev, irq_name[irq_idx]);
+ ret = devm_request_irq(ab->dev, irq, ath12k_ahb_ce_interrupt_handler,
+ IRQF_TRIGGER_RISING, irq_name[irq_idx],
+ ce_pipe);
+ if (ret)
+ return ret;
+
+ ab->irq_num[irq_idx] = irq;
+ }
+
+ /* Configure external interrupts */
+ ret = ath12k_ahb_config_ext_irq(ab);
+
+ return ret;
+}
+
+static int ath12k_ahb_map_service_to_pipe(struct ath12k_base *ab, u16 service_id,
+ u8 *ul_pipe, u8 *dl_pipe)
+{
+ const struct service_to_pipe *entry;
+ bool ul_set = false, dl_set = false;
+ u32 pipedir;
+ 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;
+
+ pipedir = __le32_to_cpu(entry->pipedir);
+ if (pipedir == PIPEDIR_IN || pipedir == PIPEDIR_INOUT) {
+ WARN_ON(dl_set);
+ *dl_pipe = __le32_to_cpu(entry->pipenum);
+ dl_set = true;
+ }
+
+ if (pipedir == PIPEDIR_OUT || pipedir == PIPEDIR_INOUT) {
+ WARN_ON(ul_set);
+ *ul_pipe = __le32_to_cpu(entry->pipenum);
+ ul_set = true;
+ }
+ }
+
+ if (WARN_ON(!ul_set || !dl_set))
+ return -ENOENT;
+
+ return 0;
+}
+
+static const struct ath12k_hif_ops ath12k_ahb_hif_ops_ipq5332 = {
+ .start = ath12k_ahb_start,
+ .stop = ath12k_ahb_stop,
+ .read32 = ath12k_ahb_read32,
+ .write32 = ath12k_ahb_write32,
+ .irq_enable = ath12k_ahb_ext_irq_enable,
+ .irq_disable = ath12k_ahb_ext_irq_disable,
+ .map_service_to_pipe = ath12k_ahb_map_service_to_pipe,
+ .power_up = ath12k_ahb_power_up,
+ .power_down = ath12k_ahb_power_down,
+};
+
+static irqreturn_t ath12k_userpd_irq_handler(int irq, void *data)
+{
+ struct ath12k_base *ab = data;
+ struct ath12k_ahb *ab_ahb = ath12k_ab_to_ahb(ab);
+
+ if (irq == ab_ahb->userpd_irq_num[ATH12K_USERPD_SPAWN_IRQ]) {
+ complete(&ab_ahb->userpd_spawned);
+ } else if (irq == ab_ahb->userpd_irq_num[ATH12K_USERPD_READY_IRQ]) {
+ complete(&ab_ahb->userpd_ready);
+ } else if (irq == ab_ahb->userpd_irq_num[ATH12K_USERPD_STOP_ACK_IRQ]) {
+ complete(&ab_ahb->userpd_stopped);
+ } else {
+ ath12k_err(ab, "Invalid userpd interrupt\n");
+ return IRQ_NONE;
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int ath12k_ahb_config_rproc_irq(struct ath12k_base *ab)
+{
+ struct ath12k_ahb *ab_ahb = ath12k_ab_to_ahb(ab);
+ int i, ret;
+ char *upd_irq_name;
+
+ for (i = 0; i < ATH12K_USERPD_MAX_IRQ; i++) {
+ ab_ahb->userpd_irq_num[i] = platform_get_irq_byname(ab->pdev,
+ ath12k_userpd_irq[i]);
+ if (ab_ahb->userpd_irq_num[i] < 0)
+ return ab_ahb->userpd_irq_num[i];
+
+ upd_irq_name = devm_kzalloc(&ab->pdev->dev, ATH12K_UPD_IRQ_WRD_LEN,
+ GFP_KERNEL);
+ if (!upd_irq_name)
+ return -ENOMEM;
+
+ scnprintf(upd_irq_name, ATH12K_UPD_IRQ_WRD_LEN, "UserPD%u-%s",
+ ab_ahb->userpd_id, ath12k_userpd_irq[i]);
+ ret = devm_request_threaded_irq(&ab->pdev->dev, ab_ahb->userpd_irq_num[i],
+ NULL, ath12k_userpd_irq_handler,
+ IRQF_TRIGGER_RISING | IRQF_ONESHOT,
+ upd_irq_name, ab);
+ if (ret)
+ return dev_err_probe(&ab->pdev->dev, ret,
+ "Request %s irq failed: %d\n",
+ ath12k_userpd_irq[i], ret);
+ }
+
+ ab_ahb->spawn_state = devm_qcom_smem_state_get(&ab->pdev->dev, "spawn",
+ &ab_ahb->spawn_bit);
+ if (IS_ERR(ab_ahb->spawn_state))
+ return dev_err_probe(&ab->pdev->dev, PTR_ERR(ab_ahb->spawn_state),
+ "Failed to acquire spawn state\n");
+
+ ab_ahb->stop_state = devm_qcom_smem_state_get(&ab->pdev->dev, "stop",
+ &ab_ahb->stop_bit);
+ if (IS_ERR(ab_ahb->stop_state))
+ return dev_err_probe(&ab->pdev->dev, PTR_ERR(ab_ahb->stop_state),
+ "Failed to acquire stop state\n");
+
+ init_completion(&ab_ahb->userpd_spawned);
+ init_completion(&ab_ahb->userpd_ready);
+ init_completion(&ab_ahb->userpd_stopped);
+ return 0;
+}
+
+static int ath12k_ahb_root_pd_state_notifier(struct notifier_block *nb,
+ const unsigned long event, void *data)
+{
+ struct ath12k_ahb *ab_ahb = container_of(nb, struct ath12k_ahb, root_pd_nb);
+ struct ath12k_base *ab = ab_ahb->ab;
+
+ if (event == ATH12K_RPROC_AFTER_POWERUP) {
+ ath12k_dbg(ab, ATH12K_DBG_AHB, "Root PD is UP\n");
+ complete(&ab_ahb->rootpd_ready);
+ }
+
+ return 0;
+}
+
+static int ath12k_ahb_register_rproc_notifier(struct ath12k_base *ab)
+{
+ struct ath12k_ahb *ab_ahb = ath12k_ab_to_ahb(ab);
+
+ ab_ahb->root_pd_nb.notifier_call = ath12k_ahb_root_pd_state_notifier;
+ init_completion(&ab_ahb->rootpd_ready);
+
+ ab_ahb->root_pd_notifier = qcom_register_ssr_notifier(ab_ahb->tgt_rproc->name,
+ &ab_ahb->root_pd_nb);
+ if (IS_ERR(ab_ahb->root_pd_notifier))
+ return PTR_ERR(ab_ahb->root_pd_notifier);
+
+ return 0;
+}
+
+static void ath12k_ahb_unregister_rproc_notifier(struct ath12k_base *ab)
+{
+ struct ath12k_ahb *ab_ahb = ath12k_ab_to_ahb(ab);
+
+ if (!ab_ahb->root_pd_notifier) {
+ ath12k_err(ab, "Rproc notifier not registered\n");
+ return;
+ }
+
+ qcom_unregister_ssr_notifier(ab_ahb->root_pd_notifier,
+ &ab_ahb->root_pd_nb);
+ ab_ahb->root_pd_notifier = NULL;
+}
+
+static int ath12k_ahb_get_rproc(struct ath12k_base *ab)
+{
+ struct ath12k_ahb *ab_ahb = ath12k_ab_to_ahb(ab);
+ struct device *dev = ab->dev;
+ struct device_node *np;
+ struct rproc *prproc;
+
+ np = of_parse_phandle(dev->of_node, "qcom,rproc", 0);
+ if (!np) {
+ ath12k_err(ab, "failed to get q6_rproc handle\n");
+ return -ENOENT;
+ }
+
+ prproc = rproc_get_by_phandle(np->phandle);
+ of_node_put(np);
+ if (!prproc)
+ return dev_err_probe(&ab->pdev->dev, -EPROBE_DEFER,
+ "failed to get rproc\n");
+
+ ab_ahb->tgt_rproc = prproc;
+
+ return 0;
+}
+
+static int ath12k_ahb_boot_root_pd(struct ath12k_base *ab)
+{
+ struct ath12k_ahb *ab_ahb = ath12k_ab_to_ahb(ab);
+ unsigned long time_left;
+ int ret;
+
+ ret = rproc_boot(ab_ahb->tgt_rproc);
+ if (ret < 0) {
+ ath12k_err(ab, "RootPD boot failed\n");
+ return ret;
+ }
+
+ time_left = wait_for_completion_timeout(&ab_ahb->rootpd_ready,
+ ATH12K_ROOTPD_READY_TIMEOUT);
+ if (!time_left) {
+ ath12k_err(ab, "RootPD ready wait timed out\n");
+ return -ETIMEDOUT;
+ }
+
+ return 0;
+}
+
+static int ath12k_ahb_configure_rproc(struct ath12k_base *ab)
+{
+ struct ath12k_ahb *ab_ahb = ath12k_ab_to_ahb(ab);
+ int ret;
+
+ ret = ath12k_ahb_get_rproc(ab);
+ if (ret < 0)
+ return ret;
+
+ ret = ath12k_ahb_register_rproc_notifier(ab);
+ if (ret < 0) {
+ ret = dev_err_probe(&ab->pdev->dev, ret,
+ "failed to register rproc notifier\n");
+ goto err_put_rproc;
+ }
+
+ if (ab_ahb->tgt_rproc->state != RPROC_RUNNING) {
+ ret = ath12k_ahb_boot_root_pd(ab);
+ if (ret < 0) {
+ ath12k_err(ab, "failed to boot the remote processor Q6\n");
+ goto err_unreg_notifier;
+ }
+ }
+
+ return ath12k_ahb_config_rproc_irq(ab);
+
+err_unreg_notifier:
+ ath12k_ahb_unregister_rproc_notifier(ab);
+
+err_put_rproc:
+ rproc_put(ab_ahb->tgt_rproc);
+ return ret;
+}
+
+static void ath12k_ahb_deconfigure_rproc(struct ath12k_base *ab)
+{
+ struct ath12k_ahb *ab_ahb = ath12k_ab_to_ahb(ab);
+
+ ath12k_ahb_unregister_rproc_notifier(ab);
+ rproc_put(ab_ahb->tgt_rproc);
+}
+
+static int ath12k_ahb_resource_init(struct ath12k_base *ab)
+{
+ struct ath12k_ahb *ab_ahb = ath12k_ab_to_ahb(ab);
+ struct platform_device *pdev = ab->pdev;
+ struct resource *mem_res;
+ int ret;
+
+ ab->mem = devm_platform_get_and_ioremap_resource(pdev, 0, &mem_res);
+ if (IS_ERR(ab->mem)) {
+ ret = dev_err_probe(&pdev->dev, PTR_ERR(ab->mem), "ioremap error\n");
+ goto out;
+ }
+
+ ab->mem_len = resource_size(mem_res);
+
+ if (ab->hw_params->ce_remap) {
+ const struct ce_remap *ce_remap = ab->hw_params->ce_remap;
+ /* CE register space is moved out of WCSS and the space is not
+ * contiguous, hence remapping the CE registers to a new space
+ * for accessing them.
+ */
+ ab->mem_ce = ioremap(ce_remap->base, ce_remap->size);
+ if (!ab->mem_ce) {
+ dev_err(&pdev->dev, "ce ioremap error\n");
+ ret = -ENOMEM;
+ goto err_mem_unmap;
+ }
+ ab->ce_remap = true;
+ ab->ce_remap_base_addr = HAL_IPQ5332_CE_WFSS_REG_BASE;
+ }
+
+ ab_ahb->xo_clk = devm_clk_get(ab->dev, "xo");
+ if (IS_ERR(ab_ahb->xo_clk)) {
+ ret = dev_err_probe(&pdev->dev, PTR_ERR(ab_ahb->xo_clk),
+ "failed to get xo clock\n");
+ goto err_mem_ce_unmap;
+ }
+
+ ret = clk_prepare_enable(ab_ahb->xo_clk);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to enable gcc_xo_clk: %d\n", ret);
+ goto err_clock_deinit;
+ }
+
+ return 0;
+
+err_clock_deinit:
+ devm_clk_put(ab->dev, ab_ahb->xo_clk);
+
+err_mem_ce_unmap:
+ ab_ahb->xo_clk = NULL;
+ if (ab->hw_params->ce_remap)
+ iounmap(ab->mem_ce);
+
+err_mem_unmap:
+ ab->mem_ce = NULL;
+ devm_iounmap(ab->dev, ab->mem);
+
+out:
+ ab->mem = NULL;
+ return ret;
+}
+
+static void ath12k_ahb_resource_deinit(struct ath12k_base *ab)
+{
+ struct ath12k_ahb *ab_ahb = ath12k_ab_to_ahb(ab);
+
+ if (ab->mem)
+ devm_iounmap(ab->dev, ab->mem);
+
+ if (ab->mem_ce)
+ iounmap(ab->mem_ce);
+
+ ab->mem = NULL;
+ ab->mem_ce = NULL;
+
+ clk_disable_unprepare(ab_ahb->xo_clk);
+ devm_clk_put(ab->dev, ab_ahb->xo_clk);
+ ab_ahb->xo_clk = NULL;
+}
+
+static int ath12k_ahb_probe(struct platform_device *pdev)
+{
+ struct ath12k_base *ab;
+ const struct ath12k_hif_ops *hif_ops;
+ struct ath12k_ahb *ab_ahb;
+ enum ath12k_hw_rev hw_rev;
+ u32 addr, userpd_id;
+ int ret;
+
+ ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32));
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to set 32-bit coherent dma\n");
+ return ret;
+ }
+
+ ab = ath12k_core_alloc(&pdev->dev, sizeof(struct ath12k_ahb),
+ ATH12K_BUS_AHB);
+ if (!ab)
+ return -ENOMEM;
+
+ hw_rev = (enum ath12k_hw_rev)(kernel_ulong_t)of_device_get_match_data(&pdev->dev);
+ switch (hw_rev) {
+ case ATH12K_HW_IPQ5332_HW10:
+ hif_ops = &ath12k_ahb_hif_ops_ipq5332;
+ userpd_id = ATH12K_IPQ5332_USERPD_ID;
+ break;
+ default:
+ ret = -EOPNOTSUPP;
+ goto err_core_free;
+ }
+
+ ab->hif.ops = hif_ops;
+ ab->pdev = pdev;
+ ab->hw_rev = hw_rev;
+ platform_set_drvdata(pdev, ab);
+ ab_ahb = ath12k_ab_to_ahb(ab);
+ ab_ahb->ab = ab;
+ ab_ahb->userpd_id = userpd_id;
+
+ /* Set fixed_mem_region to true for platforms that support fixed memory
+ * reservation from DT. If memory is reserved from DT for FW, ath12k driver
+ * need not to allocate memory.
+ */
+ if (!of_property_read_u32(ab->dev->of_node, "memory-region", &addr))
+ set_bit(ATH12K_FLAG_FIXED_MEM_REGION, &ab->dev_flags);
+
+ ret = ath12k_core_pre_init(ab);
+ if (ret)
+ goto err_core_free;
+
+ ret = ath12k_ahb_resource_init(ab);
+ if (ret)
+ goto err_core_free;
+
+ ret = ath12k_hal_srng_init(ab);
+ if (ret)
+ goto err_resource_deinit;
+
+ ret = ath12k_ce_alloc_pipes(ab);
+ if (ret) {
+ ath12k_err(ab, "failed to allocate ce pipes: %d\n", ret);
+ goto err_hal_srng_deinit;
+ }
+
+ ath12k_ahb_init_qmi_ce_config(ab);
+
+ ret = ath12k_ahb_configure_rproc(ab);
+ if (ret)
+ goto err_ce_free;
+
+ ret = ath12k_ahb_config_irq(ab);
+ if (ret) {
+ ath12k_err(ab, "failed to configure irq: %d\n", ret);
+ goto err_rproc_deconfigure;
+ }
+
+ ret = ath12k_core_init(ab);
+ if (ret) {
+ ath12k_err(ab, "failed to init core: %d\n", ret);
+ goto err_rproc_deconfigure;
+ }
+
+ return 0;
+
+err_rproc_deconfigure:
+ ath12k_ahb_deconfigure_rproc(ab);
+
+err_ce_free:
+ ath12k_ce_free_pipes(ab);
+
+err_hal_srng_deinit:
+ ath12k_hal_srng_deinit(ab);
+
+err_resource_deinit:
+ ath12k_ahb_resource_deinit(ab);
+
+err_core_free:
+ ath12k_core_free(ab);
+ platform_set_drvdata(pdev, NULL);
+
+ return ret;
+}
+
+static void ath12k_ahb_remove_prepare(struct ath12k_base *ab)
+{
+ unsigned long left;
+
+ if (test_bit(ATH12K_FLAG_RECOVERY, &ab->dev_flags)) {
+ left = wait_for_completion_timeout(&ab->driver_recovery,
+ ATH12K_AHB_RECOVERY_TIMEOUT);
+ if (!left)
+ ath12k_warn(ab, "failed to receive recovery response completion\n");
+ }
+
+ set_bit(ATH12K_FLAG_UNREGISTERING, &ab->dev_flags);
+ cancel_work_sync(&ab->restart_work);
+ cancel_work_sync(&ab->qmi.event_work);
+}
+
+static void ath12k_ahb_free_resources(struct ath12k_base *ab)
+{
+ struct platform_device *pdev = ab->pdev;
+
+ ath12k_hal_srng_deinit(ab);
+ ath12k_ce_free_pipes(ab);
+ ath12k_ahb_resource_deinit(ab);
+ ath12k_ahb_deconfigure_rproc(ab);
+ ath12k_core_free(ab);
+ platform_set_drvdata(pdev, NULL);
+}
+
+static void ath12k_ahb_remove(struct platform_device *pdev)
+{
+ struct ath12k_base *ab = platform_get_drvdata(pdev);
+
+ if (test_bit(ATH12K_FLAG_QMI_FAIL, &ab->dev_flags)) {
+ ath12k_ahb_power_down(ab, false);
+ goto qmi_fail;
+ }
+
+ ath12k_ahb_remove_prepare(ab);
+ ath12k_core_hw_group_cleanup(ab->ag);
+qmi_fail:
+ ath12k_core_deinit(ab);
+ ath12k_ahb_free_resources(ab);
+}
+
+static struct platform_driver ath12k_ahb_driver = {
+ .driver = {
+ .name = "ath12k_ahb",
+ .of_match_table = ath12k_ahb_of_match,
+ },
+ .probe = ath12k_ahb_probe,
+ .remove = ath12k_ahb_remove,
+};
+
+int ath12k_ahb_init(void)
+{
+ return platform_driver_register(&ath12k_ahb_driver);
+}
+
+void ath12k_ahb_exit(void)
+{
+ platform_driver_unregister(&ath12k_ahb_driver);
+}
diff --git a/drivers/net/wireless/ath/ath12k/ahb.h b/drivers/net/wireless/ath/ath12k/ahb.h
new file mode 100644
index 000000000000..d56244b20a6a
--- /dev/null
+++ b/drivers/net/wireless/ath/ath12k/ahb.h
@@ -0,0 +1,80 @@
+/* SPDX-License-Identifier: BSD-3-Clause-Clear */
+/*
+ * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2022-2025, Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+#ifndef ATH12K_AHB_H
+#define ATH12K_AHB_H
+
+#include <linux/clk.h>
+#include <linux/remoteproc/qcom_rproc.h>
+#include "core.h"
+
+#define ATH12K_AHB_RECOVERY_TIMEOUT (3 * HZ)
+
+#define ATH12K_AHB_SMP2P_SMEM_MSG GENMASK(15, 0)
+#define ATH12K_AHB_SMP2P_SMEM_SEQ_NO GENMASK(31, 16)
+#define ATH12K_AHB_SMP2P_SMEM_VALUE_MASK 0xFFFFFFFF
+#define ATH12K_PCI_CE_WAKE_IRQ 2
+#define ATH12K_PCI_IRQ_CE0_OFFSET 3
+#define ATH12K_ROOTPD_READY_TIMEOUT (5 * HZ)
+#define ATH12K_RPROC_AFTER_POWERUP QCOM_SSR_AFTER_POWERUP
+#define ATH12K_AHB_FW_PREFIX "q6_fw"
+#define ATH12K_AHB_FW_SUFFIX ".mdt"
+#define ATH12K_AHB_FW2 "iu_fw.mdt"
+#define ATH12K_AHB_UPD_SWID 0x12
+#define ATH12K_USERPD_SPAWN_TIMEOUT (5 * HZ)
+#define ATH12K_USERPD_READY_TIMEOUT (10 * HZ)
+#define ATH12K_USERPD_STOP_TIMEOUT (5 * HZ)
+#define ATH12K_USERPD_ID_MASK GENMASK(9, 8)
+#define ATH12K_USERPD_FW_NAME_LEN 35
+
+enum ath12k_ahb_smp2p_msg_id {
+ ATH12K_AHB_POWER_SAVE_ENTER = 1,
+ ATH12K_AHB_POWER_SAVE_EXIT,
+};
+
+enum ath12k_ahb_userpd_irq {
+ ATH12K_USERPD_SPAWN_IRQ,
+ ATH12K_USERPD_READY_IRQ,
+ ATH12K_USERPD_STOP_ACK_IRQ,
+ ATH12K_USERPD_MAX_IRQ,
+};
+
+struct ath12k_base;
+
+struct ath12k_ahb {
+ struct ath12k_base *ab;
+ struct rproc *tgt_rproc;
+ struct clk *xo_clk;
+ struct completion rootpd_ready;
+ struct notifier_block root_pd_nb;
+ void *root_pd_notifier;
+ struct qcom_smem_state *spawn_state;
+ struct qcom_smem_state *stop_state;
+ struct completion userpd_spawned;
+ struct completion userpd_ready;
+ struct completion userpd_stopped;
+ u32 userpd_id;
+ u32 spawn_bit;
+ u32 stop_bit;
+ int userpd_irq_num[ATH12K_USERPD_MAX_IRQ];
+};
+
+static inline struct ath12k_ahb *ath12k_ab_to_ahb(struct ath12k_base *ab)
+{
+ return (struct ath12k_ahb *)ab->drv_priv;
+}
+
+#ifdef CONFIG_ATH12K_AHB
+int ath12k_ahb_init(void);
+void ath12k_ahb_exit(void);
+#else
+static inline int ath12k_ahb_init(void)
+{
+ return 0;
+}
+
+static inline void ath12k_ahb_exit(void) {};
+#endif
+#endif
diff --git a/drivers/net/wireless/ath/ath12k/ce.c b/drivers/net/wireless/ath/ath12k/ce.c
index be0d669d31fc..47821a3b060b 100644
--- a/drivers/net/wireless/ath/ath12k/ce.c
+++ b/drivers/net/wireless/ath/ath12k/ce.c
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: BSD-3-Clause-Clear
/*
* Copyright (c) 2018-2021 The Linux Foundation. All rights reserved.
- * Copyright (c) 2021-2022 Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) 2021-2022, 2024-2025 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#include "dp_rx.h"
@@ -219,6 +219,96 @@ const struct ce_attr ath12k_host_ce_config_wcn7850[] = {
};
+const struct ce_attr ath12k_host_ce_config_ipq5332[] = {
+ /* CE0: host->target HTC control and raw streams */
+ {
+ .flags = CE_ATTR_FLAGS,
+ .src_nentries = 16,
+ .src_sz_max = 2048,
+ .dest_nentries = 0,
+ },
+ /* CE1: target->host HTT + HTC control */
+ {
+ .flags = CE_ATTR_FLAGS,
+ .src_nentries = 0,
+ .src_sz_max = 2048,
+ .dest_nentries = 512,
+ .recv_cb = ath12k_htc_rx_completion_handler,
+ },
+ /* CE2: target->host WMI */
+ {
+ .flags = CE_ATTR_FLAGS,
+ .src_nentries = 0,
+ .src_sz_max = 2048,
+ .dest_nentries = 128,
+ .recv_cb = ath12k_htc_rx_completion_handler,
+ },
+ /* CE3: host->target WMI */
+ {
+ .flags = CE_ATTR_FLAGS,
+ .src_nentries = 32,
+ .src_sz_max = 2048,
+ .dest_nentries = 0,
+ },
+ /* CE4: host->target HTT */
+ {
+ .flags = CE_ATTR_FLAGS | CE_ATTR_DIS_INTR,
+ .src_nentries = 2048,
+ .src_sz_max = 256,
+ .dest_nentries = 0,
+ },
+ /* CE5: target -> host PKTLOG */
+ {
+ .flags = CE_ATTR_FLAGS,
+ .src_nentries = 0,
+ .src_sz_max = 2048,
+ .dest_nentries = 512,
+ .recv_cb = ath12k_dp_htt_htc_t2h_msg_handler,
+ },
+ /* CE6: Target autonomous HIF_memcpy */
+ {
+ .flags = CE_ATTR_FLAGS | CE_ATTR_DIS_INTR,
+ .src_nentries = 0,
+ .src_sz_max = 0,
+ .dest_nentries = 0,
+ },
+ /* CE7: CV Prefetch */
+ {
+ .flags = CE_ATTR_FLAGS | CE_ATTR_DIS_INTR,
+ .src_nentries = 0,
+ .src_sz_max = 0,
+ .dest_nentries = 0,
+ },
+ /* CE8: Target HIF memcpy (Generic HIF memcypy) */
+ {
+ .flags = CE_ATTR_FLAGS | CE_ATTR_DIS_INTR,
+ .src_nentries = 0,
+ .src_sz_max = 0,
+ .dest_nentries = 0,
+ },
+ /* CE9: WMI logging/CFR/Spectral/Radar */
+ {
+ .flags = CE_ATTR_FLAGS,
+ .src_nentries = 0,
+ .src_sz_max = 2048,
+ .dest_nentries = 128,
+ },
+ /* CE10: Unused */
+ {
+ .flags = CE_ATTR_FLAGS | CE_ATTR_DIS_INTR,
+ .src_nentries = 0,
+ .src_sz_max = 0,
+ .dest_nentries = 0,
+ },
+ /* CE11: Unused */
+ {
+ .flags = CE_ATTR_FLAGS | CE_ATTR_DIS_INTR,
+ .src_nentries = 0,
+ .src_sz_max = 0,
+ .dest_nentries = 0,
+ },
+};
+
static int ath12k_ce_rx_buf_enqueue_pipe(struct ath12k_ce_pipe *pipe,
struct sk_buff *skb, dma_addr_t paddr)
{
@@ -343,11 +433,10 @@ static int ath12k_ce_completed_recv_next(struct ath12k_ce_pipe *pipe,
goto err;
}
+ /* Make sure descriptor is read after the head pointer. */
+ dma_rmb();
+
*nbytes = ath12k_hal_ce_dst_status_get_length(desc);
- if (*nbytes == 0) {
- ret = -EIO;
- goto err;
- }
*skb = pipe->dest_ring->skb[sw_index];
pipe->dest_ring->skb[sw_index] = NULL;
@@ -380,8 +469,8 @@ static void ath12k_ce_recv_process_cb(struct ath12k_ce_pipe *pipe)
dma_unmap_single(ab->dev, ATH12K_SKB_RXCB(skb)->paddr,
max_nbytes, DMA_FROM_DEVICE);
- if (unlikely(max_nbytes < nbytes)) {
- ath12k_warn(ab, "rxed more than expected (nbytes %d, max %d)",
+ if (unlikely(max_nbytes < nbytes || nbytes == 0)) {
+ ath12k_warn(ab, "unexpected rx length (nbytes %d, max %d)",
nbytes, max_nbytes);
dev_kfree_skb_any(skb);
continue;
diff --git a/drivers/net/wireless/ath/ath12k/ce.h b/drivers/net/wireless/ath/ath12k/ce.h
index 1a14b9fb86b8..57f75899ee03 100644
--- a/drivers/net/wireless/ath/ath12k/ce.h
+++ b/drivers/net/wireless/ath/ath12k/ce.h
@@ -1,7 +1,7 @@
/* SPDX-License-Identifier: BSD-3-Clause-Clear */
/*
* Copyright (c) 2018-2021 The Linux Foundation. All rights reserved.
- * Copyright (c) 2021-2022, 2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) 2021-2022, 2024-2025 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#ifndef ATH12K_CE_H
@@ -39,8 +39,8 @@
#define PIPEDIR_INOUT_H2H 4 /* bidirectional, host to host */
/* CE address/mask */
-#define CE_HOST_IE_ADDRESS 0x00A1803C
-#define CE_HOST_IE_2_ADDRESS 0x00A18040
+#define CE_HOST_IE_ADDRESS 0x75804C
+#define CE_HOST_IE_2_ADDRESS 0x758050
#define CE_HOST_IE_3_ADDRESS CE_HOST_IE_ADDRESS
#define CE_HOST_IE_3_SHIFT 0xC
@@ -76,6 +76,17 @@ struct ce_pipe_config {
__le32 reserved;
};
+struct ce_ie_addr {
+ u32 ie1_reg_addr;
+ u32 ie2_reg_addr;
+ u32 ie3_reg_addr;
+};
+
+struct ce_remap {
+ u32 base;
+ u32 size;
+};
+
struct ce_attr {
/* CE_ATTR_* values */
unsigned int flags;
@@ -164,6 +175,7 @@ struct ath12k_ce {
extern const struct ce_attr ath12k_host_ce_config_qcn9274[];
extern const struct ce_attr ath12k_host_ce_config_wcn7850[];
+extern const struct ce_attr ath12k_host_ce_config_ipq5332[];
void ath12k_ce_cleanup_pipes(struct ath12k_base *ab);
void ath12k_ce_rx_replenish_retry(struct timer_list *t);
diff --git a/drivers/net/wireless/ath/ath12k/core.c b/drivers/net/wireless/ath/ath12k/core.c
index 0606116d6b9c..31d851d8e688 100644
--- a/drivers/net/wireless/ath/ath12k/core.c
+++ b/drivers/net/wireless/ath/ath12k/core.c
@@ -10,19 +10,26 @@
#include <linux/firmware.h>
#include <linux/of.h>
#include <linux/of_graph.h>
+#include "ahb.h"
#include "core.h"
#include "dp_tx.h"
#include "dp_rx.h"
#include "debug.h"
-#include "hif.h"
-#include "fw.h"
#include "debugfs.h"
+#include "fw.h"
+#include "hif.h"
+#include "pci.h"
#include "wow.h"
+static int ahb_err, pci_err;
unsigned int ath12k_debug_mask;
module_param_named(debug_mask, ath12k_debug_mask, uint, 0644);
MODULE_PARM_DESC(debug_mask, "Debugging mask");
+bool ath12k_ftm_mode;
+module_param_named(ftm_mode, ath12k_ftm_mode, bool, 0444);
+MODULE_PARM_DESC(ftm_mode, "Boots up in factory test mode");
+
/* protected with ath12k_hw_group_mutex */
static struct list_head ath12k_hw_group_list = LIST_HEAD_INIT(ath12k_hw_group_list);
@@ -36,6 +43,9 @@ static int ath12k_core_rfkill_config(struct ath12k_base *ab)
if (!(ab->target_caps.sys_cap_info & WMI_SYS_CAP_INFO_RFKILL))
return 0;
+ if (ath12k_acpi_get_disable_rfkill(ab))
+ return 0;
+
for (i = 0; i < ab->num_radios; i++) {
ar = ab->pdevs[i].ar;
@@ -173,7 +183,7 @@ EXPORT_SYMBOL(ath12k_core_resume);
static int __ath12k_core_create_board_name(struct ath12k_base *ab, char *name,
size_t name_len, bool with_variant,
- bool bus_type_mode)
+ bool bus_type_mode, bool with_default)
{
/* strlen(',variant=') + strlen(ab->qmi.target.bdf_ext) */
char variant[9 + ATH12K_QMI_BDF_EXT_STR_LENGTH] = { 0 };
@@ -204,7 +214,9 @@ static int __ath12k_core_create_board_name(struct ath12k_base *ab, char *name,
"bus=%s,qmi-chip-id=%d,qmi-board-id=%d%s",
ath12k_bus_str(ab->hif.bus),
ab->qmi.target.chip_id,
- ab->qmi.target.board_id, variant);
+ with_default ?
+ ATH12K_BOARD_ID_DEFAULT : ab->qmi.target.board_id,
+ variant);
break;
}
@@ -216,19 +228,19 @@ static int __ath12k_core_create_board_name(struct ath12k_base *ab, char *name,
static int ath12k_core_create_board_name(struct ath12k_base *ab, char *name,
size_t name_len)
{
- return __ath12k_core_create_board_name(ab, name, name_len, true, false);
+ return __ath12k_core_create_board_name(ab, name, name_len, true, false, false);
}
static int ath12k_core_create_fallback_board_name(struct ath12k_base *ab, char *name,
size_t name_len)
{
- return __ath12k_core_create_board_name(ab, name, name_len, false, false);
+ return __ath12k_core_create_board_name(ab, name, name_len, false, false, true);
}
static int ath12k_core_create_bus_type_board_name(struct ath12k_base *ab, char *name,
size_t name_len)
{
- return __ath12k_core_create_board_name(ab, name, name_len, false, true);
+ return __ath12k_core_create_board_name(ab, name, name_len, false, true, true);
}
const struct firmware *ath12k_core_firmware_request(struct ath12k_base *ab,
@@ -603,9 +615,74 @@ u32 ath12k_core_get_max_num_tids(struct ath12k_base *ab)
return TARGET_NUM_TIDS(SINGLE);
}
+struct reserved_mem *ath12k_core_get_reserved_mem(struct ath12k_base *ab,
+ int index)
+{
+ struct device *dev = ab->dev;
+ struct reserved_mem *rmem;
+ struct device_node *node;
+
+ node = of_parse_phandle(dev->of_node, "memory-region", index);
+ if (!node) {
+ ath12k_dbg(ab, ATH12K_DBG_BOOT,
+ "failed to parse memory-region for index %d\n", index);
+ return NULL;
+ }
+
+ rmem = of_reserved_mem_lookup(node);
+ of_node_put(node);
+ if (!rmem) {
+ ath12k_dbg(ab, ATH12K_DBG_BOOT,
+ "unable to get memory-region for index %d\n", index);
+ return NULL;
+ }
+
+ return rmem;
+}
+
+static inline
+void ath12k_core_to_group_ref_get(struct ath12k_base *ab)
+{
+ struct ath12k_hw_group *ag = ab->ag;
+
+ lockdep_assert_held(&ag->mutex);
+
+ if (ab->hw_group_ref) {
+ ath12k_dbg(ab, ATH12K_DBG_BOOT, "core already attached to group %d\n",
+ ag->id);
+ return;
+ }
+
+ ab->hw_group_ref = true;
+ ag->num_started++;
+
+ ath12k_dbg(ab, ATH12K_DBG_BOOT, "core attached to group %d, num_started %d\n",
+ ag->id, ag->num_started);
+}
+
+static inline
+void ath12k_core_to_group_ref_put(struct ath12k_base *ab)
+{
+ struct ath12k_hw_group *ag = ab->ag;
+
+ lockdep_assert_held(&ag->mutex);
+
+ if (!ab->hw_group_ref) {
+ ath12k_dbg(ab, ATH12K_DBG_BOOT, "core already de-attached from group %d\n",
+ ag->id);
+ return;
+ }
+
+ ab->hw_group_ref = false;
+ ag->num_started--;
+
+ ath12k_dbg(ab, ATH12K_DBG_BOOT, "core de-attached from group %d, num_started %d\n",
+ ag->id, ag->num_started);
+}
+
static void ath12k_core_stop(struct ath12k_base *ab)
{
- ath12k_core_stopped(ab);
+ ath12k_core_to_group_ref_put(ab);
if (!test_bit(ATH12K_FLAG_CRASH_FLUSH, &ab->dev_flags))
ath12k_qmi_firmware_stop(ab);
@@ -620,7 +697,7 @@ static void ath12k_core_stop(struct ath12k_base *ab)
/* De-Init of components as needed */
}
-static void ath12k_core_check_bdfext(const struct dmi_header *hdr, void *data)
+static void ath12k_core_check_cc_code_bdfext(const struct dmi_header *hdr, void *data)
{
struct ath12k_base *ab = data;
const char *magic = ATH12K_SMBIOS_BDF_EXT_MAGIC;
@@ -642,6 +719,28 @@ static void ath12k_core_check_bdfext(const struct dmi_header *hdr, void *data)
return;
}
+ spin_lock_bh(&ab->base_lock);
+
+ switch (smbios->country_code_flag) {
+ case ATH12K_SMBIOS_CC_ISO:
+ ab->new_alpha2[0] = u16_get_bits(smbios->cc_code >> 8, 0xff);
+ ab->new_alpha2[1] = u16_get_bits(smbios->cc_code, 0xff);
+ ath12k_dbg(ab, ATH12K_DBG_BOOT, "boot smbios cc_code %c%c\n",
+ ab->new_alpha2[0], ab->new_alpha2[1]);
+ break;
+ case ATH12K_SMBIOS_CC_WW:
+ ab->new_alpha2[0] = '0';
+ ab->new_alpha2[1] = '0';
+ ath12k_dbg(ab, ATH12K_DBG_BOOT, "boot smbios worldwide regdomain\n");
+ break;
+ default:
+ ath12k_dbg(ab, ATH12K_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) {
ath12k_dbg(ab, ATH12K_DBG_BOOT, "bdf variant name not found.\n");
return;
@@ -681,7 +780,7 @@ static void ath12k_core_check_bdfext(const struct dmi_header *hdr, void *data)
int ath12k_core_check_smbios(struct ath12k_base *ab)
{
ab->qmi.target.bdf_ext[0] = '\0';
- dmi_walk(ath12k_core_check_bdfext, ab);
+ dmi_walk(ath12k_core_check_cc_code_bdfext, ab);
if (ab->qmi.target.bdf_ext[0] == '\0')
return -ENODATA;
@@ -693,6 +792,11 @@ static int ath12k_core_soc_create(struct ath12k_base *ab)
{
int ret;
+ if (ath12k_ftm_mode) {
+ ab->fw_mode = ATH12K_FIRMWARE_MODE_FTM;
+ ath12k_info(ab, "Booting in ftm mode\n");
+ }
+
ret = ath12k_qmi_init_service(ab);
if (ret) {
ath12k_err(ab, "failed to initialize qmi :%d\n", ret);
@@ -707,6 +811,8 @@ static int ath12k_core_soc_create(struct ath12k_base *ab)
goto err_qmi_deinit;
}
+ ath12k_debugfs_pdev_create(ab);
+
return 0;
err_qmi_deinit:
@@ -741,8 +847,7 @@ static void ath12k_core_pdev_destroy(struct ath12k_base *ab)
ath12k_dp_pdev_free(ab);
}
-static int ath12k_core_start(struct ath12k_base *ab,
- enum ath12k_firmware_mode mode)
+static int ath12k_core_start(struct ath12k_base *ab)
{
int ret;
@@ -836,14 +941,10 @@ static int ath12k_core_start(struct ath12k_base *ab,
goto err_reo_cleanup;
}
- ret = ath12k_acpi_start(ab);
- if (ret)
- /* ACPI is optional so continue in case of an error */
- ath12k_dbg(ab, ATH12K_DBG_BOOT, "acpi failed: %d\n", ret);
+ ath12k_acpi_set_dsm_func(ab);
- if (!test_bit(ATH12K_FLAG_RECOVERY, &ab->dev_flags))
- /* Indicate the core start in the appropriate group */
- ath12k_core_started(ab);
+ /* Indicate the core start in the appropriate group */
+ ath12k_core_to_group_ref_get(ab);
return 0;
@@ -881,16 +982,50 @@ static void ath12k_core_hw_group_stop(struct ath12k_hw_group *ag)
ab = ag->ab[i];
if (!ab)
continue;
+
+ clear_bit(ATH12K_FLAG_REGISTERED, &ab->dev_flags);
+
ath12k_core_device_cleanup(ab);
}
ath12k_mac_destroy(ag);
}
+u8 ath12k_get_num_partner_link(struct ath12k *ar)
+{
+ struct ath12k_base *partner_ab, *ab = ar->ab;
+ struct ath12k_hw_group *ag = ab->ag;
+ struct ath12k_pdev *pdev;
+ u8 num_link = 0;
+ int i, j;
+
+ lockdep_assert_held(&ag->mutex);
+
+ for (i = 0; i < ag->num_devices; i++) {
+ partner_ab = ag->ab[i];
+
+ for (j = 0; j < partner_ab->num_radios; j++) {
+ pdev = &partner_ab->pdevs[j];
+
+ /* Avoid the self link */
+ if (ar == pdev->ar)
+ continue;
+
+ num_link++;
+ }
+ }
+
+ return num_link;
+}
+
static int __ath12k_mac_mlo_ready(struct ath12k *ar)
{
+ u8 num_link = ath12k_get_num_partner_link(ar);
int ret;
+ if (num_link == 0)
+ return 0;
+
ret = ath12k_wmi_mlo_ready(ar);
if (ret) {
ath12k_err(ar->ab, "MLO ready failed for pdev %d: %d\n",
@@ -920,19 +1055,18 @@ int ath12k_mac_mlo_ready(struct ath12k_hw_group *ag)
ar = &ah->radio[j];
ret = __ath12k_mac_mlo_ready(ar);
if (ret)
- goto out;
+ return ret;
}
}
-out:
- return ret;
+ return 0;
}
static int ath12k_core_mlo_setup(struct ath12k_hw_group *ag)
{
int ret, i;
- if (!ag->mlo_capable || ag->num_devices == 1)
+ if (!ag->mlo_capable)
return 0;
ret = ath12k_mac_mlo_setup(ag);
@@ -986,6 +1120,8 @@ core_pdev_create:
mutex_lock(&ab->core_lock);
+ set_bit(ATH12K_FLAG_REGISTERED, &ab->dev_flags);
+
ret = ath12k_core_pdev_create(ab);
if (ret) {
ath12k_err(ab, "failed to create pdev core %d\n", ret);
@@ -1044,6 +1180,59 @@ bool ath12k_core_hw_group_start_ready(struct ath12k_hw_group *ag)
return (ag->num_started == ag->num_devices);
}
+static void ath12k_fw_stats_pdevs_free(struct list_head *head)
+{
+ struct ath12k_fw_stats_pdev *i, *tmp;
+
+ list_for_each_entry_safe(i, tmp, head, list) {
+ list_del(&i->list);
+ kfree(i);
+ }
+}
+
+void ath12k_fw_stats_bcn_free(struct list_head *head)
+{
+ struct ath12k_fw_stats_bcn *i, *tmp;
+
+ list_for_each_entry_safe(i, tmp, head, list) {
+ list_del(&i->list);
+ kfree(i);
+ }
+}
+
+static void ath12k_fw_stats_vdevs_free(struct list_head *head)
+{
+ struct ath12k_fw_stats_vdev *i, *tmp;
+
+ list_for_each_entry_safe(i, tmp, head, list) {
+ list_del(&i->list);
+ kfree(i);
+ }
+}
+
+void ath12k_fw_stats_init(struct ath12k *ar)
+{
+ INIT_LIST_HEAD(&ar->fw_stats.vdevs);
+ INIT_LIST_HEAD(&ar->fw_stats.pdevs);
+ INIT_LIST_HEAD(&ar->fw_stats.bcn);
+ init_completion(&ar->fw_stats_complete);
+}
+
+void ath12k_fw_stats_free(struct ath12k_fw_stats *stats)
+{
+ ath12k_fw_stats_pdevs_free(&stats->pdevs);
+ ath12k_fw_stats_vdevs_free(&stats->vdevs);
+ ath12k_fw_stats_bcn_free(&stats->bcn);
+}
+
+void ath12k_fw_stats_reset(struct ath12k *ar)
+{
+ spin_lock_bh(&ar->data_lock);
+ ar->fw_stats.fw_stats_done = false;
+ ath12k_fw_stats_free(&ar->fw_stats);
+ spin_unlock_bh(&ar->data_lock);
+}
+
static void ath12k_core_trigger_partner(struct ath12k_base *ab)
{
struct ath12k_hw_group *ag = ab->ag;
@@ -1068,7 +1257,7 @@ int ath12k_core_qmi_firmware_ready(struct ath12k_base *ab)
struct ath12k_hw_group *ag = ath12k_ab_to_ag(ab);
int ret, i;
- ret = ath12k_core_start_firmware(ab, ATH12K_FIRMWARE_MODE_NORMAL);
+ ret = ath12k_core_start_firmware(ab, ab->fw_mode);
if (ret) {
ath12k_err(ab, "failed to start firmware: %d\n", ret);
return ret;
@@ -1089,7 +1278,7 @@ int ath12k_core_qmi_firmware_ready(struct ath12k_base *ab)
mutex_lock(&ag->mutex);
mutex_lock(&ab->core_lock);
- ret = ath12k_core_start(ab, ATH12K_FIRMWARE_MODE_NORMAL);
+ ret = ath12k_core_start(ab);
if (ret) {
ath12k_err(ab, "failed to start core: %d\n", ret);
goto err_dp_free;
@@ -1122,16 +1311,18 @@ err_core_stop:
ath12k_core_stop(ab);
mutex_unlock(&ab->core_lock);
}
+ mutex_unlock(&ag->mutex);
goto exit;
err_dp_free:
ath12k_dp_free(ab);
mutex_unlock(&ab->core_lock);
+ mutex_unlock(&ag->mutex);
+
err_firmware_stop:
ath12k_qmi_firmware_stop(ab);
exit:
- mutex_unlock(&ag->mutex);
return ret;
}
@@ -1204,6 +1395,7 @@ static void ath12k_rfkill_work(struct work_struct *work)
void ath12k_core_halt(struct ath12k *ar)
{
+ struct list_head *pos, *n;
struct ath12k_base *ab = ar->ab;
lockdep_assert_wiphy(ath12k_ar_to_hw(ar)->wiphy);
@@ -1216,10 +1408,16 @@ void ath12k_core_halt(struct ath12k *ar)
cancel_delayed_work_sync(&ar->scan.timeout);
cancel_work_sync(&ar->regd_update_work);
cancel_work_sync(&ab->rfkill_work);
+ cancel_work_sync(&ab->update_11d_work);
rcu_assign_pointer(ab->pdevs_active[ar->pdev_idx], NULL);
synchronize_rcu();
- INIT_LIST_HEAD(&ar->arvifs);
+
+ spin_lock_bh(&ar->data_lock);
+ list_for_each_safe(pos, n, &ar->arvifs)
+ list_del_init(pos);
+ spin_unlock_bh(&ar->data_lock);
+
idr_init(&ar->txmgmt_idr);
}
@@ -1239,17 +1437,32 @@ static void ath12k_core_pre_reconfigure_recovery(struct ath12k_base *ab)
for (i = 0; i < ag->num_hw; i++) {
ah = ath12k_ag_to_ah(ag, i);
- if (!ah || ah->state == ATH12K_HW_STATE_OFF)
+ if (!ah || ah->state == ATH12K_HW_STATE_OFF ||
+ ah->state == ATH12K_HW_STATE_TM)
continue;
+ wiphy_lock(ah->hw->wiphy);
+
+ /* If queue 0 is stopped, it is safe to assume that all
+ * other queues are stopped by driver via
+ * ieee80211_stop_queues() below. This means, there is
+ * no need to stop it again and hence continue
+ */
+ if (ieee80211_queue_stopped(ah->hw, 0)) {
+ wiphy_unlock(ah->hw->wiphy);
+ continue;
+ }
+
ieee80211_stop_queues(ah->hw);
for (j = 0; j < ah->num_radio; j++) {
ar = &ah->radio[j];
ath12k_mac_drain_tx(ar);
+ ar->state_11d = ATH12K_11D_IDLE;
+ complete(&ar->completed_11d_scan);
complete(&ar->scan.started);
- complete(&ar->scan.completed);
+ complete_all(&ar->scan.completed);
complete(&ar->scan.on_channel);
complete(&ar->peer_assoc_done);
complete(&ar->peer_delete_done);
@@ -1263,13 +1476,47 @@ static void ath12k_core_pre_reconfigure_recovery(struct ath12k_base *ab)
ath12k_mac_tx_mgmt_pending_free, ar);
idr_destroy(&ar->txmgmt_idr);
wake_up(&ar->txmgmt_empty_waitq);
+
+ ar->monitor_vdev_id = -1;
+ ar->monitor_vdev_created = false;
+ ar->monitor_started = false;
}
+
+ wiphy_unlock(ah->hw->wiphy);
}
wake_up(&ab->wmi_ab.tx_credits_wq);
wake_up(&ab->peer_mapping_wq);
}
+static void ath12k_update_11d(struct work_struct *work)
+{
+ struct ath12k_base *ab = container_of(work, struct ath12k_base, update_11d_work);
+ struct ath12k *ar;
+ struct ath12k_pdev *pdev;
+ struct wmi_set_current_country_arg arg = {};
+ int ret, i;
+
+ spin_lock_bh(&ab->base_lock);
+ memcpy(&arg.alpha2, &ab->new_alpha2, 2);
+ spin_unlock_bh(&ab->base_lock);
+
+ ath12k_dbg(ab, ATH12K_DBG_WMI, "update 11d new cc %c%c\n",
+ arg.alpha2[0], arg.alpha2[1]);
+
+ for (i = 0; i < ab->num_radios; i++) {
+ pdev = &ab->pdevs[i];
+ ar = pdev->ar;
+
+ memcpy(&ar->alpha2, &arg.alpha2, 2);
+ ret = ath12k_wmi_send_set_current_country_cmd(ar, &arg);
+ if (ret)
+ ath12k_warn(ar->ab,
+ "pdev id %d failed set current country code: %d\n",
+ i, ret);
+ }
+}
+
static void ath12k_core_post_reconfigure_recovery(struct ath12k_base *ab)
{
struct ath12k_hw_group *ag = ab->ag;
@@ -1309,6 +1556,9 @@ static void ath12k_core_post_reconfigure_recovery(struct ath12k_base *ab)
ath12k_warn(ab,
"device is wedged, will not restart hw %d\n", i);
break;
+ case ATH12K_HW_STATE_TM:
+ ath12k_warn(ab, "fw mode reset done radio %d\n", i);
+ break;
}
mutex_unlock(&ah->hw_mutex);
@@ -1340,19 +1590,30 @@ static void ath12k_core_restart(struct work_struct *work)
ath12k_dbg(ab, ATH12K_DBG_BOOT, "reset success\n");
}
+ mutex_lock(&ag->mutex);
+
+ if (!ath12k_core_hw_group_start_ready(ag)) {
+ mutex_unlock(&ag->mutex);
+ goto exit_restart;
+ }
+
for (i = 0; i < ag->num_hw; i++) {
- ah = ath12k_ag_to_ah(ab->ag, i);
+ ah = ath12k_ag_to_ah(ag, i);
ieee80211_restart_hw(ah->hw);
}
+
+ mutex_unlock(&ag->mutex);
}
+exit_restart:
complete(&ab->restart_completed);
}
static void ath12k_core_reset(struct work_struct *work)
{
struct ath12k_base *ab = container_of(work, struct ath12k_base, reset_work);
- int reset_count, fail_cont_count;
+ struct ath12k_hw_group *ag = ab->ag;
+ int reset_count, fail_cont_count, i;
long time_left;
if (!(test_bit(ATH12K_FLAG_QMI_FW_READY_COMPLETE, &ab->dev_flags))) {
@@ -1411,9 +1672,34 @@ static void ath12k_core_reset(struct work_struct *work)
ath12k_hif_ce_irq_disable(ab);
ath12k_hif_power_down(ab, false);
- ath12k_hif_power_up(ab);
- ath12k_dbg(ab, ATH12K_DBG_BOOT, "reset started\n");
+ /* prepare for power up */
+ ab->qmi.num_radios = U8_MAX;
+
+ mutex_lock(&ag->mutex);
+ ath12k_core_to_group_ref_put(ab);
+
+ if (ag->num_started > 0) {
+ ath12k_dbg(ab, ATH12K_DBG_BOOT,
+ "waiting for %d partner device(s) to reset\n",
+ ag->num_started);
+ mutex_unlock(&ag->mutex);
+ return;
+ }
+
+ /* Prepare MLO global memory region for power up */
+ ath12k_qmi_reset_mlo_mem(ag);
+
+ for (i = 0; i < ag->num_devices; i++) {
+ ab = ag->ab[i];
+ if (!ab)
+ continue;
+
+ ath12k_hif_power_up(ab);
+ ath12k_dbg(ab, ATH12K_DBG_BOOT, "reset started\n");
+ }
+
+ mutex_unlock(&ag->mutex);
}
int ath12k_core_pre_init(struct ath12k_base *ab)
@@ -1550,9 +1836,9 @@ static int ath12k_core_get_wsi_info(struct ath12k_hw_group *ag,
of_node_put(next_rx_endpoint);
device_count++;
- if (device_count > ATH12K_MAX_SOCS) {
+ if (device_count > ATH12K_MAX_DEVICES) {
ath12k_warn(ab, "device count in DT %d is more than limit %d\n",
- device_count, ATH12K_MAX_SOCS);
+ device_count, ATH12K_MAX_DEVICES);
of_node_put(next_wsi_dev);
return -EINVAL;
}
@@ -1602,6 +1888,9 @@ static struct ath12k_hw_group *ath12k_core_hw_group_assign(struct ath12k_base *a
lockdep_assert_held(&ath12k_hw_group_mutex);
+ if (ath12k_ftm_mode)
+ goto invalid_group;
+
/* The grouping of multiple devices will be done based on device tree file.
* The platforms that do not have any valid group information would have
* each device to be part of its own invalid group.
@@ -1725,7 +2014,7 @@ static void ath12k_core_hw_group_destroy(struct ath12k_hw_group *ag)
}
}
-static void ath12k_core_hw_group_cleanup(struct ath12k_hw_group *ag)
+void ath12k_core_hw_group_cleanup(struct ath12k_hw_group *ag)
{
struct ath12k_base *ab;
int i;
@@ -1789,29 +2078,26 @@ void ath12k_core_hw_group_set_mlo_capable(struct ath12k_hw_group *ag)
struct ath12k_base *ab;
int i;
+ if (ath12k_ftm_mode)
+ return;
+
lockdep_assert_held(&ag->mutex);
- /* If more than one devices are grouped, then inter MLO
- * functionality can work still independent of whether internally
- * each device supports single_chip_mlo or not.
- * Only when there is one device, then it depends whether the
- * device can support intra chip MLO or not
- */
- if (ag->num_devices > 1) {
- ag->mlo_capable = true;
- } else {
+ if (ag->num_devices == 1) {
ab = ag->ab[0];
- ag->mlo_capable = ab->single_chip_mlo_supp;
-
- /* WCN chipsets does not advertise in firmware features
- * hence skip checking
- */
- if (ab->hw_params->def_num_link)
+ /* QCN9274 firmware uses firmware IE for MLO advertisement */
+ if (ab->fw.fw_features_valid) {
+ ag->mlo_capable =
+ ath12k_fw_feature_supported(ab, ATH12K_FW_FEATURE_MLO);
return;
- }
+ }
- if (!ag->mlo_capable)
+ /* while WCN7850 firmware uses QMI single_chip_mlo_support bit */
+ ag->mlo_capable = ab->single_chip_mlo_support;
return;
+ }
+
+ ag->mlo_capable = true;
for (i = 0; i < ag->num_devices; i++) {
ab = ag->ab[i];
@@ -1821,7 +2107,7 @@ void ath12k_core_hw_group_set_mlo_capable(struct ath12k_hw_group *ag)
/* even if 1 device's firmware feature indicates MLO
* unsupported, make MLO unsupported for the whole group
*/
- if (!test_bit(ATH12K_FW_FEATURE_MLO, ab->fw.fw_features)) {
+ if (!ath12k_fw_feature_supported(ab, ATH12K_FW_FEATURE_MLO)) {
ag->mlo_capable = false;
return;
}
@@ -1874,10 +2160,9 @@ err:
void ath12k_core_deinit(struct ath12k_base *ab)
{
- ath12k_core_panic_notifier_unregister(ab);
- ath12k_core_hw_group_cleanup(ab->ag);
ath12k_core_hw_group_destroy(ab->ag);
ath12k_core_hw_group_unassign(ab);
+ ath12k_core_panic_notifier_unregister(ab);
}
void ath12k_core_free(struct ath12k_base *ab)
@@ -1918,6 +2203,7 @@ struct ath12k_base *ath12k_core_alloc(struct device *dev, size_t priv_size,
INIT_WORK(&ab->reset_work, ath12k_core_reset);
INIT_WORK(&ab->rfkill_work, ath12k_rfkill_work);
INIT_WORK(&ab->dump_work, ath12k_coredump_upload);
+ INIT_WORK(&ab->update_11d_work, ath12k_update_11d);
timer_setup(&ab->rx_replenish_retry, ath12k_ce_rx_replenish_retry, 0);
init_completion(&ab->htc_suspend);
@@ -1927,7 +2213,7 @@ struct ath12k_base *ath12k_core_alloc(struct device *dev, size_t priv_size,
ab->dev = dev;
ab->hif.bus = bus;
ab->qmi.num_radios = U8_MAX;
- ab->single_chip_mlo_supp = false;
+ ab->single_chip_mlo_support = false;
/* Device index used to identify the devices in a group.
*
@@ -1948,5 +2234,31 @@ err_sc_free:
return NULL;
}
-MODULE_DESCRIPTION("Core module for Qualcomm Atheros 802.11be wireless LAN cards.");
+static int ath12k_init(void)
+{
+ ahb_err = ath12k_ahb_init();
+ if (ahb_err)
+ pr_warn("Failed to initialize ath12k AHB device: %d\n", ahb_err);
+
+ pci_err = ath12k_pci_init();
+ if (pci_err)
+ pr_warn("Failed to initialize ath12k PCI device: %d\n", pci_err);
+
+ /* If both failed, return one of the failures (arbitrary) */
+ return ahb_err && pci_err ? ahb_err : 0;
+}
+
+static void ath12k_exit(void)
+{
+ if (!pci_err)
+ ath12k_pci_exit();
+
+ if (!ahb_err)
+ ath12k_ahb_exit();
+}
+
+module_init(ath12k_init);
+module_exit(ath12k_exit);
+
+MODULE_DESCRIPTION("Driver support for Qualcomm Technologies 802.11be WLAN devices");
MODULE_LICENSE("Dual BSD/GPL");
diff --git a/drivers/net/wireless/ath/ath12k/core.h b/drivers/net/wireless/ath/ath12k/core.h
index ee595794a7ae..941db6e49d6e 100644
--- a/drivers/net/wireless/ath/ath12k/core.h
+++ b/drivers/net/wireless/ath/ath12k/core.h
@@ -14,7 +14,10 @@
#include <linux/dmi.h>
#include <linux/ctype.h>
#include <linux/firmware.h>
+#include <linux/of_reserved_mem.h>
#include <linux/panic_notifier.h>
+#include <linux/average.h>
+#include <linux/of.h>
#include "qmi.h"
#include "htc.h"
#include "wmi.h"
@@ -52,8 +55,6 @@
#define ATH12K_INVALID_HW_MAC_ID 0xFF
#define ATH12K_CONNECTION_LOSS_HZ (3 * HZ)
-#define ATH12K_RX_RATE_TABLE_NUM 320
-#define ATH12K_RX_RATE_TABLE_11AX_NUM 576
#define ATH12K_MON_TIMER_INTERVAL 10
#define ATH12K_RESET_TIMEOUT_HZ (20 * HZ)
@@ -63,8 +64,8 @@
#define ATH12K_RECONFIGURE_TIMEOUT_HZ (10 * HZ)
#define ATH12K_RECOVER_START_TIMEOUT_HZ (20 * HZ)
-#define ATH12K_MAX_SOCS 3
-#define ATH12K_GROUP_MAX_RADIO (ATH12K_MAX_SOCS * MAX_RADIOS)
+#define ATH12K_MAX_DEVICES 3
+#define ATH12K_GROUP_MAX_RADIO (ATH12K_MAX_DEVICES * MAX_RADIOS)
#define ATH12K_INVALID_GROUP_ID 0xFF
#define ATH12K_INVALID_DEVICE_ID 0xFF
@@ -87,6 +88,7 @@ enum wme_ac {
#define ATH12K_HT_MCS_MAX 7
#define ATH12K_VHT_MCS_MAX 9
#define ATH12K_HE_MCS_MAX 11
+#define ATH12K_EHT_MCS_MAX 15
enum ath12k_crypt_mode {
/* Only use hardware crypto engine */
@@ -141,12 +143,14 @@ struct ath12k_skb_rxcb {
u8 is_frag;
u8 tid;
u16 peer_id;
+ bool is_end_of_ppdu;
};
enum ath12k_hw_rev {
ATH12K_HW_QCN9274_HW10,
ATH12K_HW_QCN9274_HW20,
- ATH12K_HW_WCN7850_HW20
+ ATH12K_HW_WCN7850_HW20,
+ ATH12K_HW_IPQ5332_HW10,
};
enum ath12k_firmware_mode {
@@ -159,6 +163,7 @@ enum ath12k_firmware_mode {
#define ATH12K_IRQ_NUM_MAX 57
#define ATH12K_EXT_IRQ_NUM_MAX 16
+#define ATH12K_MAX_TCL_RING_NUM 3
struct ath12k_ext_irq_grp {
struct ath12k_base *ab;
@@ -166,13 +171,39 @@ struct ath12k_ext_irq_grp {
u32 num_irq;
u32 grp_id;
u64 timestamp;
+ bool napi_enabled;
struct napi_struct napi;
struct net_device *napi_ndev;
};
+enum ath12k_smbios_cc_type {
+ /* disable country code setting from SMBIOS */
+ ATH12K_SMBIOS_CC_DISABLE = 0,
+
+ /* set country code by ANSI country name, based on ISO3166-1 alpha2 */
+ ATH12K_SMBIOS_CC_ISO = 1,
+
+ /* worldwide regdomain */
+ ATH12K_SMBIOS_CC_WW = 2,
+};
+
struct ath12k_smbios_bdf {
struct dmi_header hdr;
- u32 padding;
+ u8 features_disabled;
+
+ /* enum ath12k_smbios_cc_type */
+ u8 country_code_flag;
+
+ /* To set specific country, you need to set country code
+ * flag=ATH12K_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 =
+ * ATH12K_SMBIOS_CC_WW, then you can use worldwide regulatory
+ * setting.
+ */
+ u16 cc_code;
+
u8 bdf_enabled;
u8 bdf_ext[];
} __packed;
@@ -217,6 +248,12 @@ enum ath12k_scan_state {
ATH12K_SCAN_ABORTING,
};
+enum ath12k_11d_state {
+ ATH12K_11D_IDLE,
+ ATH12K_11D_PREPARING,
+ ATH12K_11D_RUNNING,
+};
+
enum ath12k_hw_group_flags {
ATH12K_GROUP_FLAG_REGISTERED,
ATH12K_GROUP_FLAG_UNREGISTER,
@@ -235,6 +272,8 @@ enum ath12k_dev_flags {
ATH12K_FLAG_CE_IRQ_ENABLED,
ATH12K_FLAG_EXT_IRQ_ENABLED,
ATH12K_FLAG_QMI_FW_READY_COMPLETE,
+ ATH12K_FLAG_FTM_SEGMENTED,
+ ATH12K_FLAG_FIXED_MEM_REGION,
};
struct ath12k_tx_conf {
@@ -292,12 +331,20 @@ struct ath12k_link_vif {
int txpower;
bool rsnie_present;
bool wpaie_present;
- struct ieee80211_chanctx_conf chanctx;
u8 vdev_stats_id;
u32 punct_bitmap;
u8 link_id;
struct ath12k_vif *ahvif;
struct ath12k_rekey_data rekey_data;
+ struct ath12k_link_stats link_stats;
+ spinlock_t link_stats_lock; /* Protects updates to link_stats */
+
+ u8 current_cntdown_counter;
+
+ /* only used in station mode */
+ bool is_sta_assoc_link;
+
+ struct ath12k_reg_tpc_power_info reg_tpc_info;
};
struct ath12k_vif {
@@ -327,6 +374,7 @@ struct ath12k_vif {
u32 key_cipher;
u8 tx_encap_type;
bool ps;
+ atomic_t mcbc_gsn;
struct ath12k_link_vif deflink;
struct ath12k_link_vif __rcu *link[ATH12K_NUM_MAX_LINKS];
@@ -354,20 +402,22 @@ struct ath12k_vif_iter {
#define HAL_RX_MAX_MCS_HT 31
#define HAL_RX_MAX_MCS_VHT 9
#define HAL_RX_MAX_MCS_HE 11
+#define HAL_RX_MAX_MCS_BE 15
#define HAL_RX_MAX_NSS 8
#define HAL_RX_MAX_NUM_LEGACY_RATES 12
-#define ATH12K_RX_RATE_TABLE_11AX_NUM 576
-#define ATH12K_RX_RATE_TABLE_NUM 320
+
+#define ATH12K_SCAN_TIMEOUT_HZ (20 * HZ)
struct ath12k_rx_peer_rate_stats {
u64 ht_mcs_count[HAL_RX_MAX_MCS_HT + 1];
u64 vht_mcs_count[HAL_RX_MAX_MCS_VHT + 1];
u64 he_mcs_count[HAL_RX_MAX_MCS_HE + 1];
+ u64 be_mcs_count[HAL_RX_MAX_MCS_BE + 1];
u64 nss_count[HAL_RX_MAX_NSS];
u64 bw_count[HAL_RX_BW_MAX];
u64 gi_count[HAL_RX_GI_MAX];
u64 legacy_count[HAL_RX_MAX_NUM_LEGACY_RATES];
- u64 rx_rate[ATH12K_RX_RATE_TABLE_11AX_NUM];
+ u64 rx_rate[HAL_RX_BW_MAX][HAL_RX_GI_MAX][HAL_RX_MAX_NSS][HAL_RX_MAX_MCS_HT + 1];
};
struct ath12k_rx_peer_stats {
@@ -477,6 +527,8 @@ struct ath12k_wbm_tx_stats {
u64 wbm_tx_comp_stats[HAL_WBM_REL_HTT_TX_COMP_STATUS_MAX];
};
+DECLARE_EWMA(avg_rssi, 10, 8)
+
struct ath12k_link_sta {
struct ath12k_link_vif *arvif;
struct ath12k_sta *ahsta;
@@ -496,10 +548,13 @@ struct ath12k_link_sta {
u64 rx_duration;
u64 tx_duration;
u8 rssi_comb;
+ struct ewma_avg_rssi avg_rssi;
u8 link_id;
struct ath12k_rx_peer_stats *rx_stats;
struct ath12k_wbm_tx_stats *wbm_tx_stats;
u32 bw_prev;
+ u32 peer_nss;
+ s8 rssi_beacon;
/* For now the assoc link will be considered primary */
bool is_assoc_link;
@@ -508,6 +563,12 @@ struct ath12k_link_sta {
u8 link_idx;
};
+struct ath12k_reoq_buf {
+ void *vaddr;
+ dma_addr_t paddr_aligned;
+ u32 size;
+};
+
struct ath12k_sta {
struct ath12k_vif *ahvif;
enum hal_pn_type pn_type;
@@ -520,13 +581,25 @@ struct ath12k_sta {
u8 num_peer;
enum ieee80211_sta_state state;
+
+ struct ath12k_reoq_buf reoq_bufs[IEEE80211_NUM_TIDS + 1];
};
-#define ATH12K_MIN_5G_FREQ 4150
-#define ATH12K_MIN_6G_FREQ 5925
-#define ATH12K_MAX_6G_FREQ 7115
+#define ATH12K_HALF_20MHZ_BW 10
+#define ATH12K_2GHZ_MIN_CENTER 2412
+#define ATH12K_2GHZ_MAX_CENTER 2484
+#define ATH12K_5GHZ_MIN_CENTER 4900
+#define ATH12K_5GHZ_MAX_CENTER 5920
+#define ATH12K_6GHZ_MIN_CENTER 5935
+#define ATH12K_6GHZ_MAX_CENTER 7115
+#define ATH12K_MIN_2GHZ_FREQ (ATH12K_2GHZ_MIN_CENTER - ATH12K_HALF_20MHZ_BW - 1)
+#define ATH12K_MAX_2GHZ_FREQ (ATH12K_2GHZ_MAX_CENTER + ATH12K_HALF_20MHZ_BW + 1)
+#define ATH12K_MIN_5GHZ_FREQ (ATH12K_5GHZ_MIN_CENTER - ATH12K_HALF_20MHZ_BW)
+#define ATH12K_MAX_5GHZ_FREQ (ATH12K_5GHZ_MAX_CENTER + ATH12K_HALF_20MHZ_BW)
+#define ATH12K_MIN_6GHZ_FREQ (ATH12K_6GHZ_MIN_CENTER - ATH12K_HALF_20MHZ_BW)
+#define ATH12K_MAX_6GHZ_FREQ (ATH12K_6GHZ_MAX_CENTER + ATH12K_HALF_20MHZ_BW)
#define ATH12K_NUM_CHANS 101
-#define ATH12K_MAX_5G_CHAN 173
+#define ATH12K_MAX_5GHZ_CHAN 173
enum ath12k_hw_state {
ATH12K_HW_STATE_OFF,
@@ -534,18 +607,26 @@ enum ath12k_hw_state {
ATH12K_HW_STATE_RESTARTING,
ATH12K_HW_STATE_RESTARTED,
ATH12K_HW_STATE_WEDGED,
+ ATH12K_HW_STATE_TM,
/* Add other states as required */
};
/* Antenna noise floor */
#define ATH12K_DEFAULT_NOISE_FLOOR -95
+struct ath12k_ftm_event_obj {
+ u32 data_pos;
+ u32 expected_seq;
+ u8 *eventdata;
+};
+
struct ath12k_fw_stats {
u32 pdev_id;
u32 stats_id;
struct list_head pdevs;
struct list_head vdevs;
struct list_head bcn;
+ bool fw_stats_done;
};
struct ath12k_dbg_htt_stats {
@@ -559,6 +640,12 @@ struct ath12k_debug {
struct dentry *debugfs_pdev;
struct dentry *debugfs_pdev_symlink;
struct ath12k_dbg_htt_stats htt_stats;
+ enum wmi_halphy_ctrl_path_stats_id tpc_stats_type;
+ bool tpc_request;
+ struct completion tpc_complete;
+ struct wmi_tpc_stats_arg *tpc_stats;
+ u32 rx_filter;
+ bool extd_rx_stats;
};
struct ath12k_per_peer_tx_stats {
@@ -703,7 +790,6 @@ struct ath12k {
#endif
bool dfs_block_radar_events;
- bool monitor_conf_enabled;
bool monitor_vdev_created;
bool monitor_started;
int monitor_vdev_id;
@@ -712,8 +798,22 @@ struct ath12k {
bool nlo_enabled;
+ /* Protected by wiphy::mtx lock. */
+ u32 vdev_id_11d_scan;
+ struct completion completed_11d_scan;
+ enum ath12k_11d_state state_11d;
+ u8 alpha2[REG_ALPHA2_LEN];
+ bool regdom_set_by_user;
+
+ struct completion fw_stats_complete;
+
struct completion mlo_setup_done;
u32 mlo_setup_status;
+ u8 ftm_msgref;
+ struct ath12k_fw_stats fw_stats;
+ unsigned long last_tx_power_update;
+
+ s8 max_allowed_tx_power;
};
struct ath12k_hw {
@@ -805,7 +905,7 @@ struct ath12k_board_data {
size_t len;
};
-struct ath12k_soc_dp_tx_err_stats {
+struct ath12k_device_dp_tx_err_stats {
/* TCL Ring Descriptor unavailable */
u32 desc_na[DP_TCL_NUM_RING_MAX];
/* Other failures during dp_tx due to mem allocation failure
@@ -814,13 +914,25 @@ struct ath12k_soc_dp_tx_err_stats {
atomic_t misc_fail;
};
-struct ath12k_soc_dp_stats {
+struct ath12k_device_dp_stats {
u32 err_ring_pkts;
u32 invalid_rbm;
u32 rxdma_error[HAL_REO_ENTR_RING_RXDMA_ECODE_MAX];
u32 reo_error[HAL_REO_DEST_RING_ERROR_CODE_MAX];
u32 hal_reo_error[DP_REO_DST_RING_MAX];
- struct ath12k_soc_dp_tx_err_stats tx_err;
+ struct ath12k_device_dp_tx_err_stats tx_err;
+ u32 reo_rx[DP_REO_DST_RING_MAX][ATH12K_MAX_DEVICES];
+ u32 rx_wbm_rel_source[HAL_WBM_REL_SRC_MODULE_MAX][ATH12K_MAX_DEVICES];
+ u32 tqm_rel_reason[MAX_TQM_RELEASE_REASON];
+ u32 fw_tx_status[MAX_FW_TX_STATUS];
+ u32 tx_wbm_rel_source[HAL_WBM_REL_SRC_MODULE_MAX];
+ u32 tx_enqueued[DP_TCL_NUM_RING_MAX];
+ u32 tx_completed[DP_TCL_NUM_RING_MAX];
+};
+
+struct ath12k_reg_freq {
+ u32 start_freq;
+ u32 end_freq;
};
struct ath12k_mlo_memory {
@@ -844,7 +956,7 @@ struct ath12k_hw_group {
u8 num_probed;
u8 num_started;
unsigned long flags;
- struct ath12k_base *ab[ATH12K_MAX_SOCS];
+ struct ath12k_base *ab[ATH12K_MAX_DEVICES];
/* protects access to this struct */
struct mutex mutex;
@@ -858,7 +970,7 @@ struct ath12k_hw_group {
struct ath12k_hw *ah[ATH12K_GROUP_MAX_RADIO];
u8 num_hw;
bool mlo_capable;
- struct device_node *wsi_node[ATH12K_MAX_SOCS];
+ struct device_node *wsi_node[ATH12K_MAX_DEVICES];
struct ath12k_mlo_memory mlo_mem;
struct ath12k_hw_link hw_links[ATH12K_GROUP_MAX_RADIO];
bool hw_link_id_init_done;
@@ -894,6 +1006,10 @@ struct ath12k_base {
void __iomem *mem;
unsigned long mem_len;
+ void __iomem *mem_ce;
+ u32 ce_remap_base_addr;
+ bool ce_remap;
+
struct {
enum ath12k_bus bus;
const struct ath12k_hif_ops *ops;
@@ -962,9 +1078,11 @@ struct ath12k_base {
*/
struct ieee80211_regdomain *new_regd[MAX_RADIOS];
+ struct ath12k_reg_info *reg_info[MAX_RADIOS];
+
/* Current DFS Regulatory */
enum ath12k_dfs_region dfs_region;
- struct ath12k_soc_dp_stats soc_stats;
+ struct ath12k_device_dp_stats device_stats;
#ifdef CONFIG_ATH12K_DEBUGFS
struct dentry *debugfs_soc;
#endif
@@ -982,6 +1100,8 @@ struct ath12k_base {
/* continuous recovery fail count */
atomic_t fail_cont_count;
unsigned long reset_fail_timeout;
+ struct work_struct update_11d_work;
+ u8 new_alpha2[2];
struct {
/* protected by data_lock */
u32 fw_crash_counter;
@@ -991,8 +1111,6 @@ struct ath12k_base {
struct ath12k_dbring_cap *db_caps;
u32 num_db_cap;
- struct timer_list mon_reap_timer;
-
struct completion htc_suspend;
u64 fw_soc_drop_count;
@@ -1022,13 +1140,11 @@ struct ath12k_base {
size_t m3_len;
DECLARE_BITMAP(fw_features, ATH12K_FW_FEATURE_COUNT);
+ bool fw_features_valid;
} fw;
const struct hal_rx_ops *hal_rx_ops;
- /* Denotes the whether MLO is possible within the chip */
- bool single_chip_mlo_supp;
-
struct completion restart_completed;
#ifdef CONFIG_ACPI
@@ -1038,6 +1154,13 @@ struct ath12k_base {
u32 func_bit;
bool acpi_tas_enable;
bool acpi_bios_sar_enable;
+ bool acpi_disable_11be;
+ bool acpi_disable_rfkill;
+ bool acpi_cca_enable;
+ bool acpi_band_edge_enable;
+ bool acpi_enable_bdf;
+ u32 bit_flag;
+ char bdf_string[ATH12K_ACPI_BDF_MAX_LEN];
u8 tas_cfg[ATH12K_ACPI_DSM_TAS_CFG_SIZE];
u8 tas_sar_power_table[ATH12K_ACPI_DSM_TAS_DATA_SIZE];
u8 bios_sar_data[ATH12K_ACPI_DSM_BIOS_SAR_DATA_SIZE];
@@ -1052,6 +1175,16 @@ struct ath12k_base {
struct ath12k_hw_group *ag;
struct ath12k_wsi_info wsi_info;
+ enum ath12k_firmware_mode fw_mode;
+ struct ath12k_ftm_event_obj ftm_event_obj;
+ bool hw_group_ref;
+
+ /* Denote whether MLO is possible within the device */
+ bool single_chip_mlo_support;
+
+ struct ath12k_reg_freq reg_freq_2ghz;
+ struct ath12k_reg_freq reg_freq_5ghz;
+ struct ath12k_reg_freq reg_freq_6ghz;
/* must be last */
u8 drv_priv[] __aligned(sizeof(void *));
@@ -1062,7 +1195,95 @@ struct ath12k_pdev_map {
u8 pdev_idx;
};
+struct ath12k_fw_stats_vdev {
+ struct list_head list;
+
+ u32 vdev_id;
+ u32 beacon_snr;
+ u32 data_snr;
+ u32 num_tx_frames[WLAN_MAX_AC];
+ u32 num_rx_frames;
+ u32 num_tx_frames_retries[WLAN_MAX_AC];
+ u32 num_tx_frames_failures[WLAN_MAX_AC];
+ u32 num_rts_fail;
+ u32 num_rts_success;
+ u32 num_rx_err;
+ u32 num_rx_discard;
+ u32 num_tx_not_acked;
+ u32 tx_rate_history[MAX_TX_RATE_VALUES];
+ u32 beacon_rssi_history[MAX_TX_RATE_VALUES];
+};
+
+struct ath12k_fw_stats_bcn {
+ struct list_head list;
+
+ u32 vdev_id;
+ u32 tx_bcn_succ_cnt;
+ u32 tx_bcn_outage_cnt;
+};
+
+struct ath12k_fw_stats_pdev {
+ struct list_head list;
+
+ /* PDEV stats */
+ s32 ch_noise_floor;
+ u32 tx_frame_count;
+ u32 rx_frame_count;
+ u32 rx_clear_count;
+ u32 cycle_count;
+ u32 phy_err_count;
+ u32 chan_tx_power;
+ u32 ack_rx_bad;
+ u32 rts_bad;
+ u32 rts_good;
+ u32 fcs_bad;
+ u32 no_beacons;
+ u32 mib_int_count;
+
+ /* PDEV TX stats */
+ s32 comp_queued;
+ s32 comp_delivered;
+ s32 msdu_enqued;
+ s32 mpdu_enqued;
+ s32 wmm_drop;
+ s32 local_enqued;
+ s32 local_freed;
+ s32 hw_queued;
+ s32 hw_reaped;
+ s32 underrun;
+ s32 tx_abort;
+ s32 mpdus_requed;
+ u32 tx_ko;
+ u32 data_rc;
+ u32 self_triggers;
+ u32 sw_retry_failure;
+ u32 illgl_rate_phy_err;
+ u32 pdev_cont_xretry;
+ u32 pdev_tx_timeout;
+ u32 pdev_resets;
+ u32 stateless_tid_alloc_failure;
+ u32 phy_underrun;
+ u32 txop_ovf;
+
+ /* PDEV RX stats */
+ s32 mid_ppdu_route_change;
+ s32 status_rcvd;
+ s32 r0_frags;
+ s32 r1_frags;
+ s32 r2_frags;
+ s32 r3_frags;
+ s32 htt_msdus;
+ s32 htt_mpdus;
+ s32 loc_msdus;
+ s32 loc_mpdus;
+ s32 oversize_amsdu;
+ s32 phy_errs;
+ s32 phy_err_drop;
+ s32 mpdu_errs;
+};
+
int ath12k_core_qmi_firmware_ready(struct ath12k_base *ab);
+void ath12k_core_hw_group_cleanup(struct ath12k_hw_group *ag);
int ath12k_core_pre_init(struct ath12k_base *ab);
int ath12k_core_init(struct ath12k_base *ath12k);
void ath12k_core_deinit(struct ath12k_base *ath12k);
@@ -1084,6 +1305,7 @@ int ath12k_core_resume(struct ath12k_base *ab);
int ath12k_core_suspend(struct ath12k_base *ab);
int ath12k_core_suspend_late(struct ath12k_base *ab);
void ath12k_core_hw_group_unassign(struct ath12k_base *ab);
+u8 ath12k_get_num_partner_link(struct ath12k *ar);
const struct firmware *ath12k_core_firmware_request(struct ath12k_base *ab,
const char *filename);
@@ -1092,6 +1314,12 @@ u32 ath12k_core_get_max_peers_per_radio(struct ath12k_base *ab);
u32 ath12k_core_get_max_num_tids(struct ath12k_base *ab);
void ath12k_core_hw_group_set_mlo_capable(struct ath12k_hw_group *ag);
+void ath12k_fw_stats_init(struct ath12k *ar);
+void ath12k_fw_stats_bcn_free(struct list_head *head);
+void ath12k_fw_stats_free(struct ath12k_fw_stats *stats);
+void ath12k_fw_stats_reset(struct ath12k *ar);
+struct reserved_mem *ath12k_core_get_reserved_mem(struct ath12k_base *ab,
+ int index);
static inline const char *ath12k_scan_state_str(enum ath12k_scan_state state)
{
@@ -1152,8 +1380,16 @@ static inline void ath12k_core_create_firmware_path(struct ath12k_base *ab,
const char *filename,
void *buf, size_t buf_len)
{
- snprintf(buf, buf_len, "%s/%s/%s", ATH12K_FW_DIR,
- ab->hw_params->fw.dir, filename);
+ const char *fw_name = NULL;
+
+ of_property_read_string(ab->dev->of_node, "firmware-name", &fw_name);
+
+ if (fw_name && strncmp(filename, "board", 5))
+ snprintf(buf, buf_len, "%s/%s/%s/%s", ATH12K_FW_DIR,
+ ab->hw_params->fw.dir, fw_name, filename);
+ else
+ snprintf(buf, buf_len, "%s/%s/%s", ATH12K_FW_DIR,
+ ab->hw_params->fw.dir, filename);
}
static inline const char *ath12k_bus_str(enum ath12k_bus bus)
@@ -1161,6 +1397,8 @@ static inline const char *ath12k_bus_str(enum ath12k_bus bus)
switch (bus) {
case ATH12K_BUS_PCI:
return "pci";
+ case ATH12K_BUS_AHB:
+ return "ahb";
}
return "unknown";
@@ -1210,20 +1448,6 @@ static inline struct ath12k_hw_group *ath12k_ab_to_ag(struct ath12k_base *ab)
return ab->ag;
}
-static inline void ath12k_core_started(struct ath12k_base *ab)
-{
- lockdep_assert_held(&ab->ag->mutex);
-
- ab->ag->num_started++;
-}
-
-static inline void ath12k_core_stopped(struct ath12k_base *ab)
-{
- lockdep_assert_held(&ab->ag->mutex);
-
- ab->ag->num_started--;
-}
-
static inline struct ath12k_base *ath12k_ag_to_ab(struct ath12k_hw_group *ag,
u8 device_id)
{
diff --git a/drivers/net/wireless/ath/ath12k/debug.c b/drivers/net/wireless/ath/ath12k/debug.c
index ff6eaeafa092..5ce100cd9a9d 100644
--- a/drivers/net/wireless/ath/ath12k/debug.c
+++ b/drivers/net/wireless/ath/ath12k/debug.c
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: BSD-3-Clause-Clear
/*
* Copyright (c) 2018-2021 The Linux Foundation. All rights reserved.
- * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#include <linux/vmalloc.h>
@@ -63,8 +63,10 @@ void __ath12k_dbg(struct ath12k_base *ab, enum ath12k_debug_mask mask,
vaf.fmt = fmt;
vaf.va = &args;
- if (ath12k_debug_mask & mask)
+ if (likely(ab))
dev_printk(KERN_DEBUG, ab->dev, "%pV", &vaf);
+ else
+ printk(KERN_DEBUG "ath12k: %pV", &vaf);
/* TODO: trace log */
diff --git a/drivers/net/wireless/ath/ath12k/debug.h b/drivers/net/wireless/ath/ath12k/debug.h
index 90e801136bc6..48916e4e1f60 100644
--- a/drivers/net/wireless/ath/ath12k/debug.h
+++ b/drivers/net/wireless/ath/ath12k/debug.h
@@ -1,7 +1,7 @@
/* SPDX-License-Identifier: BSD-3-Clause-Clear */
/*
* Copyright (c) 2018-2021 The Linux Foundation. All rights reserved.
- * Copyright (c) 2021-2022, 2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) 2021-2022, 2024-2025 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#ifndef _ATH12K_DEBUG_H_
@@ -37,6 +37,7 @@ __printf(2, 3) void __ath12k_warn(struct device *dev, const char *fmt, ...);
#define ath12k_hw_warn(ah, fmt, ...) __ath12k_warn((ah)->dev, fmt, ##__VA_ARGS__)
extern unsigned int ath12k_debug_mask;
+extern bool ath12k_ftm_mode;
#ifdef CONFIG_ATH12K_DEBUG
__printf(3, 4) void __ath12k_dbg(struct ath12k_base *ab,
@@ -61,11 +62,14 @@ static inline void ath12k_dbg_dump(struct ath12k_base *ab,
}
#endif /* CONFIG_ATH12K_DEBUG */
-#define ath12k_dbg(ar, dbg_mask, fmt, ...) \
+#define ath12k_dbg(ab, dbg_mask, fmt, ...) \
do { \
typeof(dbg_mask) mask = (dbg_mask); \
if (ath12k_debug_mask & mask) \
- __ath12k_dbg(ar, mask, fmt, ##__VA_ARGS__); \
+ __ath12k_dbg(ab, mask, fmt, ##__VA_ARGS__); \
} while (0)
+#define ath12k_generic_dbg(dbg_mask, fmt, ...) \
+ ath12k_dbg(NULL, dbg_mask, fmt, ##__VA_ARGS__)
+
#endif /* _ATH12K_DEBUG_H_ */
diff --git a/drivers/net/wireless/ath/ath12k/debugfs.c b/drivers/net/wireless/ath/ath12k/debugfs.c
index d4b32d1a431c..dd624d73b8b2 100644
--- a/drivers/net/wireless/ath/ath12k/debugfs.c
+++ b/drivers/net/wireless/ath/ath12k/debugfs.c
@@ -1,10 +1,12 @@
// SPDX-License-Identifier: BSD-3-Clause-Clear
/*
* Copyright (c) 2018-2021 The Linux Foundation. All rights reserved.
- * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#include "core.h"
+#include "dp_tx.h"
+#include "debug.h"
#include "debugfs.h"
#include "debugfs_htt_stats.h"
@@ -31,6 +33,1187 @@ static const struct file_operations fops_simulate_radar = {
.open = simple_open
};
+static ssize_t ath12k_read_simulate_fw_crash(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ const char buf[] =
+ "To simulate firmware crash write one of the keywords to this file:\n"
+ "`assert` - send WMI_FORCE_FW_HANG_CMDID to firmware to cause assert.\n";
+
+ return simple_read_from_buffer(user_buf, count, ppos, buf, strlen(buf));
+}
+
+static ssize_t
+ath12k_write_simulate_fw_crash(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct ath12k_base *ab = file->private_data;
+ struct ath12k_pdev *pdev;
+ struct ath12k *ar = NULL;
+ char buf[32] = {0};
+ int i, ret;
+ ssize_t rc;
+
+ /* filter partial writes and invalid commands */
+ if (*ppos != 0 || count >= sizeof(buf) || count == 0)
+ return -EINVAL;
+
+ rc = simple_write_to_buffer(buf, sizeof(buf) - 1, ppos, user_buf, count);
+ if (rc < 0)
+ return rc;
+
+ /* drop the possible '\n' from the end */
+ if (buf[*ppos - 1] == '\n')
+ buf[*ppos - 1] = '\0';
+
+ for (i = 0; i < ab->num_radios; i++) {
+ pdev = &ab->pdevs[i];
+ ar = pdev->ar;
+ if (ar)
+ break;
+ }
+
+ if (!ar)
+ return -ENETDOWN;
+
+ if (!strcmp(buf, "assert")) {
+ ath12k_info(ab, "simulating firmware assert crash\n");
+ ret = ath12k_wmi_force_fw_hang_cmd(ar,
+ ATH12K_WMI_FW_HANG_ASSERT_TYPE,
+ ATH12K_WMI_FW_HANG_DELAY);
+ } else {
+ return -EINVAL;
+ }
+
+ if (ret) {
+ ath12k_warn(ab, "failed to simulate firmware crash: %d\n", ret);
+ return ret;
+ }
+
+ return count;
+}
+
+static const struct file_operations fops_simulate_fw_crash = {
+ .read = ath12k_read_simulate_fw_crash,
+ .write = ath12k_write_simulate_fw_crash,
+ .open = simple_open,
+ .owner = THIS_MODULE,
+ .llseek = default_llseek,
+};
+
+static ssize_t ath12k_write_tpc_stats_type(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct ath12k *ar = file->private_data;
+ u8 type;
+ int ret;
+
+ ret = kstrtou8_from_user(user_buf, count, 0, &type);
+ if (ret)
+ return ret;
+
+ if (type >= WMI_HALPHY_PDEV_TX_STATS_MAX)
+ return -EINVAL;
+
+ spin_lock_bh(&ar->data_lock);
+ ar->debug.tpc_stats_type = type;
+ spin_unlock_bh(&ar->data_lock);
+
+ return count;
+}
+
+static int ath12k_debug_tpc_stats_request(struct ath12k *ar)
+{
+ enum wmi_halphy_ctrl_path_stats_id tpc_stats_sub_id;
+ struct ath12k_base *ab = ar->ab;
+ int ret;
+
+ lockdep_assert_wiphy(ath12k_ar_to_hw(ar)->wiphy);
+
+ reinit_completion(&ar->debug.tpc_complete);
+
+ spin_lock_bh(&ar->data_lock);
+ ar->debug.tpc_request = true;
+ tpc_stats_sub_id = ar->debug.tpc_stats_type;
+ spin_unlock_bh(&ar->data_lock);
+
+ ret = ath12k_wmi_send_tpc_stats_request(ar, tpc_stats_sub_id);
+ if (ret) {
+ ath12k_warn(ab, "failed to request pdev tpc stats: %d\n", ret);
+ spin_lock_bh(&ar->data_lock);
+ ar->debug.tpc_request = false;
+ spin_unlock_bh(&ar->data_lock);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int ath12k_get_tpc_ctl_mode_idx(struct wmi_tpc_stats_arg *tpc_stats,
+ enum wmi_tpc_pream_bw pream_bw, int *mode_idx)
+{
+ u32 chan_freq = le32_to_cpu(tpc_stats->tpc_config.chan_freq);
+ u8 band;
+
+ band = ((chan_freq > ATH12K_MIN_6GHZ_FREQ) ? NL80211_BAND_6GHZ :
+ ((chan_freq > ATH12K_MIN_5GHZ_FREQ) ? NL80211_BAND_5GHZ :
+ NL80211_BAND_2GHZ));
+
+ if (band == NL80211_BAND_5GHZ || band == NL80211_BAND_6GHZ) {
+ switch (pream_bw) {
+ case WMI_TPC_PREAM_HT20:
+ case WMI_TPC_PREAM_VHT20:
+ *mode_idx = ATH12K_TPC_STATS_CTL_MODE_HT_VHT20_5GHZ_6GHZ;
+ break;
+ case WMI_TPC_PREAM_HE20:
+ case WMI_TPC_PREAM_EHT20:
+ *mode_idx = ATH12K_TPC_STATS_CTL_MODE_HE_EHT20_5GHZ_6GHZ;
+ break;
+ case WMI_TPC_PREAM_HT40:
+ case WMI_TPC_PREAM_VHT40:
+ *mode_idx = ATH12K_TPC_STATS_CTL_MODE_HT_VHT40_5GHZ_6GHZ;
+ break;
+ case WMI_TPC_PREAM_HE40:
+ case WMI_TPC_PREAM_EHT40:
+ *mode_idx = ATH12K_TPC_STATS_CTL_MODE_HE_EHT40_5GHZ_6GHZ;
+ break;
+ case WMI_TPC_PREAM_VHT80:
+ *mode_idx = ATH12K_TPC_STATS_CTL_MODE_VHT80_5GHZ_6GHZ;
+ break;
+ case WMI_TPC_PREAM_EHT60:
+ *mode_idx = ATH12K_TPC_STATS_CTL_MODE_EHT80_SU_PUNC20;
+ break;
+ case WMI_TPC_PREAM_HE80:
+ case WMI_TPC_PREAM_EHT80:
+ *mode_idx = ATH12K_TPC_STATS_CTL_MODE_HE_EHT80_5GHZ_6GHZ;
+ break;
+ case WMI_TPC_PREAM_VHT160:
+ *mode_idx = ATH12K_TPC_STATS_CTL_MODE_VHT160_5GHZ_6GHZ;
+ break;
+ case WMI_TPC_PREAM_EHT120:
+ case WMI_TPC_PREAM_EHT140:
+ *mode_idx = ATH12K_TPC_STATS_CTL_MODE_EHT160_SU_PUNC20;
+ break;
+ case WMI_TPC_PREAM_HE160:
+ case WMI_TPC_PREAM_EHT160:
+ *mode_idx = ATH12K_TPC_STATS_CTL_MODE_HE_EHT160_5GHZ_6GHZ;
+ break;
+ case WMI_TPC_PREAM_EHT200:
+ *mode_idx = ATH12K_TPC_STATS_CTL_MODE_EHT320_SU_PUNC120;
+ break;
+ case WMI_TPC_PREAM_EHT240:
+ *mode_idx = ATH12K_TPC_STATS_CTL_MODE_EHT320_SU_PUNC80;
+ break;
+ case WMI_TPC_PREAM_EHT280:
+ *mode_idx = ATH12K_TPC_STATS_CTL_MODE_EHT320_SU_PUNC40;
+ break;
+ case WMI_TPC_PREAM_EHT320:
+ *mode_idx = ATH12K_TPC_STATS_CTL_MODE_HE_EHT320_5GHZ_6GHZ;
+ break;
+ default:
+ /* for 5GHZ and 6GHZ, default case will be for OFDM */
+ *mode_idx = ATH12K_TPC_STATS_CTL_MODE_LEGACY_5GHZ_6GHZ;
+ break;
+ }
+ } else {
+ switch (pream_bw) {
+ case WMI_TPC_PREAM_OFDM:
+ *mode_idx = ATH12K_TPC_STATS_CTL_MODE_LEGACY_2GHZ;
+ break;
+ case WMI_TPC_PREAM_HT20:
+ case WMI_TPC_PREAM_VHT20:
+ case WMI_TPC_PREAM_HE20:
+ case WMI_TPC_PREAM_EHT20:
+ *mode_idx = ATH12K_TPC_STATS_CTL_MODE_HT20_2GHZ;
+ break;
+ case WMI_TPC_PREAM_HT40:
+ case WMI_TPC_PREAM_VHT40:
+ case WMI_TPC_PREAM_HE40:
+ case WMI_TPC_PREAM_EHT40:
+ *mode_idx = ATH12K_TPC_STATS_CTL_MODE_HT40_2GHZ;
+ break;
+ default:
+ /* for 2GHZ, default case will be CCK */
+ *mode_idx = ATH12K_TPC_STATS_CTL_MODE_CCK_2GHZ;
+ break;
+ }
+ }
+
+ return 0;
+}
+
+static s16 ath12k_tpc_get_rate(struct ath12k *ar,
+ struct wmi_tpc_stats_arg *tpc_stats,
+ u32 rate_idx, u32 num_chains, u32 rate_code,
+ enum wmi_tpc_pream_bw pream_bw,
+ enum wmi_halphy_ctrl_path_stats_id type,
+ u32 eht_rate_idx)
+{
+ u32 tot_nss, tot_modes, txbf_on_off, index_offset1, index_offset2, index_offset3;
+ u8 chain_idx, stm_idx, num_streams;
+ bool is_mu, txbf_enabled = 0;
+ s8 rates_ctl_min, tpc_ctl;
+ s16 rates, tpc, reg_pwr;
+ u16 rate1, rate2;
+ int mode, ret;
+
+ num_streams = 1 + ATH12K_HW_NSS(rate_code);
+ chain_idx = num_chains - 1;
+ stm_idx = num_streams - 1;
+ mode = -1;
+
+ ret = ath12k_get_tpc_ctl_mode_idx(tpc_stats, pream_bw, &mode);
+ if (ret) {
+ ath12k_warn(ar->ab, "Invalid mode index received\n");
+ tpc = TPC_INVAL;
+ goto out;
+ }
+
+ if (num_chains < num_streams) {
+ tpc = TPC_INVAL;
+ goto out;
+ }
+
+ if (le32_to_cpu(tpc_stats->tpc_config.num_tx_chain) <= 1) {
+ tpc = TPC_INVAL;
+ goto out;
+ }
+
+ if (type == WMI_HALPHY_PDEV_TX_SUTXBF_STATS ||
+ type == WMI_HALPHY_PDEV_TX_MUTXBF_STATS)
+ txbf_enabled = 1;
+
+ if (type == WMI_HALPHY_PDEV_TX_MU_STATS ||
+ type == WMI_HALPHY_PDEV_TX_MUTXBF_STATS) {
+ is_mu = true;
+ } else {
+ is_mu = false;
+ }
+
+ /* Below is the min calculation of ctl array, rates array and
+ * regulator power table. tpc is minimum of all 3
+ */
+ if (pream_bw >= WMI_TPC_PREAM_EHT20 && pream_bw <= WMI_TPC_PREAM_EHT320) {
+ rate2 = tpc_stats->rates_array2.rate_array[eht_rate_idx];
+ if (is_mu)
+ rates = u32_get_bits(rate2, ATH12K_TPC_RATE_ARRAY_MU);
+ else
+ rates = u32_get_bits(rate2, ATH12K_TPC_RATE_ARRAY_SU);
+ } else {
+ rate1 = tpc_stats->rates_array1.rate_array[rate_idx];
+ if (is_mu)
+ rates = u32_get_bits(rate1, ATH12K_TPC_RATE_ARRAY_MU);
+ else
+ rates = u32_get_bits(rate1, ATH12K_TPC_RATE_ARRAY_SU);
+ }
+
+ if (tpc_stats->tlvs_rcvd & WMI_TPC_CTL_PWR_ARRAY) {
+ tot_nss = le32_to_cpu(tpc_stats->ctl_array.tpc_ctl_pwr.d1);
+ tot_modes = le32_to_cpu(tpc_stats->ctl_array.tpc_ctl_pwr.d2);
+ txbf_on_off = le32_to_cpu(tpc_stats->ctl_array.tpc_ctl_pwr.d3);
+ index_offset1 = txbf_on_off * tot_modes * tot_nss;
+ index_offset2 = tot_modes * tot_nss;
+ index_offset3 = tot_nss;
+
+ tpc_ctl = *(tpc_stats->ctl_array.ctl_pwr_table +
+ chain_idx * index_offset1 + txbf_enabled * index_offset2
+ + mode * index_offset3 + stm_idx);
+ } else {
+ tpc_ctl = TPC_MAX;
+ ath12k_warn(ar->ab,
+ "ctl array for tpc stats not received from fw\n");
+ }
+
+ rates_ctl_min = min_t(s16, rates, tpc_ctl);
+
+ reg_pwr = tpc_stats->max_reg_allowed_power.reg_pwr_array[chain_idx];
+
+ if (reg_pwr < 0)
+ reg_pwr = TPC_INVAL;
+
+ tpc = min_t(s16, rates_ctl_min, reg_pwr);
+
+ /* MODULATION_LIMIT is the maximum power limit,tpc should not exceed
+ * modulation limit even if min tpc of all three array is greater
+ * modulation limit
+ */
+ tpc = min_t(s16, tpc, MODULATION_LIMIT);
+
+out:
+ return tpc;
+}
+
+static u16 ath12k_get_ratecode(u16 pream_idx, u16 nss, u16 mcs_rate)
+{
+ u16 mode_type = ~0;
+
+ /* Below assignments are just for printing purpose only */
+ switch (pream_idx) {
+ case WMI_TPC_PREAM_CCK:
+ mode_type = WMI_RATE_PREAMBLE_CCK;
+ break;
+ case WMI_TPC_PREAM_OFDM:
+ mode_type = WMI_RATE_PREAMBLE_OFDM;
+ break;
+ case WMI_TPC_PREAM_HT20:
+ case WMI_TPC_PREAM_HT40:
+ mode_type = WMI_RATE_PREAMBLE_HT;
+ break;
+ case WMI_TPC_PREAM_VHT20:
+ case WMI_TPC_PREAM_VHT40:
+ case WMI_TPC_PREAM_VHT80:
+ case WMI_TPC_PREAM_VHT160:
+ mode_type = WMI_RATE_PREAMBLE_VHT;
+ break;
+ case WMI_TPC_PREAM_HE20:
+ case WMI_TPC_PREAM_HE40:
+ case WMI_TPC_PREAM_HE80:
+ case WMI_TPC_PREAM_HE160:
+ mode_type = WMI_RATE_PREAMBLE_HE;
+ break;
+ case WMI_TPC_PREAM_EHT20:
+ case WMI_TPC_PREAM_EHT40:
+ case WMI_TPC_PREAM_EHT60:
+ case WMI_TPC_PREAM_EHT80:
+ case WMI_TPC_PREAM_EHT120:
+ case WMI_TPC_PREAM_EHT140:
+ case WMI_TPC_PREAM_EHT160:
+ case WMI_TPC_PREAM_EHT200:
+ case WMI_TPC_PREAM_EHT240:
+ case WMI_TPC_PREAM_EHT280:
+ case WMI_TPC_PREAM_EHT320:
+ mode_type = WMI_RATE_PREAMBLE_EHT;
+ if (mcs_rate == 0 || mcs_rate == 1)
+ mcs_rate += 14;
+ else
+ mcs_rate -= 2;
+ break;
+ default:
+ return mode_type;
+ }
+ return ((mode_type << 8) | ((nss & 0x7) << 5) | (mcs_rate & 0x1F));
+}
+
+static bool ath12k_he_supports_extra_mcs(struct ath12k *ar, int freq)
+{
+ struct ath12k_pdev_cap *cap = &ar->pdev->cap;
+ struct ath12k_band_cap *cap_band;
+ bool extra_mcs_supported;
+
+ if (freq <= ATH12K_2GHZ_MAX_FREQUENCY)
+ cap_band = &cap->band[NL80211_BAND_2GHZ];
+ else if (freq <= ATH12K_5GHZ_MAX_FREQUENCY)
+ cap_band = &cap->band[NL80211_BAND_5GHZ];
+ else
+ cap_band = &cap->band[NL80211_BAND_6GHZ];
+
+ extra_mcs_supported = u32_get_bits(cap_band->he_cap_info[1],
+ HE_EXTRA_MCS_SUPPORT);
+ return extra_mcs_supported;
+}
+
+static int ath12k_tpc_fill_pream(struct ath12k *ar, char *buf, int buf_len, int len,
+ enum wmi_tpc_pream_bw pream_bw, u32 max_rix,
+ int max_nss, int max_rates, int pream_type,
+ enum wmi_halphy_ctrl_path_stats_id tpc_type,
+ int rate_idx, int eht_rate_idx)
+{
+ struct wmi_tpc_stats_arg *tpc_stats = ar->debug.tpc_stats;
+ int nss, rates, chains;
+ u8 active_tx_chains;
+ u16 rate_code;
+ s16 tpc;
+
+ static const char *const pream_str[] = {
+ [WMI_TPC_PREAM_CCK] = "CCK",
+ [WMI_TPC_PREAM_OFDM] = "OFDM",
+ [WMI_TPC_PREAM_HT20] = "HT20",
+ [WMI_TPC_PREAM_HT40] = "HT40",
+ [WMI_TPC_PREAM_VHT20] = "VHT20",
+ [WMI_TPC_PREAM_VHT40] = "VHT40",
+ [WMI_TPC_PREAM_VHT80] = "VHT80",
+ [WMI_TPC_PREAM_VHT160] = "VHT160",
+ [WMI_TPC_PREAM_HE20] = "HE20",
+ [WMI_TPC_PREAM_HE40] = "HE40",
+ [WMI_TPC_PREAM_HE80] = "HE80",
+ [WMI_TPC_PREAM_HE160] = "HE160",
+ [WMI_TPC_PREAM_EHT20] = "EHT20",
+ [WMI_TPC_PREAM_EHT40] = "EHT40",
+ [WMI_TPC_PREAM_EHT60] = "EHT60",
+ [WMI_TPC_PREAM_EHT80] = "EHT80",
+ [WMI_TPC_PREAM_EHT120] = "EHT120",
+ [WMI_TPC_PREAM_EHT140] = "EHT140",
+ [WMI_TPC_PREAM_EHT160] = "EHT160",
+ [WMI_TPC_PREAM_EHT200] = "EHT200",
+ [WMI_TPC_PREAM_EHT240] = "EHT240",
+ [WMI_TPC_PREAM_EHT280] = "EHT280",
+ [WMI_TPC_PREAM_EHT320] = "EHT320"};
+
+ active_tx_chains = ar->num_tx_chains;
+
+ for (nss = 0; nss < max_nss; nss++) {
+ for (rates = 0; rates < max_rates; rates++, rate_idx++, max_rix++) {
+ /* FW send extra MCS(10&11) for VHT and HE rates,
+ * this is not used. Hence skipping it here
+ */
+ if (pream_type == WMI_RATE_PREAMBLE_VHT &&
+ rates > ATH12K_VHT_MCS_MAX)
+ continue;
+
+ if (pream_type == WMI_RATE_PREAMBLE_HE &&
+ rates > ATH12K_HE_MCS_MAX)
+ continue;
+
+ if (pream_type == WMI_RATE_PREAMBLE_EHT &&
+ rates > ATH12K_EHT_MCS_MAX)
+ continue;
+
+ rate_code = ath12k_get_ratecode(pream_bw, nss, rates);
+ len += scnprintf(buf + len, buf_len - len,
+ "%d\t %s\t 0x%03x\t", max_rix,
+ pream_str[pream_bw], rate_code);
+
+ for (chains = 0; chains < active_tx_chains; chains++) {
+ if (nss > chains) {
+ len += scnprintf(buf + len,
+ buf_len - len,
+ "\t%s", "NA");
+ } else {
+ tpc = ath12k_tpc_get_rate(ar, tpc_stats,
+ rate_idx, chains + 1,
+ rate_code, pream_bw,
+ tpc_type,
+ eht_rate_idx);
+
+ if (tpc == TPC_INVAL) {
+ len += scnprintf(buf + len,
+ buf_len - len, "\tNA");
+ } else {
+ len += scnprintf(buf + len,
+ buf_len - len, "\t%d",
+ tpc);
+ }
+ }
+ }
+ len += scnprintf(buf + len, buf_len - len, "\n");
+
+ if (pream_type == WMI_RATE_PREAMBLE_EHT)
+ /*For fetching the next eht rates pwr from rates array2*/
+ ++eht_rate_idx;
+ }
+ }
+
+ return len;
+}
+
+static int ath12k_tpc_stats_print(struct ath12k *ar,
+ struct wmi_tpc_stats_arg *tpc_stats,
+ char *buf, size_t len,
+ enum wmi_halphy_ctrl_path_stats_id type)
+{
+ u32 eht_idx = 0, pream_idx = 0, rate_pream_idx = 0, total_rates = 0, max_rix = 0;
+ u32 chan_freq, num_tx_chain, caps, i, j = 1;
+ size_t buf_len = ATH12K_TPC_STATS_BUF_SIZE;
+ u8 nss, active_tx_chains;
+ bool he_ext_mcs;
+ static const char *const type_str[WMI_HALPHY_PDEV_TX_STATS_MAX] = {
+ [WMI_HALPHY_PDEV_TX_SU_STATS] = "SU",
+ [WMI_HALPHY_PDEV_TX_SUTXBF_STATS] = "SU WITH TXBF",
+ [WMI_HALPHY_PDEV_TX_MU_STATS] = "MU",
+ [WMI_HALPHY_PDEV_TX_MUTXBF_STATS] = "MU WITH TXBF"};
+
+ u8 max_rates[WMI_TPC_PREAM_MAX] = {
+ [WMI_TPC_PREAM_CCK] = ATH12K_CCK_RATES,
+ [WMI_TPC_PREAM_OFDM] = ATH12K_OFDM_RATES,
+ [WMI_TPC_PREAM_HT20] = ATH12K_HT_RATES,
+ [WMI_TPC_PREAM_HT40] = ATH12K_HT_RATES,
+ [WMI_TPC_PREAM_VHT20] = ATH12K_VHT_RATES,
+ [WMI_TPC_PREAM_VHT40] = ATH12K_VHT_RATES,
+ [WMI_TPC_PREAM_VHT80] = ATH12K_VHT_RATES,
+ [WMI_TPC_PREAM_VHT160] = ATH12K_VHT_RATES,
+ [WMI_TPC_PREAM_HE20] = ATH12K_HE_RATES,
+ [WMI_TPC_PREAM_HE40] = ATH12K_HE_RATES,
+ [WMI_TPC_PREAM_HE80] = ATH12K_HE_RATES,
+ [WMI_TPC_PREAM_HE160] = ATH12K_HE_RATES,
+ [WMI_TPC_PREAM_EHT20] = ATH12K_EHT_RATES,
+ [WMI_TPC_PREAM_EHT40] = ATH12K_EHT_RATES,
+ [WMI_TPC_PREAM_EHT60] = ATH12K_EHT_RATES,
+ [WMI_TPC_PREAM_EHT80] = ATH12K_EHT_RATES,
+ [WMI_TPC_PREAM_EHT120] = ATH12K_EHT_RATES,
+ [WMI_TPC_PREAM_EHT140] = ATH12K_EHT_RATES,
+ [WMI_TPC_PREAM_EHT160] = ATH12K_EHT_RATES,
+ [WMI_TPC_PREAM_EHT200] = ATH12K_EHT_RATES,
+ [WMI_TPC_PREAM_EHT240] = ATH12K_EHT_RATES,
+ [WMI_TPC_PREAM_EHT280] = ATH12K_EHT_RATES,
+ [WMI_TPC_PREAM_EHT320] = ATH12K_EHT_RATES};
+ static const u8 max_nss[WMI_TPC_PREAM_MAX] = {
+ [WMI_TPC_PREAM_CCK] = ATH12K_NSS_1,
+ [WMI_TPC_PREAM_OFDM] = ATH12K_NSS_1,
+ [WMI_TPC_PREAM_HT20] = ATH12K_NSS_4,
+ [WMI_TPC_PREAM_HT40] = ATH12K_NSS_4,
+ [WMI_TPC_PREAM_VHT20] = ATH12K_NSS_8,
+ [WMI_TPC_PREAM_VHT40] = ATH12K_NSS_8,
+ [WMI_TPC_PREAM_VHT80] = ATH12K_NSS_8,
+ [WMI_TPC_PREAM_VHT160] = ATH12K_NSS_4,
+ [WMI_TPC_PREAM_HE20] = ATH12K_NSS_8,
+ [WMI_TPC_PREAM_HE40] = ATH12K_NSS_8,
+ [WMI_TPC_PREAM_HE80] = ATH12K_NSS_8,
+ [WMI_TPC_PREAM_HE160] = ATH12K_NSS_4,
+ [WMI_TPC_PREAM_EHT20] = ATH12K_NSS_4,
+ [WMI_TPC_PREAM_EHT40] = ATH12K_NSS_4,
+ [WMI_TPC_PREAM_EHT60] = ATH12K_NSS_4,
+ [WMI_TPC_PREAM_EHT80] = ATH12K_NSS_4,
+ [WMI_TPC_PREAM_EHT120] = ATH12K_NSS_4,
+ [WMI_TPC_PREAM_EHT140] = ATH12K_NSS_4,
+ [WMI_TPC_PREAM_EHT160] = ATH12K_NSS_4,
+ [WMI_TPC_PREAM_EHT200] = ATH12K_NSS_4,
+ [WMI_TPC_PREAM_EHT240] = ATH12K_NSS_4,
+ [WMI_TPC_PREAM_EHT280] = ATH12K_NSS_4,
+ [WMI_TPC_PREAM_EHT320] = ATH12K_NSS_4};
+
+ u16 rate_idx[WMI_TPC_PREAM_MAX] = {}, eht_rate_idx[WMI_TPC_PREAM_MAX] = {};
+ static const u8 pream_type[WMI_TPC_PREAM_MAX] = {
+ [WMI_TPC_PREAM_CCK] = WMI_RATE_PREAMBLE_CCK,
+ [WMI_TPC_PREAM_OFDM] = WMI_RATE_PREAMBLE_OFDM,
+ [WMI_TPC_PREAM_HT20] = WMI_RATE_PREAMBLE_HT,
+ [WMI_TPC_PREAM_HT40] = WMI_RATE_PREAMBLE_HT,
+ [WMI_TPC_PREAM_VHT20] = WMI_RATE_PREAMBLE_VHT,
+ [WMI_TPC_PREAM_VHT40] = WMI_RATE_PREAMBLE_VHT,
+ [WMI_TPC_PREAM_VHT80] = WMI_RATE_PREAMBLE_VHT,
+ [WMI_TPC_PREAM_VHT160] = WMI_RATE_PREAMBLE_VHT,
+ [WMI_TPC_PREAM_HE20] = WMI_RATE_PREAMBLE_HE,
+ [WMI_TPC_PREAM_HE40] = WMI_RATE_PREAMBLE_HE,
+ [WMI_TPC_PREAM_HE80] = WMI_RATE_PREAMBLE_HE,
+ [WMI_TPC_PREAM_HE160] = WMI_RATE_PREAMBLE_HE,
+ [WMI_TPC_PREAM_EHT20] = WMI_RATE_PREAMBLE_EHT,
+ [WMI_TPC_PREAM_EHT40] = WMI_RATE_PREAMBLE_EHT,
+ [WMI_TPC_PREAM_EHT60] = WMI_RATE_PREAMBLE_EHT,
+ [WMI_TPC_PREAM_EHT80] = WMI_RATE_PREAMBLE_EHT,
+ [WMI_TPC_PREAM_EHT120] = WMI_RATE_PREAMBLE_EHT,
+ [WMI_TPC_PREAM_EHT140] = WMI_RATE_PREAMBLE_EHT,
+ [WMI_TPC_PREAM_EHT160] = WMI_RATE_PREAMBLE_EHT,
+ [WMI_TPC_PREAM_EHT200] = WMI_RATE_PREAMBLE_EHT,
+ [WMI_TPC_PREAM_EHT240] = WMI_RATE_PREAMBLE_EHT,
+ [WMI_TPC_PREAM_EHT280] = WMI_RATE_PREAMBLE_EHT,
+ [WMI_TPC_PREAM_EHT320] = WMI_RATE_PREAMBLE_EHT};
+
+ chan_freq = le32_to_cpu(tpc_stats->tpc_config.chan_freq);
+ num_tx_chain = le32_to_cpu(tpc_stats->tpc_config.num_tx_chain);
+ caps = le32_to_cpu(tpc_stats->tpc_config.caps);
+
+ active_tx_chains = ar->num_tx_chains;
+ he_ext_mcs = ath12k_he_supports_extra_mcs(ar, chan_freq);
+
+ /* mcs 12&13 is sent by FW for certain HWs in rate array, skipping it as
+ * it is not supported
+ */
+ if (he_ext_mcs) {
+ for (i = WMI_TPC_PREAM_HE20; i <= WMI_TPC_PREAM_HE160; ++i)
+ max_rates[i] = ATH12K_HE_RATES;
+ }
+
+ if (type == WMI_HALPHY_PDEV_TX_MU_STATS ||
+ type == WMI_HALPHY_PDEV_TX_MUTXBF_STATS) {
+ pream_idx = WMI_TPC_PREAM_VHT20;
+
+ for (i = WMI_TPC_PREAM_CCK; i <= WMI_TPC_PREAM_HT40; ++i)
+ max_rix += max_nss[i] * max_rates[i];
+ }
+ /* Enumerate all the rate indices */
+ for (i = rate_pream_idx + 1; i < WMI_TPC_PREAM_MAX; i++) {
+ nss = (max_nss[i - 1] < num_tx_chain ?
+ max_nss[i - 1] : num_tx_chain);
+
+ rate_idx[i] = rate_idx[i - 1] + max_rates[i - 1] * nss;
+
+ if (pream_type[i] == WMI_RATE_PREAMBLE_EHT) {
+ eht_rate_idx[j] = eht_rate_idx[j - 1] + max_rates[i] * nss;
+ ++j;
+ }
+ }
+
+ for (i = 0; i < WMI_TPC_PREAM_MAX; i++) {
+ nss = (max_nss[i] < num_tx_chain ?
+ max_nss[i] : num_tx_chain);
+ total_rates += max_rates[i] * nss;
+ }
+
+ len += scnprintf(buf + len, buf_len - len,
+ "No.of rates-%d\n", total_rates);
+
+ len += scnprintf(buf + len, buf_len - len,
+ "**************** %s ****************\n",
+ type_str[type]);
+ len += scnprintf(buf + len, buf_len - len,
+ "\t\t\t\tTPC values for Active chains\n");
+ len += scnprintf(buf + len, buf_len - len,
+ "Rate idx Preamble Rate code");
+
+ for (i = 1; i <= active_tx_chains; ++i) {
+ len += scnprintf(buf + len, buf_len - len,
+ "\t%d-Chain", i);
+ }
+
+ len += scnprintf(buf + len, buf_len - len, "\n");
+ for (i = pream_idx; i < WMI_TPC_PREAM_MAX; i++) {
+ if (chan_freq <= 2483) {
+ if (i == WMI_TPC_PREAM_VHT80 ||
+ i == WMI_TPC_PREAM_VHT160 ||
+ i == WMI_TPC_PREAM_HE80 ||
+ i == WMI_TPC_PREAM_HE160 ||
+ (i >= WMI_TPC_PREAM_EHT60 &&
+ i <= WMI_TPC_PREAM_EHT320)) {
+ max_rix += max_nss[i] * max_rates[i];
+ continue;
+ }
+ } else {
+ if (i == WMI_TPC_PREAM_CCK) {
+ max_rix += max_rates[i];
+ continue;
+ }
+ }
+
+ nss = (max_nss[i] < ar->num_tx_chains ? max_nss[i] : ar->num_tx_chains);
+
+ if (!(caps &
+ (1 << ATH12K_TPC_STATS_SUPPORT_BE_PUNC))) {
+ if (i == WMI_TPC_PREAM_EHT60 || i == WMI_TPC_PREAM_EHT120 ||
+ i == WMI_TPC_PREAM_EHT140 || i == WMI_TPC_PREAM_EHT200 ||
+ i == WMI_TPC_PREAM_EHT240 || i == WMI_TPC_PREAM_EHT280) {
+ max_rix += max_nss[i] * max_rates[i];
+ continue;
+ }
+ }
+
+ len = ath12k_tpc_fill_pream(ar, buf, buf_len, len, i, max_rix, nss,
+ max_rates[i], pream_type[i],
+ type, rate_idx[i], eht_rate_idx[eht_idx]);
+
+ if (pream_type[i] == WMI_RATE_PREAMBLE_EHT)
+ /*For fetch the next index eht rates from rates array2*/
+ ++eht_idx;
+
+ max_rix += max_nss[i] * max_rates[i];
+ }
+ return len;
+}
+
+static void ath12k_tpc_stats_fill(struct ath12k *ar,
+ struct wmi_tpc_stats_arg *tpc_stats,
+ char *buf)
+{
+ size_t buf_len = ATH12K_TPC_STATS_BUF_SIZE;
+ struct wmi_tpc_config_params *tpc;
+ size_t len = 0;
+
+ if (!tpc_stats) {
+ ath12k_warn(ar->ab, "failed to find tpc stats\n");
+ return;
+ }
+
+ spin_lock_bh(&ar->data_lock);
+
+ tpc = &tpc_stats->tpc_config;
+ len += scnprintf(buf + len, buf_len - len, "\n");
+ len += scnprintf(buf + len, buf_len - len,
+ "*************** TPC config **************\n");
+ len += scnprintf(buf + len, buf_len - len,
+ "* powers are in 0.25 dBm steps\n");
+ len += scnprintf(buf + len, buf_len - len,
+ "reg domain-%d\t\tchan freq-%d\n",
+ tpc->reg_domain, tpc->chan_freq);
+ len += scnprintf(buf + len, buf_len - len,
+ "power limit-%d\t\tmax reg-domain Power-%d\n",
+ le32_to_cpu(tpc->twice_max_reg_power) / 2, tpc->power_limit);
+ len += scnprintf(buf + len, buf_len - len,
+ "No.of tx chain-%d\t",
+ ar->num_tx_chains);
+
+ ath12k_tpc_stats_print(ar, tpc_stats, buf, len,
+ ar->debug.tpc_stats_type);
+
+ spin_unlock_bh(&ar->data_lock);
+}
+
+static int ath12k_open_tpc_stats(struct inode *inode, struct file *file)
+{
+ struct ath12k *ar = inode->i_private;
+ struct ath12k_hw *ah = ath12k_ar_to_ah(ar);
+ int ret;
+
+ guard(wiphy)(ath12k_ar_to_hw(ar)->wiphy);
+
+ if (ah->state != ATH12K_HW_STATE_ON) {
+ ath12k_warn(ar->ab, "Interface not up\n");
+ return -ENETDOWN;
+ }
+
+ void *buf __free(kfree) = kzalloc(ATH12K_TPC_STATS_BUF_SIZE, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ ret = ath12k_debug_tpc_stats_request(ar);
+ if (ret) {
+ ath12k_warn(ar->ab, "failed to request tpc stats: %d\n",
+ ret);
+ return ret;
+ }
+
+ if (!wait_for_completion_timeout(&ar->debug.tpc_complete, TPC_STATS_WAIT_TIME)) {
+ spin_lock_bh(&ar->data_lock);
+ ath12k_wmi_free_tpc_stats_mem(ar);
+ ar->debug.tpc_request = false;
+ spin_unlock_bh(&ar->data_lock);
+ return -ETIMEDOUT;
+ }
+
+ ath12k_tpc_stats_fill(ar, ar->debug.tpc_stats, buf);
+ file->private_data = no_free_ptr(buf);
+
+ spin_lock_bh(&ar->data_lock);
+ ath12k_wmi_free_tpc_stats_mem(ar);
+ spin_unlock_bh(&ar->data_lock);
+
+ return 0;
+}
+
+static ssize_t ath12k_read_tpc_stats(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ const char *buf = file->private_data;
+ size_t len = strlen(buf);
+
+ return simple_read_from_buffer(user_buf, count, ppos, buf, len);
+}
+
+static int ath12k_release_tpc_stats(struct inode *inode,
+ struct file *file)
+{
+ kfree(file->private_data);
+ return 0;
+}
+
+static const struct file_operations fops_tpc_stats = {
+ .open = ath12k_open_tpc_stats,
+ .release = ath12k_release_tpc_stats,
+ .read = ath12k_read_tpc_stats,
+ .owner = THIS_MODULE,
+ .llseek = default_llseek,
+};
+
+static const struct file_operations fops_tpc_stats_type = {
+ .write = ath12k_write_tpc_stats_type,
+ .open = simple_open,
+ .llseek = default_llseek,
+};
+
+static ssize_t ath12k_write_extd_rx_stats(struct file *file,
+ const char __user *ubuf,
+ size_t count, loff_t *ppos)
+{
+ struct ath12k *ar = file->private_data;
+ struct htt_rx_ring_tlv_filter tlv_filter = {0};
+ u32 ring_id, rx_filter = 0;
+ bool enable;
+ int ret, i;
+
+ if (kstrtobool_from_user(ubuf, count, &enable))
+ return -EINVAL;
+
+ wiphy_lock(ath12k_ar_to_hw(ar)->wiphy);
+
+ if (!ar->ab->hw_params->rxdma1_enable) {
+ ret = count;
+ goto exit;
+ }
+
+ if (ar->ah->state != ATH12K_HW_STATE_ON) {
+ ret = -ENETDOWN;
+ goto exit;
+ }
+
+ if (enable == ar->debug.extd_rx_stats) {
+ ret = count;
+ goto exit;
+ }
+
+ if (enable) {
+ rx_filter = HTT_RX_FILTER_TLV_FLAGS_MPDU_START;
+ rx_filter |= HTT_RX_FILTER_TLV_FLAGS_PPDU_START;
+ rx_filter |= HTT_RX_FILTER_TLV_FLAGS_PPDU_END;
+ rx_filter |= HTT_RX_FILTER_TLV_FLAGS_PPDU_END_USER_STATS;
+ rx_filter |= HTT_RX_FILTER_TLV_FLAGS_PPDU_END_USER_STATS_EXT;
+ rx_filter |= HTT_RX_FILTER_TLV_FLAGS_PPDU_END_STATUS_DONE;
+ rx_filter |= HTT_RX_FILTER_TLV_FLAGS_PPDU_START_USER_INFO;
+
+ tlv_filter.rx_filter = rx_filter;
+ tlv_filter.pkt_filter_flags0 = HTT_RX_FP_MGMT_FILTER_FLAGS0;
+ tlv_filter.pkt_filter_flags1 = HTT_RX_FP_MGMT_FILTER_FLAGS1;
+ tlv_filter.pkt_filter_flags2 = HTT_RX_FP_CTRL_FILTER_FLASG2;
+ tlv_filter.pkt_filter_flags3 = HTT_RX_FP_CTRL_FILTER_FLASG3 |
+ HTT_RX_FP_DATA_FILTER_FLASG3;
+ } else {
+ tlv_filter = ath12k_mac_mon_status_filter_default;
+ }
+
+ ar->debug.rx_filter = tlv_filter.rx_filter;
+
+ for (i = 0; i < ar->ab->hw_params->num_rxdma_per_pdev; i++) {
+ ring_id = ar->dp.rxdma_mon_dst_ring[i].ring_id;
+ ret = ath12k_dp_tx_htt_rx_filter_setup(ar->ab, ring_id, ar->dp.mac_id + i,
+ HAL_RXDMA_MONITOR_DST,
+ DP_RXDMA_REFILL_RING_SIZE,
+ &tlv_filter);
+ if (ret) {
+ ath12k_warn(ar->ab, "failed to set rx filter for monitor status ring\n");
+ goto exit;
+ }
+ }
+
+ ar->debug.extd_rx_stats = !!enable;
+ ret = count;
+exit:
+ wiphy_unlock(ath12k_ar_to_hw(ar)->wiphy);
+ return ret;
+}
+
+static ssize_t ath12k_read_extd_rx_stats(struct file *file,
+ char __user *ubuf,
+ size_t count, loff_t *ppos)
+{
+ struct ath12k *ar = file->private_data;
+ char buf[32];
+ int len = 0;
+
+ wiphy_lock(ath12k_ar_to_hw(ar)->wiphy);
+ len = scnprintf(buf, sizeof(buf) - len, "%d\n",
+ ar->debug.extd_rx_stats);
+ wiphy_unlock(ath12k_ar_to_hw(ar)->wiphy);
+
+ return simple_read_from_buffer(ubuf, count, ppos, buf, len);
+}
+
+static const struct file_operations fops_extd_rx_stats = {
+ .read = ath12k_read_extd_rx_stats,
+ .write = ath12k_write_extd_rx_stats,
+ .open = simple_open,
+};
+
+static int ath12k_open_link_stats(struct inode *inode, struct file *file)
+{
+ struct ath12k_vif *ahvif = inode->i_private;
+ size_t len = 0, buf_len = (PAGE_SIZE * 2);
+ struct ath12k_link_stats linkstat;
+ struct ath12k_link_vif *arvif;
+ unsigned long links_map;
+ struct wiphy *wiphy;
+ int link_id, i;
+ char *buf;
+
+ if (!ahvif)
+ return -EINVAL;
+
+ buf = kzalloc(buf_len, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ wiphy = ahvif->ah->hw->wiphy;
+ wiphy_lock(wiphy);
+
+ links_map = ahvif->links_map;
+ for_each_set_bit(link_id, &links_map,
+ IEEE80211_MLD_MAX_NUM_LINKS) {
+ arvif = rcu_dereference_protected(ahvif->link[link_id],
+ lockdep_is_held(&wiphy->mtx));
+
+ spin_lock_bh(&arvif->link_stats_lock);
+ linkstat = arvif->link_stats;
+ spin_unlock_bh(&arvif->link_stats_lock);
+
+ len += scnprintf(buf + len, buf_len - len,
+ "link[%d] Tx Unicast Frames Enqueued = %d\n",
+ link_id, linkstat.tx_enqueued);
+ len += scnprintf(buf + len, buf_len - len,
+ "link[%d] Tx Broadcast Frames Enqueued = %d\n",
+ link_id, linkstat.tx_bcast_mcast);
+ len += scnprintf(buf + len, buf_len - len,
+ "link[%d] Tx Frames Completed = %d\n",
+ link_id, linkstat.tx_completed);
+ len += scnprintf(buf + len, buf_len - len,
+ "link[%d] Tx Frames Dropped = %d\n",
+ link_id, linkstat.tx_dropped);
+
+ len += scnprintf(buf + len, buf_len - len,
+ "link[%d] Tx Frame descriptor Encap Type = ",
+ link_id);
+
+ len += scnprintf(buf + len, buf_len - len,
+ " raw:%d",
+ linkstat.tx_encap_type[0]);
+
+ len += scnprintf(buf + len, buf_len - len,
+ " native_wifi:%d",
+ linkstat.tx_encap_type[1]);
+
+ len += scnprintf(buf + len, buf_len - len,
+ " ethernet:%d",
+ linkstat.tx_encap_type[2]);
+
+ len += scnprintf(buf + len, buf_len - len,
+ "\nlink[%d] Tx Frame descriptor Encrypt Type = ",
+ link_id);
+
+ for (i = 0; i < HAL_ENCRYPT_TYPE_MAX; i++) {
+ len += scnprintf(buf + len, buf_len - len,
+ " %d:%d", i,
+ linkstat.tx_encrypt_type[i]);
+ }
+ len += scnprintf(buf + len, buf_len - len,
+ "\nlink[%d] Tx Frame descriptor Type = buffer:%d extension:%d\n",
+ link_id, linkstat.tx_desc_type[0],
+ linkstat.tx_desc_type[1]);
+
+ len += scnprintf(buf + len, buf_len - len,
+ "------------------------------------------------------\n");
+ }
+
+ wiphy_unlock(wiphy);
+
+ file->private_data = buf;
+
+ return 0;
+}
+
+static int ath12k_release_link_stats(struct inode *inode, struct file *file)
+{
+ kfree(file->private_data);
+ return 0;
+}
+
+static ssize_t ath12k_read_link_stats(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ const char *buf = file->private_data;
+ size_t len = strlen(buf);
+
+ return simple_read_from_buffer(user_buf, count, ppos, buf, len);
+}
+
+static const struct file_operations ath12k_fops_link_stats = {
+ .open = ath12k_open_link_stats,
+ .release = ath12k_release_link_stats,
+ .read = ath12k_read_link_stats,
+ .owner = THIS_MODULE,
+ .llseek = default_llseek,
+};
+
+void ath12k_debugfs_op_vif_add(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif)
+{
+ struct ath12k_vif *ahvif = ath12k_vif_to_ahvif(vif);
+
+ debugfs_create_file("link_stats", 0400, vif->debugfs_dir, ahvif,
+ &ath12k_fops_link_stats);
+}
+
+static ssize_t ath12k_debugfs_dump_device_dp_stats(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct ath12k_base *ab = file->private_data;
+ struct ath12k_device_dp_stats *device_stats = &ab->device_stats;
+ int len = 0, i, j, ret;
+ struct ath12k *ar;
+ const int size = 4096;
+ static const char *rxdma_err[HAL_REO_ENTR_RING_RXDMA_ECODE_MAX] = {
+ [HAL_REO_ENTR_RING_RXDMA_ECODE_OVERFLOW_ERR] = "Overflow",
+ [HAL_REO_ENTR_RING_RXDMA_ECODE_MPDU_LEN_ERR] = "MPDU len",
+ [HAL_REO_ENTR_RING_RXDMA_ECODE_FCS_ERR] = "FCS",
+ [HAL_REO_ENTR_RING_RXDMA_ECODE_DECRYPT_ERR] = "Decrypt",
+ [HAL_REO_ENTR_RING_RXDMA_ECODE_TKIP_MIC_ERR] = "TKIP MIC",
+ [HAL_REO_ENTR_RING_RXDMA_ECODE_UNECRYPTED_ERR] = "Unencrypt",
+ [HAL_REO_ENTR_RING_RXDMA_ECODE_MSDU_LEN_ERR] = "MSDU len",
+ [HAL_REO_ENTR_RING_RXDMA_ECODE_MSDU_LIMIT_ERR] = "MSDU limit",
+ [HAL_REO_ENTR_RING_RXDMA_ECODE_WIFI_PARSE_ERR] = "WiFi parse",
+ [HAL_REO_ENTR_RING_RXDMA_ECODE_AMSDU_PARSE_ERR] = "AMSDU parse",
+ [HAL_REO_ENTR_RING_RXDMA_ECODE_SA_TIMEOUT_ERR] = "SA timeout",
+ [HAL_REO_ENTR_RING_RXDMA_ECODE_DA_TIMEOUT_ERR] = "DA timeout",
+ [HAL_REO_ENTR_RING_RXDMA_ECODE_FLOW_TIMEOUT_ERR] = "Flow timeout",
+ [HAL_REO_ENTR_RING_RXDMA_ECODE_FLUSH_REQUEST_ERR] = "Flush req",
+ [HAL_REO_ENTR_RING_RXDMA_ECODE_AMSDU_FRAG_ERR] = "AMSDU frag",
+ [HAL_REO_ENTR_RING_RXDMA_ECODE_MULTICAST_ECHO_ERR] = "Multicast echo",
+ [HAL_REO_ENTR_RING_RXDMA_ECODE_AMSDU_MISMATCH_ERR] = "AMSDU mismatch",
+ [HAL_REO_ENTR_RING_RXDMA_ECODE_UNAUTH_WDS_ERR] = "Unauth WDS",
+ [HAL_REO_ENTR_RING_RXDMA_ECODE_GRPCAST_AMSDU_WDS_ERR] = "AMSDU or WDS"};
+
+ static const char *reo_err[HAL_REO_DEST_RING_ERROR_CODE_MAX] = {
+ [HAL_REO_DEST_RING_ERROR_CODE_DESC_ADDR_ZERO] = "Desc addr zero",
+ [HAL_REO_DEST_RING_ERROR_CODE_DESC_INVALID] = "Desc inval",
+ [HAL_REO_DEST_RING_ERROR_CODE_AMPDU_IN_NON_BA] = "AMPDU in non BA",
+ [HAL_REO_DEST_RING_ERROR_CODE_NON_BA_DUPLICATE] = "Non BA dup",
+ [HAL_REO_DEST_RING_ERROR_CODE_BA_DUPLICATE] = "BA dup",
+ [HAL_REO_DEST_RING_ERROR_CODE_FRAME_2K_JUMP] = "Frame 2k jump",
+ [HAL_REO_DEST_RING_ERROR_CODE_BAR_2K_JUMP] = "BAR 2k jump",
+ [HAL_REO_DEST_RING_ERROR_CODE_FRAME_OOR] = "Frame OOR",
+ [HAL_REO_DEST_RING_ERROR_CODE_BAR_OOR] = "BAR OOR",
+ [HAL_REO_DEST_RING_ERROR_CODE_NO_BA_SESSION] = "No BA session",
+ [HAL_REO_DEST_RING_ERROR_CODE_FRAME_SN_EQUALS_SSN] = "Frame SN equal SSN",
+ [HAL_REO_DEST_RING_ERROR_CODE_PN_CHECK_FAILED] = "PN check fail",
+ [HAL_REO_DEST_RING_ERROR_CODE_2K_ERR_FLAG_SET] = "2k err",
+ [HAL_REO_DEST_RING_ERROR_CODE_PN_ERR_FLAG_SET] = "PN err",
+ [HAL_REO_DEST_RING_ERROR_CODE_DESC_BLOCKED] = "Desc blocked"};
+
+ static const char *wbm_rel_src[HAL_WBM_REL_SRC_MODULE_MAX] = {
+ [HAL_WBM_REL_SRC_MODULE_TQM] = "TQM",
+ [HAL_WBM_REL_SRC_MODULE_RXDMA] = "Rxdma",
+ [HAL_WBM_REL_SRC_MODULE_REO] = "Reo",
+ [HAL_WBM_REL_SRC_MODULE_FW] = "FW",
+ [HAL_WBM_REL_SRC_MODULE_SW] = "SW"};
+
+ char *buf __free(kfree) = kzalloc(size, GFP_KERNEL);
+
+ if (!buf)
+ return -ENOMEM;
+
+ len += scnprintf(buf + len, size - len, "DEVICE RX STATS:\n\n");
+ len += scnprintf(buf + len, size - len, "err ring pkts: %u\n",
+ device_stats->err_ring_pkts);
+ len += scnprintf(buf + len, size - len, "Invalid RBM: %u\n\n",
+ device_stats->invalid_rbm);
+ len += scnprintf(buf + len, size - len, "RXDMA errors:\n");
+
+ for (i = 0; i < HAL_REO_ENTR_RING_RXDMA_ECODE_MAX; i++)
+ len += scnprintf(buf + len, size - len, "%s: %u\n",
+ rxdma_err[i], device_stats->rxdma_error[i]);
+
+ len += scnprintf(buf + len, size - len, "\nREO errors:\n");
+
+ for (i = 0; i < HAL_REO_DEST_RING_ERROR_CODE_MAX; i++)
+ len += scnprintf(buf + len, size - len, "%s: %u\n",
+ reo_err[i], device_stats->reo_error[i]);
+
+ len += scnprintf(buf + len, size - len, "\nHAL REO errors:\n");
+
+ for (i = 0; i < DP_REO_DST_RING_MAX; i++)
+ len += scnprintf(buf + len, size - len,
+ "ring%d: %u\n", i,
+ device_stats->hal_reo_error[i]);
+
+ len += scnprintf(buf + len, size - len, "\nDEVICE TX STATS:\n");
+ len += scnprintf(buf + len, size - len, "\nTCL Ring Full Failures:\n");
+
+ for (i = 0; i < DP_TCL_NUM_RING_MAX; i++)
+ len += scnprintf(buf + len, size - len, "ring%d: %u\n",
+ i, device_stats->tx_err.desc_na[i]);
+
+ len += scnprintf(buf + len, size - len,
+ "\nMisc Transmit Failures: %d\n",
+ atomic_read(&device_stats->tx_err.misc_fail));
+
+ len += scnprintf(buf + len, size - len, "\ntx_wbm_rel_source:");
+
+ for (i = 0; i < HAL_WBM_REL_SRC_MODULE_MAX; i++)
+ len += scnprintf(buf + len, size - len, " %d:%u",
+ i, device_stats->tx_wbm_rel_source[i]);
+
+ len += scnprintf(buf + len, size - len, "\n");
+
+ len += scnprintf(buf + len, size - len, "\ntqm_rel_reason:");
+
+ for (i = 0; i < MAX_TQM_RELEASE_REASON; i++)
+ len += scnprintf(buf + len, size - len, " %d:%u",
+ i, device_stats->tqm_rel_reason[i]);
+
+ len += scnprintf(buf + len, size - len, "\n");
+
+ len += scnprintf(buf + len, size - len, "\nfw_tx_status:");
+
+ for (i = 0; i < MAX_FW_TX_STATUS; i++)
+ len += scnprintf(buf + len, size - len, " %d:%u",
+ i, device_stats->fw_tx_status[i]);
+
+ len += scnprintf(buf + len, size - len, "\n");
+
+ len += scnprintf(buf + len, size - len, "\ntx_enqueued:");
+
+ for (i = 0; i < DP_TCL_NUM_RING_MAX; i++)
+ len += scnprintf(buf + len, size - len, " %d:%u", i,
+ device_stats->tx_enqueued[i]);
+
+ len += scnprintf(buf + len, size - len, "\n");
+
+ len += scnprintf(buf + len, size - len, "\ntx_completed:");
+
+ for (i = 0; i < DP_TCL_NUM_RING_MAX; i++)
+ len += scnprintf(buf + len, size - len, " %d:%u",
+ i, device_stats->tx_completed[i]);
+
+ len += scnprintf(buf + len, size - len, "\n");
+
+ for (i = 0; i < ab->num_radios; i++) {
+ ar = ath12k_mac_get_ar_by_pdev_id(ab, DP_SW2HW_MACID(i));
+ if (ar) {
+ len += scnprintf(buf + len, size - len,
+ "\nradio%d tx_pending: %u\n", i,
+ atomic_read(&ar->dp.num_tx_pending));
+ }
+ }
+
+ len += scnprintf(buf + len, size - len, "\nREO Rx Received:\n");
+
+ for (i = 0; i < DP_REO_DST_RING_MAX; i++) {
+ len += scnprintf(buf + len, size - len, "Ring%d:", i + 1);
+
+ for (j = 0; j < ATH12K_MAX_DEVICES; j++) {
+ len += scnprintf(buf + len, size - len,
+ "\t%d:%u", j,
+ device_stats->reo_rx[i][j]);
+ }
+
+ len += scnprintf(buf + len, size - len, "\n");
+ }
+
+ len += scnprintf(buf + len, size - len, "\nRx WBM REL SRC Errors:\n");
+
+ for (i = 0; i < HAL_WBM_REL_SRC_MODULE_MAX; i++) {
+ len += scnprintf(buf + len, size - len, "%s:", wbm_rel_src[i]);
+
+ for (j = 0; j < ATH12K_MAX_DEVICES; j++) {
+ len += scnprintf(buf + len,
+ size - len,
+ "\t%d:%u", j,
+ device_stats->rx_wbm_rel_source[i][j]);
+ }
+
+ len += scnprintf(buf + len, size - len, "\n");
+ }
+
+ ret = simple_read_from_buffer(user_buf, count, ppos, buf, len);
+
+ return ret;
+}
+
+static const struct file_operations fops_device_dp_stats = {
+ .read = ath12k_debugfs_dump_device_dp_stats,
+ .open = simple_open,
+ .owner = THIS_MODULE,
+ .llseek = default_llseek,
+};
+
+void ath12k_debugfs_pdev_create(struct ath12k_base *ab)
+{
+ debugfs_create_file("simulate_fw_crash", 0600, ab->debugfs_soc, ab,
+ &fops_simulate_fw_crash);
+
+ debugfs_create_file("device_dp_stats", 0400, ab->debugfs_soc, ab,
+ &fops_device_dp_stats);
+}
+
void ath12k_debugfs_soc_create(struct ath12k_base *ab)
{
bool dput_needed;
@@ -68,6 +1251,278 @@ void ath12k_debugfs_soc_destroy(struct ath12k_base *ab)
*/
}
+void
+ath12k_debugfs_fw_stats_process(struct ath12k *ar,
+ struct ath12k_fw_stats *stats)
+{
+ struct ath12k_base *ab = ar->ab;
+ struct ath12k_pdev *pdev;
+ bool is_end;
+ static unsigned int num_vdev, num_bcn;
+ size_t total_vdevs_started = 0;
+ int i;
+
+ if (stats->stats_id == WMI_REQUEST_VDEV_STAT) {
+ if (list_empty(&stats->vdevs)) {
+ ath12k_warn(ab, "empty vdev stats");
+ return;
+ }
+ /* FW sends all the active VDEV stats irrespective of PDEV,
+ * hence limit until the count of all VDEVs started
+ */
+ rcu_read_lock();
+ for (i = 0; i < ab->num_radios; i++) {
+ pdev = rcu_dereference(ab->pdevs_active[i]);
+ if (pdev && pdev->ar)
+ total_vdevs_started += pdev->ar->num_started_vdevs;
+ }
+ rcu_read_unlock();
+
+ is_end = ((++num_vdev) == total_vdevs_started);
+
+ list_splice_tail_init(&stats->vdevs,
+ &ar->fw_stats.vdevs);
+
+ if (is_end) {
+ ar->fw_stats.fw_stats_done = true;
+ num_vdev = 0;
+ }
+ return;
+ }
+ if (stats->stats_id == WMI_REQUEST_BCN_STAT) {
+ if (list_empty(&stats->bcn)) {
+ ath12k_warn(ab, "empty beacon stats");
+ return;
+ }
+ /* Mark end until we reached the count of all started VDEVs
+ * within the PDEV
+ */
+ is_end = ((++num_bcn) == ar->num_started_vdevs);
+
+ list_splice_tail_init(&stats->bcn,
+ &ar->fw_stats.bcn);
+
+ if (is_end) {
+ ar->fw_stats.fw_stats_done = true;
+ num_bcn = 0;
+ }
+ }
+}
+
+static int ath12k_open_vdev_stats(struct inode *inode, struct file *file)
+{
+ struct ath12k *ar = inode->i_private;
+ struct ath12k_fw_stats_req_params param;
+ struct ath12k_hw *ah = ath12k_ar_to_ah(ar);
+ int ret;
+
+ guard(wiphy)(ath12k_ar_to_hw(ar)->wiphy);
+
+ if (!ah)
+ return -ENETDOWN;
+
+ if (ah->state != ATH12K_HW_STATE_ON)
+ return -ENETDOWN;
+
+ void *buf __free(kfree) = kzalloc(ATH12K_FW_STATS_BUF_SIZE, GFP_ATOMIC);
+ if (!buf)
+ return -ENOMEM;
+
+ param.pdev_id = ath12k_mac_get_target_pdev_id(ar);
+ /* VDEV stats is always sent for all active VDEVs from FW */
+ param.vdev_id = 0;
+ param.stats_id = WMI_REQUEST_VDEV_STAT;
+
+ ret = ath12k_mac_get_fw_stats(ar, &param);
+ if (ret) {
+ ath12k_warn(ar->ab, "failed to request fw vdev stats: %d\n", ret);
+ return ret;
+ }
+
+ ath12k_wmi_fw_stats_dump(ar, &ar->fw_stats, param.stats_id,
+ buf);
+
+ file->private_data = no_free_ptr(buf);
+
+ return 0;
+}
+
+static int ath12k_release_vdev_stats(struct inode *inode, struct file *file)
+{
+ kfree(file->private_data);
+
+ return 0;
+}
+
+static ssize_t ath12k_read_vdev_stats(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ const char *buf = file->private_data;
+ size_t len = strlen(buf);
+
+ return simple_read_from_buffer(user_buf, count, ppos, buf, len);
+}
+
+static const struct file_operations fops_vdev_stats = {
+ .open = ath12k_open_vdev_stats,
+ .release = ath12k_release_vdev_stats,
+ .read = ath12k_read_vdev_stats,
+ .owner = THIS_MODULE,
+ .llseek = default_llseek,
+};
+
+static int ath12k_open_bcn_stats(struct inode *inode, struct file *file)
+{
+ struct ath12k *ar = inode->i_private;
+ struct ath12k_link_vif *arvif;
+ struct ath12k_fw_stats_req_params param;
+ struct ath12k_hw *ah = ath12k_ar_to_ah(ar);
+ int ret;
+
+ guard(wiphy)(ath12k_ar_to_hw(ar)->wiphy);
+
+ if (ah && ah->state != ATH12K_HW_STATE_ON)
+ return -ENETDOWN;
+
+ void *buf __free(kfree) = kzalloc(ATH12K_FW_STATS_BUF_SIZE, GFP_ATOMIC);
+ if (!buf)
+ return -ENOMEM;
+
+ param.pdev_id = ath12k_mac_get_target_pdev_id(ar);
+ param.stats_id = WMI_REQUEST_BCN_STAT;
+
+ /* loop all active VDEVs for bcn stats */
+ list_for_each_entry(arvif, &ar->arvifs, list) {
+ if (!arvif->is_up)
+ continue;
+
+ param.vdev_id = arvif->vdev_id;
+ ret = ath12k_mac_get_fw_stats(ar, &param);
+ if (ret) {
+ ath12k_warn(ar->ab, "failed to request fw bcn stats: %d\n", ret);
+ return ret;
+ }
+ }
+
+ ath12k_wmi_fw_stats_dump(ar, &ar->fw_stats, param.stats_id,
+ buf);
+ /* since beacon stats request is looped for all active VDEVs, saved fw
+ * stats is not freed for each request until done for all active VDEVs
+ */
+ spin_lock_bh(&ar->data_lock);
+ ath12k_fw_stats_bcn_free(&ar->fw_stats.bcn);
+ spin_unlock_bh(&ar->data_lock);
+
+ file->private_data = no_free_ptr(buf);
+
+ return 0;
+}
+
+static int ath12k_release_bcn_stats(struct inode *inode, struct file *file)
+{
+ kfree(file->private_data);
+
+ return 0;
+}
+
+static ssize_t ath12k_read_bcn_stats(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ const char *buf = file->private_data;
+ size_t len = strlen(buf);
+
+ return simple_read_from_buffer(user_buf, count, ppos, buf, len);
+}
+
+static const struct file_operations fops_bcn_stats = {
+ .open = ath12k_open_bcn_stats,
+ .release = ath12k_release_bcn_stats,
+ .read = ath12k_read_bcn_stats,
+ .owner = THIS_MODULE,
+ .llseek = default_llseek,
+};
+
+static int ath12k_open_pdev_stats(struct inode *inode, struct file *file)
+{
+ struct ath12k *ar = inode->i_private;
+ struct ath12k_hw *ah = ath12k_ar_to_ah(ar);
+ struct ath12k_base *ab = ar->ab;
+ struct ath12k_fw_stats_req_params param;
+ int ret;
+
+ guard(wiphy)(ath12k_ar_to_hw(ar)->wiphy);
+
+ if (ah && ah->state != ATH12K_HW_STATE_ON)
+ return -ENETDOWN;
+
+ void *buf __free(kfree) = kzalloc(ATH12K_FW_STATS_BUF_SIZE, GFP_ATOMIC);
+ if (!buf)
+ return -ENOMEM;
+
+ param.pdev_id = ath12k_mac_get_target_pdev_id(ar);
+ param.vdev_id = 0;
+ param.stats_id = WMI_REQUEST_PDEV_STAT;
+
+ ret = ath12k_mac_get_fw_stats(ar, &param);
+ if (ret) {
+ ath12k_warn(ab, "failed to request fw pdev stats: %d\n", ret);
+ return ret;
+ }
+
+ ath12k_wmi_fw_stats_dump(ar, &ar->fw_stats, param.stats_id,
+ buf);
+
+ file->private_data = no_free_ptr(buf);
+
+ return 0;
+}
+
+static int ath12k_release_pdev_stats(struct inode *inode, struct file *file)
+{
+ kfree(file->private_data);
+
+ return 0;
+}
+
+static ssize_t ath12k_read_pdev_stats(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ const char *buf = file->private_data;
+ size_t len = strlen(buf);
+
+ return simple_read_from_buffer(user_buf, count, ppos, buf, len);
+}
+
+static const struct file_operations fops_pdev_stats = {
+ .open = ath12k_open_pdev_stats,
+ .release = ath12k_release_pdev_stats,
+ .read = ath12k_read_pdev_stats,
+ .owner = THIS_MODULE,
+ .llseek = default_llseek,
+};
+
+static
+void ath12k_debugfs_fw_stats_register(struct ath12k *ar)
+{
+ struct dentry *fwstats_dir = debugfs_create_dir("fw_stats",
+ ar->debug.debugfs_pdev);
+
+ /* all stats debugfs files created are under "fw_stats" directory
+ * created per PDEV
+ */
+ debugfs_create_file("vdev_stats", 0600, fwstats_dir, ar,
+ &fops_vdev_stats);
+ debugfs_create_file("beacon_stats", 0600, fwstats_dir, ar,
+ &fops_bcn_stats);
+ debugfs_create_file("pdev_stats", 0600, fwstats_dir, ar,
+ &fops_pdev_stats);
+
+ ath12k_fw_stats_init(ar);
+}
+
void ath12k_debugfs_register(struct ath12k *ar)
{
struct ath12k_base *ab = ar->ab;
@@ -91,7 +1546,18 @@ void ath12k_debugfs_register(struct ath12k *ar)
&fops_simulate_radar);
}
+ debugfs_create_file("tpc_stats", 0400, ar->debug.debugfs_pdev, ar,
+ &fops_tpc_stats);
+ debugfs_create_file("tpc_stats_type", 0200, ar->debug.debugfs_pdev,
+ ar, &fops_tpc_stats_type);
+ init_completion(&ar->debug.tpc_complete);
+
ath12k_debugfs_htt_stats_register(ar);
+ ath12k_debugfs_fw_stats_register(ar);
+
+ debugfs_create_file("ext_rx_stats", 0644,
+ ar->debug.debugfs_pdev, ar,
+ &fops_extd_rx_stats);
}
void ath12k_debugfs_unregister(struct ath12k *ar)
diff --git a/drivers/net/wireless/ath/ath12k/debugfs.h b/drivers/net/wireless/ath/ath12k/debugfs.h
index 8d64ba03aa9a..ebef7dace344 100644
--- a/drivers/net/wireless/ath/ath12k/debugfs.h
+++ b/drivers/net/wireless/ath/ath12k/debugfs.h
@@ -1,7 +1,7 @@
/* SPDX-License-Identifier: BSD-3-Clause-Clear */
/*
* Copyright (c) 2018-2021 The Linux Foundation. All rights reserved.
- * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#ifndef _ATH12K_DEBUGFS_H_
@@ -12,6 +12,103 @@ void ath12k_debugfs_soc_create(struct ath12k_base *ab);
void ath12k_debugfs_soc_destroy(struct ath12k_base *ab);
void ath12k_debugfs_register(struct ath12k *ar);
void ath12k_debugfs_unregister(struct ath12k *ar);
+void ath12k_debugfs_fw_stats_process(struct ath12k *ar,
+ struct ath12k_fw_stats *stats);
+void ath12k_debugfs_op_vif_add(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif);
+void ath12k_debugfs_pdev_create(struct ath12k_base *ab);
+
+static inline bool ath12k_debugfs_is_extd_rx_stats_enabled(struct ath12k *ar)
+{
+ return ar->debug.extd_rx_stats;
+}
+
+static inline int ath12k_debugfs_rx_filter(struct ath12k *ar)
+{
+ return ar->debug.rx_filter;
+}
+
+#define ATH12K_CCK_RATES 4
+#define ATH12K_OFDM_RATES 8
+#define ATH12K_HT_RATES 8
+#define ATH12K_VHT_RATES 12
+#define ATH12K_HE_RATES 12
+#define ATH12K_HE_RATES_WITH_EXTRA_MCS 14
+#define ATH12K_EHT_RATES 16
+#define HE_EXTRA_MCS_SUPPORT GENMASK(31, 16)
+#define ATH12K_NSS_1 1
+#define ATH12K_NSS_4 4
+#define ATH12K_NSS_8 8
+#define ATH12K_HW_NSS(_rcode) (((_rcode) >> 5) & 0x7)
+#define TPC_STATS_WAIT_TIME (1 * HZ)
+#define MAX_TPC_PREAM_STR_LEN 7
+#define TPC_INVAL -128
+#define TPC_MAX 127
+#define TPC_STATS_WAIT_TIME (1 * HZ)
+#define TPC_STATS_TOT_ROW 700
+#define TPC_STATS_TOT_COLUMN 100
+#define MODULATION_LIMIT 126
+
+#define ATH12K_TPC_STATS_BUF_SIZE (TPC_STATS_TOT_ROW * TPC_STATS_TOT_COLUMN)
+
+enum wmi_tpc_pream_bw {
+ WMI_TPC_PREAM_CCK,
+ WMI_TPC_PREAM_OFDM,
+ WMI_TPC_PREAM_HT20,
+ WMI_TPC_PREAM_HT40,
+ WMI_TPC_PREAM_VHT20,
+ WMI_TPC_PREAM_VHT40,
+ WMI_TPC_PREAM_VHT80,
+ WMI_TPC_PREAM_VHT160,
+ WMI_TPC_PREAM_HE20,
+ WMI_TPC_PREAM_HE40,
+ WMI_TPC_PREAM_HE80,
+ WMI_TPC_PREAM_HE160,
+ WMI_TPC_PREAM_EHT20,
+ WMI_TPC_PREAM_EHT40,
+ WMI_TPC_PREAM_EHT60,
+ WMI_TPC_PREAM_EHT80,
+ WMI_TPC_PREAM_EHT120,
+ WMI_TPC_PREAM_EHT140,
+ WMI_TPC_PREAM_EHT160,
+ WMI_TPC_PREAM_EHT200,
+ WMI_TPC_PREAM_EHT240,
+ WMI_TPC_PREAM_EHT280,
+ WMI_TPC_PREAM_EHT320,
+ WMI_TPC_PREAM_MAX
+};
+
+enum ath12k_debug_tpc_stats_ctl_mode {
+ ATH12K_TPC_STATS_CTL_MODE_LEGACY_5GHZ_6GHZ,
+ ATH12K_TPC_STATS_CTL_MODE_HT_VHT20_5GHZ_6GHZ,
+ ATH12K_TPC_STATS_CTL_MODE_HE_EHT20_5GHZ_6GHZ,
+ ATH12K_TPC_STATS_CTL_MODE_HT_VHT40_5GHZ_6GHZ,
+ ATH12K_TPC_STATS_CTL_MODE_HE_EHT40_5GHZ_6GHZ,
+ ATH12K_TPC_STATS_CTL_MODE_VHT80_5GHZ_6GHZ,
+ ATH12K_TPC_STATS_CTL_MODE_HE_EHT80_5GHZ_6GHZ,
+ ATH12K_TPC_STATS_CTL_MODE_VHT160_5GHZ_6GHZ,
+ ATH12K_TPC_STATS_CTL_MODE_HE_EHT160_5GHZ_6GHZ,
+ ATH12K_TPC_STATS_CTL_MODE_HE_EHT320_5GHZ_6GHZ,
+ ATH12K_TPC_STATS_CTL_MODE_CCK_2GHZ,
+ ATH12K_TPC_STATS_CTL_MODE_LEGACY_2GHZ,
+ ATH12K_TPC_STATS_CTL_MODE_HT20_2GHZ,
+ ATH12K_TPC_STATS_CTL_MODE_HT40_2GHZ,
+
+ ATH12K_TPC_STATS_CTL_MODE_EHT80_SU_PUNC20 = 23,
+ ATH12K_TPC_STATS_CTL_MODE_EHT160_SU_PUNC20,
+ ATH12K_TPC_STATS_CTL_MODE_EHT320_SU_PUNC40,
+ ATH12K_TPC_STATS_CTL_MODE_EHT320_SU_PUNC80,
+ ATH12K_TPC_STATS_CTL_MODE_EHT320_SU_PUNC120
+};
+
+enum ath12k_debug_tpc_stats_support_modes {
+ ATH12K_TPC_STATS_SUPPORT_160 = 0,
+ ATH12K_TPC_STATS_SUPPORT_320,
+ ATH12K_TPC_STATS_SUPPORT_AX,
+ ATH12K_TPC_STATS_SUPPORT_AX_EXTRA_MCS,
+ ATH12K_TPC_STATS_SUPPORT_BE,
+ ATH12K_TPC_STATS_SUPPORT_BE_PUNC,
+};
#else
static inline void ath12k_debugfs_soc_create(struct ath12k_base *ab)
{
@@ -29,6 +126,29 @@ static inline void ath12k_debugfs_unregister(struct ath12k *ar)
{
}
+static inline void ath12k_debugfs_fw_stats_process(struct ath12k *ar,
+ struct ath12k_fw_stats *stats)
+{
+}
+
+static inline bool ath12k_debugfs_is_extd_rx_stats_enabled(struct ath12k *ar)
+{
+ return false;
+}
+
+static inline int ath12k_debugfs_rx_filter(struct ath12k *ar)
+{
+ return 0;
+}
+
+static inline void ath12k_debugfs_op_vif_add(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif)
+{
+}
+
+static inline void ath12k_debugfs_pdev_create(struct ath12k_base *ab)
+{
+}
#endif /* CONFIG_ATH12K_DEBUGFS */
#endif /* _ATH12K_DEBUGFS_H_ */
diff --git a/drivers/net/wireless/ath/ath12k/debugfs_htt_stats.c b/drivers/net/wireless/ath/ath12k/debugfs_htt_stats.c
index 41e4ef2ef3af..aeaf970339d4 100644
--- a/drivers/net/wireless/ath/ath12k/debugfs_htt_stats.c
+++ b/drivers/net/wireless/ath/ath12k/debugfs_htt_stats.c
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: BSD-3-Clause-Clear
/*
* Copyright (c) 2018-2021 The Linux Foundation. All rights reserved.
- * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#include <linux/vmalloc.h>
@@ -48,6 +48,34 @@ print_array_to_buf(u8 *buf, u32 offset, const char *header,
footer);
}
+static u32
+print_array_to_buf_s8(u8 *buf, u32 offset, const char *header, u32 stats_index,
+ const s8 *array, u32 array_len, const char *footer)
+{
+ u32 buf_len = ATH12K_HTT_STATS_BUF_SIZE;
+ int index = 0;
+ u8 i;
+
+ if (header)
+ index += scnprintf(buf + offset, buf_len - offset, "%s = ", header);
+
+ for (i = 0; i < array_len; i++) {
+ index += scnprintf(buf + offset + index, (buf_len - offset) - index,
+ " %u:%d,", stats_index++, array[i]);
+ }
+
+ index--;
+ if ((offset + index) < buf_len)
+ buf[offset + index] = '\0';
+
+ if (footer) {
+ index += scnprintf(buf + offset + index, (buf_len - offset) - index,
+ "%s", footer);
+ }
+
+ return index;
+}
+
static const char *ath12k_htt_ax_tx_rx_ru_size_to_str(u8 ru_size)
{
switch (ru_size) {
@@ -2512,6 +2540,268 @@ ath12k_htt_print_pdev_stats_cca_counters_tlv(const void *tag_buf, u16 tag_len,
}
static void
+ath12k_htt_print_tx_sounding_stats_tlv(const void *tag_buf, u16 tag_len,
+ struct debug_htt_stats_req *stats_req)
+{
+ const struct ath12k_htt_tx_sounding_stats_tlv *htt_stats_buf = tag_buf;
+ const __le32 *cbf_20, *cbf_40, *cbf_80, *cbf_160, *cbf_320;
+ u32 buf_len = ATH12K_HTT_STATS_BUF_SIZE;
+ u32 len = stats_req->buf_len;
+ u8 *buf = stats_req->buf;
+ u32 tx_sounding_mode;
+ u8 i, u;
+
+ if (tag_len < sizeof(*htt_stats_buf))
+ return;
+
+ cbf_20 = htt_stats_buf->cbf_20;
+ cbf_40 = htt_stats_buf->cbf_40;
+ cbf_80 = htt_stats_buf->cbf_80;
+ cbf_160 = htt_stats_buf->cbf_160;
+ cbf_320 = htt_stats_buf->cbf_320;
+ tx_sounding_mode = le32_to_cpu(htt_stats_buf->tx_sounding_mode);
+
+ if (tx_sounding_mode == ATH12K_HTT_TX_AC_SOUNDING_MODE) {
+ len += scnprintf(buf + len, buf_len - len,
+ "HTT_TX_AC_SOUNDING_STATS_TLV:\n");
+ len += scnprintf(buf + len, buf_len - len,
+ "ac_cbf_20 = IBF: %u, SU_SIFS: %u, SU_RBO: %u, ",
+ le32_to_cpu(cbf_20[ATH12K_HTT_IMPL_STEER_STATS]),
+ le32_to_cpu(cbf_20[ATH12K_HTT_EXPL_SUSIFS_STEER_STATS]),
+ le32_to_cpu(cbf_20[ATH12K_HTT_EXPL_SURBO_STEER_STATS]));
+ len += scnprintf(buf + len, buf_len - len, "MU_SIFS: %u, MU_RBO: %u\n",
+ le32_to_cpu(cbf_20[ATH12K_HTT_EXPL_MUSIFS_STEER_STATS]),
+ le32_to_cpu(cbf_20[ATH12K_HTT_EXPL_MURBO_STEER_STATS]));
+ len += scnprintf(buf + len, buf_len - len,
+ "ac_cbf_40 = IBF: %u, SU_SIFS: %u, SU_RBO: %u, ",
+ le32_to_cpu(cbf_40[ATH12K_HTT_IMPL_STEER_STATS]),
+ le32_to_cpu(cbf_40[ATH12K_HTT_EXPL_SUSIFS_STEER_STATS]),
+ le32_to_cpu(cbf_40[ATH12K_HTT_EXPL_SURBO_STEER_STATS]));
+ len += scnprintf(buf + len, buf_len - len, "MU_SIFS: %u, MU_RBO: %u\n",
+ le32_to_cpu(cbf_40[ATH12K_HTT_EXPL_MUSIFS_STEER_STATS]),
+ le32_to_cpu(cbf_40[ATH12K_HTT_EXPL_MURBO_STEER_STATS]));
+ len += scnprintf(buf + len, buf_len - len,
+ "ac_cbf_80 = IBF: %u, SU_SIFS: %u, SU_RBO: %u, ",
+ le32_to_cpu(cbf_80[ATH12K_HTT_IMPL_STEER_STATS]),
+ le32_to_cpu(cbf_80[ATH12K_HTT_EXPL_SUSIFS_STEER_STATS]),
+ le32_to_cpu(cbf_80[ATH12K_HTT_EXPL_SURBO_STEER_STATS]));
+ len += scnprintf(buf + len, buf_len - len, "MU_SIFS: %u, MU_RBO: %u\n",
+ le32_to_cpu(cbf_80[ATH12K_HTT_EXPL_MUSIFS_STEER_STATS]),
+ le32_to_cpu(cbf_80[ATH12K_HTT_EXPL_MURBO_STEER_STATS]));
+ len += scnprintf(buf + len, buf_len - len,
+ "ac_cbf_160 = IBF: %u, SU_SIFS: %u, SU_RBO: %u, ",
+ le32_to_cpu(cbf_160[ATH12K_HTT_IMPL_STEER_STATS]),
+ le32_to_cpu(cbf_160[ATH12K_HTT_EXPL_SUSIFS_STEER_STATS]),
+ le32_to_cpu(cbf_160[ATH12K_HTT_EXPL_SURBO_STEER_STATS]));
+ len += scnprintf(buf + len, buf_len - len, "MU_SIFS: %u, MU_RBO: %u\n",
+ le32_to_cpu(cbf_160[ATH12K_HTT_EXPL_MUSIFS_STEER_STATS]),
+ le32_to_cpu(cbf_160[ATH12K_HTT_EXPL_MURBO_STEER_STATS]));
+
+ for (u = 0, i = 0; u < ATH12K_HTT_TX_NUM_AC_MUMIMO_USER_STATS; u++) {
+ len += scnprintf(buf + len, buf_len - len,
+ "Sounding User_%u = 20MHz: %u, ", u,
+ le32_to_cpu(htt_stats_buf->sounding[i++]));
+ len += scnprintf(buf + len, buf_len - len, "40MHz: %u, ",
+ le32_to_cpu(htt_stats_buf->sounding[i++]));
+ len += scnprintf(buf + len, buf_len - len, "80MHz: %u, ",
+ le32_to_cpu(htt_stats_buf->sounding[i++]));
+ len += scnprintf(buf + len, buf_len - len, "160MHz: %u\n",
+ le32_to_cpu(htt_stats_buf->sounding[i++]));
+ }
+ } else if (tx_sounding_mode == ATH12K_HTT_TX_AX_SOUNDING_MODE) {
+ 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, ",
+ le32_to_cpu(cbf_20[ATH12K_HTT_IMPL_STEER_STATS]),
+ le32_to_cpu(cbf_20[ATH12K_HTT_EXPL_SUSIFS_STEER_STATS]),
+ le32_to_cpu(cbf_20[ATH12K_HTT_EXPL_SURBO_STEER_STATS]));
+ len += scnprintf(buf + len, buf_len - len, "MU_SIFS: %u, MU_RBO: %u\n",
+ le32_to_cpu(cbf_20[ATH12K_HTT_EXPL_MUSIFS_STEER_STATS]),
+ le32_to_cpu(cbf_20[ATH12K_HTT_EXPL_MURBO_STEER_STATS]));
+ len += scnprintf(buf + len, buf_len - len,
+ "ax_cbf_40 = IBF: %u, SU_SIFS: %u, SU_RBO: %u, ",
+ le32_to_cpu(cbf_40[ATH12K_HTT_IMPL_STEER_STATS]),
+ le32_to_cpu(cbf_40[ATH12K_HTT_EXPL_SUSIFS_STEER_STATS]),
+ le32_to_cpu(cbf_40[ATH12K_HTT_EXPL_SURBO_STEER_STATS]));
+ len += scnprintf(buf + len, buf_len - len, "MU_SIFS: %u, MU_RBO: %u\n",
+ le32_to_cpu(cbf_40[ATH12K_HTT_EXPL_MUSIFS_STEER_STATS]),
+ le32_to_cpu(cbf_40[ATH12K_HTT_EXPL_MURBO_STEER_STATS]));
+ len += scnprintf(buf + len, buf_len - len,
+ "ax_cbf_80 = IBF: %u, SU_SIFS: %u, SU_RBO: %u, ",
+ le32_to_cpu(cbf_80[ATH12K_HTT_IMPL_STEER_STATS]),
+ le32_to_cpu(cbf_80[ATH12K_HTT_EXPL_SUSIFS_STEER_STATS]),
+ le32_to_cpu(cbf_80[ATH12K_HTT_EXPL_SURBO_STEER_STATS]));
+ len += scnprintf(buf + len, buf_len - len, "MU_SIFS: %u, MU_RBO: %u\n",
+ le32_to_cpu(cbf_80[ATH12K_HTT_EXPL_MUSIFS_STEER_STATS]),
+ le32_to_cpu(cbf_80[ATH12K_HTT_EXPL_MURBO_STEER_STATS]));
+ len += scnprintf(buf + len, buf_len - len,
+ "ax_cbf_160 = IBF: %u, SU_SIFS: %u, SU_RBO: %u, ",
+ le32_to_cpu(cbf_160[ATH12K_HTT_IMPL_STEER_STATS]),
+ le32_to_cpu(cbf_160[ATH12K_HTT_EXPL_SUSIFS_STEER_STATS]),
+ le32_to_cpu(cbf_160[ATH12K_HTT_EXPL_SURBO_STEER_STATS]));
+ len += scnprintf(buf + len, buf_len - len, "MU_SIFS: %u, MU_RBO: %u\n",
+ le32_to_cpu(cbf_160[ATH12K_HTT_EXPL_MUSIFS_STEER_STATS]),
+ le32_to_cpu(cbf_160[ATH12K_HTT_EXPL_MURBO_STEER_STATS]));
+
+ for (u = 0, i = 0; u < ATH12K_HTT_TX_NUM_AX_MUMIMO_USER_STATS; u++) {
+ len += scnprintf(buf + len, buf_len - len,
+ "Sounding User_%u = 20MHz: %u, ", u,
+ le32_to_cpu(htt_stats_buf->sounding[i++]));
+ len += scnprintf(buf + len, buf_len - len, "40MHz: %u, ",
+ le32_to_cpu(htt_stats_buf->sounding[i++]));
+ len += scnprintf(buf + len, buf_len - len, "80MHz: %u, ",
+ le32_to_cpu(htt_stats_buf->sounding[i++]));
+ len += scnprintf(buf + len, buf_len - len, "160MHz: %u\n",
+ le32_to_cpu(htt_stats_buf->sounding[i++]));
+ }
+ } else if (tx_sounding_mode == ATH12K_HTT_TX_BE_SOUNDING_MODE) {
+ len += scnprintf(buf + len, buf_len - len,
+ "\nHTT_TX_BE_SOUNDING_STATS_TLV:\n");
+ len += scnprintf(buf + len, buf_len - len,
+ "be_cbf_20 = IBF: %u, SU_SIFS: %u, SU_RBO: %u, ",
+ le32_to_cpu(cbf_20[ATH12K_HTT_IMPL_STEER_STATS]),
+ le32_to_cpu(cbf_20[ATH12K_HTT_EXPL_SUSIFS_STEER_STATS]),
+ le32_to_cpu(cbf_20[ATH12K_HTT_EXPL_SURBO_STEER_STATS]));
+ len += scnprintf(buf + len, buf_len - len, "MU_SIFS: %u, MU_RBO: %u\n",
+ le32_to_cpu(cbf_20[ATH12K_HTT_EXPL_MUSIFS_STEER_STATS]),
+ le32_to_cpu(cbf_20[ATH12K_HTT_EXPL_MURBO_STEER_STATS]));
+ len += scnprintf(buf + len, buf_len - len,
+ "be_cbf_40 = IBF: %u, SU_SIFS: %u, SU_RBO: %u, ",
+ le32_to_cpu(cbf_40[ATH12K_HTT_IMPL_STEER_STATS]),
+ le32_to_cpu(cbf_40[ATH12K_HTT_EXPL_SUSIFS_STEER_STATS]),
+ le32_to_cpu(cbf_40[ATH12K_HTT_EXPL_SURBO_STEER_STATS]));
+ len += scnprintf(buf + len, buf_len - len, "MU_SIFS: %u, MU_RBO: %u\n",
+ le32_to_cpu(cbf_40[ATH12K_HTT_EXPL_MUSIFS_STEER_STATS]),
+ le32_to_cpu(cbf_40[ATH12K_HTT_EXPL_MURBO_STEER_STATS]));
+ len += scnprintf(buf + len, buf_len - len,
+ "be_cbf_80 = IBF: %u, SU_SIFS: %u, SU_RBO: %u, ",
+ le32_to_cpu(cbf_80[ATH12K_HTT_IMPL_STEER_STATS]),
+ le32_to_cpu(cbf_80[ATH12K_HTT_EXPL_SUSIFS_STEER_STATS]),
+ le32_to_cpu(cbf_80[ATH12K_HTT_EXPL_SURBO_STEER_STATS]));
+ len += scnprintf(buf + len, buf_len - len, "MU_SIFS: %u, MU_RBO: %u\n",
+ le32_to_cpu(cbf_80[ATH12K_HTT_EXPL_MUSIFS_STEER_STATS]),
+ le32_to_cpu(cbf_80[ATH12K_HTT_EXPL_MURBO_STEER_STATS]));
+ len += scnprintf(buf + len, buf_len - len,
+ "be_cbf_160 = IBF: %u, SU_SIFS: %u, SU_RBO: %u, ",
+ le32_to_cpu(cbf_160[ATH12K_HTT_IMPL_STEER_STATS]),
+ le32_to_cpu(cbf_160[ATH12K_HTT_EXPL_SUSIFS_STEER_STATS]),
+ le32_to_cpu(cbf_160[ATH12K_HTT_EXPL_SURBO_STEER_STATS]));
+ len += scnprintf(buf + len, buf_len - len, "MU_SIFS: %u, MU_RBO: %u\n",
+ le32_to_cpu(cbf_160[ATH12K_HTT_EXPL_MUSIFS_STEER_STATS]),
+ le32_to_cpu(cbf_160[ATH12K_HTT_EXPL_MURBO_STEER_STATS]));
+ len += scnprintf(buf + len, buf_len - len,
+ "be_cbf_320 = IBF: %u, SU_SIFS: %u, SU_RBO: %u, ",
+ le32_to_cpu(cbf_320[ATH12K_HTT_IMPL_STEER_STATS]),
+ le32_to_cpu(cbf_320[ATH12K_HTT_EXPL_SUSIFS_STEER_STATS]),
+ le32_to_cpu(cbf_320[ATH12K_HTT_EXPL_SURBO_STEER_STATS]));
+ len += scnprintf(buf + len, buf_len - len, "MU_SIFS: %u, MU_RBO: %u\n",
+ le32_to_cpu(cbf_320[ATH12K_HTT_EXPL_MUSIFS_STEER_STATS]),
+ le32_to_cpu(cbf_320[ATH12K_HTT_EXPL_MURBO_STEER_STATS]));
+ for (u = 0, i = 0; u < ATH12K_HTT_TX_NUM_BE_MUMIMO_USER_STATS; u++) {
+ len += scnprintf(buf + len, buf_len - len,
+ "Sounding User_%u = 20MHz: %u, ", u,
+ le32_to_cpu(htt_stats_buf->sounding[i++]));
+ len += scnprintf(buf + len, buf_len - len, "40MHz: %u, ",
+ le32_to_cpu(htt_stats_buf->sounding[i++]));
+ len += scnprintf(buf + len, buf_len - len, "80MHz: %u, ",
+ le32_to_cpu(htt_stats_buf->sounding[i++]));
+ len += scnprintf(buf + len, buf_len - len,
+ "160MHz: %u, 320MHz: %u\n",
+ le32_to_cpu(htt_stats_buf->sounding[i++]),
+ le32_to_cpu(htt_stats_buf->sounding_320[u]));
+ }
+ } else if (tx_sounding_mode == ATH12K_HTT_TX_CMN_SOUNDING_MODE) {
+ len += scnprintf(buf + len, buf_len - len,
+ "\nCV UPLOAD HANDLER STATS:\n");
+ len += scnprintf(buf + len, buf_len - len, "cv_nc_mismatch_err = %u\n",
+ le32_to_cpu(htt_stats_buf->cv_nc_mismatch_err));
+ len += scnprintf(buf + len, buf_len - len, "cv_fcs_err = %u\n",
+ le32_to_cpu(htt_stats_buf->cv_fcs_err));
+ len += scnprintf(buf + len, buf_len - len, "cv_frag_idx_mismatch = %u\n",
+ le32_to_cpu(htt_stats_buf->cv_frag_idx_mismatch));
+ len += scnprintf(buf + len, buf_len - len, "cv_invalid_peer_id = %u\n",
+ le32_to_cpu(htt_stats_buf->cv_invalid_peer_id));
+ len += scnprintf(buf + len, buf_len - len, "cv_no_txbf_setup = %u\n",
+ le32_to_cpu(htt_stats_buf->cv_no_txbf_setup));
+ len += scnprintf(buf + len, buf_len - len, "cv_expiry_in_update = %u\n",
+ le32_to_cpu(htt_stats_buf->cv_expiry_in_update));
+ len += scnprintf(buf + len, buf_len - len, "cv_pkt_bw_exceed = %u\n",
+ le32_to_cpu(htt_stats_buf->cv_pkt_bw_exceed));
+ len += scnprintf(buf + len, buf_len - len, "cv_dma_not_done_err = %u\n",
+ le32_to_cpu(htt_stats_buf->cv_dma_not_done_err));
+ len += scnprintf(buf + len, buf_len - len, "cv_update_failed = %u\n",
+ le32_to_cpu(htt_stats_buf->cv_update_failed));
+ len += scnprintf(buf + len, buf_len - len, "cv_dma_timeout_error = %u\n",
+ le32_to_cpu(htt_stats_buf->cv_dma_timeout_error));
+ len += scnprintf(buf + len, buf_len - len, "cv_buf_ibf_uploads = %u\n",
+ le32_to_cpu(htt_stats_buf->cv_buf_ibf_uploads));
+ len += scnprintf(buf + len, buf_len - len, "cv_buf_ebf_uploads = %u\n",
+ le32_to_cpu(htt_stats_buf->cv_buf_ebf_uploads));
+ len += scnprintf(buf + len, buf_len - len, "cv_buf_received = %u\n",
+ le32_to_cpu(htt_stats_buf->cv_buf_received));
+ len += scnprintf(buf + len, buf_len - len, "cv_buf_fed_back = %u\n\n",
+ le32_to_cpu(htt_stats_buf->cv_buf_fed_back));
+
+ len += scnprintf(buf + len, buf_len - len, "CV QUERY STATS:\n");
+ len += scnprintf(buf + len, buf_len - len, "cv_total_query = %u\n",
+ le32_to_cpu(htt_stats_buf->cv_total_query));
+ len += scnprintf(buf + len, buf_len - len,
+ "cv_total_pattern_query = %u\n",
+ le32_to_cpu(htt_stats_buf->cv_total_pattern_query));
+ len += scnprintf(buf + len, buf_len - len, "cv_total_bw_query = %u\n",
+ le32_to_cpu(htt_stats_buf->cv_total_bw_query));
+ len += scnprintf(buf + len, buf_len - len, "cv_invalid_bw_coding = %u\n",
+ le32_to_cpu(htt_stats_buf->cv_invalid_bw_coding));
+ len += scnprintf(buf + len, buf_len - len, "cv_forced_sounding = %u\n",
+ le32_to_cpu(htt_stats_buf->cv_forced_sounding));
+ len += scnprintf(buf + len, buf_len - len,
+ "cv_standalone_sounding = %u\n",
+ le32_to_cpu(htt_stats_buf->cv_standalone_sounding));
+ len += scnprintf(buf + len, buf_len - len, "cv_nc_mismatch = %u\n",
+ le32_to_cpu(htt_stats_buf->cv_nc_mismatch));
+ len += scnprintf(buf + len, buf_len - len, "cv_fb_type_mismatch = %u\n",
+ le32_to_cpu(htt_stats_buf->cv_fb_type_mismatch));
+ len += scnprintf(buf + len, buf_len - len, "cv_ofdma_bw_mismatch = %u\n",
+ le32_to_cpu(htt_stats_buf->cv_ofdma_bw_mismatch));
+ len += scnprintf(buf + len, buf_len - len, "cv_bw_mismatch = %u\n",
+ le32_to_cpu(htt_stats_buf->cv_bw_mismatch));
+ len += scnprintf(buf + len, buf_len - len, "cv_pattern_mismatch = %u\n",
+ le32_to_cpu(htt_stats_buf->cv_pattern_mismatch));
+ len += scnprintf(buf + len, buf_len - len, "cv_preamble_mismatch = %u\n",
+ le32_to_cpu(htt_stats_buf->cv_preamble_mismatch));
+ len += scnprintf(buf + len, buf_len - len, "cv_nr_mismatch = %u\n",
+ le32_to_cpu(htt_stats_buf->cv_nr_mismatch));
+ len += scnprintf(buf + len, buf_len - len,
+ "cv_in_use_cnt_exceeded = %u\n",
+ le32_to_cpu(htt_stats_buf->cv_in_use_cnt_exceeded));
+ len += scnprintf(buf + len, buf_len - len, "cv_ntbr_sounding = %u\n",
+ le32_to_cpu(htt_stats_buf->cv_ntbr_sounding));
+ len += scnprintf(buf + len, buf_len - len,
+ "cv_found_upload_in_progress = %u\n",
+ le32_to_cpu(htt_stats_buf->cv_found_upload_in_progress));
+ len += scnprintf(buf + len, buf_len - len,
+ "cv_expired_during_query = %u\n",
+ le32_to_cpu(htt_stats_buf->cv_expired_during_query));
+ len += scnprintf(buf + len, buf_len - len, "cv_found = %u\n",
+ le32_to_cpu(htt_stats_buf->cv_found));
+ len += scnprintf(buf + len, buf_len - len, "cv_not_found = %u\n",
+ le32_to_cpu(htt_stats_buf->cv_not_found));
+ len += scnprintf(buf + len, buf_len - len, "cv_total_query_ibf = %u\n",
+ le32_to_cpu(htt_stats_buf->cv_total_query_ibf));
+ len += scnprintf(buf + len, buf_len - len, "cv_found_ibf = %u\n",
+ le32_to_cpu(htt_stats_buf->cv_found_ibf));
+ len += scnprintf(buf + len, buf_len - len, "cv_not_found_ibf = %u\n",
+ le32_to_cpu(htt_stats_buf->cv_not_found_ibf));
+ len += scnprintf(buf + len, buf_len - len,
+ "cv_expired_during_query_ibf = %u\n\n",
+ le32_to_cpu(htt_stats_buf->cv_expired_during_query_ibf));
+ }
+
+ stats_req->buf_len = len;
+}
+
+static void
ath12k_htt_print_pdev_obss_pd_stats_tlv(const void *tag_buf, u16 tag_len,
struct debug_htt_stats_req *stats_req)
{
@@ -2577,6 +2867,428 @@ ath12k_htt_print_pdev_obss_pd_stats_tlv(const void *tag_buf, u16 tag_len,
}
static void
+ath12k_htt_print_latency_prof_ctx_tlv(const void *tag_buf, u16 tag_len,
+ struct debug_htt_stats_req *stats_req)
+{
+ const struct ath12k_htt_latency_prof_ctx_tlv *htt_stats_buf = tag_buf;
+ u32 buf_len = ATH12K_HTT_STATS_BUF_SIZE;
+ u32 len = stats_req->buf_len;
+ u8 *buf = stats_req->buf;
+
+ if (tag_len < sizeof(*htt_stats_buf))
+ return;
+
+ len += scnprintf(buf + len, buf_len - len, "HTT_STATS_LATENCY_CTX_TLV:\n");
+ len += scnprintf(buf + len, buf_len - len, "duration = %u\n",
+ le32_to_cpu(htt_stats_buf->duration));
+ len += scnprintf(buf + len, buf_len - len, "tx_msdu_cnt = %u\n",
+ le32_to_cpu(htt_stats_buf->tx_msdu_cnt));
+ len += scnprintf(buf + len, buf_len - len, "tx_mpdu_cnt = %u\n",
+ le32_to_cpu(htt_stats_buf->tx_mpdu_cnt));
+ len += scnprintf(buf + len, buf_len - len, "rx_msdu_cnt = %u\n",
+ le32_to_cpu(htt_stats_buf->rx_msdu_cnt));
+ len += scnprintf(buf + len, buf_len - len, "rx_mpdu_cnt = %u\n\n",
+ le32_to_cpu(htt_stats_buf->rx_mpdu_cnt));
+
+ stats_req->buf_len = len;
+}
+
+static void
+ath12k_htt_print_latency_prof_cnt(const void *tag_buf, u16 tag_len,
+ struct debug_htt_stats_req *stats_req)
+{
+ const struct ath12k_htt_latency_prof_cnt_tlv *htt_stats_buf = tag_buf;
+ u32 buf_len = ATH12K_HTT_STATS_BUF_SIZE;
+ u32 len = stats_req->buf_len;
+ u8 *buf = stats_req->buf;
+
+ if (tag_len < sizeof(*htt_stats_buf))
+ return;
+
+ len += scnprintf(buf + len, buf_len - len, "HTT_STATS_LATENCY_CNT_TLV:\n");
+ len += scnprintf(buf + len, buf_len - len, "prof_enable_cnt = %u\n\n",
+ le32_to_cpu(htt_stats_buf->prof_enable_cnt));
+
+ stats_req->buf_len = len;
+}
+
+static void
+ath12k_htt_print_latency_prof_stats_tlv(const void *tag_buf, u16 tag_len,
+ struct debug_htt_stats_req *stats_req)
+{
+ const struct ath12k_htt_latency_prof_stats_tlv *htt_stats_buf = tag_buf;
+ u32 buf_len = ATH12K_HTT_STATS_BUF_SIZE;
+ u32 len = stats_req->buf_len;
+ u8 *buf = stats_req->buf;
+
+ if (tag_len < sizeof(*htt_stats_buf))
+ return;
+
+ if (le32_to_cpu(htt_stats_buf->print_header) == 1) {
+ len += scnprintf(buf + len, buf_len - len,
+ "HTT_STATS_LATENCY_PROF_TLV:\n");
+ }
+
+ len += scnprintf(buf + len, buf_len - len, "Latency name = %s\n",
+ htt_stats_buf->latency_prof_name);
+ len += scnprintf(buf + len, buf_len - len, "count = %u\n",
+ le32_to_cpu(htt_stats_buf->cnt));
+ len += scnprintf(buf + len, buf_len - len, "minimum = %u\n",
+ le32_to_cpu(htt_stats_buf->min));
+ len += scnprintf(buf + len, buf_len - len, "maximum = %u\n",
+ le32_to_cpu(htt_stats_buf->max));
+ len += scnprintf(buf + len, buf_len - len, "last = %u\n",
+ le32_to_cpu(htt_stats_buf->last));
+ len += scnprintf(buf + len, buf_len - len, "total = %u\n",
+ le32_to_cpu(htt_stats_buf->tot));
+ len += scnprintf(buf + len, buf_len - len, "average = %u\n",
+ le32_to_cpu(htt_stats_buf->avg));
+ len += scnprintf(buf + len, buf_len - len, "histogram interval = %u\n",
+ le32_to_cpu(htt_stats_buf->hist_intvl));
+ len += print_array_to_buf(buf, len, "histogram", htt_stats_buf->hist,
+ ATH12K_HTT_LATENCY_PROFILE_NUM_MAX_HIST, "\n\n");
+
+ stats_req->buf_len = len;
+}
+
+static void
+ath12k_htt_print_ul_ofdma_trigger_stats(const void *tag_buf, u16 tag_len,
+ struct debug_htt_stats_req *stats_req)
+{
+ const struct ath12k_htt_rx_pdev_ul_trigger_stats_tlv *htt_stats_buf = tag_buf;
+ u32 buf_len = ATH12K_HTT_STATS_BUF_SIZE;
+ u32 len = stats_req->buf_len;
+ u8 *buf = stats_req->buf;
+ u32 mac_id;
+ u8 j;
+
+ if (tag_len < sizeof(*htt_stats_buf))
+ return;
+
+ mac_id = __le32_to_cpu(htt_stats_buf->mac_id__word);
+
+ len += scnprintf(buf + len, buf_len - len,
+ "HTT_RX_PDEV_UL_TRIGGER_STATS_TLV:\n");
+ len += scnprintf(buf + len, buf_len - len, "mac_id = %u\n",
+ u32_get_bits(mac_id, ATH12K_HTT_STATS_MAC_ID));
+ len += scnprintf(buf + len, buf_len - len, "rx_11ax_ul_ofdma = %u\n",
+ le32_to_cpu(htt_stats_buf->rx_11ax_ul_ofdma));
+ len += print_array_to_buf(buf, len, "ul_ofdma_rx_mcs",
+ htt_stats_buf->ul_ofdma_rx_mcs,
+ ATH12K_HTT_RX_NUM_MCS_CNTRS, "\n");
+ for (j = 0; j < ATH12K_HTT_RX_NUM_GI_CNTRS; j++) {
+ len += scnprintf(buf + len, buf_len - len, "ul_ofdma_rx_gi[%u]", j);
+ len += print_array_to_buf(buf, len, "",
+ htt_stats_buf->ul_ofdma_rx_gi[j],
+ ATH12K_HTT_RX_NUM_MCS_CNTRS, "\n");
+ }
+
+ len += print_array_to_buf_index(buf, len, "ul_ofdma_rx_nss", 1,
+ htt_stats_buf->ul_ofdma_rx_nss,
+ ATH12K_HTT_RX_NUM_SPATIAL_STREAMS, "\n");
+ len += print_array_to_buf(buf, len, "ul_ofdma_rx_bw",
+ htt_stats_buf->ul_ofdma_rx_bw,
+ ATH12K_HTT_RX_NUM_BW_CNTRS, "\n");
+
+ for (j = 0; j < ATH12K_HTT_RX_NUM_REDUCED_CHAN_TYPES; j++) {
+ len += scnprintf(buf + len, buf_len - len, j == 0 ?
+ "half_ul_ofdma_rx_bw" :
+ "quarter_ul_ofdma_rx_bw");
+ len += print_array_to_buf(buf, len, "", htt_stats_buf->red_bw[j],
+ ATH12K_HTT_RX_NUM_BW_CNTRS, "\n");
+ }
+ len += scnprintf(buf + len, buf_len - len, "ul_ofdma_rx_stbc = %u\n",
+ le32_to_cpu(htt_stats_buf->ul_ofdma_rx_stbc));
+ len += scnprintf(buf + len, buf_len - len, "ul_ofdma_rx_ldpc = %u\n",
+ le32_to_cpu(htt_stats_buf->ul_ofdma_rx_ldpc));
+
+ len += scnprintf(buf + len, buf_len - len, "rx_ulofdma_data_ru_size_ppdu = ");
+ for (j = 0; j < ATH12K_HTT_RX_NUM_RU_SIZE_CNTRS; j++)
+ len += scnprintf(buf + len, buf_len - len, " %s:%u ",
+ ath12k_htt_ax_tx_rx_ru_size_to_str(j),
+ le32_to_cpu(htt_stats_buf->data_ru_size_ppdu[j]));
+ len += scnprintf(buf + len, buf_len - len, "\n");
+
+ len += scnprintf(buf + len, buf_len - len,
+ "rx_ulofdma_non_data_ru_size_ppdu = ");
+ for (j = 0; j < ATH12K_HTT_RX_NUM_RU_SIZE_CNTRS; j++)
+ len += scnprintf(buf + len, buf_len - len, " %s:%u ",
+ ath12k_htt_ax_tx_rx_ru_size_to_str(j),
+ le32_to_cpu(htt_stats_buf->non_data_ru_size_ppdu[j]));
+ len += scnprintf(buf + len, buf_len - len, "\n");
+
+ len += print_array_to_buf(buf, len, "rx_rssi_track_sta_aid",
+ htt_stats_buf->uplink_sta_aid,
+ ATH12K_HTT_RX_UL_MAX_UPLINK_RSSI_TRACK, "\n");
+ len += print_array_to_buf(buf, len, "rx_sta_target_rssi",
+ htt_stats_buf->uplink_sta_target_rssi,
+ ATH12K_HTT_RX_UL_MAX_UPLINK_RSSI_TRACK, "\n");
+ len += print_array_to_buf(buf, len, "rx_sta_fd_rssi",
+ htt_stats_buf->uplink_sta_fd_rssi,
+ ATH12K_HTT_RX_UL_MAX_UPLINK_RSSI_TRACK, "\n");
+ len += print_array_to_buf(buf, len, "rx_sta_power_headroom",
+ htt_stats_buf->uplink_sta_power_headroom,
+ ATH12K_HTT_RX_UL_MAX_UPLINK_RSSI_TRACK, "\n");
+ len += scnprintf(buf + len, buf_len - len,
+ "ul_ofdma_basic_trigger_rx_qos_null_only = %u\n\n",
+ le32_to_cpu(htt_stats_buf->ul_ofdma_bsc_trig_rx_qos_null_only));
+
+ stats_req->buf_len = len;
+}
+
+static void
+ath12k_htt_print_ul_ofdma_user_stats(const void *tag_buf, u16 tag_len,
+ struct debug_htt_stats_req *stats_req)
+{
+ const struct ath12k_htt_rx_pdev_ul_ofdma_user_stats_tlv *htt_stats_buf = tag_buf;
+ u32 buf_len = ATH12K_HTT_STATS_BUF_SIZE;
+ u32 len = stats_req->buf_len;
+ u8 *buf = stats_req->buf;
+ u32 user_index;
+
+ if (tag_len < sizeof(*htt_stats_buf))
+ return;
+
+ user_index = __le32_to_cpu(htt_stats_buf->user_index);
+
+ if (!user_index)
+ len += scnprintf(buf + len, buf_len - len,
+ "HTT_RX_PDEV_UL_OFDMA_USER_STAS_TLV:\n");
+ len += scnprintf(buf + len, buf_len - len, "rx_ulofdma_non_data_ppdu_%u = %u\n",
+ user_index,
+ le32_to_cpu(htt_stats_buf->rx_ulofdma_non_data_ppdu));
+ len += scnprintf(buf + len, buf_len - len, "rx_ulofdma_data_ppdu_%u = %u\n",
+ user_index,
+ le32_to_cpu(htt_stats_buf->rx_ulofdma_data_ppdu));
+ len += scnprintf(buf + len, buf_len - len, "rx_ulofdma_mpdu_ok_%u = %u\n",
+ user_index,
+ le32_to_cpu(htt_stats_buf->rx_ulofdma_mpdu_ok));
+ len += scnprintf(buf + len, buf_len - len, "rx_ulofdma_mpdu_fail_%u = %u\n",
+ user_index,
+ le32_to_cpu(htt_stats_buf->rx_ulofdma_mpdu_fail));
+ len += scnprintf(buf + len, buf_len - len,
+ "rx_ulofdma_non_data_nusers_%u = %u\n", user_index,
+ le32_to_cpu(htt_stats_buf->rx_ulofdma_non_data_nusers));
+ len += scnprintf(buf + len, buf_len - len, "rx_ulofdma_data_nusers_%u = %u\n\n",
+ user_index,
+ le32_to_cpu(htt_stats_buf->rx_ulofdma_data_nusers));
+
+ stats_req->buf_len = len;
+}
+
+static void
+ath12k_htt_print_ul_mumimo_trig_stats(const void *tag_buf, u16 tag_len,
+ struct debug_htt_stats_req *stats_req)
+{
+ const struct ath12k_htt_rx_ul_mumimo_trig_stats_tlv *htt_stats_buf = tag_buf;
+ char str_buf[ATH12K_HTT_MAX_STRING_LEN] = {0};
+ u32 buf_len = ATH12K_HTT_STATS_BUF_SIZE;
+ u32 len = stats_req->buf_len;
+ u8 *buf = stats_req->buf;
+ u32 mac_id;
+ u16 index;
+ u8 i, j;
+
+ if (tag_len < sizeof(*htt_stats_buf))
+ return;
+
+ mac_id = __le32_to_cpu(htt_stats_buf->mac_id__word);
+
+ len += scnprintf(buf + len, buf_len - len,
+ "HTT_RX_PDEV_UL_MUMIMO_TRIG_STATS_TLV:\n");
+ len += scnprintf(buf + len, buf_len - len, "mac_id = %u\n",
+ u32_get_bits(mac_id, ATH12K_HTT_STATS_MAC_ID));
+ len += scnprintf(buf + len, buf_len - len, "rx_11ax_ul_mumimo = %u\n",
+ le32_to_cpu(htt_stats_buf->rx_11ax_ul_mumimo));
+ index = 0;
+ memset(str_buf, 0x0, ATH12K_HTT_MAX_STRING_LEN);
+ for (i = 0; i < ATH12K_HTT_RX_NUM_MCS_CNTRS; i++)
+ index += scnprintf(&str_buf[index], ATH12K_HTT_MAX_STRING_LEN - index,
+ " %u:%u,", i,
+ le32_to_cpu(htt_stats_buf->ul_mumimo_rx_mcs[i]));
+
+ for (i = 0; i < ATH12K_HTT_RX_NUM_EXTRA_MCS_CNTRS; i++)
+ index += scnprintf(&str_buf[index], ATH12K_HTT_MAX_STRING_LEN - index,
+ " %u:%u,", i + ATH12K_HTT_RX_NUM_MCS_CNTRS,
+ le32_to_cpu(htt_stats_buf->ul_mumimo_rx_mcs_ext[i]));
+ str_buf[--index] = '\0';
+ len += scnprintf(buf + len, buf_len - len, "ul_mumimo_rx_mcs = %s\n", str_buf);
+
+ for (j = 0; j < ATH12K_HTT_RX_NUM_GI_CNTRS; j++) {
+ index = 0;
+ memset(&str_buf[index], 0x0, ATH12K_HTT_MAX_STRING_LEN);
+ for (i = 0; i < ATH12K_HTT_RX_NUM_MCS_CNTRS; i++)
+ index += scnprintf(&str_buf[index],
+ ATH12K_HTT_MAX_STRING_LEN - index,
+ " %u:%u,", i,
+ le32_to_cpu(htt_stats_buf->ul_rx_gi[j][i]));
+
+ for (i = 0; i < ATH12K_HTT_RX_NUM_EXTRA_MCS_CNTRS; i++)
+ index += scnprintf(&str_buf[index],
+ ATH12K_HTT_MAX_STRING_LEN - index,
+ " %u:%u,", i + ATH12K_HTT_RX_NUM_MCS_CNTRS,
+ le32_to_cpu(htt_stats_buf->ul_gi_ext[j][i]));
+ str_buf[--index] = '\0';
+ len += scnprintf(buf + len, buf_len - len,
+ "ul_mumimo_rx_gi_%u = %s\n", j, str_buf);
+ }
+
+ index = 0;
+ memset(str_buf, 0x0, ATH12K_HTT_MAX_STRING_LEN);
+ len += print_array_to_buf_index(buf, len, "ul_mumimo_rx_nss", 1,
+ htt_stats_buf->ul_mumimo_rx_nss,
+ ATH12K_HTT_RX_NUM_SPATIAL_STREAMS, "\n");
+
+ len += print_array_to_buf(buf, len, "ul_mumimo_rx_bw",
+ htt_stats_buf->ul_mumimo_rx_bw,
+ ATH12K_HTT_RX_NUM_BW_CNTRS, "\n");
+ for (i = 0; i < ATH12K_HTT_RX_NUM_REDUCED_CHAN_TYPES; i++) {
+ index = 0;
+ memset(str_buf, 0x0, ATH12K_HTT_MAX_STRING_LEN);
+ for (j = 0; j < ATH12K_HTT_RX_NUM_BW_CNTRS; j++)
+ index += scnprintf(&str_buf[index],
+ ATH12K_HTT_MAX_STRING_LEN - index,
+ " %u:%u,", j,
+ le32_to_cpu(htt_stats_buf->red_bw[i][j]));
+ str_buf[--index] = '\0';
+ len += scnprintf(buf + len, buf_len - len, "%s = %s\n",
+ i == 0 ? "half_ul_mumimo_rx_bw" :
+ "quarter_ul_mumimo_rx_bw", str_buf);
+ }
+
+ len += scnprintf(buf + len, buf_len - len, "ul_mumimo_rx_stbc = %u\n",
+ le32_to_cpu(htt_stats_buf->ul_mumimo_rx_stbc));
+ len += scnprintf(buf + len, buf_len - len, "ul_mumimo_rx_ldpc = %u\n",
+ le32_to_cpu(htt_stats_buf->ul_mumimo_rx_ldpc));
+
+ for (j = 0; j < ATH12K_HTT_RX_NUM_SPATIAL_STREAMS; j++) {
+ len += scnprintf(buf + len, buf_len - len,
+ "rx_ul_mumimo_rssi_in_dbm: chain%u ", j);
+ len += print_array_to_buf_s8(buf, len, "", 0,
+ htt_stats_buf->ul_rssi[j],
+ ATH12K_HTT_RX_NUM_BW_CNTRS, "\n");
+ }
+
+ for (j = 0; j < ATH12K_HTT_TX_UL_MUMIMO_USER_STATS; j++) {
+ len += scnprintf(buf + len, buf_len - len,
+ "rx_ul_mumimo_target_rssi: user_%u ", j);
+ len += print_array_to_buf_s8(buf, len, "", 0,
+ htt_stats_buf->tgt_rssi[j],
+ ATH12K_HTT_RX_NUM_BW_CNTRS, "\n");
+ }
+
+ for (j = 0; j < ATH12K_HTT_TX_UL_MUMIMO_USER_STATS; j++) {
+ len += scnprintf(buf + len, buf_len - len,
+ "rx_ul_mumimo_fd_rssi: user_%u ", j);
+ len += print_array_to_buf_s8(buf, len, "", 0,
+ htt_stats_buf->fd[j],
+ ATH12K_HTT_RX_NUM_SPATIAL_STREAMS, "\n");
+ }
+
+ for (j = 0; j < ATH12K_HTT_TX_UL_MUMIMO_USER_STATS; j++) {
+ len += scnprintf(buf + len, buf_len - len,
+ "rx_ulmumimo_pilot_evm_db_mean: user_%u ", j);
+ len += print_array_to_buf_s8(buf, len, "", 0,
+ htt_stats_buf->db[j],
+ ATH12K_HTT_RX_NUM_SPATIAL_STREAMS, "\n");
+ }
+
+ len += scnprintf(buf + len, buf_len - len,
+ "ul_mumimo_basic_trigger_rx_qos_null_only = %u\n\n",
+ le32_to_cpu(htt_stats_buf->mumimo_bsc_trig_rx_qos_null_only));
+
+ stats_req->buf_len = len;
+}
+
+static void
+ath12k_htt_print_rx_fse_stats_tlv(const void *tag_buf, u16 tag_len,
+ struct debug_htt_stats_req *stats_req)
+{
+ const struct ath12k_htt_rx_fse_stats_tlv *htt_stats_buf = tag_buf;
+ u32 buf_len = ATH12K_HTT_STATS_BUF_SIZE;
+ u32 len = stats_req->buf_len;
+ u8 *buf = stats_req->buf;
+
+ if (tag_len < sizeof(*htt_stats_buf))
+ return;
+
+ len += scnprintf(buf + len, buf_len - len, "HTT_STATS_RX_FSE_STATS_TLV:\n");
+ len += scnprintf(buf + len, buf_len - len, "=== Software RX FSE STATS ===\n");
+ len += scnprintf(buf + len, buf_len - len, "Enable count = %u\n",
+ le32_to_cpu(htt_stats_buf->fse_enable_cnt));
+ len += scnprintf(buf + len, buf_len - len, "Disable count = %u\n",
+ le32_to_cpu(htt_stats_buf->fse_disable_cnt));
+ len += scnprintf(buf + len, buf_len - len, "Cache invalidate entry count = %u\n",
+ le32_to_cpu(htt_stats_buf->fse_cache_invalidate_entry_cnt));
+ len += scnprintf(buf + len, buf_len - len, "Full cache invalidate count = %u\n",
+ le32_to_cpu(htt_stats_buf->fse_full_cache_invalidate_cnt));
+
+ len += scnprintf(buf + len, buf_len - len, "\n=== Hardware RX FSE STATS ===\n");
+ len += scnprintf(buf + len, buf_len - len, "Cache hits count = %u\n",
+ le32_to_cpu(htt_stats_buf->fse_num_cache_hits_cnt));
+ len += scnprintf(buf + len, buf_len - len, "Cache no. of searches = %u\n",
+ le32_to_cpu(htt_stats_buf->fse_num_searches_cnt));
+ len += scnprintf(buf + len, buf_len - len, "Cache occupancy peak count:\n");
+ len += scnprintf(buf + len, buf_len - len, "[0] = %u [1-16] = %u [17-32] = %u ",
+ le32_to_cpu(htt_stats_buf->fse_cache_occupancy_peak_cnt[0]),
+ le32_to_cpu(htt_stats_buf->fse_cache_occupancy_peak_cnt[1]),
+ le32_to_cpu(htt_stats_buf->fse_cache_occupancy_peak_cnt[2]));
+ len += scnprintf(buf + len, buf_len - len, "[33-48] = %u [49-64] = %u ",
+ le32_to_cpu(htt_stats_buf->fse_cache_occupancy_peak_cnt[3]),
+ le32_to_cpu(htt_stats_buf->fse_cache_occupancy_peak_cnt[4]));
+ len += scnprintf(buf + len, buf_len - len, "[65-80] = %u [81-96] = %u ",
+ le32_to_cpu(htt_stats_buf->fse_cache_occupancy_peak_cnt[5]),
+ le32_to_cpu(htt_stats_buf->fse_cache_occupancy_peak_cnt[6]));
+ len += scnprintf(buf + len, buf_len - len, "[97-112] = %u [113-127] = %u ",
+ le32_to_cpu(htt_stats_buf->fse_cache_occupancy_peak_cnt[7]),
+ le32_to_cpu(htt_stats_buf->fse_cache_occupancy_peak_cnt[8]));
+ len += scnprintf(buf + len, buf_len - len, "[128] = %u\n",
+ le32_to_cpu(htt_stats_buf->fse_cache_occupancy_peak_cnt[9]));
+ len += scnprintf(buf + len, buf_len - len, "Cache occupancy current count:\n");
+ len += scnprintf(buf + len, buf_len - len, "[0] = %u [1-16] = %u [17-32] = %u ",
+ le32_to_cpu(htt_stats_buf->fse_cache_occupancy_curr_cnt[0]),
+ le32_to_cpu(htt_stats_buf->fse_cache_occupancy_curr_cnt[1]),
+ le32_to_cpu(htt_stats_buf->fse_cache_occupancy_curr_cnt[2]));
+ len += scnprintf(buf + len, buf_len - len, "[33-48] = %u [49-64] = %u ",
+ le32_to_cpu(htt_stats_buf->fse_cache_occupancy_curr_cnt[3]),
+ le32_to_cpu(htt_stats_buf->fse_cache_occupancy_curr_cnt[4]));
+ len += scnprintf(buf + len, buf_len - len, "[65-80] = %u [81-96] = %u ",
+ le32_to_cpu(htt_stats_buf->fse_cache_occupancy_curr_cnt[5]),
+ le32_to_cpu(htt_stats_buf->fse_cache_occupancy_curr_cnt[6]));
+ len += scnprintf(buf + len, buf_len - len, "[97-112] = %u [113-127] = %u ",
+ le32_to_cpu(htt_stats_buf->fse_cache_occupancy_curr_cnt[7]),
+ le32_to_cpu(htt_stats_buf->fse_cache_occupancy_curr_cnt[8]));
+ len += scnprintf(buf + len, buf_len - len, "[128] = %u\n",
+ le32_to_cpu(htt_stats_buf->fse_cache_occupancy_curr_cnt[9]));
+ len += scnprintf(buf + len, buf_len - len, "Cache search square count:\n");
+ len += scnprintf(buf + len, buf_len - len, "[0] = %u [1-50] = %u [51-100] = %u ",
+ le32_to_cpu(htt_stats_buf->fse_search_stat_square_cnt[0]),
+ le32_to_cpu(htt_stats_buf->fse_search_stat_square_cnt[1]),
+ le32_to_cpu(htt_stats_buf->fse_search_stat_square_cnt[2]));
+ len += scnprintf(buf + len, buf_len - len, "[101-200] = %u [201-255] = %u ",
+ le32_to_cpu(htt_stats_buf->fse_search_stat_square_cnt[3]),
+ le32_to_cpu(htt_stats_buf->fse_search_stat_square_cnt[4]));
+ len += scnprintf(buf + len, buf_len - len, "[256] = %u\n",
+ le32_to_cpu(htt_stats_buf->fse_search_stat_square_cnt[5]));
+ len += scnprintf(buf + len, buf_len - len, "Cache search peak pending count:\n");
+ len += scnprintf(buf + len, buf_len - len, "[0] = %u [1-2] = %u [3-4] = %u ",
+ le32_to_cpu(htt_stats_buf->fse_search_stat_peak_cnt[0]),
+ le32_to_cpu(htt_stats_buf->fse_search_stat_peak_cnt[1]),
+ le32_to_cpu(htt_stats_buf->fse_search_stat_peak_cnt[2]));
+ len += scnprintf(buf + len, buf_len - len, "[Greater/Equal to 5] = %u\n",
+ le32_to_cpu(htt_stats_buf->fse_search_stat_peak_cnt[3]));
+ len += scnprintf(buf + len, buf_len - len, "Cache search tot pending count:\n");
+ len += scnprintf(buf + len, buf_len - len, "[0] = %u [1-2] = %u [3-4] = %u ",
+ le32_to_cpu(htt_stats_buf->fse_search_stat_pending_cnt[0]),
+ le32_to_cpu(htt_stats_buf->fse_search_stat_pending_cnt[1]),
+ le32_to_cpu(htt_stats_buf->fse_search_stat_pending_cnt[2]));
+ len += scnprintf(buf + len, buf_len - len, "[Greater/Equal to 5] = %u\n\n",
+ le32_to_cpu(htt_stats_buf->fse_search_stat_pending_cnt[3]));
+
+ stats_req->buf_len = len;
+}
+
+static void
ath12k_htt_print_pdev_tx_rate_txbf_stats_tlv(const void *tag_buf, u16 tag_len,
struct debug_htt_stats_req *stats_req)
{
@@ -3812,6 +4524,497 @@ ath12k_htt_print_pdev_mbssid_ctrl_frame_stats_tlv(const void *tag_buf, u16 tag_l
stats_req->buf_len = len;
}
+static inline void
+ath12k_htt_print_tx_pdev_rate_stats_tlv(const void *tag_buf, u16 tag_len,
+ struct debug_htt_stats_req *stats_req)
+{
+ const struct ath12k_htt_tx_pdev_rate_stats_tlv *htt_stats_buf = tag_buf;
+ u8 *buf = stats_req->buf;
+ u32 len = stats_req->buf_len;
+ u32 buf_len = ATH12K_HTT_STATS_BUF_SIZE;
+ u8 i, j;
+ u32 mac_id_word;
+
+ if (tag_len < sizeof(*htt_stats_buf))
+ return;
+
+ mac_id_word = le32_to_cpu(htt_stats_buf->mac_id_word);
+
+ 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",
+ u32_get_bits(mac_id_word, ATH12K_HTT_STATS_MAC_ID));
+ len += scnprintf(buf + len, buf_len - len, "tx_ldpc = %u\n",
+ le32_to_cpu(htt_stats_buf->tx_ldpc));
+ len += scnprintf(buf + len, buf_len - len, "ac_mu_mimo_tx_ldpc = %u\n",
+ le32_to_cpu(htt_stats_buf->ac_mu_mimo_tx_ldpc));
+ len += scnprintf(buf + len, buf_len - len, "ax_mu_mimo_tx_ldpc = %u\n",
+ le32_to_cpu(htt_stats_buf->ax_mu_mimo_tx_ldpc));
+ len += scnprintf(buf + len, buf_len - len, "ofdma_tx_ldpc = %u\n",
+ le32_to_cpu(htt_stats_buf->ofdma_tx_ldpc));
+ len += scnprintf(buf + len, buf_len - len, "rts_cnt = %u\n",
+ le32_to_cpu(htt_stats_buf->rts_cnt));
+ len += scnprintf(buf + len, buf_len - len, "rts_success = %u\n",
+ le32_to_cpu(htt_stats_buf->rts_success));
+ len += scnprintf(buf + len, buf_len - len, "ack_rssi = %u\n",
+ le32_to_cpu(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, 12 Mbps: %u\n",
+ le32_to_cpu(htt_stats_buf->tx_legacy_cck_rate[0]),
+ le32_to_cpu(htt_stats_buf->tx_legacy_cck_rate[1]),
+ le32_to_cpu(htt_stats_buf->tx_legacy_cck_rate[2]),
+ le32_to_cpu(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",
+ le32_to_cpu(htt_stats_buf->tx_legacy_ofdm_rate[0]),
+ le32_to_cpu(htt_stats_buf->tx_legacy_ofdm_rate[1]),
+ le32_to_cpu(htt_stats_buf->tx_legacy_ofdm_rate[2]),
+ le32_to_cpu(htt_stats_buf->tx_legacy_ofdm_rate[3]),
+ le32_to_cpu(htt_stats_buf->tx_legacy_ofdm_rate[4]),
+ le32_to_cpu(htt_stats_buf->tx_legacy_ofdm_rate[5]),
+ le32_to_cpu(htt_stats_buf->tx_legacy_ofdm_rate[6]),
+ le32_to_cpu(htt_stats_buf->tx_legacy_ofdm_rate[7]));
+ len += scnprintf(buf + len, buf_len - len, "HE LTF: 1x: %u, 2x: %u, 4x: %u\n",
+ le32_to_cpu(htt_stats_buf->tx_he_ltf[1]),
+ le32_to_cpu(htt_stats_buf->tx_he_ltf[2]),
+ le32_to_cpu(htt_stats_buf->tx_he_ltf[3]));
+
+ len += print_array_to_buf(buf, len, "tx_mcs", htt_stats_buf->tx_mcs,
+ ATH12K_HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS, NULL);
+ for (j = 0; j < ATH12K_HTT_TX_PDEV_STATS_NUM_EXTRA_MCS_COUNTERS; j++)
+ len += scnprintf(buf + len, buf_len - len, ", %u:%u",
+ j + ATH12K_HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS,
+ le32_to_cpu(htt_stats_buf->tx_mcs_ext[j]));
+ for (j = 0; j < ATH12K_HTT_TX_PDEV_STATS_NUM_EXTRA2_MCS_COUNTERS; j++)
+ len += scnprintf(buf + len, buf_len - len, ", %u:%u",
+ j + ATH12K_HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS +
+ ATH12K_HTT_TX_PDEV_STATS_NUM_EXTRA2_MCS_COUNTERS,
+ le32_to_cpu(htt_stats_buf->tx_mcs_ext_2[j]));
+ len += scnprintf(buf + len, buf_len - len, "\n");
+
+ len += print_array_to_buf(buf, len, "ax_mu_mimo_tx_mcs",
+ htt_stats_buf->ax_mu_mimo_tx_mcs,
+ ATH12K_HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS, NULL);
+ for (j = 0; j < ATH12K_HTT_TX_PDEV_STATS_NUM_EXTRA_MCS_COUNTERS; j++)
+ len += scnprintf(buf + len, buf_len - len, ", %u:%u",
+ j + ATH12K_HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS,
+ le32_to_cpu(htt_stats_buf->ax_mu_mimo_tx_mcs_ext[j]));
+ len += scnprintf(buf + len, buf_len - len, "\n");
+
+ len += print_array_to_buf(buf, len, "ofdma_tx_mcs",
+ htt_stats_buf->ofdma_tx_mcs,
+ ATH12K_HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS, NULL);
+ for (j = 0; j < ATH12K_HTT_TX_PDEV_STATS_NUM_EXTRA_MCS_COUNTERS; j++)
+ len += scnprintf(buf + len, buf_len - len, ", %u:%u",
+ j + ATH12K_HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS,
+ le32_to_cpu(htt_stats_buf->ofdma_tx_mcs_ext[j]));
+ len += scnprintf(buf + len, buf_len - len, "\n");
+
+ len += scnprintf(buf + len, buf_len - len, "tx_nss =");
+ for (j = 1; j <= ATH12K_HTT_TX_PDEV_STATS_NUM_SPATIAL_STREAMS; j++)
+ len += scnprintf(buf + len, buf_len - len, " %u:%u,",
+ j, le32_to_cpu(htt_stats_buf->tx_nss[j - 1]));
+ len--;
+ len += scnprintf(buf + len, buf_len - len, "\n");
+
+ len += scnprintf(buf + len, buf_len - len, "ac_mu_mimo_tx_nss =");
+ for (j = 1; j <= ATH12K_HTT_TX_PDEV_STATS_NUM_SPATIAL_STREAMS; j++)
+ len += scnprintf(buf + len, buf_len - len, " %u:%u,",
+ j, le32_to_cpu(htt_stats_buf->ac_mu_mimo_tx_nss[j - 1]));
+ len--;
+ len += scnprintf(buf + len, buf_len - len, "\n");
+
+ len += scnprintf(buf + len, buf_len - len, "ax_mu_mimo_tx_nss =");
+ for (j = 1; j <= ATH12K_HTT_TX_PDEV_STATS_NUM_SPATIAL_STREAMS; j++)
+ len += scnprintf(buf + len, buf_len - len, " %u:%u,",
+ j, le32_to_cpu(htt_stats_buf->ax_mu_mimo_tx_nss[j - 1]));
+ len--;
+ len += scnprintf(buf + len, buf_len - len, "\n");
+
+ len += scnprintf(buf + len, buf_len - len, "ofdma_tx_nss =");
+ for (j = 1; j <= ATH12K_HTT_TX_PDEV_STATS_NUM_SPATIAL_STREAMS; j++)
+ len += scnprintf(buf + len, buf_len - len, " %u:%u,",
+ j, le32_to_cpu(htt_stats_buf->ofdma_tx_nss[j - 1]));
+ len--;
+ len += scnprintf(buf + len, buf_len - len, "\n");
+
+ len += print_array_to_buf(buf, len, "tx_bw", htt_stats_buf->tx_bw,
+ ATH12K_HTT_TX_PDEV_STATS_NUM_BW_COUNTERS, NULL);
+ len += scnprintf(buf + len, buf_len - len, ", %u:%u\n",
+ ATH12K_HTT_TX_PDEV_STATS_NUM_BW_COUNTERS,
+ le32_to_cpu(htt_stats_buf->tx_bw_320mhz));
+
+ len += print_array_to_buf(buf, len, "tx_stbc",
+ htt_stats_buf->tx_stbc,
+ ATH12K_HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS, NULL);
+ for (j = 0; j < ATH12K_HTT_TX_PDEV_STATS_NUM_EXTRA_MCS_COUNTERS; j++)
+ len += scnprintf(buf + len, buf_len - len, ", %u:%u",
+ j + ATH12K_HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS,
+ le32_to_cpu(htt_stats_buf->tx_stbc_ext[j]));
+ len += scnprintf(buf + len, buf_len - len, "\n");
+
+ for (j = 0; j < ATH12K_HTT_TX_PDEV_STATS_NUM_GI_COUNTERS; j++) {
+ len += scnprintf(buf + len, (buf_len - len),
+ "tx_gi[%u] =", j);
+ len += print_array_to_buf(buf, len, NULL, htt_stats_buf->tx_gi[j],
+ ATH12K_HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS,
+ NULL);
+ for (i = 0; i < ATH12K_HTT_TX_PDEV_STATS_NUM_EXTRA_MCS_COUNTERS; i++)
+ len += scnprintf(buf + len, buf_len - len, ", %u:%u",
+ i + ATH12K_HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS,
+ le32_to_cpu(htt_stats_buf->tx_gi_ext[j][i]));
+ len += scnprintf(buf + len, buf_len - len, "\n");
+ }
+
+ for (j = 0; j < ATH12K_HTT_TX_PDEV_STATS_NUM_GI_COUNTERS; j++) {
+ len += scnprintf(buf + len, (buf_len - len),
+ "ac_mu_mimo_tx_gi[%u] =", j);
+ len += print_array_to_buf(buf, len, NULL,
+ htt_stats_buf->ac_mu_mimo_tx_gi[j],
+ ATH12K_HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS,
+ "\n");
+ }
+
+ for (j = 0; j < ATH12K_HTT_TX_PDEV_STATS_NUM_GI_COUNTERS; j++) {
+ len += scnprintf(buf + len, (buf_len - len),
+ "ax_mu_mimo_tx_gi[%u] =", j);
+ len += print_array_to_buf(buf, len, NULL, htt_stats_buf->ax_mimo_tx_gi[j],
+ ATH12K_HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS,
+ NULL);
+ for (i = 0; i < ATH12K_HTT_TX_PDEV_STATS_NUM_EXTRA_MCS_COUNTERS; i++)
+ len += scnprintf(buf + len, buf_len - len, ", %u:%u",
+ i + ATH12K_HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS,
+ le32_to_cpu(htt_stats_buf->ax_tx_gi_ext[j][i]));
+ len += scnprintf(buf + len, buf_len - len, "\n");
+ }
+
+ for (j = 0; j < ATH12K_HTT_TX_PDEV_STATS_NUM_GI_COUNTERS; j++) {
+ len += scnprintf(buf + len, (buf_len - len),
+ "ofdma_tx_gi[%u] = ", j);
+ len += print_array_to_buf(buf, len, NULL, htt_stats_buf->ofdma_tx_gi[j],
+ ATH12K_HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS,
+ NULL);
+ for (i = 0; i < ATH12K_HTT_TX_PDEV_STATS_NUM_EXTRA_MCS_COUNTERS; i++)
+ len += scnprintf(buf + len, buf_len - len, ", %u:%u",
+ i + ATH12K_HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS,
+ le32_to_cpu(htt_stats_buf->ofd_tx_gi_ext[j][i]));
+ len += scnprintf(buf + len, buf_len - len, "\n");
+ }
+
+ len += print_array_to_buf(buf, len, "tx_su_mcs", htt_stats_buf->tx_su_mcs,
+ ATH12K_HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS, "\n");
+ len += print_array_to_buf(buf, len, "tx_mu_mcs", htt_stats_buf->tx_mu_mcs,
+ ATH12K_HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS, "\n");
+ len += print_array_to_buf(buf, len, "ac_mu_mimo_tx_mcs",
+ htt_stats_buf->ac_mu_mimo_tx_mcs,
+ ATH12K_HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS, "\n");
+ len += print_array_to_buf(buf, len, "ac_mu_mimo_tx_bw",
+ htt_stats_buf->ac_mu_mimo_tx_bw,
+ ATH12K_HTT_TX_PDEV_STATS_NUM_BW_COUNTERS, "\n");
+ len += print_array_to_buf(buf, len, "ax_mu_mimo_tx_bw",
+ htt_stats_buf->ax_mu_mimo_tx_bw,
+ ATH12K_HTT_TX_PDEV_STATS_NUM_BW_COUNTERS, "\n");
+ len += print_array_to_buf(buf, len, "ofdma_tx_bw",
+ htt_stats_buf->ofdma_tx_bw,
+ ATH12K_HTT_TX_PDEV_STATS_NUM_BW_COUNTERS, "\n");
+ len += print_array_to_buf(buf, len, "tx_pream", htt_stats_buf->tx_pream,
+ ATH12K_HTT_TX_PDEV_STATS_NUM_PREAMBLE_TYPES, "\n");
+ len += print_array_to_buf(buf, len, "tx_dcm", htt_stats_buf->tx_dcm,
+ ATH12K_HTT_TX_PDEV_STATS_NUM_DCM_COUNTERS, "\n");
+
+ stats_req->buf_len = len;
+}
+
+static inline void
+ath12k_htt_print_rx_pdev_rate_stats_tlv(const void *tag_buf, u16 tag_len,
+ struct debug_htt_stats_req *stats_req)
+{
+ const struct ath12k_htt_rx_pdev_rate_stats_tlv *htt_stats_buf = tag_buf;
+ u8 *buf = stats_req->buf;
+ u32 len = stats_req->buf_len;
+ u32 buf_len = ATH12K_HTT_STATS_BUF_SIZE;
+ u8 i, j;
+ u32 mac_id_word;
+
+ if (tag_len < sizeof(*htt_stats_buf))
+ return;
+
+ mac_id_word = le32_to_cpu(htt_stats_buf->mac_id_word);
+
+ 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",
+ u32_get_bits(mac_id_word, ATH12K_HTT_STATS_MAC_ID));
+ len += scnprintf(buf + len, buf_len - len, "nsts = %u\n",
+ le32_to_cpu(htt_stats_buf->nsts));
+ len += scnprintf(buf + len, buf_len - len, "rx_ldpc = %u\n",
+ le32_to_cpu(htt_stats_buf->rx_ldpc));
+ len += scnprintf(buf + len, buf_len - len, "rts_cnt = %u\n",
+ le32_to_cpu(htt_stats_buf->rts_cnt));
+ len += scnprintf(buf + len, buf_len - len, "rssi_mgmt = %u\n",
+ le32_to_cpu(htt_stats_buf->rssi_mgmt));
+ len += scnprintf(buf + len, buf_len - len, "rssi_data = %u\n",
+ le32_to_cpu(htt_stats_buf->rssi_data));
+ len += scnprintf(buf + len, buf_len - len, "rssi_comb = %u\n",
+ le32_to_cpu(htt_stats_buf->rssi_comb));
+ len += scnprintf(buf + len, buf_len - len, "rssi_in_dbm = %d\n",
+ le32_to_cpu(htt_stats_buf->rssi_in_dbm));
+ len += scnprintf(buf + len, buf_len - len, "rx_evm_nss_count = %u\n",
+ le32_to_cpu(htt_stats_buf->nss_count));
+ len += scnprintf(buf + len, buf_len - len, "rx_evm_pilot_count = %u\n",
+ le32_to_cpu(htt_stats_buf->pilot_count));
+ len += scnprintf(buf + len, buf_len - len, "rx_11ax_su_ext = %u\n",
+ le32_to_cpu(htt_stats_buf->rx_11ax_su_ext));
+ len += scnprintf(buf + len, buf_len - len, "rx_11ac_mumimo = %u\n",
+ le32_to_cpu(htt_stats_buf->rx_11ac_mumimo));
+ len += scnprintf(buf + len, buf_len - len, "rx_11ax_mumimo = %u\n",
+ le32_to_cpu(htt_stats_buf->rx_11ax_mumimo));
+ len += scnprintf(buf + len, buf_len - len, "rx_11ax_ofdma = %u\n",
+ le32_to_cpu(htt_stats_buf->rx_11ax_ofdma));
+ len += scnprintf(buf + len, buf_len - len, "txbf = %u\n",
+ le32_to_cpu(htt_stats_buf->txbf));
+ len += scnprintf(buf + len, buf_len - len, "rx_su_ndpa = %u\n",
+ le32_to_cpu(htt_stats_buf->rx_su_ndpa));
+ len += scnprintf(buf + len, buf_len - len, "rx_mu_ndpa = %u\n",
+ le32_to_cpu(htt_stats_buf->rx_mu_ndpa));
+ len += scnprintf(buf + len, buf_len - len, "rx_br_poll = %u\n",
+ le32_to_cpu(htt_stats_buf->rx_br_poll));
+ len += scnprintf(buf + len, buf_len - len, "rx_active_dur_us_low = %u\n",
+ le32_to_cpu(htt_stats_buf->rx_active_dur_us_low));
+ len += scnprintf(buf + len, buf_len - len, "rx_active_dur_us_high = %u\n",
+ le32_to_cpu(htt_stats_buf->rx_active_dur_us_high));
+ len += scnprintf(buf + len, buf_len - len, "rx_11ax_ul_ofdma = %u\n",
+ le32_to_cpu(htt_stats_buf->rx_11ax_ul_ofdma));
+ len += scnprintf(buf + len, buf_len - len, "ul_ofdma_rx_stbc = %u\n",
+ le32_to_cpu(htt_stats_buf->ul_ofdma_rx_stbc));
+ len += scnprintf(buf + len, buf_len - len, "ul_ofdma_rx_ldpc = %u\n",
+ le32_to_cpu(htt_stats_buf->ul_ofdma_rx_ldpc));
+ len += scnprintf(buf + len, buf_len - len, "per_chain_rssi_pkt_type = %#x\n",
+ le32_to_cpu(htt_stats_buf->per_chain_rssi_pkt_type));
+
+ len += print_array_to_buf(buf, len, "rx_nss", htt_stats_buf->rx_nss,
+ ATH12K_HTT_RX_PDEV_STATS_NUM_SPATIAL_STREAMS, "\n");
+ len += print_array_to_buf(buf, len, "rx_dcm", htt_stats_buf->rx_dcm,
+ ATH12K_HTT_RX_PDEV_STATS_NUM_DCM_COUNTERS, "\n");
+ len += print_array_to_buf(buf, len, "rx_stbc", htt_stats_buf->rx_stbc,
+ ATH12K_HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS, "\n");
+ len += print_array_to_buf(buf, len, "rx_bw", htt_stats_buf->rx_bw,
+ ATH12K_HTT_RX_PDEV_STATS_NUM_BW_COUNTERS, "\n");
+ len += print_array_to_buf(buf, len, "rx_pream", htt_stats_buf->rx_pream,
+ ATH12K_HTT_RX_PDEV_STATS_NUM_PREAMBLE_TYPES, "\n");
+ len += print_array_to_buf(buf, len, "rx_11ax_su_txbf_mcs",
+ htt_stats_buf->rx_11ax_su_txbf_mcs,
+ ATH12K_HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS, "\n");
+ len += print_array_to_buf(buf, len, "rx_11ax_mu_txbf_mcs",
+ htt_stats_buf->rx_11ax_mu_txbf_mcs,
+ ATH12K_HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS, "\n");
+ len += print_array_to_buf(buf, len, "rx_legacy_cck_rate",
+ htt_stats_buf->rx_legacy_cck_rate,
+ ATH12K_HTT_RX_PDEV_STATS_NUM_LEGACY_CCK_STATS, "\n");
+ len += print_array_to_buf(buf, len, "rx_legacy_ofdm_rate",
+ htt_stats_buf->rx_legacy_ofdm_rate,
+ ATH12K_HTT_RX_PDEV_STATS_NUM_LEGACY_OFDM_STATS, "\n");
+ len += print_array_to_buf(buf, len, "ul_ofdma_rx_mcs",
+ htt_stats_buf->ul_ofdma_rx_mcs,
+ ATH12K_HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS, "\n");
+ len += print_array_to_buf(buf, len, "ul_ofdma_rx_nss",
+ htt_stats_buf->ul_ofdma_rx_nss,
+ ATH12K_HTT_RX_PDEV_STATS_NUM_SPATIAL_STREAMS, "\n");
+ len += print_array_to_buf(buf, len, "ul_ofdma_rx_bw",
+ htt_stats_buf->ul_ofdma_rx_bw,
+ ATH12K_HTT_RX_PDEV_STATS_NUM_BW_COUNTERS, "\n");
+ len += print_array_to_buf(buf, len, "rx_ulofdma_non_data_ppdu",
+ htt_stats_buf->rx_ulofdma_non_data_ppdu,
+ ATH12K_HTT_RX_PDEV_MAX_OFDMA_NUM_USER, "\n");
+ len += print_array_to_buf(buf, len, "rx_ulofdma_data_ppdu",
+ htt_stats_buf->rx_ulofdma_data_ppdu,
+ ATH12K_HTT_RX_PDEV_MAX_OFDMA_NUM_USER, "\n");
+ len += print_array_to_buf(buf, len, "rx_ulofdma_mpdu_ok",
+ htt_stats_buf->rx_ulofdma_mpdu_ok,
+ ATH12K_HTT_RX_PDEV_MAX_OFDMA_NUM_USER, "\n");
+ len += print_array_to_buf(buf, len, "rx_ulofdma_mpdu_fail",
+ htt_stats_buf->rx_ulofdma_mpdu_fail,
+ ATH12K_HTT_RX_PDEV_MAX_OFDMA_NUM_USER, "\n");
+ len += print_array_to_buf(buf, len, "rx_ulofdma_non_data_nusers",
+ htt_stats_buf->rx_ulofdma_non_data_nusers,
+ ATH12K_HTT_RX_PDEV_MAX_OFDMA_NUM_USER, "\n");
+ len += print_array_to_buf(buf, len, "rx_ulofdma_data_nusers",
+ htt_stats_buf->rx_ulofdma_data_nusers,
+ ATH12K_HTT_RX_PDEV_MAX_OFDMA_NUM_USER, "\n");
+ len += print_array_to_buf(buf, len, "rx_11ax_dl_ofdma_mcs",
+ htt_stats_buf->rx_11ax_dl_ofdma_mcs,
+ ATH12K_HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS, "\n");
+ len += print_array_to_buf(buf, len, "rx_11ax_dl_ofdma_ru",
+ htt_stats_buf->rx_11ax_dl_ofdma_ru,
+ ATH12K_HTT_RX_PDEV_STATS_NUM_RU_SIZE_COUNTERS, "\n");
+ len += print_array_to_buf(buf, len, "rx_ulmumimo_non_data_ppdu",
+ htt_stats_buf->rx_ulmumimo_non_data_ppdu,
+ ATH12K_HTT_RX_PDEV_MAX_ULMUMIMO_NUM_USER, "\n");
+ len += print_array_to_buf(buf, len, "rx_ulmumimo_data_ppdu",
+ htt_stats_buf->rx_ulmumimo_data_ppdu,
+ ATH12K_HTT_RX_PDEV_MAX_ULMUMIMO_NUM_USER, "\n");
+ len += print_array_to_buf(buf, len, "rx_ulmumimo_mpdu_ok",
+ htt_stats_buf->rx_ulmumimo_mpdu_ok,
+ ATH12K_HTT_RX_PDEV_MAX_ULMUMIMO_NUM_USER, "\n");
+ len += print_array_to_buf(buf, len, "rx_ulmumimo_mpdu_fail",
+ htt_stats_buf->rx_ulmumimo_mpdu_fail,
+ ATH12K_HTT_RX_PDEV_MAX_ULMUMIMO_NUM_USER, "\n");
+
+ len += print_array_to_buf(buf, len, "rx_mcs",
+ htt_stats_buf->rx_mcs,
+ ATH12K_HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS, NULL);
+ for (j = 0; j < ATH12K_HTT_TX_PDEV_STATS_NUM_EXTRA_MCS_COUNTERS; j++)
+ len += scnprintf(buf + len, buf_len - len, ", %u:%u",
+ j + ATH12K_HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS,
+ le32_to_cpu(htt_stats_buf->rx_mcs_ext[j]));
+ len += scnprintf(buf + len, buf_len - len, "\n");
+
+ for (j = 0; j < ATH12K_HTT_RX_PDEV_STATS_NUM_SPATIAL_STREAMS; j++) {
+ len += scnprintf(buf + len, buf_len - len,
+ "pilot_evm_db[%u] =", j);
+ len += print_array_to_buf(buf, len, NULL,
+ htt_stats_buf->rx_pil_evm_db[j],
+ ATH12K_HTT_RX_PDEV_STATS_RXEVM_MAX_PILOTS_NSS,
+ "\n");
+ }
+
+ len += scnprintf(buf + len, buf_len - len, "pilot_evm_db_mean =");
+ for (i = 0; i < ATH12K_HTT_RX_PDEV_STATS_NUM_SPATIAL_STREAMS; i++)
+ len += scnprintf(buf + len,
+ buf_len - len,
+ " %u:%d,", i,
+ le32_to_cpu(htt_stats_buf->rx_pilot_evm_db_mean[i]));
+ len--;
+ len += scnprintf(buf + len, buf_len - len, "\n");
+
+ for (j = 0; j < ATH12K_HTT_RX_PDEV_STATS_NUM_SPATIAL_STREAMS; j++) {
+ len += scnprintf(buf + len, buf_len - len,
+ "rssi_chain_in_db[%u] = ", j);
+ for (i = 0; i < ATH12K_HTT_RX_PDEV_STATS_NUM_BW_COUNTERS; i++)
+ len += scnprintf(buf + len,
+ buf_len - len,
+ " %u: %d,", i,
+ htt_stats_buf->rssi_chain_in_db[j][i]);
+ len--;
+ len += scnprintf(buf + len, buf_len - len, "\n");
+ }
+
+ for (j = 0; j < ATH12K_HTT_RX_PDEV_STATS_NUM_GI_COUNTERS; j++) {
+ len += scnprintf(buf + len, buf_len - len,
+ "rx_gi[%u] = ", j);
+ len += print_array_to_buf(buf, len, NULL,
+ htt_stats_buf->rx_gi[j],
+ ATH12K_HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS,
+ "\n");
+ }
+
+ for (j = 0; j < ATH12K_HTT_RX_PDEV_STATS_NUM_GI_COUNTERS; j++) {
+ len += scnprintf(buf + len, buf_len - len,
+ "ul_ofdma_rx_gi[%u] = ", j);
+ len += print_array_to_buf(buf, len, NULL,
+ htt_stats_buf->ul_ofdma_rx_gi[j],
+ ATH12K_HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS,
+ "\n");
+ }
+
+ for (j = 0; j < ATH12K_HTT_RX_PDEV_STATS_NUM_SPATIAL_STREAMS; j++) {
+ len += scnprintf(buf + len, buf_len - len,
+ "rx_ul_fd_rssi: nss[%u] = ", j);
+ for (i = 0; i < ATH12K_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--;
+ len += scnprintf(buf + len, buf_len - len, "\n");
+ }
+
+ for (j = 0; j < ATH12K_HTT_RX_PDEV_STATS_NUM_SPATIAL_STREAMS; j++) {
+ len += scnprintf(buf + len, buf_len - len,
+ "rx_per_chain_rssi_in_dbm[%u] =", j);
+ for (i = 0; i < ATH12K_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--;
+ len += scnprintf(buf + len, buf_len - len, "\n");
+ }
+
+ stats_req->buf_len = len;
+}
+
+static inline void
+ath12k_htt_print_rx_pdev_rate_ext_stats_tlv(const void *tag_buf, u16 tag_len,
+ struct debug_htt_stats_req *stats_req)
+{
+ const struct ath12k_htt_rx_pdev_rate_ext_stats_tlv *htt_stats_buf = tag_buf;
+ u8 *buf = stats_req->buf;
+ u32 len = stats_req->buf_len;
+ u32 buf_len = ATH12K_HTT_STATS_BUF_SIZE;
+ u8 j;
+
+ if (tag_len < sizeof(*htt_stats_buf))
+ return;
+
+ len += scnprintf(buf + len, buf_len - len, "HTT_RX_PDEV_RATE_EXT_STATS_TLV:\n");
+ len += scnprintf(buf + len, buf_len - len, "rssi_mgmt_in_dbm = %d\n",
+ le32_to_cpu(htt_stats_buf->rssi_mgmt_in_dbm));
+
+ len += print_array_to_buf(buf, len, "rx_stbc_ext",
+ htt_stats_buf->rx_stbc_ext,
+ ATH12K_HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS_EXT, "\n");
+ len += print_array_to_buf(buf, len, "ul_ofdma_rx_mcs_ext",
+ htt_stats_buf->ul_ofdma_rx_mcs_ext,
+ ATH12K_HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS_EXT, "\n");
+ len += print_array_to_buf(buf, len, "rx_11ax_su_txbf_mcs_ext",
+ htt_stats_buf->rx_11ax_su_txbf_mcs_ext,
+ ATH12K_HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS_EXT, "\n");
+ len += print_array_to_buf(buf, len, "rx_11ax_mu_txbf_mcs_ext",
+ htt_stats_buf->rx_11ax_mu_txbf_mcs_ext,
+ ATH12K_HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS_EXT, "\n");
+ len += print_array_to_buf(buf, len, "rx_11ax_dl_ofdma_mcs_ext",
+ htt_stats_buf->rx_11ax_dl_ofdma_mcs_ext,
+ ATH12K_HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS_EXT, "\n");
+ len += print_array_to_buf(buf, len, "rx_bw_ext",
+ htt_stats_buf->rx_bw_ext,
+ ATH12K_HTT_RX_PDEV_STATS_NUM_BW_EXT2_COUNTERS, "\n");
+ len += print_array_to_buf(buf, len, "rx_su_punctured_mode",
+ htt_stats_buf->rx_su_punctured_mode,
+ ATH12K_HTT_RX_PDEV_STATS_NUM_PUNCTURED_MODE_COUNTERS,
+ "\n");
+
+ len += print_array_to_buf(buf, len, "rx_mcs_ext",
+ htt_stats_buf->rx_mcs_ext,
+ ATH12K_HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS_EXT,
+ NULL);
+ for (j = 0; j < ATH12K_HTT_RX_PDEV_STATS_NUM_EXTRA2_MCS_COUNTERS; j++)
+ len += scnprintf(buf + len, buf_len - len, ", %u:%u",
+ j + ATH12K_HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS_EXT,
+ le32_to_cpu(htt_stats_buf->rx_mcs_ext_2[j]));
+ len += scnprintf(buf + len, buf_len - len, "\n");
+
+ for (j = 0; j < ATH12K_HTT_RX_PDEV_STATS_NUM_GI_COUNTERS; j++) {
+ len += scnprintf(buf + len, buf_len - len,
+ "rx_gi_ext[%u] = ", j);
+ len += print_array_to_buf(buf, len, NULL,
+ htt_stats_buf->rx_gi_ext[j],
+ ATH12K_HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS_EXT,
+ "\n");
+ }
+
+ for (j = 0; j < ATH12K_HTT_RX_PDEV_STATS_NUM_GI_COUNTERS; j++) {
+ len += scnprintf(buf + len, buf_len - len,
+ "ul_ofdma_rx_gi_ext[%u] = ", j);
+ len += print_array_to_buf(buf, len, NULL,
+ htt_stats_buf->ul_ofdma_rx_gi_ext[j],
+ ATH12K_HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS_EXT,
+ "\n");
+ }
+
+ stats_req->buf_len = len;
+}
+
static int ath12k_dbg_htt_ext_stats_parse(struct ath12k_base *ab,
u16 tag, u16 len, const void *tag_buf,
void *user_data)
@@ -3982,9 +5185,33 @@ static int ath12k_dbg_htt_ext_stats_parse(struct ath12k_base *ab,
case HTT_STATS_PDEV_CCA_COUNTERS_TAG:
ath12k_htt_print_pdev_stats_cca_counters_tlv(tag_buf, len, stats_req);
break;
+ case HTT_STATS_TX_SOUNDING_STATS_TAG:
+ ath12k_htt_print_tx_sounding_stats_tlv(tag_buf, len, stats_req);
+ break;
case HTT_STATS_PDEV_OBSS_PD_TAG:
ath12k_htt_print_pdev_obss_pd_stats_tlv(tag_buf, len, stats_req);
break;
+ case HTT_STATS_LATENCY_CTX_TAG:
+ ath12k_htt_print_latency_prof_ctx_tlv(tag_buf, len, stats_req);
+ break;
+ case HTT_STATS_LATENCY_CNT_TAG:
+ ath12k_htt_print_latency_prof_cnt(tag_buf, len, stats_req);
+ break;
+ case HTT_STATS_LATENCY_PROF_STATS_TAG:
+ ath12k_htt_print_latency_prof_stats_tlv(tag_buf, len, stats_req);
+ break;
+ case HTT_STATS_RX_PDEV_UL_TRIG_STATS_TAG:
+ ath12k_htt_print_ul_ofdma_trigger_stats(tag_buf, len, stats_req);
+ break;
+ case HTT_STATS_RX_PDEV_UL_OFDMA_USER_STATS_TAG:
+ ath12k_htt_print_ul_ofdma_user_stats(tag_buf, len, stats_req);
+ break;
+ case HTT_STATS_RX_PDEV_UL_MUMIMO_TRIG_STATS_TAG:
+ ath12k_htt_print_ul_mumimo_trig_stats(tag_buf, len, stats_req);
+ break;
+ case HTT_STATS_RX_FSE_STATS_TAG:
+ ath12k_htt_print_rx_fse_stats_tlv(tag_buf, len, stats_req);
+ break;
case HTT_STATS_PDEV_TX_RATE_TXBF_STATS_TAG:
ath12k_htt_print_pdev_tx_rate_txbf_stats_tlv(tag_buf, len, stats_req);
break;
@@ -4047,6 +5274,15 @@ static int ath12k_dbg_htt_ext_stats_parse(struct ath12k_base *ab,
ath12k_htt_print_pdev_mbssid_ctrl_frame_stats_tlv(tag_buf, len,
stats_req);
break;
+ case HTT_STATS_TX_PDEV_RATE_STATS_TAG:
+ ath12k_htt_print_tx_pdev_rate_stats_tlv(tag_buf, len, stats_req);
+ break;
+ case HTT_STATS_RX_PDEV_RATE_STATS_TAG:
+ ath12k_htt_print_rx_pdev_rate_stats_tlv(tag_buf, len, stats_req);
+ break;
+ case HTT_STATS_RX_PDEV_RATE_EXT_STATS_TAG:
+ ath12k_htt_print_rx_pdev_rate_ext_stats_tlv(tag_buf, len, stats_req);
+ break;
default:
break;
}
@@ -4141,6 +5377,9 @@ static ssize_t ath12k_write_htt_stats_type(struct file *file,
const int size = 32;
int num_args;
+ if (count > size)
+ return -EINVAL;
+
char *buf __free(kfree) = kzalloc(size, GFP_KERNEL);
if (!buf)
return -ENOMEM;
diff --git a/drivers/net/wireless/ath/ath12k/debugfs_htt_stats.h b/drivers/net/wireless/ath/ath12k/debugfs_htt_stats.h
index 4b59976fbc35..c2a02cf8a38b 100644
--- a/drivers/net/wireless/ath/ath12k/debugfs_htt_stats.h
+++ b/drivers/net/wireless/ath/ath12k/debugfs_htt_stats.h
@@ -1,7 +1,7 @@
/* SPDX-License-Identifier: BSD-3-Clause-Clear */
/*
* Copyright (c) 2018-2021 The Linux Foundation. All rights reserved.
- * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#ifndef DEBUG_HTT_STATS_H
@@ -123,30 +123,38 @@ struct ath12k_htt_extd_stats_msg {
/* htt_dbg_ext_stats_type */
enum ath12k_dbg_htt_ext_stats_type {
- ATH12K_DBG_HTT_EXT_STATS_RESET = 0,
- ATH12K_DBG_HTT_EXT_STATS_PDEV_TX = 1,
- ATH12K_DBG_HTT_EXT_STATS_PDEV_TX_SCHED = 4,
- ATH12K_DBG_HTT_EXT_STATS_PDEV_ERROR = 5,
- ATH12K_DBG_HTT_EXT_STATS_PDEV_TQM = 6,
- ATH12K_DBG_HTT_EXT_STATS_TX_DE_INFO = 8,
- ATH12K_DBG_HTT_EXT_STATS_TX_SELFGEN_INFO = 12,
- ATH12K_DBG_HTT_EXT_STATS_SRNG_INFO = 15,
- ATH12K_DBG_HTT_EXT_STATS_SFM_INFO = 16,
- ATH12K_DBG_HTT_EXT_STATS_PDEV_TX_MU = 17,
- ATH12K_DBG_HTT_EXT_STATS_PDEV_CCA_STATS = 19,
- ATH12K_DBG_HTT_EXT_STATS_PDEV_OBSS_PD_STATS = 23,
- ATH12K_DBG_HTT_EXT_STATS_PDEV_TX_RATE_TXBF = 31,
- ATH12K_DBG_HTT_EXT_STATS_TXBF_OFDMA = 32,
- ATH12K_DBG_HTT_EXT_STATS_DLPAGER_STATS = 36,
- ATH12K_DBG_HTT_EXT_PHY_COUNTERS_AND_PHY_STATS = 37,
- ATH12K_DBG_HTT_EXT_VDEVS_TXRX_STATS = 38,
- ATH12K_DBG_HTT_EXT_PDEV_PER_STATS = 40,
- ATH12K_DBG_HTT_EXT_AST_ENTRIES = 41,
- ATH12K_DBG_HTT_EXT_STATS_SOC_ERROR = 45,
- ATH12K_DBG_HTT_DBG_PDEV_PUNCTURE_STATS = 46,
- ATH12K_DBG_HTT_EXT_STATS_PDEV_SCHED_ALGO = 49,
- ATH12K_DBG_HTT_EXT_STATS_MANDATORY_MUOFDMA = 51,
- ATH12K_DGB_HTT_EXT_STATS_PDEV_MBSSID_CTRL_FRAME = 54,
+ ATH12K_DBG_HTT_EXT_STATS_RESET = 0,
+ ATH12K_DBG_HTT_EXT_STATS_PDEV_TX = 1,
+ ATH12K_DBG_HTT_EXT_STATS_PDEV_TX_SCHED = 4,
+ ATH12K_DBG_HTT_EXT_STATS_PDEV_ERROR = 5,
+ ATH12K_DBG_HTT_EXT_STATS_PDEV_TQM = 6,
+ ATH12K_DBG_HTT_EXT_STATS_TX_DE_INFO = 8,
+ ATH12K_DBG_HTT_EXT_STATS_PDEV_TX_RATE = 9,
+ ATH12K_DBG_HTT_EXT_STATS_PDEV_RX_RATE = 10,
+ ATH12K_DBG_HTT_EXT_STATS_TX_SELFGEN_INFO = 12,
+ ATH12K_DBG_HTT_EXT_STATS_SRNG_INFO = 15,
+ ATH12K_DBG_HTT_EXT_STATS_SFM_INFO = 16,
+ ATH12K_DBG_HTT_EXT_STATS_PDEV_TX_MU = 17,
+ ATH12K_DBG_HTT_EXT_STATS_PDEV_CCA_STATS = 19,
+ ATH12K_DBG_HTT_EXT_STATS_TX_SOUNDING_INFO = 22,
+ ATH12K_DBG_HTT_EXT_STATS_PDEV_OBSS_PD_STATS = 23,
+ ATH12K_DBG_HTT_EXT_STATS_LATENCY_PROF_STATS = 25,
+ ATH12K_DBG_HTT_EXT_STATS_PDEV_UL_TRIG_STATS = 26,
+ ATH12K_DBG_HTT_EXT_STATS_PDEV_UL_MUMIMO_TRIG_STATS = 27,
+ ATH12K_DBG_HTT_EXT_STATS_FSE_RX = 28,
+ ATH12K_DBG_HTT_EXT_STATS_PDEV_RX_RATE_EXT = 30,
+ ATH12K_DBG_HTT_EXT_STATS_PDEV_TX_RATE_TXBF = 31,
+ ATH12K_DBG_HTT_EXT_STATS_TXBF_OFDMA = 32,
+ ATH12K_DBG_HTT_EXT_STATS_DLPAGER_STATS = 36,
+ ATH12K_DBG_HTT_EXT_PHY_COUNTERS_AND_PHY_STATS = 37,
+ ATH12K_DBG_HTT_EXT_VDEVS_TXRX_STATS = 38,
+ ATH12K_DBG_HTT_EXT_PDEV_PER_STATS = 40,
+ ATH12K_DBG_HTT_EXT_AST_ENTRIES = 41,
+ ATH12K_DBG_HTT_EXT_STATS_SOC_ERROR = 45,
+ ATH12K_DBG_HTT_DBG_PDEV_PUNCTURE_STATS = 46,
+ ATH12K_DBG_HTT_EXT_STATS_PDEV_SCHED_ALGO = 49,
+ ATH12K_DBG_HTT_EXT_STATS_MANDATORY_MUOFDMA = 51,
+ ATH12K_DGB_HTT_EXT_STATS_PDEV_MBSSID_CTRL_FRAME = 54,
/* keep this last */
ATH12K_DBG_HTT_NUM_EXT_STATS,
@@ -173,6 +181,8 @@ enum ath12k_dbg_htt_tlv_tag {
HTT_STATS_TX_PDEV_MU_MIMO_STATS_TAG = 25,
HTT_STATS_SFM_CMN_TAG = 26,
HTT_STATS_SRING_STATS_TAG = 27,
+ HTT_STATS_TX_PDEV_RATE_STATS_TAG = 34,
+ HTT_STATS_RX_PDEV_RATE_STATS_TAG = 35,
HTT_STATS_TX_PDEV_SCHEDULER_TXQ_STATS_TAG = 36,
HTT_STATS_TX_SCHED_CMN_TAG = 37,
HTT_STATS_SCHED_TXQ_CMD_POSTED_TAG = 39,
@@ -195,12 +205,21 @@ enum ath12k_dbg_htt_tlv_tag {
HTT_STATS_PDEV_CCA_STAT_CUMULATIVE_TAG = 72,
HTT_STATS_PDEV_CCA_COUNTERS_TAG = 73,
HTT_STATS_TX_PDEV_MPDU_STATS_TAG = 74,
+ HTT_STATS_TX_SOUNDING_STATS_TAG = 80,
HTT_STATS_SCHED_TXQ_SCHED_ORDER_SU_TAG = 86,
HTT_STATS_SCHED_TXQ_SCHED_INELIGIBILITY_TAG = 87,
HTT_STATS_PDEV_OBSS_PD_TAG = 88,
HTT_STATS_HW_WAR_TAG = 89,
+ HTT_STATS_LATENCY_PROF_STATS_TAG = 91,
+ HTT_STATS_LATENCY_CTX_TAG = 92,
+ HTT_STATS_LATENCY_CNT_TAG = 93,
+ HTT_STATS_RX_PDEV_UL_TRIG_STATS_TAG = 94,
+ HTT_STATS_RX_PDEV_UL_OFDMA_USER_STATS_TAG = 95,
+ HTT_STATS_RX_PDEV_UL_MUMIMO_TRIG_STATS_TAG = 97,
+ HTT_STATS_RX_FSE_STATS_TAG = 98,
HTT_STATS_SCHED_TXQ_SUPERCYCLE_TRIGGER_TAG = 100,
HTT_STATS_PDEV_CTRL_PATH_TX_STATS_TAG = 102,
+ HTT_STATS_RX_PDEV_RATE_EXT_STATS_TAG = 103,
HTT_STATS_PDEV_TX_RATE_TXBF_STATS_TAG = 108,
HTT_STATS_TX_SELFGEN_AC_SCHED_STATUS_STATS_TAG = 111,
HTT_STATS_TX_SELFGEN_AX_SCHED_STATUS_STATS_TAG = 112,
@@ -387,6 +406,182 @@ struct ath12k_htt_tx_pdev_mu_ppdu_dist_stats_tlv {
__le32 num_ppdu_posted_per_burst[ATH12K_HTT_STATS_MU_PPDU_PER_BURST_WORDS];
} __packed;
+#define ATH12K_HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS 12
+#define ATH12K_HTT_TX_PDEV_STATS_NUM_GI_COUNTERS 4
+#define ATH12K_HTT_TX_PDEV_STATS_NUM_DCM_COUNTERS 5
+#define ATH12K_HTT_TX_PDEV_STATS_NUM_BW_COUNTERS 4
+#define ATH12K_HTT_TX_PDEV_STATS_NUM_SPATIAL_STREAMS 8
+#define ATH12K_HTT_TX_PDEV_STATS_NUM_PREAMBLE_TYPES 7
+#define ATH12K_HTT_TX_PDEV_STATS_NUM_LEGACY_CCK_STATS 4
+#define ATH12K_HTT_TX_PDEV_STATS_NUM_LEGACY_OFDM_STATS 8
+#define ATH12K_HTT_TX_PDEV_STATS_NUM_LTF 4
+#define ATH12K_HTT_TX_PDEV_STATS_NUM_EXTRA_MCS_COUNTERS 2
+#define ATH12K_HTT_TX_PDEV_STATS_NUM_EXTRA2_MCS_COUNTERS 2
+#define ATH12K_HTT_TX_PDEV_STATS_NUM_11AX_TRIGGER_TYPES 6
+
+struct ath12k_htt_tx_pdev_rate_stats_tlv {
+ __le32 mac_id_word;
+ __le32 tx_ldpc;
+ __le32 rts_cnt;
+ __le32 ack_rssi;
+ __le32 tx_mcs[ATH12K_HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS];
+ __le32 tx_su_mcs[ATH12K_HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS];
+ __le32 tx_mu_mcs[ATH12K_HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS];
+ __le32 tx_nss[ATH12K_HTT_TX_PDEV_STATS_NUM_SPATIAL_STREAMS];
+ __le32 tx_bw[ATH12K_HTT_TX_PDEV_STATS_NUM_BW_COUNTERS];
+ __le32 tx_stbc[ATH12K_HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS];
+ __le32 tx_pream[ATH12K_HTT_TX_PDEV_STATS_NUM_PREAMBLE_TYPES];
+ __le32 tx_gi[ATH12K_HTT_TX_PDEV_STATS_NUM_GI_COUNTERS]
+ [ATH12K_HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS];
+ __le32 tx_dcm[ATH12K_HTT_TX_PDEV_STATS_NUM_DCM_COUNTERS];
+ __le32 rts_success;
+ __le32 tx_legacy_cck_rate[ATH12K_HTT_TX_PDEV_STATS_NUM_LEGACY_CCK_STATS];
+ __le32 tx_legacy_ofdm_rate[ATH12K_HTT_TX_PDEV_STATS_NUM_LEGACY_OFDM_STATS];
+ __le32 ac_mu_mimo_tx_ldpc;
+ __le32 ax_mu_mimo_tx_ldpc;
+ __le32 ofdma_tx_ldpc;
+ __le32 tx_he_ltf[ATH12K_HTT_TX_PDEV_STATS_NUM_LTF];
+ __le32 ac_mu_mimo_tx_mcs[ATH12K_HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS];
+ __le32 ax_mu_mimo_tx_mcs[ATH12K_HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS];
+ __le32 ofdma_tx_mcs[ATH12K_HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS];
+ __le32 ac_mu_mimo_tx_nss[ATH12K_HTT_TX_PDEV_STATS_NUM_SPATIAL_STREAMS];
+ __le32 ax_mu_mimo_tx_nss[ATH12K_HTT_TX_PDEV_STATS_NUM_SPATIAL_STREAMS];
+ __le32 ofdma_tx_nss[ATH12K_HTT_TX_PDEV_STATS_NUM_SPATIAL_STREAMS];
+ __le32 ac_mu_mimo_tx_bw[ATH12K_HTT_TX_PDEV_STATS_NUM_BW_COUNTERS];
+ __le32 ax_mu_mimo_tx_bw[ATH12K_HTT_TX_PDEV_STATS_NUM_BW_COUNTERS];
+ __le32 ofdma_tx_bw[ATH12K_HTT_TX_PDEV_STATS_NUM_BW_COUNTERS];
+ __le32 ac_mu_mimo_tx_gi[ATH12K_HTT_TX_PDEV_STATS_NUM_GI_COUNTERS]
+ [ATH12K_HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS];
+ __le32 ax_mimo_tx_gi[ATH12K_HTT_TX_PDEV_STATS_NUM_GI_COUNTERS]
+ [ATH12K_HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS];
+ __le32 ofdma_tx_gi[ATH12K_HTT_TX_PDEV_STATS_NUM_GI_COUNTERS]
+ [ATH12K_HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS];
+ __le32 trigger_type_11ax[ATH12K_HTT_TX_PDEV_STATS_NUM_11AX_TRIGGER_TYPES];
+ __le32 tx_11ax_su_ext;
+ __le32 tx_mcs_ext[ATH12K_HTT_TX_PDEV_STATS_NUM_EXTRA_MCS_COUNTERS];
+ __le32 tx_stbc_ext[ATH12K_HTT_TX_PDEV_STATS_NUM_EXTRA_MCS_COUNTERS];
+ __le32 tx_gi_ext[ATH12K_HTT_TX_PDEV_STATS_NUM_GI_COUNTERS]
+ [ATH12K_HTT_TX_PDEV_STATS_NUM_EXTRA_MCS_COUNTERS];
+ __le32 ax_mu_mimo_tx_mcs_ext[ATH12K_HTT_TX_PDEV_STATS_NUM_EXTRA_MCS_COUNTERS];
+ __le32 ofdma_tx_mcs_ext[ATH12K_HTT_TX_PDEV_STATS_NUM_EXTRA_MCS_COUNTERS];
+ __le32 ax_tx_gi_ext[ATH12K_HTT_TX_PDEV_STATS_NUM_GI_COUNTERS]
+ [ATH12K_HTT_TX_PDEV_STATS_NUM_EXTRA_MCS_COUNTERS];
+ __le32 ofd_tx_gi_ext[ATH12K_HTT_TX_PDEV_STATS_NUM_GI_COUNTERS]
+ [ATH12K_HTT_TX_PDEV_STATS_NUM_EXTRA_MCS_COUNTERS];
+ __le32 tx_mcs_ext_2[ATH12K_HTT_TX_PDEV_STATS_NUM_EXTRA2_MCS_COUNTERS];
+ __le32 tx_bw_320mhz;
+};
+
+#define ATH12K_HTT_RX_PDEV_STATS_NUM_LEGACY_CCK_STATS 4
+#define ATH12K_HTT_RX_PDEV_STATS_NUM_LEGACY_OFDM_STATS 8
+#define ATH12K_HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS 12
+#define ATH12K_HTT_RX_PDEV_STATS_NUM_GI_COUNTERS 4
+#define ATH12K_HTT_RX_PDEV_STATS_NUM_DCM_COUNTERS 5
+#define ATH12K_HTT_RX_PDEV_STATS_NUM_BW_COUNTERS 4
+#define ATH12K_HTT_RX_PDEV_STATS_NUM_SPATIAL_STREAMS 8
+#define ATH12K_HTT_RX_PDEV_STATS_NUM_PREAMBLE_TYPES 7
+#define ATH12K_HTT_RX_PDEV_MAX_OFDMA_NUM_USER 8
+#define ATH12K_HTT_RX_PDEV_STATS_RXEVM_MAX_PILOTS_NSS 16
+#define ATH12K_HTT_RX_PDEV_STATS_NUM_RU_SIZE_COUNTERS 6
+#define ATH12K_HTT_RX_PDEV_MAX_ULMUMIMO_NUM_USER 8
+#define ATH12K_HTT_RX_PDEV_STATS_NUM_EXTRA_MCS_COUNTERS 2
+
+struct ath12k_htt_rx_pdev_rate_stats_tlv {
+ __le32 mac_id_word;
+ __le32 nsts;
+ __le32 rx_ldpc;
+ __le32 rts_cnt;
+ __le32 rssi_mgmt;
+ __le32 rssi_data;
+ __le32 rssi_comb;
+ __le32 rx_mcs[ATH12K_HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS];
+ __le32 rx_nss[ATH12K_HTT_RX_PDEV_STATS_NUM_SPATIAL_STREAMS];
+ __le32 rx_dcm[ATH12K_HTT_RX_PDEV_STATS_NUM_DCM_COUNTERS];
+ __le32 rx_stbc[ATH12K_HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS];
+ __le32 rx_bw[ATH12K_HTT_RX_PDEV_STATS_NUM_BW_COUNTERS];
+ __le32 rx_pream[ATH12K_HTT_RX_PDEV_STATS_NUM_PREAMBLE_TYPES];
+ u8 rssi_chain_in_db[ATH12K_HTT_RX_PDEV_STATS_NUM_SPATIAL_STREAMS]
+ [ATH12K_HTT_RX_PDEV_STATS_NUM_BW_COUNTERS];
+ __le32 rx_gi[ATH12K_HTT_RX_PDEV_STATS_NUM_GI_COUNTERS]
+ [ATH12K_HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS];
+ __le32 rssi_in_dbm;
+ __le32 rx_11ax_su_ext;
+ __le32 rx_11ac_mumimo;
+ __le32 rx_11ax_mumimo;
+ __le32 rx_11ax_ofdma;
+ __le32 txbf;
+ __le32 rx_legacy_cck_rate[ATH12K_HTT_RX_PDEV_STATS_NUM_LEGACY_CCK_STATS];
+ __le32 rx_legacy_ofdm_rate[ATH12K_HTT_RX_PDEV_STATS_NUM_LEGACY_OFDM_STATS];
+ __le32 rx_active_dur_us_low;
+ __le32 rx_active_dur_us_high;
+ __le32 rx_11ax_ul_ofdma;
+ __le32 ul_ofdma_rx_mcs[ATH12K_HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS];
+ __le32 ul_ofdma_rx_gi[ATH12K_HTT_TX_PDEV_STATS_NUM_GI_COUNTERS]
+ [ATH12K_HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS];
+ __le32 ul_ofdma_rx_nss[ATH12K_HTT_TX_PDEV_STATS_NUM_SPATIAL_STREAMS];
+ __le32 ul_ofdma_rx_bw[ATH12K_HTT_TX_PDEV_STATS_NUM_BW_COUNTERS];
+ __le32 ul_ofdma_rx_stbc;
+ __le32 ul_ofdma_rx_ldpc;
+ __le32 rx_ulofdma_non_data_ppdu[ATH12K_HTT_RX_PDEV_MAX_OFDMA_NUM_USER];
+ __le32 rx_ulofdma_data_ppdu[ATH12K_HTT_RX_PDEV_MAX_OFDMA_NUM_USER];
+ __le32 rx_ulofdma_mpdu_ok[ATH12K_HTT_RX_PDEV_MAX_OFDMA_NUM_USER];
+ __le32 rx_ulofdma_mpdu_fail[ATH12K_HTT_RX_PDEV_MAX_OFDMA_NUM_USER];
+ __le32 nss_count;
+ __le32 pilot_count;
+ __le32 rx_pil_evm_db[ATH12K_HTT_RX_PDEV_STATS_NUM_SPATIAL_STREAMS]
+ [ATH12K_HTT_RX_PDEV_STATS_RXEVM_MAX_PILOTS_NSS];
+ __le32 rx_pilot_evm_db_mean[ATH12K_HTT_RX_PDEV_STATS_NUM_SPATIAL_STREAMS];
+ s8 rx_ul_fd_rssi[ATH12K_HTT_RX_PDEV_STATS_NUM_SPATIAL_STREAMS]
+ [ATH12K_HTT_RX_PDEV_MAX_OFDMA_NUM_USER];
+ __le32 per_chain_rssi_pkt_type;
+ s8 rx_per_chain_rssi_in_dbm[ATH12K_HTT_RX_PDEV_STATS_NUM_SPATIAL_STREAMS]
+ [ATH12K_HTT_RX_PDEV_STATS_NUM_BW_COUNTERS];
+ __le32 rx_su_ndpa;
+ __le32 rx_11ax_su_txbf_mcs[ATH12K_HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS];
+ __le32 rx_mu_ndpa;
+ __le32 rx_11ax_mu_txbf_mcs[ATH12K_HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS];
+ __le32 rx_br_poll;
+ __le32 rx_11ax_dl_ofdma_mcs[ATH12K_HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS];
+ __le32 rx_11ax_dl_ofdma_ru[ATH12K_HTT_RX_PDEV_STATS_NUM_RU_SIZE_COUNTERS];
+ __le32 rx_ulmumimo_non_data_ppdu[ATH12K_HTT_RX_PDEV_MAX_ULMUMIMO_NUM_USER];
+ __le32 rx_ulmumimo_data_ppdu[ATH12K_HTT_RX_PDEV_MAX_ULMUMIMO_NUM_USER];
+ __le32 rx_ulmumimo_mpdu_ok[ATH12K_HTT_RX_PDEV_MAX_ULMUMIMO_NUM_USER];
+ __le32 rx_ulmumimo_mpdu_fail[ATH12K_HTT_RX_PDEV_MAX_ULMUMIMO_NUM_USER];
+ __le32 rx_ulofdma_non_data_nusers[ATH12K_HTT_RX_PDEV_MAX_OFDMA_NUM_USER];
+ __le32 rx_ulofdma_data_nusers[ATH12K_HTT_RX_PDEV_MAX_OFDMA_NUM_USER];
+ __le32 rx_mcs_ext[ATH12K_HTT_RX_PDEV_STATS_NUM_EXTRA_MCS_COUNTERS];
+};
+
+#define ATH12K_HTT_RX_PDEV_STATS_NUM_BW_EXT_COUNTERS 4
+#define ATH12K_HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS_EXT 14
+#define ATH12K_HTT_RX_PDEV_STATS_NUM_EXTRA2_MCS_COUNTERS 2
+#define ATH12K_HTT_RX_PDEV_STATS_NUM_BW_EXT2_COUNTERS 5
+#define ATH12K_HTT_RX_PDEV_STATS_NUM_PUNCTURED_MODE_COUNTERS 5
+
+struct ath12k_htt_rx_pdev_rate_ext_stats_tlv {
+ u8 rssi_chain_ext[ATH12K_HTT_RX_PDEV_STATS_NUM_SPATIAL_STREAMS]
+ [ATH12K_HTT_RX_PDEV_STATS_NUM_BW_EXT_COUNTERS];
+ s8 rx_per_chain_rssi_ext_in_dbm[ATH12K_HTT_RX_PDEV_STATS_NUM_SPATIAL_STREAMS]
+ [ATH12K_HTT_RX_PDEV_STATS_NUM_BW_EXT_COUNTERS];
+ __le32 rssi_mcast_in_dbm;
+ __le32 rssi_mgmt_in_dbm;
+ __le32 rx_mcs_ext[ATH12K_HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS_EXT];
+ __le32 rx_stbc_ext[ATH12K_HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS_EXT];
+ __le32 rx_gi_ext[ATH12K_HTT_RX_PDEV_STATS_NUM_GI_COUNTERS]
+ [ATH12K_HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS_EXT];
+ __le32 ul_ofdma_rx_mcs_ext[ATH12K_HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS_EXT];
+ __le32 ul_ofdma_rx_gi_ext[ATH12K_HTT_TX_PDEV_STATS_NUM_GI_COUNTERS]
+ [ATH12K_HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS_EXT];
+ __le32 rx_11ax_su_txbf_mcs_ext[ATH12K_HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS_EXT];
+ __le32 rx_11ax_mu_txbf_mcs_ext[ATH12K_HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS_EXT];
+ __le32 rx_11ax_dl_ofdma_mcs_ext[ATH12K_HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS_EXT];
+ __le32 rx_mcs_ext_2[ATH12K_HTT_RX_PDEV_STATS_NUM_EXTRA2_MCS_COUNTERS];
+ __le32 rx_bw_ext[ATH12K_HTT_RX_PDEV_STATS_NUM_BW_EXT2_COUNTERS];
+ __le32 rx_gi_ext_2[ATH12K_HTT_RX_PDEV_STATS_NUM_GI_COUNTERS]
+ [ATH12K_HTT_RX_PDEV_STATS_NUM_EXTRA2_MCS_COUNTERS];
+ __le32 rx_su_punctured_mode[ATH12K_HTT_RX_PDEV_STATS_NUM_PUNCTURED_MODE_COUNTERS];
+};
+
#define ATH12K_HTT_TX_PDEV_STATS_SCHED_PER_TXQ_MAC_ID GENMASK(7, 0)
#define ATH12K_HTT_TX_PDEV_STATS_SCHED_PER_TXQ_ID GENMASK(15, 8)
@@ -1058,6 +1253,82 @@ struct ath12k_htt_pdev_cca_stats_hist_v1_tlv {
__le32 collection_interval;
} __packed;
+#define ATH12K_HTT_TX_CV_CORR_MAX_NUM_COLUMNS 8
+#define ATH12K_HTT_TX_NUM_AC_MUMIMO_USER_STATS 4
+#define ATH12K_HTT_TX_NUM_AX_MUMIMO_USER_STATS 8
+#define ATH12K_HTT_TX_NUM_BE_MUMIMO_USER_STATS 8
+#define ATH12K_HTT_TX_PDEV_STATS_NUM_BW_COUNTERS 4
+#define ATH12K_HTT_TX_NUM_MCS_CNTRS 12
+#define ATH12K_HTT_TX_NUM_EXTRA_MCS_CNTRS 2
+
+#define ATH12K_HTT_TX_NUM_OF_SOUNDING_STATS_WORDS \
+ (ATH12K_HTT_TX_PDEV_STATS_NUM_BW_COUNTERS * \
+ ATH12K_HTT_TX_NUM_AX_MUMIMO_USER_STATS)
+
+enum ath12k_htt_txbf_sound_steer_modes {
+ ATH12K_HTT_IMPL_STEER_STATS = 0,
+ ATH12K_HTT_EXPL_SUSIFS_STEER_STATS = 1,
+ ATH12K_HTT_EXPL_SURBO_STEER_STATS = 2,
+ ATH12K_HTT_EXPL_MUSIFS_STEER_STATS = 3,
+ ATH12K_HTT_EXPL_MURBO_STEER_STATS = 4,
+ ATH12K_HTT_TXBF_MAX_NUM_OF_MODES = 5
+};
+
+enum ath12k_htt_stats_sounding_tx_mode {
+ ATH12K_HTT_TX_AC_SOUNDING_MODE = 0,
+ ATH12K_HTT_TX_AX_SOUNDING_MODE = 1,
+ ATH12K_HTT_TX_BE_SOUNDING_MODE = 2,
+ ATH12K_HTT_TX_CMN_SOUNDING_MODE = 3,
+};
+
+struct ath12k_htt_tx_sounding_stats_tlv {
+ __le32 tx_sounding_mode;
+ __le32 cbf_20[ATH12K_HTT_TXBF_MAX_NUM_OF_MODES];
+ __le32 cbf_40[ATH12K_HTT_TXBF_MAX_NUM_OF_MODES];
+ __le32 cbf_80[ATH12K_HTT_TXBF_MAX_NUM_OF_MODES];
+ __le32 cbf_160[ATH12K_HTT_TXBF_MAX_NUM_OF_MODES];
+ __le32 sounding[ATH12K_HTT_TX_NUM_OF_SOUNDING_STATS_WORDS];
+ __le32 cv_nc_mismatch_err;
+ __le32 cv_fcs_err;
+ __le32 cv_frag_idx_mismatch;
+ __le32 cv_invalid_peer_id;
+ __le32 cv_no_txbf_setup;
+ __le32 cv_expiry_in_update;
+ __le32 cv_pkt_bw_exceed;
+ __le32 cv_dma_not_done_err;
+ __le32 cv_update_failed;
+ __le32 cv_total_query;
+ __le32 cv_total_pattern_query;
+ __le32 cv_total_bw_query;
+ __le32 cv_invalid_bw_coding;
+ __le32 cv_forced_sounding;
+ __le32 cv_standalone_sounding;
+ __le32 cv_nc_mismatch;
+ __le32 cv_fb_type_mismatch;
+ __le32 cv_ofdma_bw_mismatch;
+ __le32 cv_bw_mismatch;
+ __le32 cv_pattern_mismatch;
+ __le32 cv_preamble_mismatch;
+ __le32 cv_nr_mismatch;
+ __le32 cv_in_use_cnt_exceeded;
+ __le32 cv_found;
+ __le32 cv_not_found;
+ __le32 sounding_320[ATH12K_HTT_TX_NUM_BE_MUMIMO_USER_STATS];
+ __le32 cbf_320[ATH12K_HTT_TXBF_MAX_NUM_OF_MODES];
+ __le32 cv_ntbr_sounding;
+ __le32 cv_found_upload_in_progress;
+ __le32 cv_expired_during_query;
+ __le32 cv_dma_timeout_error;
+ __le32 cv_buf_ibf_uploads;
+ __le32 cv_buf_ebf_uploads;
+ __le32 cv_buf_received;
+ __le32 cv_buf_fed_back;
+ __le32 cv_total_query_ibf;
+ __le32 cv_found_ibf;
+ __le32 cv_not_found_ibf;
+ __le32 cv_expired_during_query_ibf;
+} __packed;
+
struct ath12k_htt_pdev_obss_pd_stats_tlv {
__le32 num_obss_tx_ppdu_success;
__le32 num_obss_tx_ppdu_failure;
@@ -1080,6 +1351,127 @@ struct ath12k_htt_pdev_obss_pd_stats_tlv {
__le32 num_sr_ppdu_abort_flush_cnt;
} __packed;
+#define ATH12K_HTT_STATS_MAX_PROF_STATS_NAME_LEN 32
+#define ATH12K_HTT_LATENCY_PROFILE_NUM_MAX_HIST 3
+#define ATH12K_HTT_INTERRUPTS_LATENCY_PROFILE_MAX_HIST 3
+
+struct ath12k_htt_latency_prof_stats_tlv {
+ __le32 print_header;
+ s8 latency_prof_name[ATH12K_HTT_STATS_MAX_PROF_STATS_NAME_LEN];
+ __le32 cnt;
+ __le32 min;
+ __le32 max;
+ __le32 last;
+ __le32 tot;
+ __le32 avg;
+ __le32 hist_intvl;
+ __le32 hist[ATH12K_HTT_LATENCY_PROFILE_NUM_MAX_HIST];
+} __packed;
+
+struct ath12k_htt_latency_prof_ctx_tlv {
+ __le32 duration;
+ __le32 tx_msdu_cnt;
+ __le32 tx_mpdu_cnt;
+ __le32 tx_ppdu_cnt;
+ __le32 rx_msdu_cnt;
+ __le32 rx_mpdu_cnt;
+} __packed;
+
+struct ath12k_htt_latency_prof_cnt_tlv {
+ __le32 prof_enable_cnt;
+} __packed;
+
+#define ATH12K_HTT_RX_NUM_MCS_CNTRS 12
+#define ATH12K_HTT_RX_NUM_GI_CNTRS 4
+#define ATH12K_HTT_RX_NUM_SPATIAL_STREAMS 8
+#define ATH12K_HTT_RX_NUM_BW_CNTRS 4
+#define ATH12K_HTT_RX_NUM_RU_SIZE_CNTRS 6
+#define ATH12K_HTT_RX_NUM_RU_SIZE_160MHZ_CNTRS 7
+#define ATH12K_HTT_RX_UL_MAX_UPLINK_RSSI_TRACK 5
+#define ATH12K_HTT_RX_NUM_REDUCED_CHAN_TYPES 2
+#define ATH12K_HTT_RX_NUM_EXTRA_MCS_CNTRS 2
+
+enum ATH12K_HTT_TX_RX_PDEV_STATS_AX_RU_SIZE {
+ ATH12K_HTT_TX_RX_PDEV_STATS_AX_RU_SIZE_26,
+ ATH12K_HTT_TX_RX_PDEV_STATS_AX_RU_SIZE_52,
+ ATH12K_HTT_TX_RX_PDEV_STATS_AX_RU_SIZE_106,
+ ATH12K_HTT_TX_RX_PDEV_STATS_AX_RU_SIZE_242,
+ ATH12K_HTT_TX_RX_PDEV_STATS_AX_RU_SIZE_484,
+ ATH12K_HTT_TX_RX_PDEV_STATS_AX_RU_SIZE_996,
+ ATH12K_HTT_TX_RX_PDEV_STATS_AX_RU_SIZE_996x2,
+ ATH12K_HTT_TX_RX_PDEV_STATS_NUM_AX_RU_SIZE_CNTRS,
+};
+
+struct ath12k_htt_rx_pdev_ul_ofdma_user_stats_tlv {
+ __le32 user_index;
+ __le32 rx_ulofdma_non_data_ppdu;
+ __le32 rx_ulofdma_data_ppdu;
+ __le32 rx_ulofdma_mpdu_ok;
+ __le32 rx_ulofdma_mpdu_fail;
+ __le32 rx_ulofdma_non_data_nusers;
+ __le32 rx_ulofdma_data_nusers;
+} __packed;
+
+struct ath12k_htt_rx_pdev_ul_trigger_stats_tlv {
+ __le32 mac_id__word;
+ __le32 rx_11ax_ul_ofdma;
+ __le32 ul_ofdma_rx_mcs[ATH12K_HTT_RX_NUM_MCS_CNTRS];
+ __le32 ul_ofdma_rx_gi[ATH12K_HTT_RX_NUM_GI_CNTRS][ATH12K_HTT_RX_NUM_MCS_CNTRS];
+ __le32 ul_ofdma_rx_nss[ATH12K_HTT_RX_NUM_SPATIAL_STREAMS];
+ __le32 ul_ofdma_rx_bw[ATH12K_HTT_RX_NUM_BW_CNTRS];
+ __le32 ul_ofdma_rx_stbc;
+ __le32 ul_ofdma_rx_ldpc;
+ __le32 data_ru_size_ppdu[ATH12K_HTT_RX_NUM_RU_SIZE_160MHZ_CNTRS];
+ __le32 non_data_ru_size_ppdu[ATH12K_HTT_RX_NUM_RU_SIZE_160MHZ_CNTRS];
+ __le32 uplink_sta_aid[ATH12K_HTT_RX_UL_MAX_UPLINK_RSSI_TRACK];
+ __le32 uplink_sta_target_rssi[ATH12K_HTT_RX_UL_MAX_UPLINK_RSSI_TRACK];
+ __le32 uplink_sta_fd_rssi[ATH12K_HTT_RX_UL_MAX_UPLINK_RSSI_TRACK];
+ __le32 uplink_sta_power_headroom[ATH12K_HTT_RX_UL_MAX_UPLINK_RSSI_TRACK];
+ __le32 red_bw[ATH12K_HTT_RX_NUM_REDUCED_CHAN_TYPES][ATH12K_HTT_RX_NUM_BW_CNTRS];
+ __le32 ul_ofdma_bsc_trig_rx_qos_null_only;
+} __packed;
+
+#define ATH12K_HTT_TX_UL_MUMIMO_USER_STATS 8
+
+struct ath12k_htt_rx_ul_mumimo_trig_stats_tlv {
+ __le32 mac_id__word;
+ __le32 rx_11ax_ul_mumimo;
+ __le32 ul_mumimo_rx_mcs[ATH12K_HTT_RX_NUM_MCS_CNTRS];
+ __le32 ul_rx_gi[ATH12K_HTT_RX_NUM_GI_CNTRS][ATH12K_HTT_RX_NUM_MCS_CNTRS];
+ __le32 ul_mumimo_rx_nss[ATH12K_HTT_RX_NUM_SPATIAL_STREAMS];
+ __le32 ul_mumimo_rx_bw[ATH12K_HTT_RX_NUM_BW_CNTRS];
+ __le32 ul_mumimo_rx_stbc;
+ __le32 ul_mumimo_rx_ldpc;
+ __le32 ul_mumimo_rx_mcs_ext[ATH12K_HTT_RX_NUM_EXTRA_MCS_CNTRS];
+ __le32 ul_gi_ext[ATH12K_HTT_RX_NUM_GI_CNTRS][ATH12K_HTT_RX_NUM_EXTRA_MCS_CNTRS];
+ s8 ul_rssi[ATH12K_HTT_RX_NUM_SPATIAL_STREAMS][ATH12K_HTT_RX_NUM_BW_CNTRS];
+ s8 tgt_rssi[ATH12K_HTT_TX_UL_MUMIMO_USER_STATS][ATH12K_HTT_RX_NUM_BW_CNTRS];
+ s8 fd[ATH12K_HTT_TX_UL_MUMIMO_USER_STATS][ATH12K_HTT_RX_NUM_SPATIAL_STREAMS];
+ s8 db[ATH12K_HTT_TX_UL_MUMIMO_USER_STATS][ATH12K_HTT_RX_NUM_SPATIAL_STREAMS];
+ __le32 red_bw[ATH12K_HTT_RX_NUM_REDUCED_CHAN_TYPES][ATH12K_HTT_RX_NUM_BW_CNTRS];
+ __le32 mumimo_bsc_trig_rx_qos_null_only;
+} __packed;
+
+#define ATH12K_HTT_RX_NUM_MAX_PEAK_OCCUPANCY_INDEX 10
+#define ATH12K_HTT_RX_NUM_MAX_CURR_OCCUPANCY_INDEX 10
+#define ATH12K_HTT_RX_NUM_SQUARE_INDEX 6
+#define ATH12K_HTT_RX_NUM_MAX_PEAK_SEARCH_INDEX 4
+#define ATH12K_HTT_RX_NUM_MAX_PENDING_SEARCH_INDEX 4
+
+struct ath12k_htt_rx_fse_stats_tlv {
+ __le32 fse_enable_cnt;
+ __le32 fse_disable_cnt;
+ __le32 fse_cache_invalidate_entry_cnt;
+ __le32 fse_full_cache_invalidate_cnt;
+ __le32 fse_num_cache_hits_cnt;
+ __le32 fse_num_searches_cnt;
+ __le32 fse_cache_occupancy_peak_cnt[ATH12K_HTT_RX_NUM_MAX_PEAK_OCCUPANCY_INDEX];
+ __le32 fse_cache_occupancy_curr_cnt[ATH12K_HTT_RX_NUM_MAX_CURR_OCCUPANCY_INDEX];
+ __le32 fse_search_stat_square_cnt[ATH12K_HTT_RX_NUM_SQUARE_INDEX];
+ __le32 fse_search_stat_peak_cnt[ATH12K_HTT_RX_NUM_MAX_PEAK_SEARCH_INDEX];
+ __le32 fse_search_stat_pending_cnt[ATH12K_HTT_RX_NUM_MAX_PENDING_SEARCH_INDEX];
+} __packed;
+
#define ATH12K_HTT_TX_BF_RATE_STATS_NUM_MCS_COUNTERS 14
#define ATH12K_HTT_TX_PDEV_STATS_NUM_LEGACY_OFDM_STATS 8
#define ATH12K_HTT_TX_PDEV_STATS_NUM_SPATIAL_STREAMS 8
@@ -1417,17 +1809,6 @@ enum ATH12K_HTT_RC_MODE {
ATH12K_HTT_RC_MODE_2D_COUNT
};
-enum ATH12K_HTT_TX_RX_PDEV_STATS_AX_RU_SIZE {
- ATH12K_HTT_TX_RX_PDEV_STATS_AX_RU_SIZE_26,
- ATH12K_HTT_TX_RX_PDEV_STATS_AX_RU_SIZE_52,
- ATH12K_HTT_TX_RX_PDEV_STATS_AX_RU_SIZE_106,
- ATH12K_HTT_TX_RX_PDEV_STATS_AX_RU_SIZE_242,
- ATH12K_HTT_TX_RX_PDEV_STATS_AX_RU_SIZE_484,
- ATH12K_HTT_TX_RX_PDEV_STATS_AX_RU_SIZE_996,
- ATH12K_HTT_TX_RX_PDEV_STATS_AX_RU_SIZE_996x2,
- ATH12K_HTT_TX_RX_PDEV_STATS_NUM_AX_RU_SIZE_CNTRS
-};
-
enum ath12k_htt_stats_rc_mode {
ATH12K_HTT_STATS_RC_MODE_DLSU = 0,
ATH12K_HTT_STATS_RC_MODE_DLMUMIMO = 1,
diff --git a/drivers/net/wireless/ath/ath12k/debugfs_sta.c b/drivers/net/wireless/ath/ath12k/debugfs_sta.c
new file mode 100644
index 000000000000..5bd2bf4c9dac
--- /dev/null
+++ b/drivers/net/wireless/ath/ath12k/debugfs_sta.c
@@ -0,0 +1,337 @@
+// SPDX-License-Identifier: BSD-3-Clause-Clear
+/*
+ * Copyright (c) 2024-2025 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#include <linux/vmalloc.h>
+
+#include "debugfs_sta.h"
+#include "core.h"
+#include "peer.h"
+#include "debug.h"
+#include "debugfs_htt_stats.h"
+#include "debugfs.h"
+
+static
+u32 ath12k_dbg_sta_dump_rate_stats(u8 *buf, u32 offset, const int size,
+ bool he_rates_avail,
+ const struct ath12k_rx_peer_rate_stats *stats)
+{
+ static const char *legacy_rate_str[HAL_RX_MAX_NUM_LEGACY_RATES] = {
+ "1 Mbps", "2 Mbps", "5.5 Mbps", "6 Mbps",
+ "9 Mbps", "11 Mbps", "12 Mbps", "18 Mbps",
+ "24 Mbps", "36 Mbps", "48 Mbps", "54 Mbps"};
+ u8 max_bw = HAL_RX_BW_MAX, max_gi = HAL_RX_GI_MAX, max_mcs = HAL_RX_MAX_NSS;
+ int mcs = 0, bw = 0, nss = 0, gi = 0, bw_num = 0;
+ u32 i, len = offset, max = max_bw * max_gi * max_mcs;
+ bool found;
+
+ len += scnprintf(buf + len, size - len, "\nEHT stats:\n");
+ for (i = 0; i <= HAL_RX_MAX_MCS_BE; i++)
+ len += scnprintf(buf + len, size - len,
+ "MCS %d: %llu%s", i, stats->be_mcs_count[i],
+ (i + 1) % 8 ? "\t" : "\n");
+
+ len += scnprintf(buf + len, size - len, "\nHE stats:\n");
+ for (i = 0; i <= HAL_RX_MAX_MCS_HE; i++)
+ len += scnprintf(buf + len, size - len,
+ "MCS %d: %llu%s", i, stats->he_mcs_count[i],
+ (i + 1) % 6 ? "\t" : "\n");
+
+ len += scnprintf(buf + len, size - len, "\nVHT stats:\n");
+ for (i = 0; i <= HAL_RX_MAX_MCS_VHT; i++)
+ len += scnprintf(buf + len, size - len,
+ "MCS %d: %llu%s", i, stats->vht_mcs_count[i],
+ (i + 1) % 5 ? "\t" : "\n");
+
+ len += scnprintf(buf + len, size - len, "\nHT stats:\n");
+ for (i = 0; i <= HAL_RX_MAX_MCS_HT; i++)
+ len += scnprintf(buf + len, size - len,
+ "MCS %d: %llu%s", i, stats->ht_mcs_count[i],
+ (i + 1) % 8 ? "\t" : "\n");
+
+ len += scnprintf(buf + len, size - len, "\nLegacy stats:\n");
+ for (i = 0; i < HAL_RX_MAX_NUM_LEGACY_RATES; i++)
+ len += scnprintf(buf + len, size - len,
+ "%s: %llu%s", legacy_rate_str[i],
+ stats->legacy_count[i],
+ (i + 1) % 4 ? "\t" : "\n");
+
+ len += scnprintf(buf + len, size - len, "\nNSS stats:\n");
+ for (i = 0; i < HAL_RX_MAX_NSS; i++)
+ len += scnprintf(buf + len, size - len,
+ "%dx%d: %llu ", i + 1, i + 1,
+ stats->nss_count[i]);
+
+ len += scnprintf(buf + len, size - len,
+ "\n\nGI: 0.8 us %llu 0.4 us %llu 1.6 us %llu 3.2 us %llu\n",
+ stats->gi_count[0],
+ stats->gi_count[1],
+ stats->gi_count[2],
+ stats->gi_count[3]);
+
+ len += scnprintf(buf + len, size - len,
+ "BW: 20 MHz %llu 40 MHz %llu 80 MHz %llu 160 MHz %llu 320 MHz %llu\n",
+ stats->bw_count[0],
+ stats->bw_count[1],
+ stats->bw_count[2],
+ stats->bw_count[3],
+ stats->bw_count[4]);
+
+ for (i = 0; i < max; i++) {
+ found = false;
+
+ for (mcs = 0; mcs <= HAL_RX_MAX_MCS_HT; mcs++) {
+ if (stats->rx_rate[bw][gi][nss][mcs]) {
+ found = true;
+ break;
+ }
+ }
+
+ if (!found)
+ goto skip_report;
+
+ switch (bw) {
+ case HAL_RX_BW_20MHZ:
+ bw_num = 20;
+ break;
+ case HAL_RX_BW_40MHZ:
+ bw_num = 40;
+ break;
+ case HAL_RX_BW_80MHZ:
+ bw_num = 80;
+ break;
+ case HAL_RX_BW_160MHZ:
+ bw_num = 160;
+ break;
+ case HAL_RX_BW_320MHZ:
+ bw_num = 320;
+ break;
+ }
+
+ len += scnprintf(buf + len, size - len, "\n%d Mhz gi %d us %dx%d : ",
+ bw_num, gi, nss + 1, nss + 1);
+
+ for (mcs = 0; mcs <= HAL_RX_MAX_MCS_HT; mcs++) {
+ if (stats->rx_rate[bw][gi][nss][mcs])
+ len += scnprintf(buf + len, size - len,
+ " %d:%llu", mcs,
+ stats->rx_rate[bw][gi][nss][mcs]);
+ }
+
+skip_report:
+ if (nss++ >= max_mcs - 1) {
+ nss = 0;
+ if (gi++ >= max_gi - 1) {
+ gi = 0;
+ if (bw < max_bw - 1)
+ bw++;
+ }
+ }
+ }
+
+ len += scnprintf(buf + len, size - len, "\n");
+
+ return len - offset;
+}
+
+static ssize_t ath12k_dbg_sta_dump_rx_stats(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct ieee80211_link_sta *link_sta = file->private_data;
+ struct ath12k_sta *ahsta = ath12k_sta_to_ahsta(link_sta->sta);
+ const int size = ATH12K_STA_RX_STATS_BUF_SIZE;
+ struct ath12k_hw *ah = ahsta->ahvif->ah;
+ struct ath12k_rx_peer_stats *rx_stats;
+ struct ath12k_link_sta *arsta;
+ u8 link_id = link_sta->link_id;
+ int len = 0, i, ret = 0;
+ bool he_rates_avail;
+ struct ath12k *ar;
+
+ wiphy_lock(ah->hw->wiphy);
+
+ if (!(BIT(link_id) & ahsta->links_map)) {
+ wiphy_unlock(ah->hw->wiphy);
+ return -ENOENT;
+ }
+
+ arsta = wiphy_dereference(ah->hw->wiphy, ahsta->link[link_id]);
+ if (!arsta || !arsta->arvif->ar) {
+ wiphy_unlock(ah->hw->wiphy);
+ return -ENOENT;
+ }
+
+ ar = arsta->arvif->ar;
+
+ u8 *buf __free(kfree) = kzalloc(size, GFP_KERNEL);
+ if (!buf) {
+ ret = -ENOENT;
+ goto out;
+ }
+
+ spin_lock_bh(&ar->ab->base_lock);
+
+ rx_stats = arsta->rx_stats;
+ if (!rx_stats) {
+ ret = -ENOENT;
+ goto unlock;
+ }
+
+ len += scnprintf(buf + len, size - len, "RX peer stats:\n\n");
+ len += scnprintf(buf + len, size - len, "Num of MSDUs: %llu\n",
+ rx_stats->num_msdu);
+ len += scnprintf(buf + len, size - len, "Num of MSDUs with TCP L4: %llu\n",
+ rx_stats->tcp_msdu_count);
+ len += scnprintf(buf + len, size - len, "Num of MSDUs with UDP L4: %llu\n",
+ rx_stats->udp_msdu_count);
+ len += scnprintf(buf + len, size - len, "Num of other MSDUs: %llu\n",
+ rx_stats->other_msdu_count);
+ len += scnprintf(buf + len, size - len, "Num of MSDUs part of AMPDU: %llu\n",
+ rx_stats->ampdu_msdu_count);
+ len += scnprintf(buf + len, size - len, "Num of MSDUs not part of AMPDU: %llu\n",
+ rx_stats->non_ampdu_msdu_count);
+ len += scnprintf(buf + len, size - len, "Num of MSDUs using STBC: %llu\n",
+ rx_stats->stbc_count);
+ len += scnprintf(buf + len, size - len, "Num of MSDUs beamformed: %llu\n",
+ rx_stats->beamformed_count);
+ len += scnprintf(buf + len, size - len, "Num of MPDUs with FCS ok: %llu\n",
+ rx_stats->num_mpdu_fcs_ok);
+ len += scnprintf(buf + len, size - len, "Num of MPDUs with FCS error: %llu\n",
+ rx_stats->num_mpdu_fcs_err);
+
+ he_rates_avail = (rx_stats->pream_cnt[HAL_RX_PREAMBLE_11AX] > 1) ? true : false;
+
+ len += scnprintf(buf + len, size - len,
+ "preamble: 11A %llu 11B %llu 11N %llu 11AC %llu 11AX %llu 11BE %llu\n",
+ rx_stats->pream_cnt[0], rx_stats->pream_cnt[1],
+ rx_stats->pream_cnt[2], rx_stats->pream_cnt[3],
+ rx_stats->pream_cnt[4], rx_stats->pream_cnt[6]);
+ len += scnprintf(buf + len, size - len,
+ "reception type: SU %llu MU_MIMO %llu MU_OFDMA %llu MU_OFDMA_MIMO %llu\n",
+ rx_stats->reception_type[0], rx_stats->reception_type[1],
+ rx_stats->reception_type[2], rx_stats->reception_type[3]);
+
+ len += scnprintf(buf + len, size - len, "TID(0-15) Legacy TID(16):");
+ for (i = 0; i <= IEEE80211_NUM_TIDS; i++)
+ len += scnprintf(buf + len, size - len, "%llu ", rx_stats->tid_count[i]);
+
+ len += scnprintf(buf + len, size - len, "\nRX Duration:%llu\n",
+ rx_stats->rx_duration);
+
+ len += scnprintf(buf + len, size - len,
+ "\nDCM: %llu\nRU26: %llu\nRU52: %llu\nRU106: %llu\nRU242: %llu\nRU484: %llu\nRU996: %llu\nRU996x2: %llu\n",
+ rx_stats->dcm_count, rx_stats->ru_alloc_cnt[0],
+ rx_stats->ru_alloc_cnt[1], rx_stats->ru_alloc_cnt[2],
+ rx_stats->ru_alloc_cnt[3], rx_stats->ru_alloc_cnt[4],
+ rx_stats->ru_alloc_cnt[5], rx_stats->ru_alloc_cnt[6]);
+
+ len += scnprintf(buf + len, size - len, "\nRX success packet stats:\n");
+ len += ath12k_dbg_sta_dump_rate_stats(buf, len, size, he_rates_avail,
+ &rx_stats->pkt_stats);
+
+ len += scnprintf(buf + len, size - len, "\n");
+
+ len += scnprintf(buf + len, size - len, "\nRX success byte stats:\n");
+ len += ath12k_dbg_sta_dump_rate_stats(buf, len, size, he_rates_avail,
+ &rx_stats->byte_stats);
+
+unlock:
+ spin_unlock_bh(&ar->ab->base_lock);
+
+ if (len)
+ ret = simple_read_from_buffer(user_buf, count, ppos, buf, len);
+out:
+ wiphy_unlock(ah->hw->wiphy);
+ return ret;
+}
+
+static const struct file_operations fops_rx_stats = {
+ .read = ath12k_dbg_sta_dump_rx_stats,
+ .open = simple_open,
+ .owner = THIS_MODULE,
+ .llseek = default_llseek,
+};
+
+static ssize_t ath12k_dbg_sta_reset_rx_stats(struct file *file,
+ const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct ieee80211_link_sta *link_sta = file->private_data;
+ struct ath12k_sta *ahsta = ath12k_sta_to_ahsta(link_sta->sta);
+ struct ath12k_hw *ah = ahsta->ahvif->ah;
+ struct ath12k_rx_peer_stats *rx_stats;
+ struct ath12k_link_sta *arsta;
+ u8 link_id = link_sta->link_id;
+ struct ath12k *ar;
+ bool reset;
+ int ret;
+
+ ret = kstrtobool_from_user(buf, count, &reset);
+ if (ret)
+ return ret;
+
+ if (!reset)
+ return -EINVAL;
+
+ wiphy_lock(ah->hw->wiphy);
+
+ if (!(BIT(link_id) & ahsta->links_map)) {
+ ret = -ENOENT;
+ goto out;
+ }
+
+ arsta = wiphy_dereference(ah->hw->wiphy, ahsta->link[link_id]);
+ if (!arsta || !arsta->arvif->ar) {
+ ret = -ENOENT;
+ goto out;
+ }
+
+ ar = arsta->arvif->ar;
+
+ spin_lock_bh(&ar->ab->base_lock);
+
+ rx_stats = arsta->rx_stats;
+ if (!rx_stats) {
+ spin_unlock_bh(&ar->ab->base_lock);
+ ret = -ENOENT;
+ goto out;
+ }
+
+ memset(rx_stats, 0, sizeof(*rx_stats));
+ spin_unlock_bh(&ar->ab->base_lock);
+
+ ret = count;
+out:
+ wiphy_unlock(ah->hw->wiphy);
+ return ret;
+}
+
+static const struct file_operations fops_reset_rx_stats = {
+ .write = ath12k_dbg_sta_reset_rx_stats,
+ .open = simple_open,
+ .owner = THIS_MODULE,
+ .llseek = default_llseek,
+};
+
+void ath12k_debugfs_link_sta_op_add(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_link_sta *link_sta,
+ struct dentry *dir)
+{
+ struct ath12k *ar;
+
+ lockdep_assert_wiphy(hw->wiphy);
+
+ ar = ath12k_get_ar_by_vif(hw, vif, link_sta->link_id);
+ if (!ar)
+ return;
+
+ if (ath12k_debugfs_is_extd_rx_stats_enabled(ar)) {
+ debugfs_create_file("rx_stats", 0400, dir, link_sta,
+ &fops_rx_stats);
+ debugfs_create_file("reset_rx_stats", 0200, dir, link_sta,
+ &fops_reset_rx_stats);
+ }
+}
diff --git a/drivers/net/wireless/ath/ath12k/debugfs_sta.h b/drivers/net/wireless/ath/ath12k/debugfs_sta.h
new file mode 100644
index 000000000000..8de924f4d7d5
--- /dev/null
+++ b/drivers/net/wireless/ath/ath12k/debugfs_sta.h
@@ -0,0 +1,24 @@
+/* SPDX-License-Identifier: BSD-3-Clause-Clear */
+/*
+ * Copyright (c) 2024-2025 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#ifndef _ATH12K_DEBUGFS_STA_H_
+#define _ATH12K_DEBUGFS_STA_H_
+
+#include <net/mac80211.h>
+
+#include "core.h"
+
+#define ATH12K_STA_RX_STATS_BUF_SIZE (1024 * 16)
+
+#ifdef CONFIG_ATH12K_DEBUGFS
+
+void ath12k_debugfs_link_sta_op_add(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_link_sta *link_sta,
+ struct dentry *dir);
+
+#endif /* CONFIG_ATH12K_DEBUGFS */
+
+#endif /* _ATH12K_DEBUGFS_STA_H_ */
diff --git a/drivers/net/wireless/ath/ath12k/dp.c b/drivers/net/wireless/ath/ath12k/dp.c
index 9e5a4e75f2f6..6317c6d4c043 100644
--- a/drivers/net/wireless/ath/ath12k/dp.c
+++ b/drivers/net/wireless/ath/ath12k/dp.c
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: BSD-3-Clause-Clear
/*
* Copyright (c) 2018-2021 The Linux Foundation. All rights reserved.
- * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#include <crypto/hash.h>
@@ -168,6 +168,8 @@ static int ath12k_dp_srng_calculate_msi_group(struct ath12k_base *ab,
grp_mask = &ab->hw_params->ring_mask->reo_status[0];
break;
case HAL_RXDMA_MONITOR_STATUS:
+ grp_mask = &ab->hw_params->ring_mask->rx_mon_status[0];
+ break;
case HAL_RXDMA_MONITOR_DST:
grp_mask = &ab->hw_params->ring_mask->rx_mon_dest[0];
break;
@@ -274,12 +276,17 @@ int ath12k_dp_srng_setup(struct ath12k_base *ab, struct dp_srng *ring,
break;
case HAL_RXDMA_BUF:
case HAL_RXDMA_MONITOR_BUF:
- case HAL_RXDMA_MONITOR_STATUS:
params.low_threshold = num_entries >> 3;
params.flags |= HAL_SRNG_FLAGS_LOW_THRESH_INTR_EN;
params.intr_batch_cntr_thres_entries = 0;
params.intr_timer_thres_us = HAL_SRNG_INT_TIMER_THRESHOLD_RX;
break;
+ case HAL_RXDMA_MONITOR_STATUS:
+ params.low_threshold = num_entries >> 3;
+ params.flags |= HAL_SRNG_FLAGS_LOW_THRESH_INTR_EN;
+ params.intr_batch_cntr_thres_entries = 1;
+ params.intr_timer_thres_us = HAL_SRNG_INT_TIMER_THRESHOLD_RX;
+ break;
case HAL_TX_MONITOR_DST:
params.low_threshold = DP_TX_MONITOR_BUF_SIZE_MAX >> 3;
params.flags |= HAL_SRNG_FLAGS_LOW_THRESH_INTR_EN;
@@ -354,7 +361,10 @@ u32 ath12k_dp_tx_get_vdev_bank_config(struct ath12k_base *ab,
u32_encode_bits(0, HAL_TX_BANK_CONFIG_EPD);
/* only valid if idx_lookup_override is not set in tcl_data_cmd */
- bank_config |= u32_encode_bits(0, HAL_TX_BANK_CONFIG_INDEX_LOOKUP_EN);
+ if (ahvif->vdev_type == WMI_VDEV_TYPE_STA)
+ bank_config |= u32_encode_bits(1, HAL_TX_BANK_CONFIG_INDEX_LOOKUP_EN);
+ else
+ bank_config |= u32_encode_bits(0, HAL_TX_BANK_CONFIG_INDEX_LOOKUP_EN);
bank_config |= u32_encode_bits(arvif->hal_addr_search_flags & HAL_TX_ADDRX_EN,
HAL_TX_BANK_CONFIG_ADDRX_EN) |
@@ -919,6 +929,25 @@ int ath12k_dp_service_srng(struct ath12k_base *ab,
goto done;
}
+ if (ab->hw_params->ring_mask->rx_mon_status[grp_id]) {
+ ring_mask = ab->hw_params->ring_mask->rx_mon_status[grp_id];
+ for (i = 0; i < ab->num_radios; i++) {
+ for (j = 0; j < ab->hw_params->num_rxdma_per_pdev; j++) {
+ int id = i * ab->hw_params->num_rxdma_per_pdev + j;
+
+ if (ring_mask & BIT(id)) {
+ work_done =
+ ath12k_dp_mon_process_ring(ab, id, napi, budget,
+ 0);
+ budget -= work_done;
+ tot_work_done += work_done;
+ if (budget <= 0)
+ goto done;
+ }
+ }
+ }
+ }
+
if (ab->hw_params->ring_mask->rx_mon_dest[grp_id]) {
monitor_mode = ATH12K_DP_RX_MONITOR_MODE;
ring_mask = ab->hw_params->ring_mask->rx_mon_dest[grp_id];
@@ -982,11 +1011,6 @@ void ath12k_dp_pdev_free(struct ath12k_base *ab)
{
int i;
- if (!ab->mon_reap_timer.function)
- return;
-
- del_timer_sync(&ab->mon_reap_timer);
-
for (i = 0; i < ab->num_radios; i++)
ath12k_dp_rx_pdev_free(ab, i);
}
@@ -1024,27 +1048,6 @@ void ath12k_dp_hal_rx_desc_init(struct ath12k_base *ab)
ab->hal_rx_ops->rx_desc_get_desc_size();
}
-static void ath12k_dp_service_mon_ring(struct timer_list *t)
-{
- struct ath12k_base *ab = from_timer(ab, t, mon_reap_timer);
- int i;
-
- for (i = 0; i < ab->hw_params->num_rxdma_per_pdev; i++)
- ath12k_dp_mon_process_ring(ab, i, NULL, DP_MON_SERVICE_BUDGET,
- ATH12K_DP_RX_MONITOR_MODE);
-
- mod_timer(&ab->mon_reap_timer, jiffies +
- msecs_to_jiffies(ATH12K_MON_TIMER_INTERVAL));
-}
-
-static void ath12k_dp_mon_reap_timer_init(struct ath12k_base *ab)
-{
- if (ab->hw_params->rxdma1_enable)
- return;
-
- timer_setup(&ab->mon_reap_timer, ath12k_dp_service_mon_ring, 0);
-}
-
int ath12k_dp_pdev_alloc(struct ath12k_base *ab)
{
struct ath12k *ar;
@@ -1055,8 +1058,6 @@ int ath12k_dp_pdev_alloc(struct ath12k_base *ab)
if (ret)
goto out;
- ath12k_dp_mon_reap_timer_init(ab);
-
/* TODO: Per-pdev rx ring unlike tx ring which is mapped to different AC's */
for (i = 0; i < ab->num_radios; i++) {
ar = ab->pdevs[i].ar;
@@ -1107,11 +1108,8 @@ static void ath12k_dp_update_vdev_search(struct ath12k_link_vif *arvif)
{
switch (arvif->ahvif->vdev_type) {
case WMI_VDEV_TYPE_STA:
- /* TODO: Verify the search type and flags since ast hash
- * is not part of peer mapv3
- */
arvif->hal_addr_search_flags = HAL_TX_ADDRY_EN;
- arvif->search_type = HAL_TX_ADDR_SEARCH_DEFAULT;
+ arvif->search_type = HAL_TX_ADDR_SEARCH_INDEX;
break;
case WMI_VDEV_TYPE_AP:
case WMI_VDEV_TYPE_IBSS:
@@ -1206,11 +1204,19 @@ static void ath12k_dp_cc_cleanup(struct ath12k_base *ab)
if (!skb)
continue;
+ skb_cb = ATH12K_SKB_CB(skb);
+ if (skb_cb->paddr_ext_desc) {
+ dma_unmap_single(ab->dev,
+ skb_cb->paddr_ext_desc,
+ tx_desc_info->skb_ext_desc->len,
+ DMA_TO_DEVICE);
+ dev_kfree_skb_any(tx_desc_info->skb_ext_desc);
+ }
+
/* if we are unregistering, hw would've been destroyed and
* ar is no longer valid.
*/
if (!(test_bit(ATH12K_FLAG_UNREGISTERING, &ab->dev_flags))) {
- skb_cb = ATH12K_SKB_CB(skb);
ar = skb_cb->ar;
if (atomic_dec_and_test(&ar->dp.num_tx_pending))
@@ -1261,22 +1267,24 @@ static void ath12k_dp_reoq_lut_cleanup(struct ath12k_base *ab)
if (!ab->hw_params->reoq_lut_support)
return;
- if (dp->reoq_lut.vaddr) {
+ if (dp->reoq_lut.vaddr_unaligned) {
ath12k_hif_write32(ab,
HAL_SEQ_WCSS_UMAC_REO_REG +
HAL_REO1_QDESC_LUT_BASE0(ab), 0);
- dma_free_coherent(ab->dev, DP_REOQ_LUT_SIZE,
- dp->reoq_lut.vaddr, dp->reoq_lut.paddr);
- dp->reoq_lut.vaddr = NULL;
+ dma_free_coherent(ab->dev, dp->reoq_lut.size,
+ dp->reoq_lut.vaddr_unaligned,
+ dp->reoq_lut.paddr_unaligned);
+ dp->reoq_lut.vaddr_unaligned = NULL;
}
- if (dp->ml_reoq_lut.vaddr) {
+ if (dp->ml_reoq_lut.vaddr_unaligned) {
ath12k_hif_write32(ab,
HAL_SEQ_WCSS_UMAC_REO_REG +
HAL_REO1_QDESC_LUT_BASE1(ab), 0);
- dma_free_coherent(ab->dev, DP_REOQ_LUT_SIZE,
- dp->ml_reoq_lut.vaddr, dp->ml_reoq_lut.paddr);
- dp->ml_reoq_lut.vaddr = NULL;
+ dma_free_coherent(ab->dev, dp->ml_reoq_lut.size,
+ dp->ml_reoq_lut.vaddr_unaligned,
+ dp->ml_reoq_lut.paddr_unaligned);
+ dp->ml_reoq_lut.vaddr_unaligned = NULL;
}
}
@@ -1315,6 +1323,9 @@ void ath12k_dp_cc_config(struct ath12k_base *ab)
u32 wbm_base = HAL_SEQ_WCSS_UMAC_WBM_REG;
u32 val = 0;
+ if (ath12k_ftm_mode)
+ return;
+
ath12k_hif_write32(ab, reo_base + HAL_REO1_SW_COOKIE_CFG0(ab), cmem_base);
val |= u32_encode_bits(ATH12K_CMEM_ADDR_MSB,
@@ -1605,39 +1616,67 @@ free:
return ret;
}
+static int ath12k_dp_alloc_reoq_lut(struct ath12k_base *ab,
+ struct ath12k_reo_q_addr_lut *lut)
+{
+ lut->size = DP_REOQ_LUT_SIZE + HAL_REO_QLUT_ADDR_ALIGN - 1;
+ lut->vaddr_unaligned = dma_alloc_coherent(ab->dev, lut->size,
+ &lut->paddr_unaligned,
+ GFP_KERNEL | __GFP_ZERO);
+ if (!lut->vaddr_unaligned)
+ return -ENOMEM;
+
+ lut->vaddr = PTR_ALIGN(lut->vaddr_unaligned, HAL_REO_QLUT_ADDR_ALIGN);
+ lut->paddr = lut->paddr_unaligned +
+ ((unsigned long)lut->vaddr - (unsigned long)lut->vaddr_unaligned);
+ return 0;
+}
+
static int ath12k_dp_reoq_lut_setup(struct ath12k_base *ab)
{
struct ath12k_dp *dp = &ab->dp;
+ u32 val;
+ int ret;
if (!ab->hw_params->reoq_lut_support)
return 0;
- dp->reoq_lut.vaddr = dma_alloc_coherent(ab->dev,
- DP_REOQ_LUT_SIZE,
- &dp->reoq_lut.paddr,
- GFP_KERNEL | __GFP_ZERO);
- if (!dp->reoq_lut.vaddr) {
+ ret = ath12k_dp_alloc_reoq_lut(ab, &dp->reoq_lut);
+ if (ret) {
ath12k_warn(ab, "failed to allocate memory for reoq table");
- return -ENOMEM;
+ return ret;
}
- dp->ml_reoq_lut.vaddr = dma_alloc_coherent(ab->dev,
- DP_REOQ_LUT_SIZE,
- &dp->ml_reoq_lut.paddr,
- GFP_KERNEL | __GFP_ZERO);
- if (!dp->ml_reoq_lut.vaddr) {
+ ret = ath12k_dp_alloc_reoq_lut(ab, &dp->ml_reoq_lut);
+ if (ret) {
ath12k_warn(ab, "failed to allocate memory for ML reoq table");
- dma_free_coherent(ab->dev, DP_REOQ_LUT_SIZE,
- dp->reoq_lut.vaddr, dp->reoq_lut.paddr);
- dp->reoq_lut.vaddr = NULL;
- return -ENOMEM;
+ dma_free_coherent(ab->dev, dp->reoq_lut.size,
+ dp->reoq_lut.vaddr_unaligned,
+ dp->reoq_lut.paddr_unaligned);
+ dp->reoq_lut.vaddr_unaligned = NULL;
+ return ret;
}
+ /* Bits in the register have address [39:8] LUT base address to be
+ * allocated such that LSBs are assumed to be zero. Also, current
+ * design supports paddr up to 4 GB max hence it fits in 32 bit
+ * register only
+ */
+
ath12k_hif_write32(ab, HAL_SEQ_WCSS_UMAC_REO_REG + HAL_REO1_QDESC_LUT_BASE0(ab),
- dp->reoq_lut.paddr);
+ dp->reoq_lut.paddr >> 8);
+
ath12k_hif_write32(ab, HAL_SEQ_WCSS_UMAC_REO_REG + HAL_REO1_QDESC_LUT_BASE1(ab),
dp->ml_reoq_lut.paddr >> 8);
+ val = ath12k_hif_read32(ab, HAL_SEQ_WCSS_UMAC_REO_REG + HAL_REO1_QDESC_ADDR(ab));
+
+ ath12k_hif_write32(ab, HAL_SEQ_WCSS_UMAC_REO_REG + HAL_REO1_QDESC_ADDR(ab),
+ val | HAL_REO_QDESC_ADDR_READ_LUT_ENABLE);
+
+ ath12k_hif_write32(ab, HAL_SEQ_WCSS_UMAC_REO_REG + HAL_REO1_QDESC_MAX_PEERID(ab),
+ HAL_REO_QDESC_MAX_PEERID);
+
return 0;
}
diff --git a/drivers/net/wireless/ath/ath12k/dp.h b/drivers/net/wireless/ath/ath12k/dp.h
index 7ac3143de016..a353333f83b6 100644
--- a/drivers/net/wireless/ath/ath12k/dp.h
+++ b/drivers/net/wireless/ath/ath12k/dp.h
@@ -1,12 +1,13 @@
/* SPDX-License-Identifier: BSD-3-Clause-Clear */
/*
* Copyright (c) 2018-2021 The Linux Foundation. All rights reserved.
- * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#ifndef ATH12K_DP_H
#define ATH12K_DP_H
+#include "hal_desc.h"
#include "hal_rx.h"
#include "hw.h"
@@ -69,6 +70,16 @@ struct ath12k_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;
+};
+
+enum dp_mon_status_buf_state {
+ DP_MON_STATUS_MATCH,
+ DP_MON_STATUS_NO_DMA,
+ DP_MON_STATUS_LAG,
+ DP_MON_STATUS_LEAD,
+ DP_MON_STATUS_REPLINISH,
};
struct dp_link_desc_bank {
@@ -106,6 +117,8 @@ struct dp_mon_mpdu {
struct list_head list;
struct sk_buff *head;
struct sk_buff *tail;
+ u32 err_bitmap;
+ u8 decap_format;
};
#define DP_MON_MAX_STATUS_BUF 32
@@ -118,14 +131,16 @@ struct ath12k_mon_data {
u32 mon_last_buf_cookie;
u64 mon_last_linkdesc_paddr;
u16 chan_noise_floor;
+ u32 err_bitmap;
+ u8 decap_format;
struct ath12k_pdev_mon_stats rx_mon_stats;
+ enum dp_mon_status_buf_state buf_state;
/* lock for monitor data */
spinlock_t mon_lock;
struct sk_buff_head rx_status_q;
struct dp_mon_mpdu *mon_mpdu;
struct list_head dp_rx_mon_mpdu_list;
- struct sk_buff *dest_skb_q[DP_MON_MAX_STATUS_BUF];
struct dp_mon_tx_ppdu_info *tx_prot_ppdu_info;
struct dp_mon_tx_ppdu_info *tx_data_ppdu_info;
};
@@ -176,7 +191,7 @@ struct ath12k_pdev_dp {
#define DP_RXDMA_ERR_DST_RING_SIZE 1024
#define DP_RXDMA_MON_STATUS_RING_SIZE 1024
#define DP_RXDMA_MONITOR_BUF_RING_SIZE 4096
-#define DP_RXDMA_MONITOR_DST_RING_SIZE 2048
+#define DP_RXDMA_MONITOR_DST_RING_SIZE 8092
#define DP_RXDMA_MONITOR_DESC_RING_SIZE 4096
#define DP_TX_MONITOR_BUF_RING_SIZE 4096
#define DP_TX_MONITOR_DEST_RING_SIZE 2048
@@ -189,6 +204,14 @@ struct ath12k_pdev_dp {
#define DP_RX_BUFFER_SIZE_LITE 1024
#define DP_RX_BUFFER_ALIGN_SIZE 128
+#define RX_MON_STATUS_BASE_BUF_SIZE 2048
+#define RX_MON_STATUS_BUF_ALIGN 128
+#define RX_MON_STATUS_BUF_RESERVATION 128
+#define RX_MON_STATUS_BUF_SIZE (RX_MON_STATUS_BASE_BUF_SIZE - \
+ (RX_MON_STATUS_BUF_RESERVATION + \
+ RX_MON_STATUS_BUF_ALIGN + \
+ SKB_DATA_ALIGN(sizeof(struct skb_shared_info))))
+
#define DP_RXDMA_BUF_COOKIE_BUF_ID GENMASK(17, 0)
#define DP_RXDMA_BUF_COOKIE_PDEV_ID GENMASK(19, 18)
@@ -264,6 +287,9 @@ struct ath12k_pdev_dp {
/* Invalid TX Bank ID value */
#define DP_INVALID_BANK_ID -1
+#define MAX_TQM_RELEASE_REASON 15
+#define MAX_FW_TX_STATUS 7
+
struct ath12k_dp_tx_bank_profile {
u8 is_configured;
u32 num_users;
@@ -294,11 +320,18 @@ struct ath12k_rx_desc_info {
struct ath12k_tx_desc_info {
struct list_head list;
struct sk_buff *skb;
+ struct sk_buff *skb_ext_desc;
u32 desc_id; /* Cookie */
u8 mac_id;
u8 pool_id;
};
+struct ath12k_tx_desc_params {
+ struct sk_buff *skb;
+ struct sk_buff *skb_ext_desc;
+ u8 mac_id;
+};
+
struct ath12k_spt_info {
dma_addr_t paddr;
u64 *vaddr;
@@ -310,12 +343,26 @@ struct ath12k_reo_queue_ref {
} __packed;
struct ath12k_reo_q_addr_lut {
- dma_addr_t paddr;
+ u32 *vaddr_unaligned;
u32 *vaddr;
+ dma_addr_t paddr_unaligned;
+ dma_addr_t paddr;
+ u32 size;
+};
+
+struct ath12k_link_stats {
+ u32 tx_enqueued;
+ u32 tx_completed;
+ u32 tx_bcast_mcast;
+ u32 tx_dropped;
+ u32 tx_encap_type[HAL_TCL_ENCAP_TYPE_MAX];
+ u32 tx_encrypt_type[HAL_ENCRYPT_TYPE_MAX];
+ u32 tx_desc_type[HAL_TCL_DESC_TYPE_MAX];
};
struct ath12k_dp {
struct ath12k_base *ab;
+ u32 mon_dest_ring_stuck_cnt;
u8 num_bank_profiles;
/* protects the access and update of bank_profiles */
spinlock_t tx_bank_lock;
@@ -368,22 +415,30 @@ struct ath12k_dp {
struct dp_srng rxdma_err_dst_ring[MAX_RXDMA_PER_PDEV];
struct dp_rxdma_mon_ring rxdma_mon_buf_ring;
struct dp_rxdma_mon_ring tx_mon_buf_ring;
+ struct dp_rxdma_mon_ring rx_mon_status_refill_ring[MAX_RXDMA_PER_PDEV];
struct ath12k_reo_q_addr_lut reoq_lut;
struct ath12k_reo_q_addr_lut ml_reoq_lut;
};
/* HTT definitions */
+#define HTT_TAG_TCL_METADATA_VERSION 5
-#define HTT_TCL_META_DATA_TYPE BIT(0)
-#define HTT_TCL_META_DATA_VALID_HTT BIT(1)
+#define HTT_TCL_META_DATA_TYPE GENMASK(1, 0)
+#define HTT_TCL_META_DATA_VALID_HTT BIT(2)
/* vdev meta data */
-#define HTT_TCL_META_DATA_VDEV_ID GENMASK(9, 2)
-#define HTT_TCL_META_DATA_PDEV_ID GENMASK(11, 10)
-#define HTT_TCL_META_DATA_HOST_INSPECTED BIT(12)
+#define HTT_TCL_META_DATA_VDEV_ID GENMASK(10, 3)
+#define HTT_TCL_META_DATA_PDEV_ID GENMASK(12, 11)
+#define HTT_TCL_META_DATA_HOST_INSPECTED_MISSION BIT(13)
/* peer meta data */
-#define HTT_TCL_META_DATA_PEER_ID GENMASK(15, 2)
+#define HTT_TCL_META_DATA_PEER_ID GENMASK(15, 3)
+
+/* Global sequence number */
+#define HTT_TCL_META_DATA_TYPE_GLOBAL_SEQ_NUM 3
+#define HTT_TCL_META_DATA_GLOBAL_SEQ_HOST_INSPECTED BIT(2)
+#define HTT_TCL_META_DATA_GLOBAL_SEQ_NUM GENMASK(14, 3)
+#define HTT_TX_MLO_MCAST_HOST_REINJECT_BASE_VDEV_ID 128
/* HTT tx completion is overlaid in wbm_release_ring */
#define HTT_TX_WBM_COMP_INFO0_STATUS GENMASK(16, 13)
@@ -414,9 +469,15 @@ enum htt_h2t_msg_type {
};
#define HTT_VER_REQ_INFO_MSG_ID GENMASK(7, 0)
+#define HTT_OPTION_TCL_METADATA_VER_V2 2
+#define HTT_OPTION_TAG GENMASK(7, 0)
+#define HTT_OPTION_LEN GENMASK(15, 8)
+#define HTT_OPTION_VALUE GENMASK(31, 16)
+#define HTT_TCL_METADATA_VER_SZ 4
struct htt_ver_req_cmd {
__le32 ver_reg_info;
+ __le32 tcl_metadata_version;
} __packed;
enum htt_srng_ring_type {
@@ -434,8 +495,11 @@ enum htt_srng_ring_id {
HTT_HOST1_TO_FW_RXBUF_RING,
HTT_HOST2_TO_FW_RXBUF_RING,
HTT_RXDMA_NON_MONITOR_DEST_RING,
+ HTT_RXDMA_HOST_BUF_RING2,
HTT_TX_MON_HOST2MON_BUF_RING,
HTT_TX_MON_MON2HOST_DEST_RING,
+ HTT_RX_MON_HOST2MON_BUF_RING,
+ HTT_RX_MON_MON2HOST_DEST_RING,
};
/* host -> target HTT_SRING_SETUP message
@@ -767,8 +831,22 @@ enum htt_stats_internal_ppdu_frametype {
#define HTT_RX_RING_SELECTION_CFG_CMD_INFO0_RING_ID GENMASK(23, 16)
#define HTT_RX_RING_SELECTION_CFG_CMD_INFO0_SS BIT(24)
#define HTT_RX_RING_SELECTION_CFG_CMD_INFO0_PS BIT(25)
-#define HTT_RX_RING_SELECTION_CFG_CMD_INFO1_BUF_SIZE GENMASK(15, 0)
-#define HTT_RX_RING_SELECTION_CFG_CMD_OFFSET_VALID BIT(26)
+#define HTT_RX_RING_SELECTION_CFG_CMD_INFO0_OFFSET_VALID BIT(26)
+#define HTT_RX_RING_SELECTION_CFG_CMD_INFO0_DROP_THRES_VAL BIT(27)
+#define HTT_RX_RING_SELECTION_CFG_CMD_INFO0_EN_RXMON BIT(28)
+
+#define HTT_RX_RING_SELECTION_CFG_CMD_INFO1_BUF_SIZE GENMASK(15, 0)
+#define HTT_RX_RING_SELECTION_CFG_CMD_INFO1_CONF_LEN_MGMT GENMASK(18, 16)
+#define HTT_RX_RING_SELECTION_CFG_CMD_INFO1_CONF_LEN_CTRL GENMASK(21, 19)
+#define HTT_RX_RING_SELECTION_CFG_CMD_INFO1_CONF_LEN_DATA GENMASK(24, 22)
+
+#define HTT_RX_RING_SELECTION_CFG_CMD_INFO2_DROP_THRESHOLD GENMASK(9, 0)
+#define HTT_RX_RING_SELECTION_CFG_CMD_INFO2_EN_LOG_MGMT_TYPE BIT(17)
+#define HTT_RX_RING_SELECTION_CFG_CMD_INFO2_EN_CTRL_TYPE BIT(18)
+#define HTT_RX_RING_SELECTION_CFG_CMD_INFO2_EN_LOG_DATA_TYPE BIT(19)
+
+#define HTT_RX_RING_SELECTION_CFG_CMD_INFO3_EN_TLV_PKT_OFFSET BIT(0)
+#define HTT_RX_RING_SELECTION_CFG_CMD_INFO3_PKT_TLV_OFFSET GENMASK(14, 1)
#define HTT_RX_RING_SELECTION_CFG_RX_PACKET_OFFSET GENMASK(15, 0)
#define HTT_RX_RING_SELECTION_CFG_RX_HEADER_OFFSET GENMASK(31, 16)
@@ -797,6 +875,7 @@ enum htt_rx_filter_tlv_flags {
HTT_RX_FILTER_TLV_FLAGS_PPDU_END_USER_STATS = BIT(10),
HTT_RX_FILTER_TLV_FLAGS_PPDU_END_USER_STATS_EXT = BIT(11),
HTT_RX_FILTER_TLV_FLAGS_PPDU_END_STATUS_DONE = BIT(12),
+ HTT_RX_FILTER_TLV_FLAGS_PPDU_START_USER_INFO = BIT(13),
};
enum htt_rx_mgmt_pkt_filter_tlv_flags0 {
@@ -1085,6 +1164,21 @@ enum htt_rx_data_pkt_filter_tlv_flasg3 {
HTT_RX_FILTER_TLV_FLAGS_PER_MSDU_HEADER | \
HTT_RX_FILTER_TLV_FLAGS_ATTENTION)
+#define HTT_RX_MON_FILTER_TLV_FLAGS_MON_DEST_RING \
+ (HTT_RX_FILTER_TLV_FLAGS_MPDU_START | \
+ HTT_RX_FILTER_TLV_FLAGS_MSDU_START | \
+ HTT_RX_FILTER_TLV_FLAGS_RX_PACKET | \
+ HTT_RX_FILTER_TLV_FLAGS_MSDU_END | \
+ HTT_RX_FILTER_TLV_FLAGS_MPDU_END | \
+ HTT_RX_FILTER_TLV_FLAGS_PACKET_HEADER | \
+ HTT_RX_FILTER_TLV_FLAGS_PER_MSDU_HEADER | \
+ HTT_RX_FILTER_TLV_FLAGS_PPDU_START | \
+ HTT_RX_FILTER_TLV_FLAGS_PPDU_END | \
+ HTT_RX_FILTER_TLV_FLAGS_PPDU_END_USER_STATS | \
+ HTT_RX_FILTER_TLV_FLAGS_PPDU_END_USER_STATS_EXT | \
+ HTT_RX_FILTER_TLV_FLAGS_PPDU_END_STATUS_DONE | \
+ HTT_RX_FILTER_TLV_FLAGS_PPDU_START_USER_INFO)
+
/* msdu start. mpdu end, attention, rx hdr tlv's are not subscribed */
#define HTT_RX_TLV_FLAGS_RXDMA_RING \
(HTT_RX_FILTER_TLV_FLAGS_MPDU_START | \
@@ -1113,6 +1207,10 @@ struct htt_rx_ring_selection_cfg_cmd {
__le32 info3;
} __packed;
+#define HTT_RX_RING_TLV_DROP_THRESHOLD_VALUE 32
+#define HTT_RX_RING_DEFAULT_DMA_LENGTH 0x7
+#define HTT_RX_RING_PKT_TLV_OFFSET 0x1
+
struct htt_rx_ring_tlv_filter {
u32 rx_filter; /* see htt_rx_filter_tlv_flags */
u32 pkt_filter_flags0; /* MGMT */
@@ -1130,6 +1228,17 @@ struct htt_rx_ring_tlv_filter {
u16 rx_mpdu_start_wmask;
u16 rx_mpdu_end_wmask;
u32 rx_msdu_end_wmask;
+ u32 conf_len_ctrl;
+ u32 conf_len_mgmt;
+ u32 conf_len_data;
+ u16 rx_drop_threshold;
+ bool enable_log_mgmt_type;
+ bool enable_log_ctrl_type;
+ bool enable_log_data_type;
+ bool enable_rx_tlv_offset;
+ u16 rx_tlv_offset;
+ bool drop_threshold_valid;
+ bool rxmon_disable;
};
#define HTT_STATS_FRAME_CTRL_TYPE_MGMT 0x0
@@ -1247,6 +1356,8 @@ struct htt_t2h_version_conf_msg {
#define HTT_T2H_PEER_MAP_INFO1_MAC_ADDR_H16 GENMASK(15, 0)
#define HTT_T2H_PEER_MAP_INFO1_HW_PEER_ID GENMASK(31, 16)
#define HTT_T2H_PEER_MAP_INFO2_AST_HASH_VAL GENMASK(15, 0)
+#define HTT_T2H_PEER_MAP3_INFO2_HW_PEER_ID GENMASK(15, 0)
+#define HTT_T2H_PEER_MAP3_INFO2_AST_HASH_VAL GENMASK(31, 16)
#define HTT_T2H_PEER_MAP_INFO2_NEXT_HOP_M BIT(16)
#define HTT_T2H_PEER_MAP_INFO2_NEXT_HOP_S 16
diff --git a/drivers/net/wireless/ath/ath12k/dp_mon.c b/drivers/net/wireless/ath/ath12k/dp_mon.c
index 5a21961cfd46..28cadc4167f7 100644
--- a/drivers/net/wireless/ath/ath12k/dp_mon.c
+++ b/drivers/net/wireless/ath/ath12k/dp_mon.c
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: BSD-3-Clause-Clear
/*
* Copyright (c) 2019-2021 The Linux Foundation. All rights reserved.
- * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#include "dp_mon.h"
@@ -10,6 +10,12 @@
#include "dp_tx.h"
#include "peer.h"
+#define ATH12K_LE32_DEC_ENC(value, dec_bits, enc_bits) \
+ u32_encode_bits(le32_get_bits(value, dec_bits), enc_bits)
+
+#define ATH12K_LE64_DEC_ENC(value, dec_bits, enc_bits) \
+ u32_encode_bits(le64_get_bits(value, dec_bits), enc_bits)
+
static void
ath12k_dp_mon_rx_handle_ofdma_info(const struct hal_rx_ppdu_end_user_stats *ppdu_end_user,
struct hal_rx_user_status *rx_user_status)
@@ -75,7 +81,7 @@ ath12k_dp_mon_rx_populate_mu_user_info(const struct hal_rx_ppdu_end_user_stats *
static void ath12k_dp_mon_parse_vht_sig_a(const struct hal_rx_vht_sig_a_info *vht_sig,
struct hal_rx_mon_ppdu_info *ppdu_info)
{
- u32 nsts, group_id, info0, info1;
+ u32 nsts, info0, info1;
u8 gi_setting;
info0 = __le32_to_cpu(vht_sig->info0);
@@ -103,12 +109,8 @@ static void ath12k_dp_mon_parse_vht_sig_a(const struct hal_rx_vht_sig_a_info *vh
ppdu_info->bw = u32_get_bits(info0, HAL_RX_VHT_SIG_A_INFO_INFO0_BW);
ppdu_info->beamformed = u32_get_bits(info1,
HAL_RX_VHT_SIG_A_INFO_INFO1_BEAMFORMED);
- group_id = u32_get_bits(info0, HAL_RX_VHT_SIG_A_INFO_INFO0_GROUP_ID);
- if (group_id == 0 || group_id == 63)
- ppdu_info->reception_type = HAL_RX_RECEPTION_TYPE_SU;
- else
- ppdu_info->reception_type = HAL_RX_RECEPTION_TYPE_MU_MIMO;
- ppdu_info->vht_flag_values5 = group_id;
+ ppdu_info->vht_flag_values5 = u32_get_bits(info0,
+ HAL_RX_VHT_SIG_A_INFO_INFO0_GROUP_ID);
ppdu_info->vht_flag_values3[0] = (((ppdu_info->mcs) << 4) |
ppdu_info->nss);
ppdu_info->vht_flag_values2 = ppdu_info->bw;
@@ -128,7 +130,6 @@ static void ath12k_dp_mon_parse_ht_sig(const struct hal_rx_ht_sig_info *ht_sig,
ppdu_info->ldpc = u32_get_bits(info1, HAL_RX_HT_SIG_INFO_INFO1_FEC_CODING);
ppdu_info->gi = u32_get_bits(info1, HAL_RX_HT_SIG_INFO_INFO1_GI);
ppdu_info->nss = (ppdu_info->mcs >> 3);
- ppdu_info->reception_type = HAL_RX_RECEPTION_TYPE_SU;
}
static void ath12k_dp_mon_parse_l_sig_b(const struct hal_rx_lsig_b_info *lsigb,
@@ -160,7 +161,6 @@ static void ath12k_dp_mon_parse_l_sig_b(const struct hal_rx_lsig_b_info *lsigb,
ppdu_info->rate = rate;
ppdu_info->cck_flag = 1;
- ppdu_info->reception_type = HAL_RX_RECEPTION_TYPE_SU;
}
static void ath12k_dp_mon_parse_l_sig_a(const struct hal_rx_lsig_a_info *lsiga,
@@ -200,7 +200,6 @@ static void ath12k_dp_mon_parse_l_sig_a(const struct hal_rx_lsig_a_info *lsiga,
}
ppdu_info->rate = rate;
- ppdu_info->reception_type = HAL_RX_RECEPTION_TYPE_SU;
}
static void
@@ -237,7 +236,6 @@ ath12k_dp_mon_parse_he_sig_b2_ofdma(const struct hal_rx_he_sig_b2_ofdma_info *of
ppdu_info->nss = u32_get_bits(info0, HAL_RX_HE_SIG_B2_OFDMA_INFO_INFO0_STA_NSTS);
ppdu_info->beamformed = u32_get_bits(info0,
HAL_RX_HE_SIG_B2_OFDMA_INFO_INFO0_STA_TXBF);
- ppdu_info->reception_type = HAL_RX_RECEPTION_TYPE_MU_OFDMA;
}
static void
@@ -277,7 +275,6 @@ ath12k_dp_mon_parse_he_sig_b1_mu(const struct hal_rx_he_sig_b1_mu_info *he_sig_b
HAL_RX_HE_SIG_B1_MU_INFO_INFO0_RU_ALLOCATION);
ppdu_info->ru_alloc = ath12k_he_ru_tones_to_nl80211_he_ru_alloc(ru_tones);
ppdu_info->he_RU[0] = ru_tones;
- ppdu_info->reception_type = HAL_RX_RECEPTION_TYPE_MU_MIMO;
}
static void
@@ -411,7 +408,6 @@ ath12k_dp_mon_parse_he_sig_mu(const struct hal_rx_he_sig_a_mu_dl_info *he_sig_a_
ppdu_info->is_stbc = info1 &
HAL_RX_HE_SIG_A_MU_DL_INFO1_STBC;
- ppdu_info->reception_type = HAL_RX_RECEPTION_TYPE_MU_MIMO;
}
static void ath12k_dp_mon_parse_he_sig_su(const struct hal_rx_he_sig_a_su_info *he_sig_a,
@@ -559,17 +555,921 @@ static void ath12k_dp_mon_parse_he_sig_su(const struct hal_rx_he_sig_a_su_info *
dcm = u32_get_bits(info0, HAL_RX_HE_SIG_A_SU_INFO_INFO0_DCM);
ppdu_info->nss = u32_get_bits(info0, HAL_RX_HE_SIG_A_SU_INFO_INFO0_NSTS);
ppdu_info->dcm = dcm;
- ppdu_info->reception_type = HAL_RX_RECEPTION_TYPE_SU;
+}
+
+static void
+ath12k_dp_mon_hal_rx_parse_u_sig_cmn(const struct hal_mon_usig_cmn *cmn,
+ struct hal_rx_mon_ppdu_info *ppdu_info)
+{
+ u32 common;
+
+ ppdu_info->u_sig_info.bw = le32_get_bits(cmn->info0,
+ HAL_RX_USIG_CMN_INFO0_BW);
+ ppdu_info->u_sig_info.ul_dl = le32_get_bits(cmn->info0,
+ HAL_RX_USIG_CMN_INFO0_UL_DL);
+
+ common = __le32_to_cpu(ppdu_info->u_sig_info.usig.common);
+ common |= IEEE80211_RADIOTAP_EHT_USIG_COMMON_PHY_VER_KNOWN |
+ IEEE80211_RADIOTAP_EHT_USIG_COMMON_BW_KNOWN |
+ IEEE80211_RADIOTAP_EHT_USIG_COMMON_UL_DL_KNOWN |
+ IEEE80211_RADIOTAP_EHT_USIG_COMMON_BSS_COLOR_KNOWN |
+ IEEE80211_RADIOTAP_EHT_USIG_COMMON_TXOP_KNOWN |
+ ATH12K_LE32_DEC_ENC(cmn->info0,
+ HAL_RX_USIG_CMN_INFO0_PHY_VERSION,
+ IEEE80211_RADIOTAP_EHT_USIG_COMMON_PHY_VER) |
+ u32_encode_bits(ppdu_info->u_sig_info.bw,
+ IEEE80211_RADIOTAP_EHT_USIG_COMMON_BW) |
+ u32_encode_bits(ppdu_info->u_sig_info.ul_dl,
+ IEEE80211_RADIOTAP_EHT_USIG_COMMON_UL_DL) |
+ ATH12K_LE32_DEC_ENC(cmn->info0,
+ HAL_RX_USIG_CMN_INFO0_BSS_COLOR,
+ IEEE80211_RADIOTAP_EHT_USIG_COMMON_BSS_COLOR) |
+ ATH12K_LE32_DEC_ENC(cmn->info0,
+ HAL_RX_USIG_CMN_INFO0_TXOP,
+ IEEE80211_RADIOTAP_EHT_USIG_COMMON_TXOP);
+ ppdu_info->u_sig_info.usig.common = cpu_to_le32(common);
+
+ switch (ppdu_info->u_sig_info.bw) {
+ default:
+ fallthrough;
+ case HAL_EHT_BW_20:
+ ppdu_info->bw = HAL_RX_BW_20MHZ;
+ break;
+ case HAL_EHT_BW_40:
+ ppdu_info->bw = HAL_RX_BW_40MHZ;
+ break;
+ case HAL_EHT_BW_80:
+ ppdu_info->bw = HAL_RX_BW_80MHZ;
+ break;
+ case HAL_EHT_BW_160:
+ ppdu_info->bw = HAL_RX_BW_160MHZ;
+ break;
+ case HAL_EHT_BW_320_1:
+ case HAL_EHT_BW_320_2:
+ ppdu_info->bw = HAL_RX_BW_320MHZ;
+ break;
+ }
+}
+
+static void
+ath12k_dp_mon_hal_rx_parse_u_sig_tb(const struct hal_mon_usig_tb *usig_tb,
+ struct hal_rx_mon_ppdu_info *ppdu_info)
+{
+ struct ieee80211_radiotap_eht_usig *usig = &ppdu_info->u_sig_info.usig;
+ enum ieee80211_radiotap_eht_usig_tb spatial_reuse1, spatial_reuse2;
+ u32 common, value, mask;
+
+ spatial_reuse1 = IEEE80211_RADIOTAP_EHT_USIG2_TB_B3_B6_SPATIAL_REUSE_1;
+ spatial_reuse2 = IEEE80211_RADIOTAP_EHT_USIG2_TB_B7_B10_SPATIAL_REUSE_2;
+
+ common = __le32_to_cpu(usig->common);
+ value = __le32_to_cpu(usig->value);
+ mask = __le32_to_cpu(usig->mask);
+
+ ppdu_info->u_sig_info.ppdu_type_comp_mode =
+ le32_get_bits(usig_tb->info0,
+ HAL_RX_USIG_TB_INFO0_PPDU_TYPE_COMP_MODE);
+
+ common |= ATH12K_LE32_DEC_ENC(usig_tb->info0,
+ HAL_RX_USIG_TB_INFO0_RX_INTEG_CHECK_PASS,
+ IEEE80211_RADIOTAP_EHT_USIG_COMMON_BAD_USIG_CRC);
+
+ value |= IEEE80211_RADIOTAP_EHT_USIG1_TB_B20_B25_DISREGARD |
+ u32_encode_bits(ppdu_info->u_sig_info.ppdu_type_comp_mode,
+ IEEE80211_RADIOTAP_EHT_USIG2_TB_B0_B1_PPDU_TYPE) |
+ IEEE80211_RADIOTAP_EHT_USIG2_TB_B2_VALIDATE |
+ ATH12K_LE32_DEC_ENC(usig_tb->info0,
+ HAL_RX_USIG_TB_INFO0_SPATIAL_REUSE_1,
+ spatial_reuse1) |
+ ATH12K_LE32_DEC_ENC(usig_tb->info0,
+ HAL_RX_USIG_TB_INFO0_SPATIAL_REUSE_2,
+ spatial_reuse2) |
+ IEEE80211_RADIOTAP_EHT_USIG2_TB_B11_B15_DISREGARD |
+ ATH12K_LE32_DEC_ENC(usig_tb->info0,
+ HAL_RX_USIG_TB_INFO0_CRC,
+ IEEE80211_RADIOTAP_EHT_USIG2_TB_B16_B19_CRC) |
+ ATH12K_LE32_DEC_ENC(usig_tb->info0,
+ HAL_RX_USIG_TB_INFO0_TAIL,
+ IEEE80211_RADIOTAP_EHT_USIG2_TB_B20_B25_TAIL);
+
+ mask |= IEEE80211_RADIOTAP_EHT_USIG1_TB_B20_B25_DISREGARD |
+ IEEE80211_RADIOTAP_EHT_USIG2_TB_B0_B1_PPDU_TYPE |
+ IEEE80211_RADIOTAP_EHT_USIG2_TB_B2_VALIDATE |
+ spatial_reuse1 | spatial_reuse2 |
+ IEEE80211_RADIOTAP_EHT_USIG2_TB_B11_B15_DISREGARD |
+ IEEE80211_RADIOTAP_EHT_USIG2_TB_B16_B19_CRC |
+ IEEE80211_RADIOTAP_EHT_USIG2_TB_B20_B25_TAIL;
+
+ usig->common = cpu_to_le32(common);
+ usig->value = cpu_to_le32(value);
+ usig->mask = cpu_to_le32(mask);
+}
+
+static void
+ath12k_dp_mon_hal_rx_parse_u_sig_mu(const struct hal_mon_usig_mu *usig_mu,
+ struct hal_rx_mon_ppdu_info *ppdu_info)
+{
+ struct ieee80211_radiotap_eht_usig *usig = &ppdu_info->u_sig_info.usig;
+ enum ieee80211_radiotap_eht_usig_mu sig_symb, punc;
+ u32 common, value, mask;
+
+ sig_symb = IEEE80211_RADIOTAP_EHT_USIG2_MU_B11_B15_EHT_SIG_SYMBOLS;
+ punc = IEEE80211_RADIOTAP_EHT_USIG2_MU_B3_B7_PUNCTURED_INFO;
+
+ common = __le32_to_cpu(usig->common);
+ value = __le32_to_cpu(usig->value);
+ mask = __le32_to_cpu(usig->mask);
+
+ ppdu_info->u_sig_info.ppdu_type_comp_mode =
+ le32_get_bits(usig_mu->info0,
+ HAL_RX_USIG_MU_INFO0_PPDU_TYPE_COMP_MODE);
+ ppdu_info->u_sig_info.eht_sig_mcs =
+ le32_get_bits(usig_mu->info0,
+ HAL_RX_USIG_MU_INFO0_EHT_SIG_MCS);
+ ppdu_info->u_sig_info.num_eht_sig_sym =
+ le32_get_bits(usig_mu->info0,
+ HAL_RX_USIG_MU_INFO0_NUM_EHT_SIG_SYM);
+
+ common |= ATH12K_LE32_DEC_ENC(usig_mu->info0,
+ HAL_RX_USIG_MU_INFO0_RX_INTEG_CHECK_PASS,
+ IEEE80211_RADIOTAP_EHT_USIG_COMMON_BAD_USIG_CRC);
+
+ value |= IEEE80211_RADIOTAP_EHT_USIG1_MU_B20_B24_DISREGARD |
+ IEEE80211_RADIOTAP_EHT_USIG1_MU_B25_VALIDATE |
+ u32_encode_bits(ppdu_info->u_sig_info.ppdu_type_comp_mode,
+ IEEE80211_RADIOTAP_EHT_USIG2_MU_B0_B1_PPDU_TYPE) |
+ IEEE80211_RADIOTAP_EHT_USIG2_MU_B2_VALIDATE |
+ ATH12K_LE32_DEC_ENC(usig_mu->info0,
+ HAL_RX_USIG_MU_INFO0_PUNC_CH_INFO,
+ punc) |
+ IEEE80211_RADIOTAP_EHT_USIG2_MU_B8_VALIDATE |
+ u32_encode_bits(ppdu_info->u_sig_info.eht_sig_mcs,
+ IEEE80211_RADIOTAP_EHT_USIG2_MU_B9_B10_SIG_MCS) |
+ u32_encode_bits(ppdu_info->u_sig_info.num_eht_sig_sym,
+ sig_symb) |
+ ATH12K_LE32_DEC_ENC(usig_mu->info0,
+ HAL_RX_USIG_MU_INFO0_CRC,
+ IEEE80211_RADIOTAP_EHT_USIG2_MU_B16_B19_CRC) |
+ ATH12K_LE32_DEC_ENC(usig_mu->info0,
+ HAL_RX_USIG_MU_INFO0_TAIL,
+ IEEE80211_RADIOTAP_EHT_USIG2_MU_B20_B25_TAIL);
+
+ mask |= IEEE80211_RADIOTAP_EHT_USIG1_MU_B20_B24_DISREGARD |
+ IEEE80211_RADIOTAP_EHT_USIG1_MU_B25_VALIDATE |
+ IEEE80211_RADIOTAP_EHT_USIG2_MU_B0_B1_PPDU_TYPE |
+ IEEE80211_RADIOTAP_EHT_USIG2_MU_B2_VALIDATE |
+ punc |
+ IEEE80211_RADIOTAP_EHT_USIG2_MU_B8_VALIDATE |
+ IEEE80211_RADIOTAP_EHT_USIG2_MU_B9_B10_SIG_MCS |
+ sig_symb |
+ IEEE80211_RADIOTAP_EHT_USIG2_MU_B16_B19_CRC |
+ IEEE80211_RADIOTAP_EHT_USIG2_MU_B20_B25_TAIL;
+
+ usig->common = cpu_to_le32(common);
+ usig->value = cpu_to_le32(value);
+ usig->mask = cpu_to_le32(mask);
+}
+
+static void
+ath12k_dp_mon_hal_rx_parse_u_sig_hdr(const struct hal_mon_usig_hdr *usig,
+ struct hal_rx_mon_ppdu_info *ppdu_info)
+{
+ u8 comp_mode;
+
+ ppdu_info->eht_usig = true;
+
+ ath12k_dp_mon_hal_rx_parse_u_sig_cmn(&usig->cmn, ppdu_info);
+
+ comp_mode = le32_get_bits(usig->non_cmn.mu.info0,
+ HAL_RX_USIG_MU_INFO0_PPDU_TYPE_COMP_MODE);
+
+ if (comp_mode == 0 && ppdu_info->u_sig_info.ul_dl)
+ ath12k_dp_mon_hal_rx_parse_u_sig_tb(&usig->non_cmn.tb, ppdu_info);
+ else
+ ath12k_dp_mon_hal_rx_parse_u_sig_mu(&usig->non_cmn.mu, ppdu_info);
+}
+
+static void
+ath12k_dp_mon_hal_aggr_tlv(struct hal_rx_mon_ppdu_info *ppdu_info,
+ u16 tlv_len, const void *tlv_data)
+{
+ if (tlv_len <= HAL_RX_MON_MAX_AGGR_SIZE - ppdu_info->tlv_aggr.cur_len) {
+ memcpy(ppdu_info->tlv_aggr.buf + ppdu_info->tlv_aggr.cur_len,
+ tlv_data, tlv_len);
+ ppdu_info->tlv_aggr.cur_len += tlv_len;
+ }
+}
+
+static inline bool
+ath12k_dp_mon_hal_rx_is_frame_type_ndp(const struct hal_rx_u_sig_info *usig_info)
+{
+ if (usig_info->ppdu_type_comp_mode == 1 &&
+ usig_info->eht_sig_mcs == 0 &&
+ usig_info->num_eht_sig_sym == 0)
+ return true;
+
+ return false;
+}
+
+static inline bool
+ath12k_dp_mon_hal_rx_is_non_ofdma(const struct hal_rx_u_sig_info *usig_info)
+{
+ u32 ppdu_type_comp_mode = usig_info->ppdu_type_comp_mode;
+ u32 ul_dl = usig_info->ul_dl;
+
+ if ((ppdu_type_comp_mode == HAL_RX_RECEPTION_TYPE_MU_MIMO && ul_dl == 0) ||
+ (ppdu_type_comp_mode == HAL_RX_RECEPTION_TYPE_MU_OFDMA && ul_dl == 0) ||
+ (ppdu_type_comp_mode == HAL_RX_RECEPTION_TYPE_MU_MIMO && ul_dl == 1))
+ return true;
+
+ return false;
+}
+
+static inline bool
+ath12k_dp_mon_hal_rx_is_ofdma(const struct hal_rx_u_sig_info *usig_info)
+{
+ if (usig_info->ppdu_type_comp_mode == 0 && usig_info->ul_dl == 0)
+ return true;
+
+ return false;
+}
+
+static void
+ath12k_dp_mon_hal_rx_parse_eht_sig_ndp(const struct hal_eht_sig_ndp_cmn_eb *eht_sig_ndp,
+ struct hal_rx_mon_ppdu_info *ppdu_info)
+{
+ struct hal_rx_radiotap_eht *eht = &ppdu_info->eht_info.eht;
+ u32 known, data;
+
+ known = __le32_to_cpu(eht->known);
+ known |= IEEE80211_RADIOTAP_EHT_KNOWN_SPATIAL_REUSE |
+ IEEE80211_RADIOTAP_EHT_KNOWN_EHT_LTF |
+ IEEE80211_RADIOTAP_EHT_KNOWN_NSS_S |
+ IEEE80211_RADIOTAP_EHT_KNOWN_BEAMFORMED_S |
+ IEEE80211_RADIOTAP_EHT_KNOWN_DISREGARD_S |
+ IEEE80211_RADIOTAP_EHT_KNOWN_CRC1 |
+ IEEE80211_RADIOTAP_EHT_KNOWN_TAIL1;
+ eht->known = cpu_to_le32(known);
+
+ data = __le32_to_cpu(eht->data[0]);
+ data |= ATH12K_LE32_DEC_ENC(eht_sig_ndp->info0,
+ HAL_RX_EHT_SIG_NDP_CMN_INFO0_SPATIAL_REUSE,
+ IEEE80211_RADIOTAP_EHT_DATA0_SPATIAL_REUSE);
+ /* GI and LTF size are separately indicated in radiotap header
+ * and hence will be parsed from other TLV
+ */
+ data |= ATH12K_LE32_DEC_ENC(eht_sig_ndp->info0,
+ HAL_RX_EHT_SIG_NDP_CMN_INFO0_NUM_LTF_SYM,
+ IEEE80211_RADIOTAP_EHT_DATA0_EHT_LTF);
+
+ data |= ATH12K_LE32_DEC_ENC(eht_sig_ndp->info0,
+ HAL_RX_EHT_SIG_NDP_CMN_INFO0_CRC,
+ IEEE80211_RADIOTAP_EHT_DATA0_CRC1_O);
+
+ data |= ATH12K_LE32_DEC_ENC(eht_sig_ndp->info0,
+ HAL_RX_EHT_SIG_NDP_CMN_INFO0_DISREGARD,
+ IEEE80211_RADIOTAP_EHT_DATA0_DISREGARD_S);
+ eht->data[0] = cpu_to_le32(data);
+
+ data = __le32_to_cpu(eht->data[7]);
+ data |= ATH12K_LE32_DEC_ENC(eht_sig_ndp->info0,
+ HAL_RX_EHT_SIG_NDP_CMN_INFO0_NSS,
+ IEEE80211_RADIOTAP_EHT_DATA7_NSS_S);
+
+ data |= ATH12K_LE32_DEC_ENC(eht_sig_ndp->info0,
+ HAL_RX_EHT_SIG_NDP_CMN_INFO0_BEAMFORMED,
+ IEEE80211_RADIOTAP_EHT_DATA7_BEAMFORMED_S);
+ eht->data[7] = cpu_to_le32(data);
+}
+
+static void
+ath12k_dp_mon_hal_rx_parse_usig_overflow(const struct hal_eht_sig_usig_overflow *ovflow,
+ struct hal_rx_mon_ppdu_info *ppdu_info)
+{
+ struct hal_rx_radiotap_eht *eht = &ppdu_info->eht_info.eht;
+ u32 known, data;
+
+ known = __le32_to_cpu(eht->known);
+ known |= IEEE80211_RADIOTAP_EHT_KNOWN_SPATIAL_REUSE |
+ IEEE80211_RADIOTAP_EHT_KNOWN_EHT_LTF |
+ IEEE80211_RADIOTAP_EHT_KNOWN_LDPC_EXTRA_SYM_OM |
+ IEEE80211_RADIOTAP_EHT_KNOWN_PRE_PADD_FACOR_OM |
+ IEEE80211_RADIOTAP_EHT_KNOWN_PE_DISAMBIGUITY_OM |
+ IEEE80211_RADIOTAP_EHT_KNOWN_DISREGARD_O;
+ eht->known = cpu_to_le32(known);
+
+ data = __le32_to_cpu(eht->data[0]);
+ data |= ATH12K_LE32_DEC_ENC(ovflow->info0,
+ HAL_RX_EHT_SIG_OVERFLOW_INFO0_SPATIAL_REUSE,
+ IEEE80211_RADIOTAP_EHT_DATA0_SPATIAL_REUSE);
+
+ /* GI and LTF size are separately indicated in radiotap header
+ * and hence will be parsed from other TLV
+ */
+ data |= ATH12K_LE32_DEC_ENC(ovflow->info0,
+ HAL_RX_EHT_SIG_OVERFLOW_INFO0_NUM_LTF_SYM,
+ IEEE80211_RADIOTAP_EHT_DATA0_EHT_LTF);
+
+ data |= ATH12K_LE32_DEC_ENC(ovflow->info0,
+ HAL_RX_EHT_SIG_OVERFLOW_INFO0_LDPC_EXTA_SYM,
+ IEEE80211_RADIOTAP_EHT_DATA0_LDPC_EXTRA_SYM_OM);
+
+ data |= ATH12K_LE32_DEC_ENC(ovflow->info0,
+ HAL_RX_EHT_SIG_OVERFLOW_INFO0_PRE_FEC_PAD_FACTOR,
+ IEEE80211_RADIOTAP_EHT_DATA0_PRE_PADD_FACOR_OM);
+
+ data |= ATH12K_LE32_DEC_ENC(ovflow->info0,
+ HAL_RX_EHT_SIG_OVERFLOW_INFO0_DISAMBIGUITY,
+ IEEE80211_RADIOTAP_EHT_DATA0_PE_DISAMBIGUITY_OM);
+
+ data |= ATH12K_LE32_DEC_ENC(ovflow->info0,
+ HAL_RX_EHT_SIG_OVERFLOW_INFO0_DISREGARD,
+ IEEE80211_RADIOTAP_EHT_DATA0_DISREGARD_O);
+ eht->data[0] = cpu_to_le32(data);
+}
+
+static void
+ath12k_dp_mon_hal_rx_parse_non_ofdma_users(const struct hal_eht_sig_non_ofdma_cmn_eb *eb,
+ struct hal_rx_mon_ppdu_info *ppdu_info)
+{
+ struct hal_rx_radiotap_eht *eht = &ppdu_info->eht_info.eht;
+ u32 known, data;
+
+ known = __le32_to_cpu(eht->known);
+ known |= IEEE80211_RADIOTAP_EHT_KNOWN_NR_NON_OFDMA_USERS_M;
+ eht->known = cpu_to_le32(known);
+
+ data = __le32_to_cpu(eht->data[7]);
+ data |= ATH12K_LE32_DEC_ENC(eb->info0,
+ HAL_RX_EHT_SIG_NON_OFDMA_INFO0_NUM_USERS,
+ IEEE80211_RADIOTAP_EHT_DATA7_NUM_OF_NON_OFDMA_USERS);
+ eht->data[7] = cpu_to_le32(data);
+}
+
+static void
+ath12k_dp_mon_hal_rx_parse_eht_mumimo_user(const struct hal_eht_sig_mu_mimo *user,
+ struct hal_rx_mon_ppdu_info *ppdu_info)
+{
+ struct hal_rx_eht_info *eht_info = &ppdu_info->eht_info;
+ u32 user_idx;
+
+ if (eht_info->num_user_info >= ARRAY_SIZE(eht_info->user_info))
+ return;
+
+ user_idx = eht_info->num_user_info++;
+
+ eht_info->user_info[user_idx] |=
+ IEEE80211_RADIOTAP_EHT_USER_INFO_STA_ID_KNOWN |
+ IEEE80211_RADIOTAP_EHT_USER_INFO_MCS_KNOWN |
+ IEEE80211_RADIOTAP_EHT_USER_INFO_CODING_KNOWN |
+ IEEE80211_RADIOTAP_EHT_USER_INFO_SPATIAL_CONFIG_KNOWN_M |
+ ATH12K_LE32_DEC_ENC(user->info0,
+ HAL_RX_EHT_SIG_MUMIMO_USER_INFO0_STA_ID,
+ IEEE80211_RADIOTAP_EHT_USER_INFO_STA_ID) |
+ ATH12K_LE32_DEC_ENC(user->info0,
+ HAL_RX_EHT_SIG_MUMIMO_USER_INFO0_CODING,
+ IEEE80211_RADIOTAP_EHT_USER_INFO_CODING) |
+ ATH12K_LE32_DEC_ENC(user->info0,
+ HAL_RX_EHT_SIG_MUMIMO_USER_INFO0_MCS,
+ IEEE80211_RADIOTAP_EHT_USER_INFO_MCS) |
+ ATH12K_LE32_DEC_ENC(user->info0,
+ HAL_RX_EHT_SIG_MUMIMO_USER_INFO0_SPATIAL_CODING,
+ IEEE80211_RADIOTAP_EHT_USER_INFO_SPATIAL_CONFIG_M);
+
+ ppdu_info->mcs = le32_get_bits(user->info0,
+ HAL_RX_EHT_SIG_MUMIMO_USER_INFO0_MCS);
+}
+
+static void
+ath12k_dp_mon_hal_rx_parse_eht_non_mumimo_user(const struct hal_eht_sig_non_mu_mimo *user,
+ struct hal_rx_mon_ppdu_info *ppdu_info)
+{
+ struct hal_rx_eht_info *eht_info = &ppdu_info->eht_info;
+ u32 user_idx;
+
+ if (eht_info->num_user_info >= ARRAY_SIZE(eht_info->user_info))
+ return;
+
+ user_idx = eht_info->num_user_info++;
+
+ eht_info->user_info[user_idx] |=
+ IEEE80211_RADIOTAP_EHT_USER_INFO_STA_ID_KNOWN |
+ IEEE80211_RADIOTAP_EHT_USER_INFO_MCS_KNOWN |
+ IEEE80211_RADIOTAP_EHT_USER_INFO_CODING_KNOWN |
+ IEEE80211_RADIOTAP_EHT_USER_INFO_NSS_KNOWN_O |
+ IEEE80211_RADIOTAP_EHT_USER_INFO_BEAMFORMING_KNOWN_O |
+ ATH12K_LE32_DEC_ENC(user->info0,
+ HAL_RX_EHT_SIG_NON_MUMIMO_USER_INFO0_STA_ID,
+ IEEE80211_RADIOTAP_EHT_USER_INFO_STA_ID) |
+ ATH12K_LE32_DEC_ENC(user->info0,
+ HAL_RX_EHT_SIG_NON_MUMIMO_USER_INFO0_CODING,
+ IEEE80211_RADIOTAP_EHT_USER_INFO_CODING) |
+ ATH12K_LE32_DEC_ENC(user->info0,
+ HAL_RX_EHT_SIG_NON_MUMIMO_USER_INFO0_MCS,
+ IEEE80211_RADIOTAP_EHT_USER_INFO_MCS) |
+ ATH12K_LE32_DEC_ENC(user->info0,
+ HAL_RX_EHT_SIG_NON_MUMIMO_USER_INFO0_NSS,
+ IEEE80211_RADIOTAP_EHT_USER_INFO_NSS_O) |
+ ATH12K_LE32_DEC_ENC(user->info0,
+ HAL_RX_EHT_SIG_NON_MUMIMO_USER_INFO0_BEAMFORMED,
+ IEEE80211_RADIOTAP_EHT_USER_INFO_BEAMFORMING_O);
+
+ ppdu_info->mcs = le32_get_bits(user->info0,
+ HAL_RX_EHT_SIG_NON_MUMIMO_USER_INFO0_MCS);
+
+ ppdu_info->nss = le32_get_bits(user->info0,
+ HAL_RX_EHT_SIG_NON_MUMIMO_USER_INFO0_NSS) + 1;
+}
+
+static inline bool
+ath12k_dp_mon_hal_rx_is_mu_mimo_user(const struct hal_rx_u_sig_info *usig_info)
+{
+ if (usig_info->ppdu_type_comp_mode == HAL_RX_RECEPTION_TYPE_SU &&
+ usig_info->ul_dl == 1)
+ return true;
+
+ return false;
+}
+
+static void
+ath12k_dp_mon_hal_rx_parse_eht_sig_non_ofdma(const void *tlv,
+ struct hal_rx_mon_ppdu_info *ppdu_info)
+{
+ const struct hal_eht_sig_non_ofdma_cmn_eb *eb = tlv;
+
+ ath12k_dp_mon_hal_rx_parse_usig_overflow(tlv, ppdu_info);
+ ath12k_dp_mon_hal_rx_parse_non_ofdma_users(eb, ppdu_info);
+
+ if (ath12k_dp_mon_hal_rx_is_mu_mimo_user(&ppdu_info->u_sig_info))
+ ath12k_dp_mon_hal_rx_parse_eht_mumimo_user(&eb->user_field.mu_mimo,
+ ppdu_info);
+ else
+ ath12k_dp_mon_hal_rx_parse_eht_non_mumimo_user(&eb->user_field.n_mu_mimo,
+ ppdu_info);
+}
+
+static void
+ath12k_dp_mon_hal_rx_parse_ru_allocation(const struct hal_eht_sig_ofdma_cmn_eb *eb,
+ struct hal_rx_mon_ppdu_info *ppdu_info)
+{
+ const struct hal_eht_sig_ofdma_cmn_eb1 *ofdma_cmn_eb1 = &eb->eb1;
+ const struct hal_eht_sig_ofdma_cmn_eb2 *ofdma_cmn_eb2 = &eb->eb2;
+ struct hal_rx_radiotap_eht *eht = &ppdu_info->eht_info.eht;
+ enum ieee80211_radiotap_eht_data ru_123, ru_124, ru_125, ru_126;
+ enum ieee80211_radiotap_eht_data ru_121, ru_122, ru_112, ru_111;
+ u32 data;
+
+ ru_123 = IEEE80211_RADIOTAP_EHT_DATA4_RU_ALLOC_CC_1_2_3;
+ ru_124 = IEEE80211_RADIOTAP_EHT_DATA5_RU_ALLOC_CC_1_2_4;
+ ru_125 = IEEE80211_RADIOTAP_EHT_DATA5_RU_ALLOC_CC_1_2_5;
+ ru_126 = IEEE80211_RADIOTAP_EHT_DATA6_RU_ALLOC_CC_1_2_6;
+ ru_121 = IEEE80211_RADIOTAP_EHT_DATA3_RU_ALLOC_CC_1_2_1;
+ ru_122 = IEEE80211_RADIOTAP_EHT_DATA3_RU_ALLOC_CC_1_2_2;
+ ru_112 = IEEE80211_RADIOTAP_EHT_DATA2_RU_ALLOC_CC_1_1_2;
+ ru_111 = IEEE80211_RADIOTAP_EHT_DATA1_RU_ALLOC_CC_1_1_1;
+
+ switch (ppdu_info->u_sig_info.bw) {
+ case HAL_EHT_BW_320_2:
+ case HAL_EHT_BW_320_1:
+ data = __le32_to_cpu(eht->data[4]);
+ /* CC1 2::3 */
+ data |= IEEE80211_RADIOTAP_EHT_DATA4_RU_ALLOC_CC_1_2_3_KNOWN |
+ ATH12K_LE64_DEC_ENC(ofdma_cmn_eb2->info0,
+ HAL_RX_EHT_SIG_OFDMA_EB2_RU_ALLOC_2_3,
+ ru_123);
+ eht->data[4] = cpu_to_le32(data);
+
+ data = __le32_to_cpu(eht->data[5]);
+ /* CC1 2::4 */
+ data |= IEEE80211_RADIOTAP_EHT_DATA5_RU_ALLOC_CC_1_2_4_KNOWN |
+ ATH12K_LE64_DEC_ENC(ofdma_cmn_eb2->info0,
+ HAL_RX_EHT_SIG_OFDMA_EB2_RU_ALLOC_2_4,
+ ru_124);
+
+ /* CC1 2::5 */
+ data |= IEEE80211_RADIOTAP_EHT_DATA5_RU_ALLOC_CC_1_2_5_KNOWN |
+ ATH12K_LE64_DEC_ENC(ofdma_cmn_eb2->info0,
+ HAL_RX_EHT_SIG_OFDMA_EB2_RU_ALLOC_2_5,
+ ru_125);
+ eht->data[5] = cpu_to_le32(data);
+
+ data = __le32_to_cpu(eht->data[6]);
+ /* CC1 2::6 */
+ data |= IEEE80211_RADIOTAP_EHT_DATA6_RU_ALLOC_CC_1_2_6_KNOWN |
+ ATH12K_LE64_DEC_ENC(ofdma_cmn_eb2->info0,
+ HAL_RX_EHT_SIG_OFDMA_EB2_RU_ALLOC_2_6,
+ ru_126);
+ eht->data[6] = cpu_to_le32(data);
+
+ fallthrough;
+ case HAL_EHT_BW_160:
+ data = __le32_to_cpu(eht->data[3]);
+ /* CC1 2::1 */
+ data |= IEEE80211_RADIOTAP_EHT_DATA3_RU_ALLOC_CC_1_2_1_KNOWN |
+ ATH12K_LE64_DEC_ENC(ofdma_cmn_eb2->info0,
+ HAL_RX_EHT_SIG_OFDMA_EB2_RU_ALLOC_2_1,
+ ru_121);
+ /* CC1 2::2 */
+ data |= IEEE80211_RADIOTAP_EHT_DATA3_RU_ALLOC_CC_1_2_2_KNOWN |
+ ATH12K_LE64_DEC_ENC(ofdma_cmn_eb2->info0,
+ HAL_RX_EHT_SIG_OFDMA_EB2_RU_ALLOC_2_2,
+ ru_122);
+ eht->data[3] = cpu_to_le32(data);
+
+ fallthrough;
+ case HAL_EHT_BW_80:
+ data = __le32_to_cpu(eht->data[2]);
+ /* CC1 1::2 */
+ data |= IEEE80211_RADIOTAP_EHT_DATA2_RU_ALLOC_CC_1_1_2_KNOWN |
+ ATH12K_LE64_DEC_ENC(ofdma_cmn_eb1->info0,
+ HAL_RX_EHT_SIG_OFDMA_EB1_RU_ALLOC_1_2,
+ ru_112);
+ eht->data[2] = cpu_to_le32(data);
+
+ fallthrough;
+ case HAL_EHT_BW_40:
+ fallthrough;
+ case HAL_EHT_BW_20:
+ data = __le32_to_cpu(eht->data[1]);
+ /* CC1 1::1 */
+ data |= IEEE80211_RADIOTAP_EHT_DATA1_RU_ALLOC_CC_1_1_1_KNOWN |
+ ATH12K_LE64_DEC_ENC(ofdma_cmn_eb1->info0,
+ HAL_RX_EHT_SIG_OFDMA_EB1_RU_ALLOC_1_1,
+ ru_111);
+ eht->data[1] = cpu_to_le32(data);
+ break;
+ default:
+ break;
+ }
+}
+
+static void
+ath12k_dp_mon_hal_rx_parse_eht_sig_ofdma(const void *tlv,
+ struct hal_rx_mon_ppdu_info *ppdu_info)
+{
+ const struct hal_eht_sig_ofdma_cmn_eb *ofdma = tlv;
+
+ ath12k_dp_mon_hal_rx_parse_usig_overflow(tlv, ppdu_info);
+ ath12k_dp_mon_hal_rx_parse_ru_allocation(ofdma, ppdu_info);
+
+ ath12k_dp_mon_hal_rx_parse_eht_non_mumimo_user(&ofdma->user_field.n_mu_mimo,
+ ppdu_info);
+}
+
+static void
+ath12k_dp_mon_parse_eht_sig_hdr(struct hal_rx_mon_ppdu_info *ppdu_info,
+ const void *tlv_data)
+{
+ ppdu_info->is_eht = true;
+
+ if (ath12k_dp_mon_hal_rx_is_frame_type_ndp(&ppdu_info->u_sig_info))
+ ath12k_dp_mon_hal_rx_parse_eht_sig_ndp(tlv_data, ppdu_info);
+ else if (ath12k_dp_mon_hal_rx_is_non_ofdma(&ppdu_info->u_sig_info))
+ ath12k_dp_mon_hal_rx_parse_eht_sig_non_ofdma(tlv_data, ppdu_info);
+ else if (ath12k_dp_mon_hal_rx_is_ofdma(&ppdu_info->u_sig_info))
+ ath12k_dp_mon_hal_rx_parse_eht_sig_ofdma(tlv_data, ppdu_info);
+}
+
+static inline enum ath12k_eht_ru_size
+hal_rx_mon_hal_ru_size_to_ath12k_ru_size(u32 hal_ru_size)
+{
+ switch (hal_ru_size) {
+ case HAL_EHT_RU_26:
+ return ATH12K_EHT_RU_26;
+ case HAL_EHT_RU_52:
+ return ATH12K_EHT_RU_52;
+ case HAL_EHT_RU_78:
+ return ATH12K_EHT_RU_52_26;
+ case HAL_EHT_RU_106:
+ return ATH12K_EHT_RU_106;
+ case HAL_EHT_RU_132:
+ return ATH12K_EHT_RU_106_26;
+ case HAL_EHT_RU_242:
+ return ATH12K_EHT_RU_242;
+ case HAL_EHT_RU_484:
+ return ATH12K_EHT_RU_484;
+ case HAL_EHT_RU_726:
+ return ATH12K_EHT_RU_484_242;
+ case HAL_EHT_RU_996:
+ return ATH12K_EHT_RU_996;
+ case HAL_EHT_RU_996x2:
+ return ATH12K_EHT_RU_996x2;
+ case HAL_EHT_RU_996x3:
+ return ATH12K_EHT_RU_996x3;
+ case HAL_EHT_RU_996x4:
+ return ATH12K_EHT_RU_996x4;
+ case HAL_EHT_RU_NONE:
+ return ATH12K_EHT_RU_INVALID;
+ case HAL_EHT_RU_996_484:
+ return ATH12K_EHT_RU_996_484;
+ case HAL_EHT_RU_996x2_484:
+ return ATH12K_EHT_RU_996x2_484;
+ case HAL_EHT_RU_996x3_484:
+ return ATH12K_EHT_RU_996x3_484;
+ case HAL_EHT_RU_996_484_242:
+ return ATH12K_EHT_RU_996_484_242;
+ default:
+ return ATH12K_EHT_RU_INVALID;
+ }
+}
+
+static inline u32
+hal_rx_ul_ofdma_ru_size_to_width(enum ath12k_eht_ru_size ru_size)
+{
+ switch (ru_size) {
+ case ATH12K_EHT_RU_26:
+ return RU_26;
+ case ATH12K_EHT_RU_52:
+ return RU_52;
+ case ATH12K_EHT_RU_52_26:
+ return RU_52_26;
+ case ATH12K_EHT_RU_106:
+ return RU_106;
+ case ATH12K_EHT_RU_106_26:
+ return RU_106_26;
+ case ATH12K_EHT_RU_242:
+ return RU_242;
+ case ATH12K_EHT_RU_484:
+ return RU_484;
+ case ATH12K_EHT_RU_484_242:
+ return RU_484_242;
+ case ATH12K_EHT_RU_996:
+ return RU_996;
+ case ATH12K_EHT_RU_996_484:
+ return RU_996_484;
+ case ATH12K_EHT_RU_996_484_242:
+ return RU_996_484_242;
+ case ATH12K_EHT_RU_996x2:
+ return RU_2X996;
+ case ATH12K_EHT_RU_996x2_484:
+ return RU_2X996_484;
+ case ATH12K_EHT_RU_996x3:
+ return RU_3X996;
+ case ATH12K_EHT_RU_996x3_484:
+ return RU_3X996_484;
+ case ATH12K_EHT_RU_996x4:
+ return RU_4X996;
+ default:
+ return RU_INVALID;
+ }
+}
+
+static void
+ath12k_dp_mon_hal_rx_parse_user_info(const struct hal_receive_user_info *rx_usr_info,
+ u16 user_id,
+ struct hal_rx_mon_ppdu_info *ppdu_info)
+{
+ struct hal_rx_user_status *mon_rx_user_status = NULL;
+ struct hal_rx_radiotap_eht *eht = &ppdu_info->eht_info.eht;
+ enum ath12k_eht_ru_size rtap_ru_size = ATH12K_EHT_RU_INVALID;
+ u32 ru_width, reception_type, ru_index = HAL_EHT_RU_INVALID;
+ u32 ru_type_80_0, ru_start_index_80_0;
+ u32 ru_type_80_1, ru_start_index_80_1;
+ u32 ru_type_80_2, ru_start_index_80_2;
+ u32 ru_type_80_3, ru_start_index_80_3;
+ u32 ru_size = 0, num_80mhz_with_ru = 0;
+ u64 ru_index_320mhz = 0;
+ u32 ru_index_per80mhz;
+
+ reception_type = le32_get_bits(rx_usr_info->info0,
+ HAL_RX_USR_INFO0_RECEPTION_TYPE);
+
+ switch (reception_type) {
+ case HAL_RECEPTION_TYPE_SU:
+ ppdu_info->reception_type = HAL_RX_RECEPTION_TYPE_SU;
+ break;
+ case HAL_RECEPTION_TYPE_DL_MU_MIMO:
+ case HAL_RECEPTION_TYPE_UL_MU_MIMO:
+ ppdu_info->reception_type = HAL_RX_RECEPTION_TYPE_MU_MIMO;
+ break;
+ case HAL_RECEPTION_TYPE_DL_MU_OFMA:
+ case HAL_RECEPTION_TYPE_UL_MU_OFDMA:
+ ppdu_info->reception_type = HAL_RX_RECEPTION_TYPE_MU_OFDMA;
+ break;
+ case HAL_RECEPTION_TYPE_DL_MU_OFDMA_MIMO:
+ case HAL_RECEPTION_TYPE_UL_MU_OFDMA_MIMO:
+ ppdu_info->reception_type = HAL_RX_RECEPTION_TYPE_MU_OFDMA_MIMO;
+ }
+
+ ppdu_info->is_stbc = le32_get_bits(rx_usr_info->info0, HAL_RX_USR_INFO0_STBC);
+ ppdu_info->ldpc = le32_get_bits(rx_usr_info->info2, HAL_RX_USR_INFO2_LDPC);
+ ppdu_info->dcm = le32_get_bits(rx_usr_info->info2, HAL_RX_USR_INFO2_STA_DCM);
+ ppdu_info->bw = le32_get_bits(rx_usr_info->info1, HAL_RX_USR_INFO1_RX_BW);
+ ppdu_info->mcs = le32_get_bits(rx_usr_info->info1, HAL_RX_USR_INFO1_MCS);
+ ppdu_info->nss = le32_get_bits(rx_usr_info->info2, HAL_RX_USR_INFO2_NSS) + 1;
+
+ if (user_id < HAL_MAX_UL_MU_USERS) {
+ mon_rx_user_status = &ppdu_info->userstats[user_id];
+ mon_rx_user_status->mcs = ppdu_info->mcs;
+ mon_rx_user_status->nss = ppdu_info->nss;
+ }
+
+ if (!(ppdu_info->reception_type == HAL_RX_RECEPTION_TYPE_MU_MIMO ||
+ ppdu_info->reception_type == HAL_RX_RECEPTION_TYPE_MU_OFDMA ||
+ ppdu_info->reception_type == HAL_RX_RECEPTION_TYPE_MU_OFDMA_MIMO))
+ return;
+
+ /* RU allocation present only for OFDMA reception */
+ ru_type_80_0 = le32_get_bits(rx_usr_info->info2, HAL_RX_USR_INFO2_RU_TYPE_80_0);
+ ru_start_index_80_0 = le32_get_bits(rx_usr_info->info3,
+ HAL_RX_USR_INFO3_RU_START_IDX_80_0);
+ if (ru_type_80_0 != HAL_EHT_RU_NONE) {
+ ru_size += ru_type_80_0;
+ ru_index_per80mhz = ru_start_index_80_0;
+ ru_index = ru_index_per80mhz;
+ ru_index_320mhz |= HAL_RU_PER80(ru_type_80_0, 0, ru_index_per80mhz);
+ num_80mhz_with_ru++;
+ }
+
+ ru_type_80_1 = le32_get_bits(rx_usr_info->info2, HAL_RX_USR_INFO2_RU_TYPE_80_1);
+ ru_start_index_80_1 = le32_get_bits(rx_usr_info->info3,
+ HAL_RX_USR_INFO3_RU_START_IDX_80_1);
+ if (ru_type_80_1 != HAL_EHT_RU_NONE) {
+ ru_size += ru_type_80_1;
+ ru_index_per80mhz = ru_start_index_80_1;
+ ru_index = ru_index_per80mhz;
+ ru_index_320mhz |= HAL_RU_PER80(ru_type_80_1, 1, ru_index_per80mhz);
+ num_80mhz_with_ru++;
+ }
+
+ ru_type_80_2 = le32_get_bits(rx_usr_info->info2, HAL_RX_USR_INFO2_RU_TYPE_80_2);
+ ru_start_index_80_2 = le32_get_bits(rx_usr_info->info3,
+ HAL_RX_USR_INFO3_RU_START_IDX_80_2);
+ if (ru_type_80_2 != HAL_EHT_RU_NONE) {
+ ru_size += ru_type_80_2;
+ ru_index_per80mhz = ru_start_index_80_2;
+ ru_index = ru_index_per80mhz;
+ ru_index_320mhz |= HAL_RU_PER80(ru_type_80_2, 2, ru_index_per80mhz);
+ num_80mhz_with_ru++;
+ }
+
+ ru_type_80_3 = le32_get_bits(rx_usr_info->info2, HAL_RX_USR_INFO2_RU_TYPE_80_3);
+ ru_start_index_80_3 = le32_get_bits(rx_usr_info->info2,
+ HAL_RX_USR_INFO3_RU_START_IDX_80_3);
+ if (ru_type_80_3 != HAL_EHT_RU_NONE) {
+ ru_size += ru_type_80_3;
+ ru_index_per80mhz = ru_start_index_80_3;
+ ru_index = ru_index_per80mhz;
+ ru_index_320mhz |= HAL_RU_PER80(ru_type_80_3, 3, ru_index_per80mhz);
+ num_80mhz_with_ru++;
+ }
+
+ if (num_80mhz_with_ru > 1) {
+ /* Calculate the MRU index */
+ switch (ru_index_320mhz) {
+ case HAL_EHT_RU_996_484_0:
+ case HAL_EHT_RU_996x2_484_0:
+ case HAL_EHT_RU_996x3_484_0:
+ ru_index = 0;
+ break;
+ case HAL_EHT_RU_996_484_1:
+ case HAL_EHT_RU_996x2_484_1:
+ case HAL_EHT_RU_996x3_484_1:
+ ru_index = 1;
+ break;
+ case HAL_EHT_RU_996_484_2:
+ case HAL_EHT_RU_996x2_484_2:
+ case HAL_EHT_RU_996x3_484_2:
+ ru_index = 2;
+ break;
+ case HAL_EHT_RU_996_484_3:
+ case HAL_EHT_RU_996x2_484_3:
+ case HAL_EHT_RU_996x3_484_3:
+ ru_index = 3;
+ break;
+ case HAL_EHT_RU_996_484_4:
+ case HAL_EHT_RU_996x2_484_4:
+ case HAL_EHT_RU_996x3_484_4:
+ ru_index = 4;
+ break;
+ case HAL_EHT_RU_996_484_5:
+ case HAL_EHT_RU_996x2_484_5:
+ case HAL_EHT_RU_996x3_484_5:
+ ru_index = 5;
+ break;
+ case HAL_EHT_RU_996_484_6:
+ case HAL_EHT_RU_996x2_484_6:
+ case HAL_EHT_RU_996x3_484_6:
+ ru_index = 6;
+ break;
+ case HAL_EHT_RU_996_484_7:
+ case HAL_EHT_RU_996x2_484_7:
+ case HAL_EHT_RU_996x3_484_7:
+ ru_index = 7;
+ break;
+ case HAL_EHT_RU_996x2_484_8:
+ ru_index = 8;
+ break;
+ case HAL_EHT_RU_996x2_484_9:
+ ru_index = 9;
+ break;
+ case HAL_EHT_RU_996x2_484_10:
+ ru_index = 10;
+ break;
+ case HAL_EHT_RU_996x2_484_11:
+ ru_index = 11;
+ break;
+ default:
+ ru_index = HAL_EHT_RU_INVALID;
+ break;
+ }
+
+ ru_size += 4;
+ }
+
+ rtap_ru_size = hal_rx_mon_hal_ru_size_to_ath12k_ru_size(ru_size);
+ if (rtap_ru_size != ATH12K_EHT_RU_INVALID) {
+ u32 known, data;
+
+ known = __le32_to_cpu(eht->known);
+ known |= IEEE80211_RADIOTAP_EHT_KNOWN_RU_MRU_SIZE_OM;
+ eht->known = cpu_to_le32(known);
+
+ data = __le32_to_cpu(eht->data[1]);
+ data |= u32_encode_bits(rtap_ru_size,
+ IEEE80211_RADIOTAP_EHT_DATA1_RU_SIZE);
+ eht->data[1] = cpu_to_le32(data);
+ }
+
+ if (ru_index != HAL_EHT_RU_INVALID) {
+ u32 known, data;
+
+ known = __le32_to_cpu(eht->known);
+ known |= IEEE80211_RADIOTAP_EHT_KNOWN_RU_MRU_INDEX_OM;
+ eht->known = cpu_to_le32(known);
+
+ data = __le32_to_cpu(eht->data[1]);
+ data |= u32_encode_bits(rtap_ru_size,
+ IEEE80211_RADIOTAP_EHT_DATA1_RU_INDEX);
+ eht->data[1] = cpu_to_le32(data);
+ }
+
+ if (mon_rx_user_status && ru_index != HAL_EHT_RU_INVALID &&
+ rtap_ru_size != ATH12K_EHT_RU_INVALID) {
+ mon_rx_user_status->ul_ofdma_ru_start_index = ru_index;
+ mon_rx_user_status->ul_ofdma_ru_size = rtap_ru_size;
+
+ ru_width = hal_rx_ul_ofdma_ru_size_to_width(rtap_ru_size);
+
+ mon_rx_user_status->ul_ofdma_ru_width = ru_width;
+ mon_rx_user_status->ofdma_info_valid = 1;
+ }
+}
+
+static void ath12k_dp_mon_parse_rx_msdu_end_err(u32 info, u32 *errmap)
+{
+ if (info & RX_MSDU_END_INFO13_FCS_ERR)
+ *errmap |= HAL_RX_MPDU_ERR_FCS;
+
+ if (info & RX_MSDU_END_INFO13_DECRYPT_ERR)
+ *errmap |= HAL_RX_MPDU_ERR_DECRYPT;
+
+ if (info & RX_MSDU_END_INFO13_TKIP_MIC_ERR)
+ *errmap |= HAL_RX_MPDU_ERR_TKIP_MIC;
+
+ if (info & RX_MSDU_END_INFO13_A_MSDU_ERROR)
+ *errmap |= HAL_RX_MPDU_ERR_AMSDU_ERR;
+
+ if (info & RX_MSDU_END_INFO13_OVERFLOW_ERR)
+ *errmap |= HAL_RX_MPDU_ERR_OVERFLOW;
+
+ if (info & RX_MSDU_END_INFO13_MSDU_LEN_ERR)
+ *errmap |= HAL_RX_MPDU_ERR_MSDU_LEN;
+
+ if (info & RX_MSDU_END_INFO13_MPDU_LEN_ERR)
+ *errmap |= HAL_RX_MPDU_ERR_MPDU_LEN;
+}
+
+static void
+ath12k_dp_mon_parse_status_msdu_end(struct ath12k_mon_data *pmon,
+ const struct hal_rx_msdu_end *msdu_end)
+{
+ ath12k_dp_mon_parse_rx_msdu_end_err(__le32_to_cpu(msdu_end->info2),
+ &pmon->err_bitmap);
+ pmon->decap_format = le32_get_bits(msdu_end->info1,
+ RX_MSDU_END_INFO11_DECAP_FORMAT);
}
static enum hal_rx_mon_status
-ath12k_dp_mon_rx_parse_status_tlv(struct ath12k_base *ab,
+ath12k_dp_mon_rx_parse_status_tlv(struct ath12k *ar,
struct ath12k_mon_data *pmon,
- u32 tlv_tag, const void *tlv_data,
- u32 userid)
+ const struct hal_tlv_64_hdr *tlv)
{
struct hal_rx_mon_ppdu_info *ppdu_info = &pmon->mon_ppdu_info;
- u32 info[7];
+ const void *tlv_data = tlv->value;
+ u32 info[7], userid;
+ u16 tlv_tag, tlv_len;
+
+ tlv_tag = le64_get_bits(tlv->tl, HAL_TLV_64_HDR_TAG);
+ tlv_len = le64_get_bits(tlv->tl, HAL_TLV_64_HDR_LEN);
+ userid = le64_get_bits(tlv->tl, HAL_TLV_64_USR_ID);
+
+ if (ppdu_info->tlv_aggr.in_progress && ppdu_info->tlv_aggr.tlv_tag != tlv_tag) {
+ ath12k_dp_mon_parse_eht_sig_hdr(ppdu_info, ppdu_info->tlv_aggr.buf);
+
+ ppdu_info->tlv_aggr.in_progress = false;
+ ppdu_info->tlv_aggr.cur_len = 0;
+ }
switch (tlv_tag) {
case HAL_RX_PPDU_START: {
@@ -638,6 +1538,9 @@ ath12k_dp_mon_rx_parse_status_tlv(struct ath12k_base *ab,
ppdu_info->num_mpdu_fcs_err =
u32_get_bits(info[0],
HAL_RX_PPDU_END_USER_STATS_INFO0_MPDU_CNT_FCS_ERR);
+ ppdu_info->peer_id =
+ u32_get_bits(info[0], HAL_RX_PPDU_END_USER_STATS_INFO0_PEER_ID);
+
switch (ppdu_info->preamble_type) {
case HAL_RX_PREAMBLE_11N:
ppdu_info->ht_flags = 1;
@@ -648,6 +1551,9 @@ ath12k_dp_mon_rx_parse_status_tlv(struct ath12k_base *ab,
case HAL_RX_PREAMBLE_11AX:
ppdu_info->he_flags = 1;
break;
+ case HAL_RX_PREAMBLE_11BE:
+ ppdu_info->is_eht = true;
+ break;
default:
break;
}
@@ -655,6 +1561,11 @@ ath12k_dp_mon_rx_parse_status_tlv(struct ath12k_base *ab,
if (userid < HAL_MAX_UL_MU_USERS) {
struct hal_rx_user_status *rxuser_stats =
&ppdu_info->userstats[userid];
+
+ if (ppdu_info->num_mpdu_fcs_ok > 1 ||
+ ppdu_info->num_mpdu_fcs_err > 1)
+ ppdu_info->userstats[userid].ampdu_present = true;
+
ppdu_info->num_users += 1;
ath12k_dp_mon_rx_handle_ofdma_info(eu_stats, rxuser_stats);
@@ -730,6 +1641,17 @@ ath12k_dp_mon_rx_parse_status_tlv(struct ath12k_base *ab,
HAL_RX_PHYRX_RSSI_LEGACY_INFO_INFO0_RX_BW);
break;
}
+ case HAL_PHYRX_OTHER_RECEIVE_INFO: {
+ const struct hal_phyrx_common_user_info *cmn_usr_info = tlv_data;
+
+ ppdu_info->gi = le32_get_bits(cmn_usr_info->info0,
+ HAL_RX_PHY_CMN_USER_INFO0_GI);
+ break;
+ }
+ case HAL_RX_PPDU_START_USER_INFO:
+ ath12k_dp_mon_hal_rx_parse_user_info(tlv_data, userid, ppdu_info);
+ break;
+
case HAL_RXPCU_PPDU_END_INFO: {
const struct hal_rx_ppdu_end_duration *ppdu_rx_duration = tlv_data;
@@ -743,7 +1665,6 @@ ath12k_dp_mon_rx_parse_status_tlv(struct ath12k_base *ab,
}
case HAL_RX_MPDU_START: {
const struct hal_rx_mpdu_start *mpdu_start = tlv_data;
- struct dp_mon_mpdu *mon_mpdu = pmon->mon_mpdu;
u16 peer_id;
info[1] = __le32_to_cpu(mpdu_start->info1);
@@ -756,68 +1677,39 @@ ath12k_dp_mon_rx_parse_status_tlv(struct ath12k_base *ab,
if (userid < HAL_MAX_UL_MU_USERS) {
info[0] = __le32_to_cpu(mpdu_start->info0);
ppdu_info->userid = userid;
- ppdu_info->ampdu_id[userid] =
- u32_get_bits(info[0], HAL_RX_MPDU_START_INFO1_PEERID);
+ ppdu_info->userstats[userid].ampdu_id =
+ u32_get_bits(info[0], HAL_RX_MPDU_START_INFO0_PPDU_ID);
}
- mon_mpdu = kzalloc(sizeof(*mon_mpdu), GFP_ATOMIC);
- if (!mon_mpdu)
- return HAL_RX_MON_STATUS_PPDU_NOT_DONE;
-
- break;
+ return HAL_RX_MON_STATUS_MPDU_START;
}
case HAL_RX_MSDU_START:
/* TODO: add msdu start parsing logic */
break;
- case HAL_MON_BUF_ADDR: {
- struct dp_rxdma_mon_ring *buf_ring = &ab->dp.rxdma_mon_buf_ring;
- const struct dp_mon_packet_info *packet_info = tlv_data;
- int buf_id = u32_get_bits(packet_info->cookie,
- DP_RXDMA_BUF_COOKIE_BUF_ID);
- struct sk_buff *msdu;
- struct dp_mon_mpdu *mon_mpdu = pmon->mon_mpdu;
- struct ath12k_skb_rxcb *rxcb;
-
- spin_lock_bh(&buf_ring->idr_lock);
- msdu = idr_remove(&buf_ring->bufs_idr, buf_id);
- spin_unlock_bh(&buf_ring->idr_lock);
-
- if (unlikely(!msdu)) {
- ath12k_warn(ab, "monitor destination with invalid buf_id %d\n",
- buf_id);
- return HAL_RX_MON_STATUS_PPDU_NOT_DONE;
+ case HAL_MON_BUF_ADDR:
+ return HAL_RX_MON_STATUS_BUF_ADDR;
+ case HAL_RX_MSDU_END:
+ ath12k_dp_mon_parse_status_msdu_end(pmon, tlv_data);
+ return HAL_RX_MON_STATUS_MSDU_END;
+ case HAL_RX_MPDU_END:
+ return HAL_RX_MON_STATUS_MPDU_END;
+ case HAL_PHYRX_GENERIC_U_SIG:
+ ath12k_dp_mon_hal_rx_parse_u_sig_hdr(tlv_data, ppdu_info);
+ break;
+ case HAL_PHYRX_GENERIC_EHT_SIG:
+ /* Handle the case where aggregation is in progress
+ * or the current TLV is one of the TLVs which should be
+ * aggregated
+ */
+ if (!ppdu_info->tlv_aggr.in_progress) {
+ ppdu_info->tlv_aggr.in_progress = true;
+ ppdu_info->tlv_aggr.tlv_tag = tlv_tag;
+ ppdu_info->tlv_aggr.cur_len = 0;
}
- rxcb = ATH12K_SKB_RXCB(msdu);
- dma_unmap_single(ab->dev, rxcb->paddr,
- msdu->len + skb_tailroom(msdu),
- DMA_FROM_DEVICE);
-
- if (mon_mpdu->tail)
- mon_mpdu->tail->next = msdu;
- else
- mon_mpdu->tail = msdu;
-
- ath12k_dp_mon_buf_replenish(ab, buf_ring, 1);
+ ppdu_info->is_eht = true;
- break;
- }
- case HAL_RX_MSDU_END: {
- const struct rx_msdu_end_qcn9274 *msdu_end = tlv_data;
- bool is_first_msdu_in_mpdu;
- u16 msdu_end_info;
-
- msdu_end_info = __le16_to_cpu(msdu_end->info5);
- is_first_msdu_in_mpdu = u32_get_bits(msdu_end_info,
- RX_MSDU_END_INFO5_FIRST_MSDU);
- if (is_first_msdu_in_mpdu) {
- pmon->mon_mpdu->head = pmon->mon_mpdu->tail;
- pmon->mon_mpdu->tail = NULL;
- }
- break;
- }
- case HAL_RX_MPDU_END:
- list_add_tail(&pmon->mon_mpdu->list, &pmon->dp_rx_mon_mpdu_list);
+ ath12k_dp_mon_hal_aggr_tlv(ppdu_info, tlv_len, tlv_data);
break;
case HAL_DUMMY:
return HAL_RX_MON_STATUS_BUF_DONE;
@@ -831,45 +1723,295 @@ ath12k_dp_mon_rx_parse_status_tlv(struct ath12k_base *ab,
return HAL_RX_MON_STATUS_PPDU_NOT_DONE;
}
+static void
+ath12k_dp_mon_fill_rx_stats_info(struct ath12k *ar,
+ struct hal_rx_mon_ppdu_info *ppdu_info,
+ struct ieee80211_rx_status *rx_status)
+{
+ u32 center_freq = ppdu_info->freq;
+
+ rx_status->freq = center_freq;
+ rx_status->bw = ath12k_mac_bw_to_mac80211_bw(ppdu_info->bw);
+ rx_status->nss = ppdu_info->nss;
+ rx_status->rate_idx = 0;
+ rx_status->encoding = RX_ENC_LEGACY;
+ rx_status->flag |= RX_FLAG_NO_SIGNAL_VAL;
+
+ if (center_freq >= ATH12K_MIN_6GHZ_FREQ &&
+ center_freq <= ATH12K_MAX_6GHZ_FREQ) {
+ rx_status->band = NL80211_BAND_6GHZ;
+ } else if (center_freq >= ATH12K_MIN_2GHZ_FREQ &&
+ center_freq <= ATH12K_MAX_2GHZ_FREQ) {
+ rx_status->band = NL80211_BAND_2GHZ;
+ } else if (center_freq >= ATH12K_MIN_5GHZ_FREQ &&
+ center_freq <= ATH12K_MAX_5GHZ_FREQ) {
+ rx_status->band = NL80211_BAND_5GHZ;
+ } else {
+ rx_status->band = NUM_NL80211_BANDS;
+ }
+}
+
+static struct sk_buff
+*ath12k_dp_rx_alloc_mon_status_buf(struct ath12k_base *ab,
+ struct dp_rxdma_mon_ring *rx_ring,
+ int *buf_id)
+{
+ struct sk_buff *skb;
+ dma_addr_t paddr;
+
+ skb = dev_alloc_skb(RX_MON_STATUS_BUF_SIZE);
+
+ if (!skb)
+ goto fail_alloc_skb;
+
+ if (!IS_ALIGNED((unsigned long)skb->data,
+ RX_MON_STATUS_BUF_ALIGN)) {
+ skb_pull(skb, PTR_ALIGN(skb->data, RX_MON_STATUS_BUF_ALIGN) -
+ skb->data);
+ }
+
+ paddr = dma_map_single(ab->dev, skb->data,
+ skb->len + skb_tailroom(skb),
+ DMA_FROM_DEVICE);
+ if (unlikely(dma_mapping_error(ab->dev, paddr)))
+ goto fail_free_skb;
+
+ spin_lock_bh(&rx_ring->idr_lock);
+ *buf_id = idr_alloc(&rx_ring->bufs_idr, skb, 0,
+ rx_ring->bufs_max, GFP_ATOMIC);
+ spin_unlock_bh(&rx_ring->idr_lock);
+ if (*buf_id < 0)
+ goto fail_dma_unmap;
+
+ ATH12K_SKB_RXCB(skb)->paddr = paddr;
+ return skb;
+
+fail_dma_unmap:
+ dma_unmap_single(ab->dev, paddr, skb->len + skb_tailroom(skb),
+ DMA_FROM_DEVICE);
+fail_free_skb:
+ dev_kfree_skb_any(skb);
+fail_alloc_skb:
+ return NULL;
+}
+
+static enum dp_mon_status_buf_state
+ath12k_dp_rx_mon_buf_done(struct ath12k_base *ab, struct hal_srng *srng,
+ struct dp_rxdma_mon_ring *rx_ring)
+{
+ struct ath12k_skb_rxcb *rxcb;
+ struct hal_tlv_64_hdr *tlv;
+ struct sk_buff *skb;
+ void *status_desc;
+ dma_addr_t paddr;
+ u32 cookie;
+ int buf_id;
+ u8 rbm;
+
+ status_desc = ath12k_hal_srng_src_next_peek(ab, srng);
+ if (!status_desc)
+ return DP_MON_STATUS_NO_DMA;
+
+ ath12k_hal_rx_buf_addr_info_get(status_desc, &paddr, &cookie, &rbm);
+
+ buf_id = u32_get_bits(cookie, DP_RXDMA_BUF_COOKIE_BUF_ID);
+
+ spin_lock_bh(&rx_ring->idr_lock);
+ skb = idr_find(&rx_ring->bufs_idr, buf_id);
+ spin_unlock_bh(&rx_ring->idr_lock);
+
+ if (!skb)
+ return DP_MON_STATUS_NO_DMA;
+
+ rxcb = ATH12K_SKB_RXCB(skb);
+ dma_sync_single_for_cpu(ab->dev, rxcb->paddr,
+ skb->len + skb_tailroom(skb),
+ DMA_FROM_DEVICE);
+
+ tlv = (struct hal_tlv_64_hdr *)skb->data;
+ if (le64_get_bits(tlv->tl, HAL_TLV_HDR_TAG) != HAL_RX_STATUS_BUFFER_DONE)
+ return DP_MON_STATUS_NO_DMA;
+
+ return DP_MON_STATUS_REPLINISH;
+}
+
+static u32 ath12k_dp_mon_comp_ppduid(u32 msdu_ppdu_id, u32 *ppdu_id)
+{
+ u32 ret = 0;
+
+ if ((*ppdu_id < msdu_ppdu_id) &&
+ ((msdu_ppdu_id - *ppdu_id) < DP_NOT_PPDU_ID_WRAP_AROUND)) {
+ /* Hold on mon dest ring, and reap mon status ring. */
+ *ppdu_id = msdu_ppdu_id;
+ ret = msdu_ppdu_id;
+ } else if ((*ppdu_id > msdu_ppdu_id) &&
+ ((*ppdu_id - msdu_ppdu_id) > DP_NOT_PPDU_ID_WRAP_AROUND)) {
+ /* PPDU ID has exceeded the maximum value and will
+ * restart from 0.
+ */
+ *ppdu_id = msdu_ppdu_id;
+ ret = msdu_ppdu_id;
+ }
+ return ret;
+}
+
+static
+void ath12k_dp_mon_next_link_desc_get(struct hal_rx_msdu_link *msdu_link,
+ dma_addr_t *paddr, u32 *sw_cookie, u8 *rbm,
+ struct ath12k_buffer_addr **pp_buf_addr_info)
+{
+ struct ath12k_buffer_addr *buf_addr_info;
+
+ buf_addr_info = &msdu_link->buf_addr_info;
+
+ ath12k_hal_rx_buf_addr_info_get(buf_addr_info, paddr, sw_cookie, rbm);
+
+ *pp_buf_addr_info = buf_addr_info;
+}
+
+static void
+ath12k_dp_mon_fill_rx_rate(struct ath12k *ar,
+ struct hal_rx_mon_ppdu_info *ppdu_info,
+ struct ieee80211_rx_status *rx_status)
+{
+ struct ieee80211_supported_band *sband;
+ enum rx_msdu_start_pkt_type pkt_type;
+ u8 rate_mcs, nss, sgi;
+ bool is_cck;
+
+ pkt_type = ppdu_info->preamble_type;
+ rate_mcs = ppdu_info->rate;
+ nss = ppdu_info->nss;
+ sgi = ppdu_info->gi;
+
+ switch (pkt_type) {
+ case RX_MSDU_START_PKT_TYPE_11A:
+ case RX_MSDU_START_PKT_TYPE_11B:
+ is_cck = (pkt_type == RX_MSDU_START_PKT_TYPE_11B);
+ if (rx_status->band < NUM_NL80211_BANDS) {
+ sband = &ar->mac.sbands[rx_status->band];
+ rx_status->rate_idx = ath12k_mac_hw_rate_to_idx(sband, rate_mcs,
+ is_cck);
+ }
+ break;
+ case RX_MSDU_START_PKT_TYPE_11N:
+ rx_status->encoding = RX_ENC_HT;
+ if (rate_mcs > ATH12K_HT_MCS_MAX) {
+ ath12k_warn(ar->ab,
+ "Received with invalid mcs in HT mode %d\n",
+ rate_mcs);
+ break;
+ }
+ rx_status->rate_idx = rate_mcs + (8 * (nss - 1));
+ if (sgi)
+ rx_status->enc_flags |= RX_ENC_FLAG_SHORT_GI;
+ break;
+ case RX_MSDU_START_PKT_TYPE_11AC:
+ rx_status->encoding = RX_ENC_VHT;
+ rx_status->rate_idx = rate_mcs;
+ if (rate_mcs > ATH12K_VHT_MCS_MAX) {
+ ath12k_warn(ar->ab,
+ "Received with invalid mcs in VHT mode %d\n",
+ rate_mcs);
+ break;
+ }
+ if (sgi)
+ rx_status->enc_flags |= RX_ENC_FLAG_SHORT_GI;
+ break;
+ case RX_MSDU_START_PKT_TYPE_11AX:
+ rx_status->rate_idx = rate_mcs;
+ if (rate_mcs > ATH12K_HE_MCS_MAX) {
+ ath12k_warn(ar->ab,
+ "Received with invalid mcs in HE mode %d\n",
+ rate_mcs);
+ break;
+ }
+ rx_status->encoding = RX_ENC_HE;
+ rx_status->he_gi = ath12k_he_gi_to_nl80211_he_gi(sgi);
+ break;
+ case RX_MSDU_START_PKT_TYPE_11BE:
+ rx_status->rate_idx = rate_mcs;
+ if (rate_mcs > ATH12K_EHT_MCS_MAX) {
+ ath12k_warn(ar->ab,
+ "Received with invalid mcs in EHT mode %d\n",
+ rate_mcs);
+ break;
+ }
+ rx_status->encoding = RX_ENC_EHT;
+ rx_status->he_gi = ath12k_he_gi_to_nl80211_he_gi(sgi);
+ break;
+ default:
+ ath12k_dbg(ar->ab, ATH12K_DBG_DATA,
+ "monitor receives invalid preamble type %d",
+ pkt_type);
+ break;
+ }
+}
+
static void ath12k_dp_mon_rx_msdus_set_payload(struct ath12k *ar,
struct sk_buff *head_msdu,
struct sk_buff *tail_msdu)
{
- u32 rx_pkt_offset, l2_hdr_offset;
+ u32 rx_pkt_offset, l2_hdr_offset, total_offset;
rx_pkt_offset = ar->ab->hal.hal_desc_sz;
l2_hdr_offset =
ath12k_dp_rx_h_l3pad(ar->ab, (struct hal_rx_desc *)tail_msdu->data);
- skb_pull(head_msdu, rx_pkt_offset + l2_hdr_offset);
+
+ if (ar->ab->hw_params->rxdma1_enable)
+ total_offset = ATH12K_MON_RX_PKT_OFFSET;
+ else
+ total_offset = rx_pkt_offset + l2_hdr_offset;
+
+ skb_pull(head_msdu, total_offset);
}
static struct sk_buff *
-ath12k_dp_mon_rx_merg_msdus(struct ath12k *ar, u32 mac_id,
- struct sk_buff *head_msdu, struct sk_buff *tail_msdu,
- struct ieee80211_rx_status *rxs, bool *fcs_err)
+ath12k_dp_mon_rx_merg_msdus(struct ath12k *ar,
+ struct dp_mon_mpdu *mon_mpdu,
+ struct hal_rx_mon_ppdu_info *ppdu_info,
+ struct ieee80211_rx_status *rxs)
{
struct ath12k_base *ab = ar->ab;
struct sk_buff *msdu, *mpdu_buf, *prev_buf, *head_frag_list;
- struct hal_rx_desc *rx_desc, *tail_rx_desc;
- u8 *hdr_desc, *dest, decap_format;
+ struct sk_buff *head_msdu, *tail_msdu;
+ struct hal_rx_desc *rx_desc;
+ u8 *hdr_desc, *dest, decap_format = mon_mpdu->decap_format;
struct ieee80211_hdr_3addr *wh;
- u32 err_bitmap, frag_list_sum_len = 0;
+ struct ieee80211_channel *channel;
+ u32 frag_list_sum_len = 0;
+ u8 channel_num = ppdu_info->chan_num;
mpdu_buf = NULL;
+ head_msdu = mon_mpdu->head;
+ tail_msdu = mon_mpdu->tail;
- if (!head_msdu)
+ if (!head_msdu || !tail_msdu)
goto err_merge_fail;
- rx_desc = (struct hal_rx_desc *)head_msdu->data;
- tail_rx_desc = (struct hal_rx_desc *)tail_msdu->data;
+ ath12k_dp_mon_fill_rx_stats_info(ar, ppdu_info, rxs);
- err_bitmap = ath12k_dp_rx_h_mpdu_err(ab, tail_rx_desc);
- if (err_bitmap & HAL_RX_MPDU_ERR_FCS)
- *fcs_err = true;
+ if (unlikely(rxs->band == NUM_NL80211_BANDS ||
+ !ath12k_ar_to_hw(ar)->wiphy->bands[rxs->band])) {
+ ath12k_dbg(ar->ab, ATH12K_DBG_DATA,
+ "sband is NULL for status band %d channel_num %d center_freq %d pdev_id %d\n",
+ rxs->band, channel_num, ppdu_info->freq, ar->pdev_idx);
- decap_format = ath12k_dp_rx_h_decap_type(ab, tail_rx_desc);
+ spin_lock_bh(&ar->data_lock);
+ channel = ar->rx_channel;
+ if (channel) {
+ rxs->band = channel->band;
+ channel_num =
+ ieee80211_frequency_to_channel(channel->center_freq);
+ }
+ spin_unlock_bh(&ar->data_lock);
+ }
- ath12k_dp_rx_h_ppdu(ar, tail_rx_desc, rxs);
+ if (rxs->band < NUM_NL80211_BANDS)
+ rxs->freq = ieee80211_channel_to_frequency(channel_num,
+ rxs->band);
+
+ ath12k_dp_mon_fill_rx_rate(ar, ppdu_info, rxs);
if (decap_format == DP_RX_DECAP_TYPE_RAW) {
ath12k_dp_mon_rx_msdus_set_payload(ar, head_msdu, tail_msdu);
@@ -879,7 +2021,7 @@ ath12k_dp_mon_rx_merg_msdus(struct ath12k *ar, u32 mac_id,
head_frag_list = NULL;
while (msdu) {
- ath12k_dp_mon_rx_msdus_set_payload(ar, msdu, tail_msdu);
+ ath12k_dp_mon_rx_msdus_set_payload(ar, head_msdu, tail_msdu);
if (!head_frag_list)
head_frag_list = msdu;
@@ -891,7 +2033,7 @@ ath12k_dp_mon_rx_merg_msdus(struct ath12k *ar, u32 mac_id,
prev_buf->next = NULL;
- skb_trim(prev_buf, prev_buf->len - HAL_RX_FCS_LEN);
+ skb_trim(prev_buf, prev_buf->len);
if (head_frag_list) {
skb_shinfo(head_msdu)->frag_list = head_frag_list;
head_msdu->data_len = frag_list_sum_len;
@@ -914,7 +2056,7 @@ ath12k_dp_mon_rx_merg_msdus(struct ath12k *ar, u32 mac_id,
msdu = head_msdu;
while (msdu) {
- ath12k_dp_mon_rx_msdus_set_payload(ar, msdu, tail_msdu);
+ ath12k_dp_mon_rx_msdus_set_payload(ar, head_msdu, tail_msdu);
if (qos_pkt) {
dest = skb_push(msdu, sizeof(__le16));
if (!dest)
@@ -1005,18 +2147,70 @@ static void ath12k_dp_mon_update_radiotap(struct ath12k *ar,
{
struct ieee80211_supported_band *sband;
u8 *ptr = NULL;
- u16 ampdu_id = ppduinfo->ampdu_id[ppduinfo->userid];
rxs->flag |= RX_FLAG_MACTIME_START;
rxs->signal = ppduinfo->rssi_comb + ATH12K_DEFAULT_NOISE_FLOOR;
rxs->nss = ppduinfo->nss + 1;
- if (ampdu_id) {
+ if (ppduinfo->userstats[ppduinfo->userid].ampdu_present) {
rxs->flag |= RX_FLAG_AMPDU_DETAILS;
- rxs->ampdu_reference = ampdu_id;
+ rxs->ampdu_reference = ppduinfo->userstats[ppduinfo->userid].ampdu_id;
}
- if (ppduinfo->he_mu_flags) {
+ if (ppduinfo->is_eht || ppduinfo->eht_usig) {
+ struct ieee80211_radiotap_tlv *tlv;
+ struct ieee80211_radiotap_eht *eht;
+ struct ieee80211_radiotap_eht_usig *usig;
+ u16 len = 0, i, eht_len, usig_len;
+ u8 user;
+
+ if (ppduinfo->is_eht) {
+ eht_len = struct_size(eht,
+ user_info,
+ ppduinfo->eht_info.num_user_info);
+ len += sizeof(*tlv) + eht_len;
+ }
+
+ if (ppduinfo->eht_usig) {
+ usig_len = sizeof(*usig);
+ len += sizeof(*tlv) + usig_len;
+ }
+
+ rxs->flag |= RX_FLAG_RADIOTAP_TLV_AT_END;
+ rxs->encoding = RX_ENC_EHT;
+
+ skb_reset_mac_header(mon_skb);
+
+ tlv = skb_push(mon_skb, len);
+
+ if (ppduinfo->is_eht) {
+ tlv->type = cpu_to_le16(IEEE80211_RADIOTAP_EHT);
+ tlv->len = cpu_to_le16(eht_len);
+
+ eht = (struct ieee80211_radiotap_eht *)tlv->data;
+ eht->known = ppduinfo->eht_info.eht.known;
+
+ for (i = 0;
+ i < ARRAY_SIZE(eht->data) &&
+ i < ARRAY_SIZE(ppduinfo->eht_info.eht.data);
+ i++)
+ eht->data[i] = ppduinfo->eht_info.eht.data[i];
+
+ for (user = 0; user < ppduinfo->eht_info.num_user_info; user++)
+ put_unaligned_le32(ppduinfo->eht_info.user_info[user],
+ &eht->user_info[user]);
+
+ tlv = (struct ieee80211_radiotap_tlv *)&tlv->data[eht_len];
+ }
+
+ if (ppduinfo->eht_usig) {
+ tlv->type = cpu_to_le16(IEEE80211_RADIOTAP_EHT_USIG);
+ tlv->len = cpu_to_le16(usig_len);
+
+ usig = (struct ieee80211_radiotap_eht_usig *)tlv->data;
+ *usig = ppduinfo->u_sig_info.usig;
+ }
+ } else 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));
@@ -1045,7 +2239,8 @@ static void ath12k_dp_mon_update_radiotap(struct ath12k *ar,
static void ath12k_dp_mon_rx_deliver_msdu(struct ath12k *ar, struct napi_struct *napi,
struct sk_buff *msdu,
- struct ieee80211_rx_status *status)
+ struct ieee80211_rx_status *status,
+ u8 decap)
{
static const struct ieee80211_radiotap_he known = {
.data1 = cpu_to_le16(IEEE80211_RADIOTAP_HE_DATA1_DATA_MCS_KNOWN |
@@ -1057,10 +2252,12 @@ static void ath12k_dp_mon_rx_deliver_msdu(struct ath12k *ar, struct napi_struct
struct ieee80211_sta *pubsta = NULL;
struct ath12k_peer *peer;
struct ath12k_skb_rxcb *rxcb = ATH12K_SKB_RXCB(msdu);
- u8 decap = DP_RX_DECAP_TYPE_RAW;
+ struct ath12k_dp_rx_info rx_info;
bool is_mcbc = rxcb->is_mcbc;
bool is_eapol_tkip = rxcb->is_eapol;
+ status->link_valid = 0;
+
if ((status->encoding == RX_ENC_HE) && !(status->flag & RX_FLAG_RADIOTAP_HE) &&
!(status->flag & RX_FLAG_SKIP_MONITOR)) {
he = skb_push(msdu, sizeof(known));
@@ -1068,10 +2265,9 @@ static void ath12k_dp_mon_rx_deliver_msdu(struct ath12k *ar, struct napi_struct
status->flag |= RX_FLAG_RADIOTAP_HE;
}
- if (!(status->flag & RX_FLAG_ONLY_MONITOR))
- decap = ath12k_dp_rx_h_decap_type(ar->ab, rxcb->rx_desc);
spin_lock_bh(&ar->ab->base_lock);
- peer = ath12k_dp_rx_h_find_peer(ar->ab, msdu);
+ rx_info.addr2_present = false;
+ peer = ath12k_dp_rx_h_find_peer(ar->ab, msdu, &rx_info);
if (peer && peer->sta) {
pubsta = peer->sta;
if (pubsta->valid_links) {
@@ -1125,26 +2321,24 @@ static void ath12k_dp_mon_rx_deliver_msdu(struct ath12k *ar, struct napi_struct
ieee80211_rx_napi(ath12k_ar_to_hw(ar), pubsta, msdu, napi);
}
-static int ath12k_dp_mon_rx_deliver(struct ath12k *ar, u32 mac_id,
- struct sk_buff *head_msdu, struct sk_buff *tail_msdu,
+static int ath12k_dp_mon_rx_deliver(struct ath12k *ar,
+ struct dp_mon_mpdu *mon_mpdu,
struct hal_rx_mon_ppdu_info *ppduinfo,
struct napi_struct *napi)
{
struct ath12k_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;
+ u8 decap = DP_RX_DECAP_TYPE_RAW;
- mon_skb = ath12k_dp_mon_rx_merg_msdus(ar, mac_id,
- head_msdu, tail_msdu,
- rxs, &fcs_err);
+ mon_skb = ath12k_dp_mon_rx_merg_msdus(ar, mon_mpdu, ppduinfo, rxs);
if (!mon_skb)
goto mon_deliver_fail;
header = mon_skb;
rxs->flag = 0;
- if (fcs_err)
+ if (mon_mpdu->err_bitmap & HAL_RX_MPDU_ERR_FCS)
rxs->flag = RX_FLAG_FAILED_FCS_CRC;
do {
@@ -1161,8 +2355,12 @@ static int ath12k_dp_mon_rx_deliver(struct ath12k *ar, u32 mac_id,
rxs->flag |= RX_FLAG_ALLOW_SAME_PN;
}
rxs->flag |= RX_FLAG_ONLY_MONITOR;
+
+ if (!(rxs->flag & RX_FLAG_ONLY_MONITOR))
+ decap = mon_mpdu->decap_format;
+
ath12k_dp_mon_update_radiotap(ar, ppduinfo, mon_skb, rxs);
- ath12k_dp_mon_rx_deliver_msdu(ar, napi, mon_skb, rxs);
+ ath12k_dp_mon_rx_deliver_msdu(ar, napi, mon_skb, rxs, decap);
mon_skb = skb_next;
} while (mon_skb);
rxs->flag = 0;
@@ -1170,7 +2368,7 @@ static int ath12k_dp_mon_rx_deliver(struct ath12k *ar, u32 mac_id,
return 0;
mon_deliver_fail:
- mon_skb = head_msdu;
+ mon_skb = mon_mpdu->head;
while (mon_skb) {
skb_next = mon_skb->next;
dev_kfree_skb_any(mon_skb);
@@ -1179,25 +2377,146 @@ mon_deliver_fail:
return -EINVAL;
}
+static int ath12k_dp_pkt_set_pktlen(struct sk_buff *skb, u32 len)
+{
+ if (skb->len > len) {
+ skb_trim(skb, len);
+ } else {
+ if (skb_tailroom(skb) < len - skb->len) {
+ if ((pskb_expand_head(skb, 0,
+ len - skb->len - skb_tailroom(skb),
+ GFP_ATOMIC))) {
+ return -ENOMEM;
+ }
+ }
+ skb_put(skb, (len - skb->len));
+ }
+
+ return 0;
+}
+
+/* Hardware fill buffer with 128 bytes aligned. So need to reap it
+ * with 128 bytes aligned.
+ */
+#define RXDMA_DATA_DMA_BLOCK_SIZE 128
+
+static void
+ath12k_dp_mon_get_buf_len(struct hal_rx_msdu_desc_info *info,
+ bool *is_frag, u32 *total_len,
+ u32 *frag_len, u32 *msdu_cnt)
+{
+ if (info->msdu_flags & RX_MSDU_DESC_INFO0_MSDU_CONTINUATION) {
+ *is_frag = true;
+ *frag_len = (RX_MON_STATUS_BASE_BUF_SIZE -
+ sizeof(struct hal_rx_desc)) &
+ ~(RXDMA_DATA_DMA_BLOCK_SIZE - 1);
+ *total_len += *frag_len;
+ } else {
+ if (*is_frag)
+ *frag_len = info->msdu_len - *total_len;
+ else
+ *frag_len = info->msdu_len;
+
+ *msdu_cnt -= 1;
+ }
+}
+
+static int
+ath12k_dp_mon_parse_status_buf(struct ath12k *ar,
+ struct ath12k_mon_data *pmon,
+ const struct dp_mon_packet_info *packet_info)
+{
+ struct ath12k_base *ab = ar->ab;
+ struct dp_rxdma_mon_ring *buf_ring = &ab->dp.rxdma_mon_buf_ring;
+ struct sk_buff *msdu;
+ int buf_id;
+ u32 offset;
+
+ buf_id = u32_get_bits(packet_info->cookie, DP_RXDMA_BUF_COOKIE_BUF_ID);
+
+ spin_lock_bh(&buf_ring->idr_lock);
+ msdu = idr_remove(&buf_ring->bufs_idr, buf_id);
+ spin_unlock_bh(&buf_ring->idr_lock);
+
+ if (unlikely(!msdu)) {
+ ath12k_warn(ab, "mon dest desc with inval buf_id %d\n", buf_id);
+ return 0;
+ }
+
+ dma_unmap_single(ab->dev, ATH12K_SKB_RXCB(msdu)->paddr,
+ msdu->len + skb_tailroom(msdu),
+ DMA_FROM_DEVICE);
+
+ offset = packet_info->dma_length + ATH12K_MON_RX_DOT11_OFFSET;
+ if (ath12k_dp_pkt_set_pktlen(msdu, offset)) {
+ dev_kfree_skb_any(msdu);
+ goto dest_replenish;
+ }
+
+ if (!pmon->mon_mpdu->head)
+ pmon->mon_mpdu->head = msdu;
+ else
+ pmon->mon_mpdu->tail->next = msdu;
+
+ pmon->mon_mpdu->tail = msdu;
+
+dest_replenish:
+ ath12k_dp_mon_buf_replenish(ab, buf_ring, 1);
+
+ return 0;
+}
+
+static int
+ath12k_dp_mon_parse_rx_dest_tlv(struct ath12k *ar,
+ struct ath12k_mon_data *pmon,
+ enum hal_rx_mon_status hal_status,
+ const void *tlv_data)
+{
+ switch (hal_status) {
+ case HAL_RX_MON_STATUS_MPDU_START:
+ if (WARN_ON_ONCE(pmon->mon_mpdu))
+ break;
+
+ pmon->mon_mpdu = kzalloc(sizeof(*pmon->mon_mpdu), GFP_ATOMIC);
+ if (!pmon->mon_mpdu)
+ return -ENOMEM;
+ break;
+ case HAL_RX_MON_STATUS_BUF_ADDR:
+ return ath12k_dp_mon_parse_status_buf(ar, pmon, tlv_data);
+ case HAL_RX_MON_STATUS_MPDU_END:
+ /* If no MSDU then free empty MPDU */
+ if (pmon->mon_mpdu->tail) {
+ pmon->mon_mpdu->tail->next = NULL;
+ list_add_tail(&pmon->mon_mpdu->list, &pmon->dp_rx_mon_mpdu_list);
+ } else {
+ kfree(pmon->mon_mpdu);
+ }
+ pmon->mon_mpdu = NULL;
+ break;
+ case HAL_RX_MON_STATUS_MSDU_END:
+ pmon->mon_mpdu->decap_format = pmon->decap_format;
+ pmon->mon_mpdu->err_bitmap = pmon->err_bitmap;
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
static enum hal_rx_mon_status
-ath12k_dp_mon_parse_rx_dest(struct ath12k_base *ab, struct ath12k_mon_data *pmon,
+ath12k_dp_mon_parse_rx_dest(struct ath12k *ar, struct ath12k_mon_data *pmon,
struct sk_buff *skb)
{
- struct hal_rx_mon_ppdu_info *ppdu_info = &pmon->mon_ppdu_info;
struct hal_tlv_64_hdr *tlv;
+ struct ath12k_skb_rxcb *rxcb;
enum hal_rx_mon_status hal_status;
- u32 tlv_userid;
u16 tlv_tag, tlv_len;
u8 *ptr = skb->data;
- memset(ppdu_info, 0, sizeof(struct hal_rx_mon_ppdu_info));
-
do {
tlv = (struct hal_tlv_64_hdr *)ptr;
tlv_tag = le64_get_bits(tlv->tl, HAL_TLV_64_HDR_TAG);
- tlv_len = le64_get_bits(tlv->tl, HAL_TLV_64_HDR_LEN);
- tlv_userid = le64_get_bits(tlv->tl, HAL_TLV_64_USR_ID);
- ptr += sizeof(*tlv);
/* The actual length of PPDU_END is the combined length of many PHY
* TLVs that follow. Skip the TLV header and
@@ -1207,16 +2526,30 @@ ath12k_dp_mon_parse_rx_dest(struct ath12k_base *ab, struct ath12k_mon_data *pmon
if (tlv_tag == HAL_RX_PPDU_END)
tlv_len = sizeof(struct hal_rx_rxpcu_classification_overview);
+ else
+ tlv_len = le64_get_bits(tlv->tl, HAL_TLV_64_HDR_LEN);
- hal_status = ath12k_dp_mon_rx_parse_status_tlv(ab, pmon,
- tlv_tag, ptr, tlv_userid);
- ptr += tlv_len;
+ hal_status = ath12k_dp_mon_rx_parse_status_tlv(ar, pmon, tlv);
+
+ if (ar->monitor_started && ar->ab->hw_params->rxdma1_enable &&
+ ath12k_dp_mon_parse_rx_dest_tlv(ar, pmon, hal_status, tlv->value))
+ return HAL_RX_MON_STATUS_PPDU_DONE;
+
+ ptr += sizeof(*tlv) + tlv_len;
ptr = PTR_ALIGN(ptr, HAL_TLV_64_ALIGN);
- if ((ptr - skb->data) >= DP_RX_BUFFER_SIZE)
+ if ((ptr - skb->data) > skb->len)
break;
- } while (hal_status == HAL_RX_MON_STATUS_PPDU_NOT_DONE);
+ } while ((hal_status == HAL_RX_MON_STATUS_PPDU_NOT_DONE) ||
+ (hal_status == HAL_RX_MON_STATUS_BUF_ADDR) ||
+ (hal_status == HAL_RX_MON_STATUS_MPDU_START) ||
+ (hal_status == HAL_RX_MON_STATUS_MPDU_END) ||
+ (hal_status == HAL_RX_MON_STATUS_MSDU_END));
+
+ rxcb = ATH12K_SKB_RXCB(skb);
+ if (rxcb->is_end_of_ppdu)
+ hal_status = HAL_RX_MON_STATUS_PPDU_DONE;
return hal_status;
}
@@ -1224,31 +2557,27 @@ ath12k_dp_mon_parse_rx_dest(struct ath12k_base *ab, struct ath12k_mon_data *pmon
enum hal_rx_mon_status
ath12k_dp_mon_rx_parse_mon_status(struct ath12k *ar,
struct ath12k_mon_data *pmon,
- int mac_id,
struct sk_buff *skb,
struct napi_struct *napi)
{
- struct ath12k_base *ab = ar->ab;
struct hal_rx_mon_ppdu_info *ppdu_info = &pmon->mon_ppdu_info;
struct dp_mon_mpdu *tmp;
struct dp_mon_mpdu *mon_mpdu = pmon->mon_mpdu;
- struct sk_buff *head_msdu, *tail_msdu;
- enum hal_rx_mon_status hal_status = HAL_RX_MON_STATUS_BUF_DONE;
+ enum hal_rx_mon_status hal_status;
- ath12k_dp_mon_parse_rx_dest(ab, pmon, skb);
+ hal_status = ath12k_dp_mon_parse_rx_dest(ar, pmon, skb);
+ if (hal_status != HAL_RX_MON_STATUS_PPDU_DONE)
+ return hal_status;
list_for_each_entry_safe(mon_mpdu, tmp, &pmon->dp_rx_mon_mpdu_list, list) {
list_del(&mon_mpdu->list);
- head_msdu = mon_mpdu->head;
- tail_msdu = mon_mpdu->tail;
- if (head_msdu && tail_msdu) {
- ath12k_dp_mon_rx_deliver(ar, mac_id, head_msdu,
- tail_msdu, ppdu_info, napi);
- }
+ if (mon_mpdu->head && mon_mpdu->tail)
+ ath12k_dp_mon_rx_deliver(ar, mon_mpdu, ppdu_info, napi);
kfree(mon_mpdu);
}
+
return hal_status;
}
@@ -1327,6 +2656,94 @@ fail_alloc_skb:
return -ENOMEM;
}
+int ath12k_dp_mon_status_bufs_replenish(struct ath12k_base *ab,
+ struct dp_rxdma_mon_ring *rx_ring,
+ int req_entries)
+{
+ enum hal_rx_buf_return_buf_manager mgr =
+ ab->hw_params->hal_params->rx_buf_rbm;
+ int num_free, num_remain, buf_id;
+ struct ath12k_buffer_addr *desc;
+ struct hal_srng *srng;
+ struct sk_buff *skb;
+ dma_addr_t paddr;
+ u32 cookie;
+
+ req_entries = min(req_entries, rx_ring->bufs_max);
+
+ srng = &ab->hal.srng_list[rx_ring->refill_buf_ring.ring_id];
+
+ spin_lock_bh(&srng->lock);
+
+ ath12k_hal_srng_access_begin(ab, srng);
+
+ num_free = ath12k_hal_srng_src_num_free(ab, srng, true);
+ if (!req_entries && (num_free > (rx_ring->bufs_max * 3) / 4))
+ req_entries = num_free;
+
+ req_entries = min(num_free, req_entries);
+ num_remain = req_entries;
+
+ while (num_remain > 0) {
+ skb = dev_alloc_skb(RX_MON_STATUS_BUF_SIZE);
+ if (!skb)
+ break;
+
+ if (!IS_ALIGNED((unsigned long)skb->data,
+ RX_MON_STATUS_BUF_ALIGN)) {
+ skb_pull(skb,
+ PTR_ALIGN(skb->data, RX_MON_STATUS_BUF_ALIGN) -
+ skb->data);
+ }
+
+ paddr = dma_map_single(ab->dev, skb->data,
+ skb->len + skb_tailroom(skb),
+ DMA_FROM_DEVICE);
+ if (dma_mapping_error(ab->dev, paddr))
+ goto fail_free_skb;
+
+ spin_lock_bh(&rx_ring->idr_lock);
+ buf_id = idr_alloc(&rx_ring->bufs_idr, skb, 0,
+ rx_ring->bufs_max * 3, GFP_ATOMIC);
+ spin_unlock_bh(&rx_ring->idr_lock);
+ if (buf_id < 0)
+ goto fail_dma_unmap;
+ cookie = u32_encode_bits(buf_id, DP_RXDMA_BUF_COOKIE_BUF_ID);
+
+ desc = ath12k_hal_srng_src_get_next_entry(ab, srng);
+ if (!desc)
+ goto fail_buf_unassign;
+
+ ATH12K_SKB_RXCB(skb)->paddr = paddr;
+
+ num_remain--;
+
+ ath12k_hal_rx_buf_addr_info_set(desc, paddr, cookie, mgr);
+ }
+
+ ath12k_hal_srng_access_end(ab, srng);
+
+ spin_unlock_bh(&srng->lock);
+
+ return req_entries - num_remain;
+
+fail_buf_unassign:
+ spin_lock_bh(&rx_ring->idr_lock);
+ idr_remove(&rx_ring->bufs_idr, buf_id);
+ spin_unlock_bh(&rx_ring->idr_lock);
+fail_dma_unmap:
+ dma_unmap_single(ab->dev, paddr, skb->len + skb_tailroom(skb),
+ DMA_FROM_DEVICE);
+fail_free_skb:
+ dev_kfree_skb_any(skb);
+
+ ath12k_hal_srng_access_end(ab, srng);
+
+ spin_unlock_bh(&srng->lock);
+
+ return req_entries - num_remain;
+}
+
static struct dp_mon_tx_ppdu_info *
ath12k_dp_mon_tx_get_ppdu_info(struct ath12k_mon_data *pmon,
unsigned int ppdu_id,
@@ -1924,21 +3341,18 @@ ath12k_dp_mon_tx_status_get_num_user(u16 tlv_tag,
}
static void
-ath12k_dp_mon_tx_process_ppdu_info(struct ath12k *ar, int mac_id,
+ath12k_dp_mon_tx_process_ppdu_info(struct ath12k *ar,
struct napi_struct *napi,
struct dp_mon_tx_ppdu_info *tx_ppdu_info)
{
struct dp_mon_mpdu *tmp, *mon_mpdu;
- struct sk_buff *head_msdu, *tail_msdu;
list_for_each_entry_safe(mon_mpdu, tmp,
&tx_ppdu_info->dp_tx_mon_mpdu_list, list) {
list_del(&mon_mpdu->list);
- head_msdu = mon_mpdu->head;
- tail_msdu = mon_mpdu->tail;
- if (head_msdu)
- ath12k_dp_mon_rx_deliver(ar, mac_id, head_msdu, tail_msdu,
+ if (mon_mpdu->head)
+ ath12k_dp_mon_rx_deliver(ar, mon_mpdu,
&tx_ppdu_info->rx_status, napi);
kfree(mon_mpdu);
@@ -1948,7 +3362,6 @@ ath12k_dp_mon_tx_process_ppdu_info(struct ath12k *ar, int mac_id,
enum hal_rx_mon_status
ath12k_dp_mon_tx_parse_mon_status(struct ath12k *ar,
struct ath12k_mon_data *pmon,
- int mac_id,
struct sk_buff *skb,
struct napi_struct *napi,
u32 ppdu_id)
@@ -1995,155 +3408,43 @@ ath12k_dp_mon_tx_parse_mon_status(struct ath12k *ar,
break;
} while (tlv_status != DP_MON_TX_FES_STATUS_END);
- ath12k_dp_mon_tx_process_ppdu_info(ar, mac_id, napi, tx_data_ppdu_info);
- ath12k_dp_mon_tx_process_ppdu_info(ar, mac_id, napi, tx_prot_ppdu_info);
+ ath12k_dp_mon_tx_process_ppdu_info(ar, napi, tx_data_ppdu_info);
+ ath12k_dp_mon_tx_process_ppdu_info(ar, napi, tx_prot_ppdu_info);
return tlv_status;
}
-int ath12k_dp_mon_srng_process(struct ath12k *ar, int mac_id, int *budget,
- enum dp_monitor_mode monitor_mode,
- struct napi_struct *napi)
-{
- struct hal_mon_dest_desc *mon_dst_desc;
- struct ath12k_pdev_dp *pdev_dp = &ar->dp;
- struct ath12k_mon_data *pmon = (struct ath12k_mon_data *)&pdev_dp->mon_data;
- struct ath12k_base *ab = ar->ab;
- struct ath12k_dp *dp = &ab->dp;
- struct sk_buff *skb;
- struct ath12k_skb_rxcb *rxcb;
- struct dp_srng *mon_dst_ring;
- struct hal_srng *srng;
- struct dp_rxdma_mon_ring *buf_ring;
- u64 cookie;
- u32 ppdu_id;
- int num_buffs_reaped = 0, srng_id, buf_id;
- u8 dest_idx = 0, i;
- bool end_of_ppdu;
- struct hal_rx_mon_ppdu_info *ppdu_info;
- struct ath12k_peer *peer = NULL;
-
- ppdu_info = &pmon->mon_ppdu_info;
- memset(ppdu_info, 0, sizeof(*ppdu_info));
- ppdu_info->peer_id = HAL_INVALID_PEERID;
-
- srng_id = ath12k_hw_mac_id_to_srng_id(ab->hw_params, mac_id);
-
- if (monitor_mode == ATH12K_DP_RX_MONITOR_MODE) {
- mon_dst_ring = &pdev_dp->rxdma_mon_dst_ring[srng_id];
- buf_ring = &dp->rxdma_mon_buf_ring;
- } else {
- return 0;
- }
-
- srng = &ab->hal.srng_list[mon_dst_ring->ring_id];
-
- spin_lock_bh(&srng->lock);
- ath12k_hal_srng_access_begin(ab, srng);
-
- while (likely(*budget)) {
- *budget -= 1;
- mon_dst_desc = ath12k_hal_srng_dst_peek(ab, srng);
- if (unlikely(!mon_dst_desc))
- break;
-
- cookie = le32_to_cpu(mon_dst_desc->cookie);
- buf_id = u32_get_bits(cookie, DP_RXDMA_BUF_COOKIE_BUF_ID);
-
- spin_lock_bh(&buf_ring->idr_lock);
- skb = idr_remove(&buf_ring->bufs_idr, buf_id);
- spin_unlock_bh(&buf_ring->idr_lock);
-
- if (unlikely(!skb)) {
- ath12k_warn(ab, "monitor destination with invalid buf_id %d\n",
- buf_id);
- goto move_next;
- }
-
- rxcb = ATH12K_SKB_RXCB(skb);
- dma_unmap_single(ab->dev, rxcb->paddr,
- skb->len + skb_tailroom(skb),
- DMA_FROM_DEVICE);
-
- pmon->dest_skb_q[dest_idx] = skb;
- dest_idx++;
- ppdu_id = le32_to_cpu(mon_dst_desc->ppdu_id);
- end_of_ppdu = le32_get_bits(mon_dst_desc->info0,
- HAL_MON_DEST_INFO0_END_OF_PPDU);
- if (!end_of_ppdu)
- continue;
-
- for (i = 0; i < dest_idx; i++) {
- skb = pmon->dest_skb_q[i];
-
- if (monitor_mode == ATH12K_DP_RX_MONITOR_MODE)
- ath12k_dp_mon_rx_parse_mon_status(ar, pmon, mac_id,
- skb, napi);
- else
- ath12k_dp_mon_tx_parse_mon_status(ar, pmon, mac_id,
- skb, napi, ppdu_id);
-
- peer = ath12k_peer_find_by_id(ab, ppdu_info->peer_id);
-
- if (!peer || !peer->sta) {
- ath12k_dbg(ab, ATH12K_DBG_DATA,
- "failed to find the peer with peer_id %d\n",
- ppdu_info->peer_id);
- dev_kfree_skb_any(skb);
- continue;
- }
-
- dev_kfree_skb_any(skb);
- pmon->dest_skb_q[i] = NULL;
- }
-
- dest_idx = 0;
-move_next:
- ath12k_dp_mon_buf_replenish(ab, buf_ring, 1);
- ath12k_hal_srng_src_get_next_entry(ab, srng);
- num_buffs_reaped++;
- }
-
- ath12k_hal_srng_access_end(ab, srng);
- spin_unlock_bh(&srng->lock);
-
- return num_buffs_reaped;
-}
-
static void
ath12k_dp_mon_rx_update_peer_rate_table_stats(struct ath12k_rx_peer_stats *rx_stats,
struct hal_rx_mon_ppdu_info *ppdu_info,
struct hal_rx_user_status *user_stats,
u32 num_msdu)
{
- u32 rate_idx = 0;
+ struct ath12k_rx_peer_rate_stats *stats;
u32 mcs_idx = (user_stats) ? user_stats->mcs : ppdu_info->mcs;
u32 nss_idx = (user_stats) ? user_stats->nss - 1 : ppdu_info->nss - 1;
u32 bw_idx = ppdu_info->bw;
u32 gi_idx = ppdu_info->gi;
+ u32 len;
- if ((mcs_idx > HAL_RX_MAX_MCS_HE) || (nss_idx >= HAL_RX_MAX_NSS) ||
- (bw_idx >= HAL_RX_BW_MAX) || (gi_idx >= HAL_RX_GI_MAX)) {
+ if (mcs_idx > HAL_RX_MAX_MCS_HT || nss_idx >= HAL_RX_MAX_NSS ||
+ bw_idx >= HAL_RX_BW_MAX || gi_idx >= HAL_RX_GI_MAX) {
return;
}
- if (ppdu_info->preamble_type == HAL_RX_PREAMBLE_11N ||
- ppdu_info->preamble_type == HAL_RX_PREAMBLE_11AC) {
- rate_idx = mcs_idx * 8 + 8 * 10 * nss_idx;
- rate_idx += bw_idx * 2 + gi_idx;
- } else if (ppdu_info->preamble_type == HAL_RX_PREAMBLE_11AX) {
+ if (ppdu_info->preamble_type == HAL_RX_PREAMBLE_11AX ||
+ ppdu_info->preamble_type == HAL_RX_PREAMBLE_11BE)
gi_idx = ath12k_he_gi_to_nl80211_he_gi(ppdu_info->gi);
- rate_idx = mcs_idx * 12 + 12 * 12 * nss_idx;
- rate_idx += bw_idx * 3 + gi_idx;
- } else {
- return;
- }
- rx_stats->pkt_stats.rx_rate[rate_idx] += num_msdu;
+ rx_stats->pkt_stats.rx_rate[bw_idx][gi_idx][nss_idx][mcs_idx] += num_msdu;
+ stats = &rx_stats->byte_stats;
+
if (user_stats)
- rx_stats->byte_stats.rx_rate[rate_idx] += user_stats->mpdu_ok_byte_count;
+ len = user_stats->mpdu_ok_byte_count;
else
- rx_stats->byte_stats.rx_rate[rate_idx] += ppdu_info->mpdu_len;
+ len = ppdu_info->mpdu_len;
+
+ stats->rx_rate[bw_idx][gi_idx][nss_idx][mcs_idx] += len;
}
static void ath12k_dp_mon_rx_update_peer_su_stats(struct ath12k *ar,
@@ -2153,11 +3454,11 @@ static void ath12k_dp_mon_rx_update_peer_su_stats(struct ath12k *ar,
struct ath12k_rx_peer_stats *rx_stats = arsta->rx_stats;
u32 num_msdu;
+ arsta->rssi_comb = ppdu_info->rssi_comb;
+ ewma_avg_rssi_add(&arsta->avg_rssi, ppdu_info->rssi_comb);
if (!rx_stats)
return;
- arsta->rssi_comb = 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;
@@ -2229,6 +3530,12 @@ static void ath12k_dp_mon_rx_update_peer_su_stats(struct ath12k *ar,
rx_stats->byte_stats.he_mcs_count[ppdu_info->mcs] += ppdu_info->mpdu_len;
}
+ if (ppdu_info->preamble_type == HAL_RX_PREAMBLE_11BE &&
+ ppdu_info->mcs <= HAL_RX_MAX_MCS_BE) {
+ rx_stats->pkt_stats.be_mcs_count[ppdu_info->mcs] += num_msdu;
+ rx_stats->byte_stats.be_mcs_count[ppdu_info->mcs] += ppdu_info->mpdu_len;
+ }
+
if ((ppdu_info->preamble_type == HAL_RX_PREAMBLE_11A ||
ppdu_info->preamble_type == HAL_RX_PREAMBLE_11B) &&
ppdu_info->rate < HAL_RX_LEGACY_RATE_INVALID) {
@@ -2323,13 +3630,12 @@ ath12k_dp_mon_rx_update_user_stats(struct ath12k *ar,
ahsta = ath12k_sta_to_ahsta(peer->sta);
arsta = &ahsta->deflink;
+ arsta->rssi_comb = ppdu_info->rssi_comb;
+ ewma_avg_rssi_add(&arsta->avg_rssi, ppdu_info->rssi_comb);
rx_stats = arsta->rx_stats;
-
if (!rx_stats)
return;
- arsta->rssi_comb = ppdu_info->rssi_comb;
-
num_msdu = user_stats->tcp_msdu_count + user_stats->tcp_ack_msdu_count +
user_stats->udp_msdu_count + user_stats->other_msdu_count;
@@ -2415,8 +3721,15 @@ ath12k_dp_mon_rx_update_peer_mu_stats(struct ath12k *ar,
ath12k_dp_mon_rx_update_user_stats(ar, ppdu_info, i);
}
-int ath12k_dp_mon_rx_process_stats(struct ath12k *ar, int mac_id,
- struct napi_struct *napi, int *budget)
+static void
+ath12k_dp_mon_rx_memset_ppdu_info(struct hal_rx_mon_ppdu_info *ppdu_info)
+{
+ memset(ppdu_info, 0, sizeof(*ppdu_info));
+ ppdu_info->peer_id = HAL_INVALID_PEERID;
+}
+
+int ath12k_dp_mon_srng_process(struct ath12k *ar, int *budget,
+ struct napi_struct *napi)
{
struct ath12k_base *ab = ar->ab;
struct ath12k_pdev_dp *pdev_dp = &ar->dp;
@@ -2432,13 +3745,14 @@ int ath12k_dp_mon_rx_process_stats(struct ath12k *ar, int mac_id,
struct ath12k_sta *ahsta = NULL;
struct ath12k_link_sta *arsta;
struct ath12k_peer *peer;
+ struct sk_buff_head skb_list;
u64 cookie;
int num_buffs_reaped = 0, srng_id, buf_id;
- u8 dest_idx = 0, i;
- bool end_of_ppdu;
- u32 hal_status;
+ u32 hal_status, end_offset, info0, end_reason;
+ u8 pdev_idx = ath12k_hw_mac_id_to_pdev_id(ab->hw_params, ar->pdev_idx);
- srng_id = ath12k_hw_mac_id_to_srng_id(ab->hw_params, mac_id);
+ __skb_queue_head_init(&skb_list);
+ srng_id = ath12k_hw_mac_id_to_srng_id(ab->hw_params, pdev_idx);
mon_dst_ring = &pdev_dp->rxdma_mon_dst_ring[srng_id];
buf_ring = &dp->rxdma_mon_buf_ring;
@@ -2451,6 +3765,15 @@ int ath12k_dp_mon_rx_process_stats(struct ath12k *ar, int mac_id,
mon_dst_desc = ath12k_hal_srng_dst_peek(ab, srng);
if (unlikely(!mon_dst_desc))
break;
+
+ /* In case of empty descriptor, the cookie in the ring descriptor
+ * is invalid. Therefore, this entry is skipped, and ring processing
+ * continues.
+ */
+ info0 = le32_to_cpu(mon_dst_desc->info0);
+ if (u32_get_bits(info0, HAL_MON_DEST_INFO0_EMPTY_DESC))
+ goto move_next;
+
cookie = le32_to_cpu(mon_dst_desc->cookie);
buf_id = u32_get_bits(cookie, DP_RXDMA_BUF_COOKIE_BUF_ID);
@@ -2468,63 +3791,583 @@ int ath12k_dp_mon_rx_process_stats(struct ath12k *ar, int mac_id,
dma_unmap_single(ab->dev, rxcb->paddr,
skb->len + skb_tailroom(skb),
DMA_FROM_DEVICE);
- pmon->dest_skb_q[dest_idx] = skb;
- dest_idx++;
- end_of_ppdu = le32_get_bits(mon_dst_desc->info0,
- HAL_MON_DEST_INFO0_END_OF_PPDU);
- if (!end_of_ppdu)
+
+ end_reason = u32_get_bits(info0, HAL_MON_DEST_INFO0_END_REASON);
+
+ /* HAL_MON_FLUSH_DETECTED implies that an rx flush received at the end of
+ * rx PPDU and HAL_MON_PPDU_TRUNCATED implies that the PPDU got
+ * truncated due to a system level error. In both the cases, buffer data
+ * can be discarded
+ */
+ if ((end_reason == HAL_MON_FLUSH_DETECTED) ||
+ (end_reason == HAL_MON_PPDU_TRUNCATED)) {
+ ath12k_dbg(ab, ATH12K_DBG_DATA,
+ "Monitor dest descriptor end reason %d", end_reason);
+ dev_kfree_skb_any(skb);
+ goto move_next;
+ }
+
+ /* Calculate the budget when the ring descriptor with the
+ * HAL_MON_END_OF_PPDU to ensure that one PPDU worth of data is always
+ * reaped. This helps to efficiently utilize the NAPI budget.
+ */
+ if (end_reason == HAL_MON_END_OF_PPDU) {
+ *budget -= 1;
+ rxcb->is_end_of_ppdu = true;
+ }
+
+ end_offset = u32_get_bits(info0, HAL_MON_DEST_INFO0_END_OFFSET);
+ if (likely(end_offset <= DP_RX_BUFFER_SIZE)) {
+ skb_put(skb, end_offset);
+ } else {
+ ath12k_warn(ab,
+ "invalid offset on mon stats destination %u\n",
+ end_offset);
+ skb_put(skb, DP_RX_BUFFER_SIZE);
+ }
+
+ __skb_queue_tail(&skb_list, skb);
+
+move_next:
+ ath12k_dp_mon_buf_replenish(ab, buf_ring, 1);
+ ath12k_hal_srng_dst_get_next_entry(ab, srng);
+ num_buffs_reaped++;
+ }
+
+ ath12k_hal_srng_access_end(ab, srng);
+ spin_unlock_bh(&srng->lock);
+
+ if (!num_buffs_reaped)
+ return 0;
+
+ /* In some cases, one PPDU worth of data can be spread across multiple NAPI
+ * schedules, To avoid losing existing parsed ppdu_info information, skip
+ * the memset of the ppdu_info structure and continue processing it.
+ */
+ if (!ppdu_info->ppdu_continuation)
+ ath12k_dp_mon_rx_memset_ppdu_info(ppdu_info);
+
+ while ((skb = __skb_dequeue(&skb_list))) {
+ hal_status = ath12k_dp_mon_rx_parse_mon_status(ar, pmon, skb, napi);
+ if (hal_status != HAL_RX_MON_STATUS_PPDU_DONE) {
+ ppdu_info->ppdu_continuation = true;
+ dev_kfree_skb_any(skb);
continue;
+ }
+
+ if (ppdu_info->peer_id == HAL_INVALID_PEERID)
+ goto free_skb;
+
+ rcu_read_lock();
+ spin_lock_bh(&ab->base_lock);
+ peer = ath12k_peer_find_by_id(ab, ppdu_info->peer_id);
+ if (!peer || !peer->sta) {
+ ath12k_dbg(ab, ATH12K_DBG_DATA,
+ "failed to find the peer with monitor peer_id %d\n",
+ ppdu_info->peer_id);
+ goto next_skb;
+ }
+
+ if (ppdu_info->reception_type == HAL_RX_RECEPTION_TYPE_SU) {
+ ahsta = ath12k_sta_to_ahsta(peer->sta);
+ arsta = &ahsta->deflink;
+ ath12k_dp_mon_rx_update_peer_su_stats(ar, arsta,
+ ppdu_info);
+ } else if ((ppdu_info->fc_valid) &&
+ (ppdu_info->ast_index != HAL_AST_IDX_INVALID)) {
+ ath12k_dp_mon_rx_process_ulofdma(ppdu_info);
+ ath12k_dp_mon_rx_update_peer_mu_stats(ar, ppdu_info);
+ }
+
+next_skb:
+ spin_unlock_bh(&ab->base_lock);
+ rcu_read_unlock();
+free_skb:
+ dev_kfree_skb_any(skb);
+ ath12k_dp_mon_rx_memset_ppdu_info(ppdu_info);
+ }
- for (i = 0; i < dest_idx; i++) {
- skb = pmon->dest_skb_q[i];
- hal_status = ath12k_dp_mon_parse_rx_dest(ab, pmon, skb);
+ return num_buffs_reaped;
+}
+
+static int ath12k_dp_rx_reap_mon_status_ring(struct ath12k_base *ab, int mac_id,
+ int *budget, struct sk_buff_head *skb_list)
+{
+ const struct ath12k_hw_hal_params *hal_params;
+ int buf_id, srng_id, num_buffs_reaped = 0;
+ enum dp_mon_status_buf_state reap_status;
+ struct dp_rxdma_mon_ring *rx_ring;
+ struct ath12k_mon_data *pmon;
+ struct ath12k_skb_rxcb *rxcb;
+ struct hal_tlv_64_hdr *tlv;
+ void *rx_mon_status_desc;
+ struct hal_srng *srng;
+ struct ath12k_dp *dp;
+ struct sk_buff *skb;
+ struct ath12k *ar;
+ dma_addr_t paddr;
+ u32 cookie;
+ u8 rbm;
+
+ ar = ab->pdevs[ath12k_hw_mac_id_to_pdev_id(ab->hw_params, mac_id)].ar;
+ dp = &ab->dp;
+ pmon = &ar->dp.mon_data;
+ srng_id = ath12k_hw_mac_id_to_srng_id(ab->hw_params, mac_id);
+ rx_ring = &dp->rx_mon_status_refill_ring[srng_id];
+
+ srng = &ab->hal.srng_list[rx_ring->refill_buf_ring.ring_id];
+
+ spin_lock_bh(&srng->lock);
+
+ ath12k_hal_srng_access_begin(ab, srng);
+
+ while (*budget) {
+ *budget -= 1;
+ rx_mon_status_desc = ath12k_hal_srng_src_peek(ab, srng);
+ if (!rx_mon_status_desc) {
+ pmon->buf_state = DP_MON_STATUS_REPLINISH;
+ break;
+ }
+ ath12k_hal_rx_buf_addr_info_get(rx_mon_status_desc, &paddr,
+ &cookie, &rbm);
+ if (paddr) {
+ buf_id = u32_get_bits(cookie, DP_RXDMA_BUF_COOKIE_BUF_ID);
+
+ spin_lock_bh(&rx_ring->idr_lock);
+ skb = idr_find(&rx_ring->bufs_idr, buf_id);
+ spin_unlock_bh(&rx_ring->idr_lock);
+
+ if (!skb) {
+ ath12k_warn(ab, "rx monitor status with invalid buf_id %d\n",
+ buf_id);
+ pmon->buf_state = DP_MON_STATUS_REPLINISH;
+ goto move_next;
+ }
+
+ rxcb = ATH12K_SKB_RXCB(skb);
+
+ dma_sync_single_for_cpu(ab->dev, rxcb->paddr,
+ skb->len + skb_tailroom(skb),
+ DMA_FROM_DEVICE);
+
+ tlv = (struct hal_tlv_64_hdr *)skb->data;
+ if (le64_get_bits(tlv->tl, HAL_TLV_HDR_TAG) !=
+ HAL_RX_STATUS_BUFFER_DONE) {
+ pmon->buf_state = DP_MON_STATUS_NO_DMA;
+ ath12k_warn(ab,
+ "mon status DONE not set %llx, buf_id %d\n",
+ le64_get_bits(tlv->tl, HAL_TLV_HDR_TAG),
+ buf_id);
+ /* RxDMA status done bit might not be set even
+ * though tp is moved by HW.
+ */
+
+ /* If done status is missing:
+ * 1. As per MAC team's suggestion,
+ * when HP + 1 entry is peeked and if DMA
+ * is not done and if HP + 2 entry's DMA done
+ * is set. skip HP + 1 entry and
+ * start processing in next interrupt.
+ * 2. If HP + 2 entry's DMA done is not set,
+ * poll onto HP + 1 entry DMA done to be set.
+ * Check status for same buffer for next time
+ * dp_rx_mon_status_srng_process
+ */
+ reap_status = ath12k_dp_rx_mon_buf_done(ab, srng,
+ rx_ring);
+ if (reap_status == DP_MON_STATUS_NO_DMA)
+ continue;
+
+ spin_lock_bh(&rx_ring->idr_lock);
+ idr_remove(&rx_ring->bufs_idr, buf_id);
+ spin_unlock_bh(&rx_ring->idr_lock);
+
+ dma_unmap_single(ab->dev, rxcb->paddr,
+ skb->len + skb_tailroom(skb),
+ DMA_FROM_DEVICE);
- if (ppdu_info->peer_id == HAL_INVALID_PEERID ||
- hal_status != HAL_RX_MON_STATUS_PPDU_DONE) {
dev_kfree_skb_any(skb);
- continue;
+ pmon->buf_state = DP_MON_STATUS_REPLINISH;
+ goto move_next;
}
- rcu_read_lock();
- spin_lock_bh(&ab->base_lock);
- peer = ath12k_peer_find_by_id(ab, ppdu_info->peer_id);
- if (!peer || !peer->sta) {
- ath12k_dbg(ab, ATH12K_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();
+ spin_lock_bh(&rx_ring->idr_lock);
+ idr_remove(&rx_ring->bufs_idr, buf_id);
+ spin_unlock_bh(&rx_ring->idr_lock);
+
+ dma_unmap_single(ab->dev, rxcb->paddr,
+ skb->len + skb_tailroom(skb),
+ DMA_FROM_DEVICE);
+
+ if (ath12k_dp_pkt_set_pktlen(skb, RX_MON_STATUS_BUF_SIZE)) {
dev_kfree_skb_any(skb);
+ goto move_next;
+ }
+ __skb_queue_tail(skb_list, skb);
+ } else {
+ pmon->buf_state = DP_MON_STATUS_REPLINISH;
+ }
+move_next:
+ skb = ath12k_dp_rx_alloc_mon_status_buf(ab, rx_ring,
+ &buf_id);
+
+ if (!skb) {
+ ath12k_warn(ab, "failed to alloc buffer for status ring\n");
+ hal_params = ab->hw_params->hal_params;
+ ath12k_hal_rx_buf_addr_info_set(rx_mon_status_desc, 0, 0,
+ hal_params->rx_buf_rbm);
+ num_buffs_reaped++;
+ break;
+ }
+ rxcb = ATH12K_SKB_RXCB(skb);
+
+ cookie = u32_encode_bits(mac_id, DP_RXDMA_BUF_COOKIE_PDEV_ID) |
+ u32_encode_bits(buf_id, DP_RXDMA_BUF_COOKIE_BUF_ID);
+
+ ath12k_hal_rx_buf_addr_info_set(rx_mon_status_desc, rxcb->paddr,
+ cookie,
+ ab->hw_params->hal_params->rx_buf_rbm);
+ ath12k_hal_srng_src_get_next_entry(ab, srng);
+ num_buffs_reaped++;
+ }
+ ath12k_hal_srng_access_end(ab, srng);
+ spin_unlock_bh(&srng->lock);
+
+ return num_buffs_reaped;
+}
+
+static u32
+ath12k_dp_rx_mon_mpdu_pop(struct ath12k *ar, int mac_id,
+ void *ring_entry, struct sk_buff **head_msdu,
+ struct sk_buff **tail_msdu,
+ struct list_head *used_list,
+ u32 *npackets, u32 *ppdu_id)
+{
+ struct ath12k_mon_data *pmon = (struct ath12k_mon_data *)&ar->dp.mon_data;
+ struct ath12k_buffer_addr *p_buf_addr_info, *p_last_buf_addr_info;
+ u32 msdu_ppdu_id = 0, msdu_cnt = 0, total_len = 0, frag_len = 0;
+ u32 rx_buf_size, rx_pkt_offset, sw_cookie;
+ bool is_frag, is_first_msdu, drop_mpdu = false;
+ struct hal_reo_entrance_ring *ent_desc =
+ (struct hal_reo_entrance_ring *)ring_entry;
+ u32 rx_bufs_used = 0, i = 0, desc_bank = 0;
+ struct hal_rx_desc *rx_desc, *tail_rx_desc;
+ struct hal_rx_msdu_link *msdu_link_desc;
+ struct sk_buff *msdu = NULL, *last = NULL;
+ struct ath12k_rx_desc_info *desc_info;
+ struct ath12k_buffer_addr buf_info;
+ struct hal_rx_msdu_list msdu_list;
+ struct ath12k_skb_rxcb *rxcb;
+ u16 num_msdus = 0;
+ dma_addr_t paddr;
+ u8 rbm;
+
+ ath12k_hal_rx_reo_ent_buf_paddr_get(ring_entry, &paddr,
+ &sw_cookie,
+ &p_last_buf_addr_info, &rbm,
+ &msdu_cnt);
+
+ spin_lock_bh(&pmon->mon_lock);
+
+ if (le32_get_bits(ent_desc->info1,
+ HAL_REO_ENTR_RING_INFO1_RXDMA_PUSH_REASON) ==
+ HAL_REO_DEST_RING_PUSH_REASON_ERR_DETECTED) {
+ u8 rxdma_err = le32_get_bits(ent_desc->info1,
+ HAL_REO_ENTR_RING_INFO1_RXDMA_ERROR_CODE);
+ 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) {
+ drop_mpdu = true;
+ pmon->rx_mon_stats.dest_mpdu_drop++;
+ }
+ }
+
+ is_frag = false;
+ is_first_msdu = true;
+ rx_pkt_offset = sizeof(struct hal_rx_desc);
+
+ do {
+ if (pmon->mon_last_linkdesc_paddr == paddr) {
+ pmon->rx_mon_stats.dup_mon_linkdesc_cnt++;
+ spin_unlock_bh(&pmon->mon_lock);
+ return rx_bufs_used;
+ }
+
+ desc_bank = u32_get_bits(sw_cookie, DP_LINK_DESC_BANK_MASK);
+ msdu_link_desc =
+ ar->ab->dp.link_desc_banks[desc_bank].vaddr +
+ (paddr - ar->ab->dp.link_desc_banks[desc_bank].paddr);
+
+ ath12k_hal_rx_msdu_list_get(ar, msdu_link_desc, &msdu_list,
+ &num_msdus);
+ desc_info = ath12k_dp_get_rx_desc(ar->ab,
+ msdu_list.sw_cookie[num_msdus - 1]);
+ tail_rx_desc = (struct hal_rx_desc *)(desc_info->skb)->data;
+
+ for (i = 0; i < num_msdus; i++) {
+ u32 l2_hdr_offset;
+
+ if (pmon->mon_last_buf_cookie == msdu_list.sw_cookie[i]) {
+ ath12k_dbg(ar->ab, ATH12K_DBG_DATA,
+ "i %d last_cookie %d is same\n",
+ i, pmon->mon_last_buf_cookie);
+ drop_mpdu = true;
+ pmon->rx_mon_stats.dup_mon_buf_cnt++;
continue;
}
- if (ppdu_info->reception_type == HAL_RX_RECEPTION_TYPE_SU) {
- ahsta = ath12k_sta_to_ahsta(peer->sta);
- arsta = &ahsta->deflink;
- ath12k_dp_mon_rx_update_peer_su_stats(ar, arsta,
- ppdu_info);
- } else if ((ppdu_info->fc_valid) &&
- (ppdu_info->ast_index != HAL_AST_IDX_INVALID)) {
- ath12k_dp_mon_rx_process_ulofdma(ppdu_info);
- ath12k_dp_mon_rx_update_peer_mu_stats(ar, ppdu_info);
+ desc_info =
+ ath12k_dp_get_rx_desc(ar->ab, msdu_list.sw_cookie[i]);
+ msdu = desc_info->skb;
+
+ if (!msdu) {
+ ath12k_dbg(ar->ab, ATH12K_DBG_DATA,
+ "msdu_pop: invalid msdu (%d/%d)\n",
+ i + 1, num_msdus);
+ goto next_msdu;
+ }
+ rxcb = ATH12K_SKB_RXCB(msdu);
+ if (rxcb->paddr != msdu_list.paddr[i]) {
+ ath12k_dbg(ar->ab, ATH12K_DBG_DATA,
+ "i %d paddr %lx != %lx\n",
+ i, (unsigned long)rxcb->paddr,
+ (unsigned long)msdu_list.paddr[i]);
+ drop_mpdu = true;
+ continue;
+ }
+ 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) {
+ ath12k_dbg(ar->ab, ATH12K_DBG_DATA,
+ "i %d drop msdu %p *ppdu_id %x\n",
+ i, msdu, *ppdu_id);
+ dev_kfree_skb_any(msdu);
+ msdu = NULL;
+ goto next_msdu;
}
- 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;
+ rx_desc = (struct hal_rx_desc *)msdu->data;
+ l2_hdr_offset = ath12k_dp_rx_h_l3pad(ar->ab, tail_rx_desc);
+ if (is_first_msdu) {
+ if (!ath12k_dp_rxdesc_mpdu_valid(ar->ab, rx_desc)) {
+ drop_mpdu = true;
+ dev_kfree_skb_any(msdu);
+ msdu = NULL;
+ pmon->mon_last_linkdesc_paddr = paddr;
+ goto next_msdu;
+ }
+ msdu_ppdu_id =
+ ath12k_dp_rxdesc_get_ppduid(ar->ab, rx_desc);
+
+ if (ath12k_dp_mon_comp_ppduid(msdu_ppdu_id,
+ ppdu_id)) {
+ spin_unlock_bh(&pmon->mon_lock);
+ return rx_bufs_used;
+ }
+ pmon->mon_last_linkdesc_paddr = paddr;
+ is_first_msdu = false;
+ }
+ ath12k_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;
+
+ if (ath12k_dp_pkt_set_pktlen(msdu, rx_buf_size)) {
+ dev_kfree_skb_any(msdu);
+ goto next_msdu;
+ }
+
+ if (!(*head_msdu))
+ *head_msdu = msdu;
+ else if (last)
+ last->next = msdu;
+
+ last = msdu;
+next_msdu:
+ pmon->mon_last_buf_cookie = msdu_list.sw_cookie[i];
+ rx_bufs_used++;
+ desc_info->skb = NULL;
+ list_add_tail(&desc_info->list, used_list);
}
- dest_idx = 0;
-move_next:
- ath12k_dp_mon_buf_replenish(ab, buf_ring, 1);
- ath12k_hal_srng_src_get_next_entry(ab, srng);
- num_buffs_reaped++;
+ ath12k_hal_rx_buf_addr_info_set(&buf_info, paddr, sw_cookie, rbm);
+
+ ath12k_dp_mon_next_link_desc_get(msdu_link_desc, &paddr,
+ &sw_cookie, &rbm,
+ &p_buf_addr_info);
+
+ ath12k_dp_rx_link_desc_return(ar->ab, &buf_info,
+ HAL_WBM_REL_BM_ACT_PUT_IN_IDLE);
+
+ p_last_buf_addr_info = p_buf_addr_info;
+
+ } while (paddr && msdu_cnt);
+
+ spin_unlock_bh(&pmon->mon_lock);
+
+ if (last)
+ last->next = NULL;
+
+ *tail_msdu = msdu;
+
+ if (msdu_cnt == 0)
+ *npackets = 1;
+
+ return rx_bufs_used;
+}
+
+/* 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 ath12k_dp_rx_mon_dest_process(struct ath12k *ar, int mac_id,
+ u32 quota, struct napi_struct *napi)
+{
+ struct ath12k_mon_data *pmon = (struct ath12k_mon_data *)&ar->dp.mon_data;
+ struct ath12k_pdev_mon_stats *rx_mon_stats;
+ u32 ppdu_id, rx_bufs_used = 0, ring_id;
+ u32 mpdu_rx_bufs_used, npackets = 0;
+ struct ath12k_dp *dp = &ar->ab->dp;
+ struct ath12k_base *ab = ar->ab;
+ void *ring_entry, *mon_dst_srng;
+ struct dp_mon_mpdu *tmp_mpdu;
+ LIST_HEAD(rx_desc_used_list);
+ struct hal_srng *srng;
+
+ ring_id = dp->rxdma_err_dst_ring[mac_id].ring_id;
+ srng = &ab->hal.srng_list[ring_id];
+
+ mon_dst_srng = &ab->hal.srng_list[ring_id];
+
+ spin_lock_bh(&srng->lock);
+
+ ath12k_hal_srng_access_begin(ab, mon_dst_srng);
+
+ ppdu_id = pmon->mon_ppdu_info.ppdu_id;
+ rx_mon_stats = &pmon->rx_mon_stats;
+
+ while ((ring_entry = ath12k_hal_srng_dst_peek(ar->ab, mon_dst_srng))) {
+ struct sk_buff *head_msdu, *tail_msdu;
+
+ head_msdu = NULL;
+ tail_msdu = NULL;
+
+ mpdu_rx_bufs_used = ath12k_dp_rx_mon_mpdu_pop(ar, mac_id, ring_entry,
+ &head_msdu, &tail_msdu,
+ &rx_desc_used_list,
+ &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++;
+ ath12k_dbg(ar->ab, ATH12K_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);
+ spin_lock_bh(&pmon->mon_lock);
+ pmon->mon_ppdu_info.ppdu_id = ppdu_id;
+ spin_unlock_bh(&pmon->mon_lock);
+ continue;
+ }
+
+ if (ppdu_id != pmon->mon_ppdu_info.ppdu_id) {
+ spin_lock_bh(&pmon->mon_lock);
+ pmon->mon_ppdu_status = DP_PPDU_STATUS_START;
+ spin_unlock_bh(&pmon->mon_lock);
+ ath12k_dbg(ar->ab, ATH12K_DBG_DATA,
+ "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) {
+ tmp_mpdu = kzalloc(sizeof(*tmp_mpdu), GFP_ATOMIC);
+ if (!tmp_mpdu)
+ break;
+
+ tmp_mpdu->head = head_msdu;
+ tmp_mpdu->tail = tail_msdu;
+ tmp_mpdu->err_bitmap = pmon->err_bitmap;
+ tmp_mpdu->decap_format = pmon->decap_format;
+ ath12k_dp_mon_rx_deliver(ar, tmp_mpdu,
+ &pmon->mon_ppdu_info, napi);
+ rx_mon_stats->dest_mpdu_done++;
+ kfree(tmp_mpdu);
+ }
+
+ ring_entry = ath12k_hal_srng_dst_get_next_entry(ar->ab,
+ mon_dst_srng);
}
+ ath12k_hal_srng_access_end(ar->ab, mon_dst_srng);
- ath12k_hal_srng_access_end(ab, srng);
spin_unlock_bh(&srng->lock);
+
+ if (rx_bufs_used) {
+ rx_mon_stats->dest_ppdu_done++;
+ ath12k_dp_rx_bufs_replenish(ar->ab,
+ &dp->rx_refill_buf_ring,
+ &rx_desc_used_list,
+ rx_bufs_used);
+ }
+}
+
+static int
+__ath12k_dp_mon_process_ring(struct ath12k *ar, int mac_id,
+ struct napi_struct *napi, int *budget)
+{
+ struct ath12k_mon_data *pmon = (struct ath12k_mon_data *)&ar->dp.mon_data;
+ struct ath12k_pdev_mon_stats *rx_mon_stats = &pmon->rx_mon_stats;
+ struct hal_rx_mon_ppdu_info *ppdu_info = &pmon->mon_ppdu_info;
+ enum hal_rx_mon_status hal_status;
+ struct sk_buff_head skb_list;
+ int num_buffs_reaped;
+ struct sk_buff *skb;
+
+ __skb_queue_head_init(&skb_list);
+
+ num_buffs_reaped = ath12k_dp_rx_reap_mon_status_ring(ar->ab, mac_id,
+ budget, &skb_list);
+ 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;
+
+ hal_status = ath12k_dp_mon_parse_rx_dest(ar, pmon, skb);
+
+ if (ar->monitor_started &&
+ 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;
+ ath12k_dp_rx_mon_dest_process(ar, mac_id, *budget, napi);
+ pmon->mon_ppdu_status = DP_PPDU_STATUS_START;
+ }
+
+ dev_kfree_skb_any(skb);
+ }
+
+exit:
return num_buffs_reaped;
}
@@ -2535,11 +4378,14 @@ int ath12k_dp_mon_process_ring(struct ath12k_base *ab, int mac_id,
struct ath12k *ar = ath12k_ab_to_ar(ab, mac_id);
int num_buffs_reaped = 0;
- if (!ar->monitor_started)
- ath12k_dp_mon_rx_process_stats(ar, mac_id, napi, &budget);
- else
- num_buffs_reaped = ath12k_dp_mon_srng_process(ar, mac_id, &budget,
- monitor_mode, napi);
+ if (ab->hw_params->rxdma1_enable) {
+ if (monitor_mode == ATH12K_DP_RX_MONITOR_MODE)
+ num_buffs_reaped = ath12k_dp_mon_srng_process(ar, &budget, napi);
+ } else {
+ if (ar->monitor_started)
+ num_buffs_reaped =
+ __ath12k_dp_mon_process_ring(ar, mac_id, napi, &budget);
+ }
return num_buffs_reaped;
}
diff --git a/drivers/net/wireless/ath/ath12k/dp_mon.h b/drivers/net/wireless/ath/ath12k/dp_mon.h
index fb9e9c176ce5..e25595cbdcf3 100644
--- a/drivers/net/wireless/ath/ath12k/dp_mon.h
+++ b/drivers/net/wireless/ath/ath12k/dp_mon.h
@@ -1,7 +1,7 @@
/* SPDX-License-Identifier: BSD-3-Clause-Clear */
/*
* Copyright (c) 2019-2021 The Linux Foundation. All rights reserved.
- * Copyright (c) 2021-2023 Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#ifndef ATH12K_DP_MON_H
@@ -9,6 +9,9 @@
#include "core.h"
+#define ATH12K_MON_RX_DOT11_OFFSET 5
+#define ATH12K_MON_RX_PKT_OFFSET 8
+
enum dp_monitor_mode {
ATH12K_DP_TX_MONITOR_MODE,
ATH12K_DP_RX_MONITOR_MODE
@@ -77,14 +80,14 @@ struct dp_mon_tx_ppdu_info {
enum hal_rx_mon_status
ath12k_dp_mon_rx_parse_mon_status(struct ath12k *ar,
struct ath12k_mon_data *pmon,
- int mac_id, struct sk_buff *skb,
+ struct sk_buff *skb,
struct napi_struct *napi);
int ath12k_dp_mon_buf_replenish(struct ath12k_base *ab,
struct dp_rxdma_mon_ring *buf_ring,
int req_entries);
-int ath12k_dp_mon_srng_process(struct ath12k *ar, int mac_id,
- int *budget, enum dp_monitor_mode monitor_mode,
- struct napi_struct *napi);
+int ath12k_dp_mon_status_bufs_replenish(struct ath12k_base *ab,
+ struct dp_rxdma_mon_ring *rx_ring,
+ int req_entries);
int ath12k_dp_mon_process_ring(struct ath12k_base *ab, int mac_id,
struct napi_struct *napi, int budget,
enum dp_monitor_mode monitor_mode);
@@ -96,11 +99,9 @@ ath12k_dp_mon_tx_status_get_num_user(u16 tlv_tag,
enum hal_rx_mon_status
ath12k_dp_mon_tx_parse_mon_status(struct ath12k *ar,
struct ath12k_mon_data *pmon,
- int mac_id,
struct sk_buff *skb,
struct napi_struct *napi,
u32 ppdu_id);
void ath12k_dp_mon_rx_process_ulofdma(struct hal_rx_mon_ppdu_info *ppdu_info);
-int ath12k_dp_mon_rx_process_stats(struct ath12k *ar, int mac_id,
- struct napi_struct *napi, int *budget);
+int ath12k_dp_mon_srng_process(struct ath12k *ar, int *budget, struct napi_struct *napi);
#endif
diff --git a/drivers/net/wireless/ath/ath12k/dp_rx.c b/drivers/net/wireless/ath/ath12k/dp_rx.c
index dad35bfd83f6..7e1a8f29c7b6 100644
--- a/drivers/net/wireless/ath/ath12k/dp_rx.c
+++ b/drivers/net/wireless/ath/ath12k/dp_rx.c
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: BSD-3-Clause-Clear
/*
* Copyright (c) 2018-2021 The Linux Foundation. All rights reserved.
- * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#include <linux/ieee80211.h>
@@ -194,6 +194,22 @@ static void ath12k_dp_rxdesc_set_msdu_len(struct ath12k_base *ab,
ab->hal_rx_ops->rx_desc_set_msdu_len(desc, len);
}
+u32 ath12k_dp_rxdesc_get_ppduid(struct ath12k_base *ab,
+ struct hal_rx_desc *rx_desc)
+{
+ return ab->hal_rx_ops->rx_desc_get_mpdu_ppdu_id(rx_desc);
+}
+
+bool ath12k_dp_rxdesc_mpdu_valid(struct ath12k_base *ab,
+ struct hal_rx_desc *rx_desc)
+{
+ u32 tlv_tag;
+
+ tlv_tag = ab->hal_rx_ops->rx_desc_get_mpdu_start_tag(rx_desc);
+
+ return tlv_tag == HAL_RX_MPDU_START;
+}
+
static bool ath12k_dp_rx_h_is_da_mcbc(struct ath12k_base *ab,
struct hal_rx_desc *desc)
{
@@ -228,12 +244,6 @@ static void ath12k_dp_rx_desc_get_crypto_header(struct ath12k_base *ab,
ab->hal_rx_ops->rx_desc_get_crypto_header(desc, crypto_hdr, enctype);
}
-static u16 ath12k_dp_rxdesc_get_mpdu_frame_ctrl(struct ath12k_base *ab,
- struct hal_rx_desc *desc)
-{
- return ab->hal_rx_ops->rx_desc_get_mpdu_frame_ctl(desc);
-}
-
static inline u8 ath12k_dp_rx_get_msdu_src_link(struct ath12k_base *ab,
struct hal_rx_desc *desc)
{
@@ -420,9 +430,17 @@ static int ath12k_dp_rxdma_mon_buf_ring_free(struct ath12k_base *ab,
static int ath12k_dp_rxdma_buf_free(struct ath12k_base *ab)
{
struct ath12k_dp *dp = &ab->dp;
+ int i;
ath12k_dp_rxdma_mon_buf_ring_free(ab, &dp->rxdma_mon_buf_ring);
+ if (ab->hw_params->rxdma1_enable)
+ return 0;
+
+ for (i = 0; i < ab->hw_params->num_rxdma_per_pdev; i++)
+ ath12k_dp_rxdma_mon_buf_ring_free(ab,
+ &dp->rx_mon_status_refill_ring[i]);
+
return 0;
}
@@ -436,7 +454,12 @@ static int ath12k_dp_rxdma_mon_ring_buf_setup(struct ath12k_base *ab,
ath12k_hal_srng_get_entrysize(ab, ringtype);
rx_ring->bufs_max = num_entries;
- ath12k_dp_mon_buf_replenish(ab, rx_ring, num_entries);
+
+ if (ringtype == HAL_RXDMA_MONITOR_STATUS)
+ ath12k_dp_mon_status_bufs_replenish(ab, rx_ring,
+ num_entries);
+ else
+ ath12k_dp_mon_buf_replenish(ab, rx_ring, num_entries);
return 0;
}
@@ -457,7 +480,8 @@ static int ath12k_dp_rxdma_ring_buf_setup(struct ath12k_base *ab,
static int ath12k_dp_rxdma_buf_setup(struct ath12k_base *ab)
{
struct ath12k_dp *dp = &ab->dp;
- int ret;
+ struct dp_rxdma_mon_ring *mon_ring;
+ int ret, i;
ret = ath12k_dp_rxdma_ring_buf_setup(ab, &dp->rx_refill_buf_ring);
if (ret) {
@@ -470,9 +494,19 @@ static int ath12k_dp_rxdma_buf_setup(struct ath12k_base *ab)
ret = ath12k_dp_rxdma_mon_ring_buf_setup(ab,
&dp->rxdma_mon_buf_ring,
HAL_RXDMA_MONITOR_BUF);
- if (ret) {
+ if (ret)
ath12k_warn(ab,
"failed to setup HAL_RXDMA_MONITOR_BUF\n");
+ return ret;
+ }
+
+ for (i = 0; i < ab->hw_params->num_rxdma_per_pdev; i++) {
+ mon_ring = &dp->rx_mon_status_refill_ring[i];
+ ret = ath12k_dp_rxdma_mon_ring_buf_setup(ab, mon_ring,
+ HAL_RXDMA_MONITOR_STATUS);
+ if (ret) {
+ ath12k_warn(ab,
+ "failed to setup HAL_RXDMA_MONITOR_STATUS\n");
return ret;
}
}
@@ -556,9 +590,9 @@ void ath12k_dp_rx_reo_cmd_list_cleanup(struct ath12k_base *ab)
spin_lock_bh(&dp->reo_cmd_lock);
list_for_each_entry_safe(cmd, tmp, &dp->reo_cmd_list, list) {
list_del(&cmd->list);
- dma_unmap_single(ab->dev, cmd->data.paddr,
- cmd->data.size, DMA_BIDIRECTIONAL);
- kfree(cmd->data.vaddr);
+ dma_unmap_single(ab->dev, cmd->data.qbuf.paddr_aligned,
+ cmd->data.qbuf.size, DMA_BIDIRECTIONAL);
+ kfree(cmd->data.qbuf.vaddr);
kfree(cmd);
}
@@ -566,9 +600,9 @@ void ath12k_dp_rx_reo_cmd_list_cleanup(struct ath12k_base *ab)
&dp->reo_cmd_cache_flush_list, list) {
list_del(&cmd_cache->list);
dp->reo_cmd_cache_flush_count--;
- dma_unmap_single(ab->dev, cmd_cache->data.paddr,
- cmd_cache->data.size, DMA_BIDIRECTIONAL);
- kfree(cmd_cache->data.vaddr);
+ dma_unmap_single(ab->dev, cmd_cache->data.qbuf.paddr_aligned,
+ cmd_cache->data.qbuf.size, DMA_BIDIRECTIONAL);
+ kfree(cmd_cache->data.qbuf.vaddr);
kfree(cmd_cache);
}
spin_unlock_bh(&dp->reo_cmd_lock);
@@ -583,10 +617,10 @@ static void ath12k_dp_reo_cmd_free(struct ath12k_dp *dp, void *ctx,
ath12k_warn(dp->ab, "failed to flush rx tid hw desc, tid %d status %d\n",
rx_tid->tid, status);
- dma_unmap_single(dp->ab->dev, rx_tid->paddr, rx_tid->size,
+ dma_unmap_single(dp->ab->dev, rx_tid->qbuf.paddr_aligned, rx_tid->qbuf.size,
DMA_BIDIRECTIONAL);
- kfree(rx_tid->vaddr);
- rx_tid->vaddr = NULL;
+ kfree(rx_tid->qbuf.vaddr);
+ rx_tid->qbuf.vaddr = NULL;
}
static int ath12k_dp_reo_cmd_send(struct ath12k_base *ab, struct ath12k_dp_rx_tid *rx_tid,
@@ -641,13 +675,13 @@ static void ath12k_dp_reo_cache_flush(struct ath12k_base *ab,
unsigned long tot_desc_sz, desc_sz;
int ret;
- tot_desc_sz = rx_tid->size;
+ tot_desc_sz = rx_tid->qbuf.size;
desc_sz = ath12k_hal_reo_qdesc_size(0, HAL_DESC_REO_NON_QOS_TID);
while (tot_desc_sz > desc_sz) {
tot_desc_sz -= desc_sz;
- cmd.addr_lo = lower_32_bits(rx_tid->paddr + tot_desc_sz);
- cmd.addr_hi = upper_32_bits(rx_tid->paddr);
+ cmd.addr_lo = lower_32_bits(rx_tid->qbuf.paddr_aligned + tot_desc_sz);
+ cmd.addr_hi = upper_32_bits(rx_tid->qbuf.paddr_aligned);
ret = ath12k_dp_reo_cmd_send(ab, rx_tid,
HAL_REO_CMD_FLUSH_CACHE, &cmd,
NULL);
@@ -658,8 +692,8 @@ static void ath12k_dp_reo_cache_flush(struct ath12k_base *ab,
}
memset(&cmd, 0, sizeof(cmd));
- cmd.addr_lo = lower_32_bits(rx_tid->paddr);
- cmd.addr_hi = upper_32_bits(rx_tid->paddr);
+ cmd.addr_lo = lower_32_bits(rx_tid->qbuf.paddr_aligned);
+ cmd.addr_hi = upper_32_bits(rx_tid->qbuf.paddr_aligned);
cmd.flag = HAL_REO_CMD_FLG_NEED_STATUS;
ret = ath12k_dp_reo_cmd_send(ab, rx_tid,
HAL_REO_CMD_FLUSH_CACHE,
@@ -667,10 +701,10 @@ static void ath12k_dp_reo_cache_flush(struct ath12k_base *ab,
if (ret) {
ath12k_err(ab, "failed to send HAL_REO_CMD_FLUSH_CACHE cmd, tid %d (%d)\n",
rx_tid->tid, ret);
- dma_unmap_single(ab->dev, rx_tid->paddr, rx_tid->size,
+ dma_unmap_single(ab->dev, rx_tid->qbuf.paddr_aligned, rx_tid->qbuf.size,
DMA_BIDIRECTIONAL);
- kfree(rx_tid->vaddr);
- rx_tid->vaddr = NULL;
+ kfree(rx_tid->qbuf.vaddr);
+ rx_tid->qbuf.vaddr = NULL;
}
}
@@ -729,10 +763,10 @@ static void ath12k_dp_rx_tid_del_func(struct ath12k_dp *dp, void *ctx,
return;
free_desc:
- dma_unmap_single(ab->dev, rx_tid->paddr, rx_tid->size,
+ dma_unmap_single(ab->dev, rx_tid->qbuf.paddr_aligned, rx_tid->qbuf.size,
DMA_BIDIRECTIONAL);
- kfree(rx_tid->vaddr);
- rx_tid->vaddr = NULL;
+ kfree(rx_tid->qbuf.vaddr);
+ rx_tid->qbuf.vaddr = NULL;
}
static void ath12k_peer_rx_tid_qref_setup(struct ath12k_base *ab, u16 peer_id, u16 tid,
@@ -762,6 +796,7 @@ static void ath12k_peer_rx_tid_qref_setup(struct ath12k_base *ab, u16 peer_id, u
qref->info1 = u32_encode_bits(upper_32_bits(paddr),
BUFFER_ADDR_INFO1_ADDR) |
u32_encode_bits(tid, DP_REO_QREF_NUM);
+ ath12k_hal_reo_shared_qaddr_cache_clear(ab);
}
static void ath12k_peer_rx_tid_qref_reset(struct ath12k_base *ab, u16 peer_id, u16 tid)
@@ -801,8 +836,8 @@ void ath12k_dp_rx_peer_tid_delete(struct ath12k *ar,
return;
cmd.flag = HAL_REO_CMD_FLG_NEED_STATUS;
- cmd.addr_lo = lower_32_bits(rx_tid->paddr);
- cmd.addr_hi = upper_32_bits(rx_tid->paddr);
+ cmd.addr_lo = lower_32_bits(rx_tid->qbuf.paddr_aligned);
+ cmd.addr_hi = upper_32_bits(rx_tid->qbuf.paddr_aligned);
cmd.upd0 = HAL_REO_CMD_UPD0_VLD;
ret = ath12k_dp_reo_cmd_send(ar->ab, rx_tid,
HAL_REO_CMD_UPDATE_RX_QUEUE, &cmd,
@@ -810,10 +845,10 @@ void ath12k_dp_rx_peer_tid_delete(struct ath12k *ar,
if (ret) {
ath12k_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);
- rx_tid->vaddr = NULL;
+ dma_unmap_single(ar->ab->dev, rx_tid->qbuf.paddr_aligned,
+ rx_tid->qbuf.size, DMA_BIDIRECTIONAL);
+ kfree(rx_tid->qbuf.vaddr);
+ rx_tid->qbuf.vaddr = NULL;
}
if (peer->mlo)
@@ -824,15 +859,10 @@ void ath12k_dp_rx_peer_tid_delete(struct ath12k *ar,
rx_tid->active = false;
}
-/* TODO: it's strange (and ugly) that struct hal_reo_dest_ring is converted
- * to struct hal_wbm_release_ring, I couldn't figure out the logic behind
- * that.
- */
-static int ath12k_dp_rx_link_desc_return(struct ath12k_base *ab,
- struct hal_reo_dest_ring *ring,
- enum hal_wbm_rel_bm_act action)
+int ath12k_dp_rx_link_desc_return(struct ath12k_base *ab,
+ struct ath12k_buffer_addr *buf_addr_info,
+ enum hal_wbm_rel_bm_act action)
{
- struct hal_wbm_release_ring *link_desc = (struct hal_wbm_release_ring *)ring;
struct hal_wbm_release_ring *desc;
struct ath12k_dp *dp = &ab->dp;
struct hal_srng *srng;
@@ -850,7 +880,7 @@ static int ath12k_dp_rx_link_desc_return(struct ath12k_base *ab,
goto exit;
}
- ath12k_hal_rx_msdu_link_desc_set(ab, desc, link_desc, action);
+ ath12k_hal_rx_msdu_link_desc_set(ab, desc, buf_addr_info, action);
exit:
ath12k_hal_srng_access_end(ab, srng);
@@ -863,14 +893,17 @@ exit:
static void ath12k_dp_rx_frags_cleanup(struct ath12k_dp_rx_tid *rx_tid,
bool rel_link_desc)
{
+ struct ath12k_buffer_addr *buf_addr_info;
struct ath12k_base *ab = rx_tid->ab;
lockdep_assert_held(&ab->base_lock);
if (rx_tid->dst_ring_desc) {
- if (rel_link_desc)
- ath12k_dp_rx_link_desc_return(ab, rx_tid->dst_ring_desc,
+ if (rel_link_desc) {
+ buf_addr_info = &rx_tid->dst_ring_desc->buf_addr_info;
+ ath12k_dp_rx_link_desc_return(ab, buf_addr_info,
HAL_WBM_REL_BM_ACT_PUT_IN_IDLE);
+ }
kfree(rx_tid->dst_ring_desc);
rx_tid->dst_ring_desc = NULL;
}
@@ -895,7 +928,7 @@ void ath12k_dp_rx_peer_tid_cleanup(struct ath12k *ar, struct ath12k_peer *peer)
ath12k_dp_rx_frags_cleanup(rx_tid, true);
spin_unlock_bh(&ar->ab->base_lock);
- del_timer_sync(&rx_tid->frag_timer);
+ timer_delete_sync(&rx_tid->frag_timer);
spin_lock_bh(&ar->ab->base_lock);
}
}
@@ -909,8 +942,8 @@ static int ath12k_peer_rx_tid_reo_update(struct ath12k *ar,
struct ath12k_hal_reo_cmd cmd = {0};
int ret;
- cmd.addr_lo = lower_32_bits(rx_tid->paddr);
- cmd.addr_hi = upper_32_bits(rx_tid->paddr);
+ cmd.addr_lo = lower_32_bits(rx_tid->qbuf.paddr_aligned);
+ cmd.addr_hi = upper_32_bits(rx_tid->qbuf.paddr_aligned);
cmd.flag = HAL_REO_CMD_FLG_NEED_STATUS;
cmd.upd0 = HAL_REO_CMD_UPD0_BA_WINDOW_SIZE;
cmd.ba_window_size = ba_win_sz;
@@ -934,18 +967,67 @@ static int ath12k_peer_rx_tid_reo_update(struct ath12k *ar,
return 0;
}
+static int ath12k_dp_rx_assign_reoq(struct ath12k_base *ab,
+ struct ath12k_sta *ahsta,
+ struct ath12k_dp_rx_tid *rx_tid,
+ u16 ssn, enum hal_pn_type pn_type)
+{
+ u32 ba_win_sz = rx_tid->ba_win_sz;
+ struct ath12k_reoq_buf *buf;
+ void *vaddr, *vaddr_aligned;
+ dma_addr_t paddr_aligned;
+ u8 tid = rx_tid->tid;
+ u32 hw_desc_sz;
+ int ret;
+
+ buf = &ahsta->reoq_bufs[tid];
+ if (!buf->vaddr) {
+ /* TODO: Optimize the memory allocation for qos tid based on
+ * the actual BA window size in REO tid update path.
+ */
+ if (tid == HAL_DESC_REO_NON_QOS_TID)
+ hw_desc_sz = ath12k_hal_reo_qdesc_size(ba_win_sz, tid);
+ else
+ hw_desc_sz = ath12k_hal_reo_qdesc_size(DP_BA_WIN_SZ_MAX, tid);
+
+ vaddr = kzalloc(hw_desc_sz + HAL_LINK_DESC_ALIGN - 1, GFP_ATOMIC);
+ if (!vaddr)
+ return -ENOMEM;
+
+ vaddr_aligned = PTR_ALIGN(vaddr, HAL_LINK_DESC_ALIGN);
+
+ ath12k_hal_reo_qdesc_setup(vaddr_aligned, tid, ba_win_sz,
+ ssn, pn_type);
+
+ paddr_aligned = dma_map_single(ab->dev, vaddr_aligned, hw_desc_sz,
+ DMA_BIDIRECTIONAL);
+ ret = dma_mapping_error(ab->dev, paddr_aligned);
+ if (ret) {
+ kfree(vaddr);
+ return ret;
+ }
+
+ buf->vaddr = vaddr;
+ buf->paddr_aligned = paddr_aligned;
+ buf->size = hw_desc_sz;
+ }
+
+ rx_tid->qbuf = *buf;
+ rx_tid->active = true;
+
+ return 0;
+}
+
int ath12k_dp_rx_peer_tid_setup(struct ath12k *ar, const u8 *peer_mac, int vdev_id,
u8 tid, u32 ba_win_sz, u16 ssn,
enum hal_pn_type pn_type)
{
struct ath12k_base *ab = ar->ab;
struct ath12k_dp *dp = &ab->dp;
- struct hal_rx_reo_queue *addr_aligned;
struct ath12k_peer *peer;
+ struct ath12k_sta *ahsta;
struct ath12k_dp_rx_tid *rx_tid;
- u32 hw_desc_sz;
- void *vaddr;
- dma_addr_t paddr;
+ dma_addr_t paddr_aligned;
int ret;
spin_lock_bh(&ab->base_lock);
@@ -957,7 +1039,8 @@ int ath12k_dp_rx_peer_tid_setup(struct ath12k *ar, const u8 *peer_mac, int vdev_
return -ENOENT;
}
- if (!peer->primary_link) {
+ if (ab->hw_params->dp_primary_link_only &&
+ !peer->primary_link) {
spin_unlock_bh(&ab->base_lock);
return 0;
}
@@ -977,9 +1060,9 @@ int ath12k_dp_rx_peer_tid_setup(struct ath12k *ar, const u8 *peer_mac, int vdev_
}
rx_tid = &peer->rx_tid[tid];
+ paddr_aligned = rx_tid->qbuf.paddr_aligned;
/* Update the tid queue if it is already setup */
if (rx_tid->active) {
- paddr = rx_tid->paddr;
ret = ath12k_peer_rx_tid_reo_update(ar, peer, rx_tid,
ba_win_sz, ssn, true);
spin_unlock_bh(&ab->base_lock);
@@ -991,8 +1074,8 @@ int ath12k_dp_rx_peer_tid_setup(struct ath12k *ar, const u8 *peer_mac, int vdev_
if (!ab->hw_params->reoq_lut_support) {
ret = ath12k_wmi_peer_rx_reorder_queue_setup(ar, vdev_id,
peer_mac,
- paddr, tid, 1,
- ba_win_sz);
+ paddr_aligned, tid,
+ 1, ba_win_sz);
if (ret) {
ath12k_warn(ab, "failed to setup peer rx reorder queuefor tid %d: %d\n",
tid, ret);
@@ -1007,61 +1090,34 @@ int ath12k_dp_rx_peer_tid_setup(struct ath12k *ar, const u8 *peer_mac, int vdev_
rx_tid->ba_win_sz = ba_win_sz;
- /* TODO: Optimize the memory allocation for qos tid based on
- * the actual BA window size in REO tid update path.
- */
- if (tid == HAL_DESC_REO_NON_QOS_TID)
- hw_desc_sz = ath12k_hal_reo_qdesc_size(ba_win_sz, tid);
- else
- hw_desc_sz = ath12k_hal_reo_qdesc_size(DP_BA_WIN_SZ_MAX, tid);
-
- vaddr = kzalloc(hw_desc_sz + HAL_LINK_DESC_ALIGN - 1, GFP_ATOMIC);
- if (!vaddr) {
- spin_unlock_bh(&ab->base_lock);
- return -ENOMEM;
- }
-
- addr_aligned = PTR_ALIGN(vaddr, HAL_LINK_DESC_ALIGN);
-
- ath12k_hal_reo_qdesc_setup(addr_aligned, tid, ba_win_sz,
- ssn, pn_type);
-
- paddr = dma_map_single(ab->dev, addr_aligned, hw_desc_sz,
- DMA_BIDIRECTIONAL);
-
- ret = dma_mapping_error(ab->dev, paddr);
+ ahsta = ath12k_sta_to_ahsta(peer->sta);
+ ret = ath12k_dp_rx_assign_reoq(ab, ahsta, rx_tid, ssn, pn_type);
if (ret) {
spin_unlock_bh(&ab->base_lock);
- goto err_mem_free;
+ ath12k_warn(ab, "failed to assign reoq buf for rx tid %u\n", tid);
+ return ret;
}
- rx_tid->vaddr = vaddr;
- rx_tid->paddr = paddr;
- rx_tid->size = hw_desc_sz;
- rx_tid->active = true;
-
if (ab->hw_params->reoq_lut_support) {
/* Update the REO queue LUT at the corresponding peer id
* and tid with qaddr.
*/
if (peer->mlo)
- ath12k_peer_rx_tid_qref_setup(ab, peer->ml_id, tid, paddr);
+ ath12k_peer_rx_tid_qref_setup(ab, peer->ml_id, tid,
+ paddr_aligned);
else
- ath12k_peer_rx_tid_qref_setup(ab, peer->peer_id, tid, paddr);
+ ath12k_peer_rx_tid_qref_setup(ab, peer->peer_id, tid,
+ paddr_aligned);
spin_unlock_bh(&ab->base_lock);
} else {
spin_unlock_bh(&ab->base_lock);
ret = ath12k_wmi_peer_rx_reorder_queue_setup(ar, vdev_id, peer_mac,
- paddr, tid, 1, ba_win_sz);
+ paddr_aligned, tid, 1,
+ ba_win_sz);
}
return ret;
-
-err_mem_free:
- kfree(vaddr);
-
- return ret;
}
int ath12k_dp_rx_ampdu_start(struct ath12k *ar,
@@ -1196,8 +1252,8 @@ int ath12k_dp_rx_peer_pn_replay_config(struct ath12k_link_vif *arvif,
rx_tid = &peer->rx_tid[tid];
if (!rx_tid->active)
continue;
- cmd.addr_lo = lower_32_bits(rx_tid->paddr);
- cmd.addr_hi = upper_32_bits(rx_tid->paddr);
+ cmd.addr_lo = lower_32_bits(rx_tid->qbuf.paddr_aligned);
+ cmd.addr_hi = upper_32_bits(rx_tid->qbuf.paddr_aligned);
ret = ath12k_dp_reo_cmd_send(ab, rx_tid,
HAL_REO_CMD_UPDATE_RX_QUEUE,
&cmd, NULL);
@@ -1784,8 +1840,12 @@ void ath12k_dp_htt_htc_t2h_msg_handler(struct ath12k_base *ab,
HTT_T2H_PEER_MAP_INFO1_MAC_ADDR_H16);
ath12k_dp_get_mac_addr(le32_to_cpu(resp->peer_map_ev.mac_addr_l32),
peer_mac_h16, mac_addr);
+ ast_hash = le32_get_bits(resp->peer_map_ev.info2,
+ HTT_T2H_PEER_MAP3_INFO2_AST_HASH_VAL);
+ hw_peer_id = le32_get_bits(resp->peer_map_ev.info2,
+ HTT_T2H_PEER_MAP3_INFO2_HW_PEER_ID);
ath12k_peer_map_event(ab, vdev_id, peer_id, mac_addr, ast_hash,
- peer_id);
+ hw_peer_id);
break;
case HTT_T2H_MSG_TYPE_PEER_UNMAP:
case HTT_T2H_MSG_TYPE_PEER_UNMAP2:
@@ -1823,6 +1883,7 @@ static int ath12k_dp_rx_msdu_coalesce(struct ath12k *ar,
struct hal_rx_desc *ldesc;
int space_extra, rem_len, buf_len;
u32 hal_rx_desc_sz = ar->ab->hal.hal_desc_sz;
+ bool is_continuation;
/* As the msdu is spread across multiple rx buffers,
* find the offset to the start of msdu for computing
@@ -1871,7 +1932,8 @@ static int ath12k_dp_rx_msdu_coalesce(struct ath12k *ar,
rem_len = msdu_len - buf_first_len;
while ((skb = __skb_dequeue(msdu_list)) != NULL && rem_len > 0) {
rxcb = ATH12K_SKB_RXCB(skb);
- if (rxcb->is_continuation)
+ is_continuation = rxcb->is_continuation;
+ if (is_continuation)
buf_len = DP_RX_BUFFER_SIZE - hal_rx_desc_sz;
else
buf_len = rem_len;
@@ -1889,7 +1951,7 @@ static int ath12k_dp_rx_msdu_coalesce(struct ath12k *ar,
dev_kfree_skb_any(skb);
rem_len -= buf_len;
- if (!rxcb->is_continuation)
+ if (!is_continuation)
break;
}
@@ -1914,21 +1976,14 @@ static struct sk_buff *ath12k_dp_rx_get_msdu_last_buf(struct sk_buff_head *msdu_
return NULL;
}
-static void ath12k_dp_rx_h_csum_offload(struct ath12k *ar, struct sk_buff *msdu)
+static void ath12k_dp_rx_h_csum_offload(struct sk_buff *msdu,
+ struct ath12k_dp_rx_info *rx_info)
{
- struct ath12k_skb_rxcb *rxcb = ATH12K_SKB_RXCB(msdu);
- struct ath12k_base *ab = ar->ab;
- bool ip_csum_fail, l4_csum_fail;
-
- ip_csum_fail = ath12k_dp_rx_h_ip_cksum_fail(ab, rxcb->rx_desc);
- l4_csum_fail = ath12k_dp_rx_h_l4_cksum_fail(ab, rxcb->rx_desc);
-
- msdu->ip_summed = (ip_csum_fail || l4_csum_fail) ?
- CHECKSUM_NONE : CHECKSUM_UNNECESSARY;
+ msdu->ip_summed = (rx_info->ip_csum_fail || rx_info->l4_csum_fail) ?
+ CHECKSUM_NONE : CHECKSUM_UNNECESSARY;
}
-static int ath12k_dp_rx_crypto_mic_len(struct ath12k *ar,
- enum hal_encrypt_type enctype)
+int ath12k_dp_rx_crypto_mic_len(struct ath12k *ar, enum hal_encrypt_type enctype)
{
switch (enctype) {
case HAL_ENCRYPT_TYPE_OPEN:
@@ -2122,10 +2177,13 @@ static void ath12k_get_dot11_hdr_from_rx_desc(struct ath12k *ar,
struct hal_rx_desc *rx_desc = rxcb->rx_desc;
struct ath12k_base *ab = ar->ab;
size_t hdr_len, crypto_len;
- struct ieee80211_hdr *hdr;
- u16 qos_ctl;
- __le16 fc;
- u8 *crypto_hdr;
+ struct ieee80211_hdr hdr;
+ __le16 qos_ctl;
+ u8 *crypto_hdr, mesh_ctrl;
+
+ ath12k_dp_rx_desc_get_dot11_hdr(ab, rx_desc, &hdr);
+ hdr_len = ieee80211_hdrlen(hdr.frame_control);
+ mesh_ctrl = ath12k_dp_rx_h_mesh_ctl_present(ab, rx_desc);
if (!(status->flag & RX_FLAG_IV_STRIPPED)) {
crypto_len = ath12k_dp_rx_crypto_param_len(ar, enctype);
@@ -2133,27 +2191,21 @@ static void ath12k_get_dot11_hdr_from_rx_desc(struct ath12k *ar,
ath12k_dp_rx_desc_get_crypto_header(ab, rx_desc, crypto_hdr, enctype);
}
- fc = cpu_to_le16(ath12k_dp_rxdesc_get_mpdu_frame_ctrl(ab, rx_desc));
- hdr_len = ieee80211_hdrlen(fc);
skb_push(msdu, hdr_len);
- hdr = (struct ieee80211_hdr *)msdu->data;
- hdr->frame_control = fc;
-
- /* Get wifi header from rx_desc */
- ath12k_dp_rx_desc_get_dot11_hdr(ab, rx_desc, hdr);
+ memcpy(msdu->data, &hdr, min(hdr_len, sizeof(hdr)));
if (rxcb->is_mcbc)
status->flag &= ~RX_FLAG_PN_VALIDATED;
/* Add QOS header */
- if (ieee80211_is_data_qos(hdr->frame_control)) {
- qos_ctl = rxcb->tid;
- if (ath12k_dp_rx_h_mesh_ctl_present(ab, rx_desc))
- qos_ctl |= IEEE80211_QOS_CTL_MESH_CONTROL_PRESENT;
+ if (ieee80211_is_data_qos(hdr.frame_control)) {
+ struct ieee80211_hdr *qos_ptr = (struct ieee80211_hdr *)msdu->data;
- /* TODO: Add other QoS ctl fields when required */
- memcpy(msdu->data + (hdr_len - IEEE80211_QOS_CTL_LEN),
- &qos_ctl, IEEE80211_QOS_CTL_LEN);
+ qos_ctl = cpu_to_le16(rxcb->tid & IEEE80211_QOS_CTL_TID_MASK);
+ if (mesh_ctrl)
+ qos_ctl |= cpu_to_le16(IEEE80211_QOS_CTL_MESH_CONTROL_PRESENT);
+
+ memcpy(ieee80211_get_qos_ctl(qos_ptr), &qos_ctl, IEEE80211_QOS_CTL_LEN);
}
}
@@ -2229,10 +2281,10 @@ static void ath12k_dp_rx_h_undecap(struct ath12k *ar, struct sk_buff *msdu,
}
struct ath12k_peer *
-ath12k_dp_rx_h_find_peer(struct ath12k_base *ab, struct sk_buff *msdu)
+ath12k_dp_rx_h_find_peer(struct ath12k_base *ab, struct sk_buff *msdu,
+ struct ath12k_dp_rx_info *rx_info)
{
struct ath12k_skb_rxcb *rxcb = ATH12K_SKB_RXCB(msdu);
- struct hal_rx_desc *rx_desc = rxcb->rx_desc;
struct ath12k_peer *peer = NULL;
lockdep_assert_held(&ab->base_lock);
@@ -2243,40 +2295,41 @@ ath12k_dp_rx_h_find_peer(struct ath12k_base *ab, struct sk_buff *msdu)
if (peer)
return peer;
- if (!rx_desc || !(ath12k_dp_rxdesc_mac_addr2_valid(ab, rx_desc)))
- return NULL;
+ if (rx_info->addr2_present)
+ peer = ath12k_peer_find_by_addr(ab, rx_info->addr2);
- peer = ath12k_peer_find_by_addr(ab,
- ath12k_dp_rxdesc_get_mpdu_start_addr2(ab,
- rx_desc));
return peer;
}
static void ath12k_dp_rx_h_mpdu(struct ath12k *ar,
struct sk_buff *msdu,
struct hal_rx_desc *rx_desc,
- struct ieee80211_rx_status *rx_status)
+ struct ath12k_dp_rx_info *rx_info)
{
- bool fill_crypto_hdr;
struct ath12k_base *ab = ar->ab;
struct ath12k_skb_rxcb *rxcb;
enum hal_encrypt_type enctype;
bool is_decrypted = false;
struct ieee80211_hdr *hdr;
struct ath12k_peer *peer;
+ struct ieee80211_rx_status *rx_status = rx_info->rx_status;
u32 err_bitmap;
/* PN for multicast packets will be checked in mac80211 */
rxcb = ATH12K_SKB_RXCB(msdu);
- fill_crypto_hdr = ath12k_dp_rx_h_is_da_mcbc(ar->ab, rx_desc);
- rxcb->is_mcbc = fill_crypto_hdr;
+ rxcb->is_mcbc = rx_info->is_mcbc;
if (rxcb->is_mcbc)
- rxcb->peer_id = ath12k_dp_rx_h_peer_id(ar->ab, rx_desc);
+ rxcb->peer_id = rx_info->peer_id;
spin_lock_bh(&ar->ab->base_lock);
- peer = ath12k_dp_rx_h_find_peer(ar->ab, msdu);
+ peer = ath12k_dp_rx_h_find_peer(ar->ab, msdu, rx_info);
if (peer) {
+ /* resetting mcbc bit because mcbc packets are unicast
+ * packets only for AP as STA sends unicast packets.
+ */
+ rxcb->is_mcbc = rxcb->is_mcbc && !peer->ucast_ra_only;
+
if (rxcb->is_mcbc)
enctype = peer->sec_type_grp;
else
@@ -2305,7 +2358,7 @@ static void ath12k_dp_rx_h_mpdu(struct ath12k *ar,
if (is_decrypted) {
rx_status->flag |= RX_FLAG_DECRYPTED | RX_FLAG_MMIC_STRIPPED;
- if (fill_crypto_hdr)
+ if (rx_info->is_mcbc)
rx_status->flag |= RX_FLAG_MIC_STRIPPED |
RX_FLAG_ICV_STRIPPED;
else
@@ -2313,37 +2366,28 @@ static void ath12k_dp_rx_h_mpdu(struct ath12k *ar,
RX_FLAG_PN_VALIDATED;
}
- ath12k_dp_rx_h_csum_offload(ar, msdu);
+ ath12k_dp_rx_h_csum_offload(msdu, rx_info);
ath12k_dp_rx_h_undecap(ar, msdu, rx_desc,
enctype, rx_status, is_decrypted);
- if (!is_decrypted || fill_crypto_hdr)
+ if (!is_decrypted || rx_info->is_mcbc)
return;
- if (ath12k_dp_rx_h_decap_type(ar->ab, rx_desc) !=
- DP_RX_DECAP_TYPE_ETHERNET2_DIX) {
+ if (rx_info->decap_type != DP_RX_DECAP_TYPE_ETHERNET2_DIX) {
hdr = (void *)msdu->data;
hdr->frame_control &= ~__cpu_to_le16(IEEE80211_FCTL_PROTECTED);
}
}
-static void ath12k_dp_rx_h_rate(struct ath12k *ar, struct hal_rx_desc *rx_desc,
- struct ieee80211_rx_status *rx_status)
+static void ath12k_dp_rx_h_rate(struct ath12k *ar, struct ath12k_dp_rx_info *rx_info)
{
- struct ath12k_base *ab = ar->ab;
struct ieee80211_supported_band *sband;
- enum rx_msdu_start_pkt_type pkt_type;
- u8 bw;
- u8 rate_mcs, nss;
- u8 sgi;
+ struct ieee80211_rx_status *rx_status = rx_info->rx_status;
+ enum rx_msdu_start_pkt_type pkt_type = rx_info->pkt_type;
+ u8 bw = rx_info->bw, sgi = rx_info->sgi;
+ u8 rate_mcs = rx_info->rate_mcs, nss = rx_info->nss;
bool is_cck;
- pkt_type = ath12k_dp_rx_h_pkt_type(ab, rx_desc);
- bw = ath12k_dp_rx_h_rx_bw(ab, rx_desc);
- rate_mcs = ath12k_dp_rx_h_rate_mcs(ab, rx_desc);
- nss = ath12k_dp_rx_h_nss(ab, rx_desc);
- sgi = ath12k_dp_rx_h_sgi(ab, rx_desc);
-
switch (pkt_type) {
case RX_MSDU_START_PKT_TYPE_11A:
case RX_MSDU_START_PKT_TYPE_11B:
@@ -2392,13 +2436,55 @@ static void ath12k_dp_rx_h_rate(struct ath12k *ar, struct hal_rx_desc *rx_desc,
rx_status->he_gi = ath12k_he_gi_to_nl80211_he_gi(sgi);
rx_status->bw = ath12k_mac_bw_to_mac80211_bw(bw);
break;
+ case RX_MSDU_START_PKT_TYPE_11BE:
+ rx_status->rate_idx = rate_mcs;
+
+ if (rate_mcs > ATH12K_EHT_MCS_MAX) {
+ ath12k_warn(ar->ab,
+ "Received with invalid mcs in EHT mode %d\n",
+ rate_mcs);
+ break;
+ }
+
+ rx_status->encoding = RX_ENC_EHT;
+ rx_status->nss = nss;
+ rx_status->eht.gi = ath12k_mac_eht_gi_to_nl80211_eht_gi(sgi);
+ rx_status->bw = ath12k_mac_bw_to_mac80211_bw(bw);
+ break;
+ default:
+ break;
}
}
-void ath12k_dp_rx_h_ppdu(struct ath12k *ar, struct hal_rx_desc *rx_desc,
- struct ieee80211_rx_status *rx_status)
+void ath12k_dp_rx_h_fetch_info(struct ath12k_base *ab, struct hal_rx_desc *rx_desc,
+ struct ath12k_dp_rx_info *rx_info)
{
- struct ath12k_base *ab = ar->ab;
+ rx_info->ip_csum_fail = ath12k_dp_rx_h_ip_cksum_fail(ab, rx_desc);
+ rx_info->l4_csum_fail = ath12k_dp_rx_h_l4_cksum_fail(ab, rx_desc);
+ rx_info->is_mcbc = ath12k_dp_rx_h_is_da_mcbc(ab, rx_desc);
+ rx_info->decap_type = ath12k_dp_rx_h_decap_type(ab, rx_desc);
+ rx_info->pkt_type = ath12k_dp_rx_h_pkt_type(ab, rx_desc);
+ rx_info->sgi = ath12k_dp_rx_h_sgi(ab, rx_desc);
+ rx_info->rate_mcs = ath12k_dp_rx_h_rate_mcs(ab, rx_desc);
+ rx_info->bw = ath12k_dp_rx_h_rx_bw(ab, rx_desc);
+ rx_info->nss = ath12k_dp_rx_h_nss(ab, rx_desc);
+ rx_info->tid = ath12k_dp_rx_h_tid(ab, rx_desc);
+ rx_info->peer_id = ath12k_dp_rx_h_peer_id(ab, rx_desc);
+ rx_info->phy_meta_data = ath12k_dp_rx_h_freq(ab, rx_desc);
+
+ if (ath12k_dp_rxdesc_mac_addr2_valid(ab, rx_desc)) {
+ ether_addr_copy(rx_info->addr2,
+ ath12k_dp_rxdesc_get_mpdu_start_addr2(ab, rx_desc));
+ rx_info->addr2_present = true;
+ }
+
+ ath12k_dbg_dump(ab, ATH12K_DBG_DATA, NULL, "rx_desc: ",
+ rx_desc, sizeof(*rx_desc));
+}
+
+void ath12k_dp_rx_h_ppdu(struct ath12k *ar, struct ath12k_dp_rx_info *rx_info)
+{
+ struct ieee80211_rx_status *rx_status = rx_info->rx_status;
u8 channel_num;
u32 center_freq, meta_data;
struct ieee80211_channel *channel;
@@ -2412,12 +2498,12 @@ void ath12k_dp_rx_h_ppdu(struct ath12k *ar, struct hal_rx_desc *rx_desc,
rx_status->flag |= RX_FLAG_NO_SIGNAL_VAL;
- meta_data = ath12k_dp_rx_h_freq(ab, rx_desc);
+ meta_data = rx_info->phy_meta_data;
channel_num = meta_data;
center_freq = meta_data >> 16;
- if (center_freq >= ATH12K_MIN_6G_FREQ &&
- center_freq <= ATH12K_MAX_6G_FREQ) {
+ if (center_freq >= ATH12K_MIN_6GHZ_FREQ &&
+ center_freq <= ATH12K_MAX_6GHZ_FREQ) {
rx_status->band = NL80211_BAND_6GHZ;
rx_status->freq = center_freq;
} else if (channel_num >= 1 && channel_num <= 14) {
@@ -2433,20 +2519,18 @@ void ath12k_dp_rx_h_ppdu(struct ath12k *ar, struct hal_rx_desc *rx_desc,
ieee80211_frequency_to_channel(channel->center_freq);
}
spin_unlock_bh(&ar->data_lock);
- ath12k_dbg_dump(ar->ab, ATH12K_DBG_DATA, NULL, "rx_desc: ",
- rx_desc, sizeof(*rx_desc));
}
if (rx_status->band != NL80211_BAND_6GHZ)
rx_status->freq = ieee80211_channel_to_frequency(channel_num,
rx_status->band);
- ath12k_dp_rx_h_rate(ar, rx_desc, rx_status);
+ ath12k_dp_rx_h_rate(ar, rx_info);
}
static void ath12k_dp_rx_deliver_msdu(struct ath12k *ar, struct napi_struct *napi,
struct sk_buff *msdu,
- struct ieee80211_rx_status *status)
+ struct ath12k_dp_rx_info *rx_info)
{
struct ath12k_base *ab = ar->ab;
static const struct ieee80211_radiotap_he known = {
@@ -2459,6 +2543,7 @@ static void ath12k_dp_rx_deliver_msdu(struct ath12k *ar, struct napi_struct *nap
struct ieee80211_sta *pubsta;
struct ath12k_peer *peer;
struct ath12k_skb_rxcb *rxcb = ATH12K_SKB_RXCB(msdu);
+ struct ieee80211_rx_status *status = rx_info->rx_status;
u8 decap = DP_RX_DECAP_TYPE_RAW;
bool is_mcbc = rxcb->is_mcbc;
bool is_eapol = rxcb->is_eapol;
@@ -2471,10 +2556,10 @@ static void ath12k_dp_rx_deliver_msdu(struct ath12k *ar, struct napi_struct *nap
}
if (!(status->flag & RX_FLAG_ONLY_MONITOR))
- decap = ath12k_dp_rx_h_decap_type(ab, rxcb->rx_desc);
+ decap = rx_info->decap_type;
spin_lock_bh(&ab->base_lock);
- peer = ath12k_dp_rx_h_find_peer(ab, msdu);
+ peer = ath12k_dp_rx_h_find_peer(ab, msdu, rx_info);
pubsta = peer ? peer->sta : NULL;
@@ -2486,7 +2571,7 @@ static void ath12k_dp_rx_deliver_msdu(struct ath12k *ar, struct napi_struct *nap
spin_unlock_bh(&ab->base_lock);
ath12k_dbg(ab, ATH12K_DBG_DATA,
- "rx skb %p len %u peer %pM %d %s sn %u %s%s%s%s%s%s%s%s%s rate_idx %u vht_nss %u freq %u band %u flag 0x%x fcs-err %i mic-err %i amsdu-more %i\n",
+ "rx skb %p len %u peer %pM %d %s sn %u %s%s%s%s%s%s%s%s%s%s rate_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,
peer ? peer->addr : NULL,
@@ -2497,6 +2582,7 @@ static void ath12k_dp_rx_deliver_msdu(struct ath12k *ar, struct napi_struct *nap
(status->encoding == RX_ENC_HT) ? "ht" : "",
(status->encoding == RX_ENC_VHT) ? "vht" : "",
(status->encoding == RX_ENC_HE) ? "he" : "",
+ (status->encoding == RX_ENC_EHT) ? "eht" : "",
(status->bw == RATE_INFO_BW_40) ? "40" : "",
(status->bw == RATE_INFO_BW_80) ? "80" : "",
(status->bw == RATE_INFO_BW_160) ? "160" : "",
@@ -2530,10 +2616,33 @@ static void ath12k_dp_rx_deliver_msdu(struct ath12k *ar, struct napi_struct *nap
ieee80211_rx_napi(ath12k_ar_to_hw(ar), pubsta, msdu, napi);
}
+static bool ath12k_dp_rx_check_nwifi_hdr_len_valid(struct ath12k_base *ab,
+ struct hal_rx_desc *rx_desc,
+ struct sk_buff *msdu)
+{
+ struct ieee80211_hdr *hdr;
+ u8 decap_type;
+ u32 hdr_len;
+
+ decap_type = ath12k_dp_rx_h_decap_type(ab, rx_desc);
+ if (decap_type != DP_RX_DECAP_TYPE_NATIVE_WIFI)
+ return true;
+
+ hdr = (struct ieee80211_hdr *)msdu->data;
+ hdr_len = ieee80211_hdrlen(hdr->frame_control);
+
+ if ((likely(hdr_len <= DP_MAX_NWIFI_HDR_LEN)))
+ return true;
+
+ ab->device_stats.invalid_rbm++;
+ WARN_ON_ONCE(1);
+ return false;
+}
+
static int ath12k_dp_rx_process_msdu(struct ath12k *ar,
struct sk_buff *msdu,
struct sk_buff_head *msdu_list,
- struct ieee80211_rx_status *rx_status)
+ struct ath12k_dp_rx_info *rx_info)
{
struct ath12k_base *ab = ar->ab;
struct hal_rx_desc *rx_desc, *lrx_desc;
@@ -2588,10 +2697,16 @@ static int ath12k_dp_rx_process_msdu(struct ath12k *ar,
}
}
- ath12k_dp_rx_h_ppdu(ar, rx_desc, rx_status);
- ath12k_dp_rx_h_mpdu(ar, msdu, rx_desc, rx_status);
+ if (unlikely(!ath12k_dp_rx_check_nwifi_hdr_len_valid(ab, rx_desc, msdu))) {
+ ret = -EINVAL;
+ goto free_out;
+ }
+
+ ath12k_dp_rx_h_fetch_info(ab, rx_desc, rx_info);
+ ath12k_dp_rx_h_ppdu(ar, rx_info);
+ ath12k_dp_rx_h_mpdu(ar, msdu, rx_desc, rx_info);
- rx_status->flag |= RX_FLAG_SKIP_MONITOR | RX_FLAG_DUP_VALIDATED;
+ rx_info->rx_status->flag |= RX_FLAG_SKIP_MONITOR | RX_FLAG_DUP_VALIDATED;
return 0;
@@ -2611,12 +2726,16 @@ static void ath12k_dp_rx_process_received_packets(struct ath12k_base *ab,
struct ath12k *ar;
struct ath12k_hw_link *hw_links = ag->hw_links;
struct ath12k_base *partner_ab;
+ struct ath12k_dp_rx_info rx_info;
u8 hw_link_id, pdev_id;
int ret;
if (skb_queue_empty(msdu_list))
return;
+ rx_info.addr2_present = false;
+ rx_info.rx_status = &rx_status;
+
rcu_read_lock();
while ((msdu = __skb_dequeue(msdu_list))) {
@@ -2637,7 +2756,7 @@ static void ath12k_dp_rx_process_received_packets(struct ath12k_base *ab,
continue;
}
- ret = ath12k_dp_rx_process_msdu(ar, msdu, msdu_list, &rx_status);
+ ret = ath12k_dp_rx_process_msdu(ar, msdu, msdu_list, &rx_info);
if (ret) {
ath12k_dbg(ab, ATH12K_DBG_DATA,
"Unable to process msdu %d", ret);
@@ -2645,7 +2764,7 @@ static void ath12k_dp_rx_process_received_packets(struct ath12k_base *ab,
continue;
}
- ath12k_dp_rx_deliver_msdu(ar, napi, msdu, &rx_status);
+ ath12k_dp_rx_deliver_msdu(ar, napi, msdu, &rx_info);
}
rcu_read_unlock();
@@ -2678,9 +2797,9 @@ int ath12k_dp_rx_process(struct ath12k_base *ab, int ring_id,
struct napi_struct *napi, int budget)
{
struct ath12k_hw_group *ag = ab->ag;
- struct list_head rx_desc_used_list[ATH12K_MAX_SOCS];
+ struct list_head rx_desc_used_list[ATH12K_MAX_DEVICES];
struct ath12k_hw_link *hw_links = ag->hw_links;
- int num_buffs_reaped[ATH12K_MAX_SOCS] = {};
+ int num_buffs_reaped[ATH12K_MAX_DEVICES] = {};
struct ath12k_rx_desc_info *desc_info;
struct ath12k_dp *dp = &ab->dp;
struct dp_rxdma_ring *rx_ring = &dp->rx_refill_buf_ring;
@@ -2697,7 +2816,7 @@ int ath12k_dp_rx_process(struct ath12k_base *ab, int ring_id,
__skb_queue_head_init(&msdu_list);
- for (device_id = 0; device_id < ATH12K_MAX_SOCS; device_id++)
+ for (device_id = 0; device_id < ATH12K_MAX_DEVICES; device_id++)
INIT_LIST_HEAD(&rx_desc_used_list[device_id]);
srng = &ab->hal.srng_list[dp->reo_dst_ring[ring_id].ring_id];
@@ -2758,13 +2877,14 @@ try_again:
DMA_FROM_DEVICE);
num_buffs_reaped[device_id]++;
+ ab->device_stats.reo_rx[ring_id][ab->device_id]++;
push_reason = le32_get_bits(desc->info0,
HAL_REO_DEST_RING_INFO0_PUSH_REASON);
if (push_reason !=
HAL_REO_DEST_RING_PUSH_REASON_ROUTING_INSTRUCTION) {
dev_kfree_skb_any(msdu);
- ab->soc_stats.hal_reo_error[ring_id]++;
+ ab->device_stats.hal_reo_error[ring_id]++;
continue;
}
@@ -2814,7 +2934,7 @@ try_again:
if (!total_msdu_reaped)
goto exit;
- for (device_id = 0; device_id < ATH12K_MAX_SOCS; device_id++) {
+ for (device_id = 0; device_id < ATH12K_MAX_DEVICES; device_id++) {
if (!num_buffs_reaped[device_id])
continue;
@@ -2938,6 +3058,7 @@ static int ath12k_dp_rx_h_verify_tkip_mic(struct ath12k *ar, struct ath12k_peer
struct ieee80211_rx_status *rxs = IEEE80211_SKB_RXCB(msdu);
struct ieee80211_key_conf *key_conf;
struct ieee80211_hdr *hdr;
+ struct ath12k_dp_rx_info rx_info;
u8 mic[IEEE80211_CCMP_MIC_LEN];
int head_len, tail_len, ret;
size_t data_len;
@@ -2948,6 +3069,9 @@ static int ath12k_dp_rx_h_verify_tkip_mic(struct ath12k *ar, struct ath12k_peer
if (ath12k_dp_rx_h_enctype(ab, rx_desc) != HAL_ENCRYPT_TYPE_TKIP_MIC)
return 0;
+ rx_info.addr2_present = false;
+ rx_info.rx_status = rxs;
+
hdr = (struct ieee80211_hdr *)(msdu->data + hal_rx_desc_sz);
hdr_len = ieee80211_hdrlen(hdr->frame_control);
head_len = hdr_len + hal_rx_desc_sz + IEEE80211_TKIP_IV_LEN;
@@ -2974,11 +3098,16 @@ mic_fail:
(ATH12K_SKB_RXCB(msdu))->is_first_msdu = true;
(ATH12K_SKB_RXCB(msdu))->is_last_msdu = true;
+ ath12k_dp_rx_h_fetch_info(ab, rx_desc, &rx_info);
+
rxs->flag |= RX_FLAG_MMIC_ERROR | RX_FLAG_MMIC_STRIPPED |
RX_FLAG_IV_STRIPPED | RX_FLAG_DECRYPTED;
skb_pull(msdu, hal_rx_desc_sz);
- ath12k_dp_rx_h_ppdu(ar, rx_desc, rxs);
+ if (unlikely(!ath12k_dp_rx_check_nwifi_hdr_len_valid(ab, rx_desc, msdu)))
+ return -EINVAL;
+
+ ath12k_dp_rx_h_ppdu(ar, &rx_info);
ath12k_dp_rx_h_undecap(ar, msdu, rx_desc,
HAL_ENCRYPT_TYPE_TKIP_MIC, rxs, true);
ieee80211_rx(ath12k_ar_to_hw(ar), msdu);
@@ -3193,8 +3322,15 @@ static int ath12k_dp_rx_h_defrag_reo_reinject(struct ath12k *ar,
reo_ent_ring->rx_mpdu_info.peer_meta_data =
reo_dest_ring->rx_mpdu_info.peer_meta_data;
- reo_ent_ring->queue_addr_lo = cpu_to_le32(lower_32_bits(rx_tid->paddr));
- queue_addr_hi = upper_32_bits(rx_tid->paddr);
+ if (ab->hw_params->reoq_lut_support) {
+ reo_ent_ring->queue_addr_lo = reo_dest_ring->rx_mpdu_info.peer_meta_data;
+ queue_addr_hi = 0;
+ } else {
+ reo_ent_ring->queue_addr_lo =
+ cpu_to_le32(lower_32_bits(rx_tid->qbuf.paddr_aligned));
+ queue_addr_hi = upper_32_bits(rx_tid->qbuf.paddr_aligned);
+ }
+
reo_ent_ring->info0 = le32_encode_bits(queue_addr_hi,
HAL_REO_ENTR_RING_INFO0_QUEUE_ADDR_HI) |
le32_encode_bits(dst_ind,
@@ -3390,7 +3526,7 @@ static int ath12k_dp_rx_frag_h_mpdu(struct ath12k *ar,
goto out_unlock;
}
} else {
- ath12k_dp_rx_link_desc_return(ab, ring_desc,
+ ath12k_dp_rx_link_desc_return(ab, &ring_desc->buf_addr_info,
HAL_WBM_REL_BM_ACT_PUT_IN_IDLE);
}
@@ -3402,7 +3538,7 @@ static int ath12k_dp_rx_frag_h_mpdu(struct ath12k *ar,
}
spin_unlock_bh(&ab->base_lock);
- del_timer_sync(&rx_tid->frag_timer);
+ timer_delete_sync(&rx_tid->frag_timer);
spin_lock_bh(&ab->base_lock);
peer = ath12k_peer_find_by_id(ab, peer_id);
@@ -3503,7 +3639,7 @@ ath12k_dp_process_rx_err_buf(struct ath12k *ar, struct hal_reo_dest_ring *desc,
if (ath12k_dp_rx_frag_h_mpdu(ar, msdu, desc)) {
dev_kfree_skb_any(msdu);
- ath12k_dp_rx_link_desc_return(ar->ab, desc,
+ ath12k_dp_rx_link_desc_return(ar->ab, &desc->buf_addr_info,
HAL_WBM_REL_BM_ACT_PUT_IN_IDLE);
}
exit:
@@ -3515,9 +3651,9 @@ int ath12k_dp_rx_process_err(struct ath12k_base *ab, struct napi_struct *napi,
int budget)
{
struct ath12k_hw_group *ag = ab->ag;
- struct list_head rx_desc_used_list[ATH12K_MAX_SOCS];
+ struct list_head rx_desc_used_list[ATH12K_MAX_DEVICES];
u32 msdu_cookies[HAL_NUM_RX_MSDUS_PER_LINK_DESC];
- int num_buffs_reaped[ATH12K_MAX_SOCS] = {};
+ int num_buffs_reaped[ATH12K_MAX_DEVICES] = {};
struct dp_link_desc_bank *link_desc_banks;
enum hal_rx_buf_return_buf_manager rbm;
struct hal_rx_msdu_link *link_desc_va;
@@ -3539,7 +3675,7 @@ int ath12k_dp_rx_process_err(struct ath12k_base *ab, struct napi_struct *napi,
tot_n_bufs_reaped = 0;
quota = budget;
- for (device_id = 0; device_id < ATH12K_MAX_SOCS; device_id++)
+ for (device_id = 0; device_id < ATH12K_MAX_DEVICES; device_id++)
INIT_LIST_HEAD(&rx_desc_used_list[device_id]);
reo_except = &ab->dp.reo_except_ring;
@@ -3553,7 +3689,7 @@ int ath12k_dp_rx_process_err(struct ath12k_base *ab, struct napi_struct *napi,
while (budget &&
(reo_desc = ath12k_hal_srng_dst_get_next_entry(ab, srng))) {
drop = false;
- ab->soc_stats.err_ring_pkts++;
+ ab->device_stats.err_ring_pkts++;
ret = ath12k_hal_desc_reo_parse_err(ab, reo_desc, &paddr,
&desc_bank);
@@ -3580,9 +3716,10 @@ int ath12k_dp_rx_process_err(struct ath12k_base *ab, struct napi_struct *napi,
if (rbm != partner_ab->dp.idle_link_rbm &&
rbm != HAL_RX_BUF_RBM_SW3_BM &&
rbm != partner_ab->hw_params->hal_params->rx_buf_rbm) {
- ab->soc_stats.invalid_rbm++;
+ ab->device_stats.invalid_rbm++;
ath12k_warn(ab, "invalid return buffer manager %d\n", rbm);
- ath12k_dp_rx_link_desc_return(partner_ab, reo_desc,
+ ath12k_dp_rx_link_desc_return(partner_ab,
+ &reo_desc->buf_addr_info,
HAL_WBM_REL_BM_ACT_REL_MSDU);
continue;
}
@@ -3600,7 +3737,8 @@ int ath12k_dp_rx_process_err(struct ath12k_base *ab, struct napi_struct *napi,
drop = true;
/* Return the link desc back to wbm idle list */
- ath12k_dp_rx_link_desc_return(partner_ab, reo_desc,
+ ath12k_dp_rx_link_desc_return(partner_ab,
+ &reo_desc->buf_addr_info,
HAL_WBM_REL_BM_ACT_PUT_IN_IDLE);
}
@@ -3627,7 +3765,7 @@ exit:
spin_unlock_bh(&srng->lock);
- for (device_id = 0; device_id < ATH12K_MAX_SOCS; device_id++) {
+ for (device_id = 0; device_id < ATH12K_MAX_DEVICES; device_id++) {
if (!num_buffs_reaped[device_id])
continue;
@@ -3667,7 +3805,7 @@ static void ath12k_dp_rx_null_q_desc_sg_drop(struct ath12k *ar,
}
static int ath12k_dp_rx_h_null_q_desc(struct ath12k *ar, struct sk_buff *msdu,
- struct ieee80211_rx_status *status,
+ struct ath12k_dp_rx_info *rx_info,
struct sk_buff_head *msdu_list)
{
struct ath12k_base *ab = ar->ab;
@@ -3720,11 +3858,14 @@ static int ath12k_dp_rx_h_null_q_desc(struct ath12k *ar, struct sk_buff *msdu,
skb_put(msdu, hal_rx_desc_sz + l3pad_bytes + msdu_len);
skb_pull(msdu, hal_rx_desc_sz + l3pad_bytes);
}
- ath12k_dp_rx_h_ppdu(ar, desc, status);
+ if (unlikely(!ath12k_dp_rx_check_nwifi_hdr_len_valid(ab, desc, msdu)))
+ return -EINVAL;
- ath12k_dp_rx_h_mpdu(ar, msdu, desc, status);
+ ath12k_dp_rx_h_fetch_info(ab, desc, rx_info);
+ ath12k_dp_rx_h_ppdu(ar, rx_info);
+ ath12k_dp_rx_h_mpdu(ar, msdu, desc, rx_info);
- rxcb->tid = ath12k_dp_rx_h_tid(ab, desc);
+ rxcb->tid = rx_info->tid;
/* Please note that caller will having the access to msdu and completing
* rx with mac80211. Need not worry about cleaning up amsdu_list.
@@ -3734,17 +3875,17 @@ static int ath12k_dp_rx_h_null_q_desc(struct ath12k *ar, struct sk_buff *msdu,
}
static bool ath12k_dp_rx_h_reo_err(struct ath12k *ar, struct sk_buff *msdu,
- struct ieee80211_rx_status *status,
+ struct ath12k_dp_rx_info *rx_info,
struct sk_buff_head *msdu_list)
{
struct ath12k_skb_rxcb *rxcb = ATH12K_SKB_RXCB(msdu);
bool drop = false;
- ar->ab->soc_stats.reo_error[rxcb->err_code]++;
+ ar->ab->device_stats.reo_error[rxcb->err_code]++;
switch (rxcb->err_code) {
case HAL_REO_DEST_RING_ERROR_CODE_DESC_ADDR_ZERO:
- if (ath12k_dp_rx_h_null_q_desc(ar, msdu, status, msdu_list))
+ if (ath12k_dp_rx_h_null_q_desc(ar, msdu, rx_info, msdu_list))
drop = true;
break;
case HAL_REO_DEST_RING_ERROR_CODE_PN_CHECK_FAILED:
@@ -3764,8 +3905,8 @@ static bool ath12k_dp_rx_h_reo_err(struct ath12k *ar, struct sk_buff *msdu,
return drop;
}
-static void ath12k_dp_rx_h_tkip_mic_err(struct ath12k *ar, struct sk_buff *msdu,
- struct ieee80211_rx_status *status)
+static bool ath12k_dp_rx_h_tkip_mic_err(struct ath12k *ar, struct sk_buff *msdu,
+ struct ath12k_dp_rx_info *rx_info)
{
struct ath12k_base *ab = ar->ab;
u16 msdu_len;
@@ -3779,20 +3920,33 @@ static void ath12k_dp_rx_h_tkip_mic_err(struct ath12k *ar, struct sk_buff *msdu,
l3pad_bytes = ath12k_dp_rx_h_l3pad(ab, desc);
msdu_len = ath12k_dp_rx_h_msdu_len(ab, desc);
+
+ if ((hal_rx_desc_sz + l3pad_bytes + msdu_len) > DP_RX_BUFFER_SIZE) {
+ ath12k_dbg(ab, ATH12K_DBG_DATA,
+ "invalid msdu len in tkip mic err %u\n", msdu_len);
+ ath12k_dbg_dump(ab, ATH12K_DBG_DATA, NULL, "", desc,
+ sizeof(*desc));
+ return true;
+ }
+
skb_put(msdu, hal_rx_desc_sz + l3pad_bytes + msdu_len);
skb_pull(msdu, hal_rx_desc_sz + l3pad_bytes);
- ath12k_dp_rx_h_ppdu(ar, desc, status);
+ if (unlikely(!ath12k_dp_rx_check_nwifi_hdr_len_valid(ab, desc, msdu)))
+ return true;
+
+ ath12k_dp_rx_h_ppdu(ar, rx_info);
- status->flag |= (RX_FLAG_MMIC_STRIPPED | RX_FLAG_MMIC_ERROR |
- RX_FLAG_DECRYPTED);
+ rx_info->rx_status->flag |= (RX_FLAG_MMIC_STRIPPED | RX_FLAG_MMIC_ERROR |
+ RX_FLAG_DECRYPTED);
ath12k_dp_rx_h_undecap(ar, msdu, desc,
- HAL_ENCRYPT_TYPE_TKIP_MIC, status, false);
+ HAL_ENCRYPT_TYPE_TKIP_MIC, rx_info->rx_status, false);
+ return false;
}
static bool ath12k_dp_rx_h_rxdma_err(struct ath12k *ar, struct sk_buff *msdu,
- struct ieee80211_rx_status *status)
+ struct ath12k_dp_rx_info *rx_info)
{
struct ath12k_base *ab = ar->ab;
struct ath12k_skb_rxcb *rxcb = ATH12K_SKB_RXCB(msdu);
@@ -3800,14 +3954,15 @@ static bool ath12k_dp_rx_h_rxdma_err(struct ath12k *ar, struct sk_buff *msdu,
bool drop = false;
u32 err_bitmap;
- ar->ab->soc_stats.rxdma_error[rxcb->err_code]++;
+ ar->ab->device_stats.rxdma_error[rxcb->err_code]++;
switch (rxcb->err_code) {
case HAL_REO_ENTR_RING_RXDMA_ECODE_DECRYPT_ERR:
case HAL_REO_ENTR_RING_RXDMA_ECODE_TKIP_MIC_ERR:
err_bitmap = ath12k_dp_rx_h_mpdu_err(ab, rx_desc);
if (err_bitmap & HAL_RX_MPDU_ERR_TKIP_MIC) {
- ath12k_dp_rx_h_tkip_mic_err(ar, msdu, status);
+ ath12k_dp_rx_h_fetch_info(ab, rx_desc, rx_info);
+ drop = ath12k_dp_rx_h_tkip_mic_err(ar, msdu, rx_info);
break;
}
fallthrough;
@@ -3829,14 +3984,18 @@ static void ath12k_dp_rx_wbm_err(struct ath12k *ar,
{
struct ath12k_skb_rxcb *rxcb = ATH12K_SKB_RXCB(msdu);
struct ieee80211_rx_status rxs = {0};
+ struct ath12k_dp_rx_info rx_info;
bool drop = true;
+ rx_info.addr2_present = false;
+ rx_info.rx_status = &rxs;
+
switch (rxcb->err_rel_src) {
case HAL_WBM_REL_SRC_MODULE_REO:
- drop = ath12k_dp_rx_h_reo_err(ar, msdu, &rxs, msdu_list);
+ drop = ath12k_dp_rx_h_reo_err(ar, msdu, &rx_info, msdu_list);
break;
case HAL_WBM_REL_SRC_MODULE_RXDMA:
- drop = ath12k_dp_rx_h_rxdma_err(ar, msdu, &rxs);
+ drop = ath12k_dp_rx_h_rxdma_err(ar, msdu, &rx_info);
break;
default:
/* msdu will get freed */
@@ -3848,13 +4007,13 @@ static void ath12k_dp_rx_wbm_err(struct ath12k *ar,
return;
}
- ath12k_dp_rx_deliver_msdu(ar, napi, msdu, &rxs);
+ ath12k_dp_rx_deliver_msdu(ar, napi, msdu, &rx_info);
}
int ath12k_dp_rx_process_wbm_err(struct ath12k_base *ab,
struct napi_struct *napi, int budget)
{
- struct list_head rx_desc_used_list[ATH12K_MAX_SOCS];
+ struct list_head rx_desc_used_list[ATH12K_MAX_DEVICES];
struct ath12k_hw_group *ag = ab->ag;
struct ath12k *ar;
struct ath12k_dp *dp = &ab->dp;
@@ -3865,9 +4024,10 @@ int ath12k_dp_rx_process_wbm_err(struct ath12k_base *ab,
struct sk_buff_head msdu_list, scatter_msdu_list;
struct ath12k_skb_rxcb *rxcb;
void *rx_desc;
- int num_buffs_reaped[ATH12K_MAX_SOCS] = {};
+ int num_buffs_reaped[ATH12K_MAX_DEVICES] = {};
int total_num_buffs_reaped = 0;
struct ath12k_rx_desc_info *desc_info;
+ struct ath12k_device_dp_stats *device_stats = &ab->device_stats;
struct ath12k_hw_link *hw_links = ag->hw_links;
struct ath12k_base *partner_ab;
u8 hw_link_id, device_id;
@@ -3877,7 +4037,7 @@ int ath12k_dp_rx_process_wbm_err(struct ath12k_base *ab,
__skb_queue_head_init(&msdu_list);
__skb_queue_head_init(&scatter_msdu_list);
- for (device_id = 0; device_id < ATH12K_MAX_SOCS; device_id++)
+ for (device_id = 0; device_id < ATH12K_MAX_DEVICES; device_id++)
INIT_LIST_HEAD(&rx_desc_used_list[device_id]);
srng = &ab->hal.srng_list[dp->rx_rel_ring.ring_id];
@@ -4001,7 +4161,7 @@ int ath12k_dp_rx_process_wbm_err(struct ath12k_base *ab,
if (!total_num_buffs_reaped)
goto done;
- for (device_id = 0; device_id < ATH12K_MAX_SOCS; device_id++) {
+ for (device_id = 0; device_id < ATH12K_MAX_DEVICES; device_id++) {
if (!num_buffs_reaped[device_id])
continue;
@@ -4032,7 +4192,7 @@ int ath12k_dp_rx_process_wbm_err(struct ath12k_base *ab,
hw_links[hw_link_id].pdev_idx);
ar = partner_ab->pdevs[pdev_id].ar;
- if (!ar || !rcu_dereference(ar->ab->pdevs_active[hw_link_id])) {
+ if (!ar || !rcu_dereference(ar->ab->pdevs_active[pdev_id])) {
dev_kfree_skb_any(msdu);
continue;
}
@@ -4041,6 +4201,12 @@ int ath12k_dp_rx_process_wbm_err(struct ath12k_base *ab,
dev_kfree_skb_any(msdu);
continue;
}
+
+ if (rxcb->err_rel_src < HAL_WBM_REL_SRC_MODULE_MAX) {
+ device_id = ar->ab->device_id;
+ device_stats->rx_wbm_rel_source[rxcb->err_rel_src][device_id]++;
+ }
+
ath12k_dp_rx_wbm_err(ar, napi, msdu, &msdu_list);
}
rcu_read_unlock();
@@ -4130,6 +4296,7 @@ void ath12k_dp_rx_process_reo_status(struct ath12k_base *ab)
void ath12k_dp_rx_free(struct ath12k_base *ab)
{
struct ath12k_dp *dp = &ab->dp;
+ struct dp_srng *srng;
int i;
ath12k_dp_srng_cleanup(ab, &dp->rx_refill_buf_ring.refill_buf_ring);
@@ -4137,6 +4304,10 @@ void ath12k_dp_rx_free(struct ath12k_base *ab)
for (i = 0; i < ab->hw_params->num_rxdma_per_pdev; i++) {
if (ab->hw_params->rx_mac_buf_ring)
ath12k_dp_srng_cleanup(ab, &dp->rx_mac_buf_ring[i]);
+ if (!ab->hw_params->rxdma1_enable) {
+ srng = &dp->rx_mon_status_refill_ring[i].refill_buf_ring;
+ ath12k_dp_srng_cleanup(ab, srng);
+ }
}
for (i = 0; i < ab->hw_params->num_rxdma_dst_ring; i++)
@@ -4285,6 +4456,19 @@ int ath12k_dp_rx_htt_setup(struct ath12k_base *ab)
ret);
return ret;
}
+ } else {
+ for (i = 0; i < ab->hw_params->num_rxdma_per_pdev; i++) {
+ ring_id =
+ dp->rx_mon_status_refill_ring[i].refill_buf_ring.ring_id;
+ ret = ath12k_dp_tx_htt_srng_setup(ab, ring_id, i,
+ HAL_RXDMA_MONITOR_STATUS);
+ if (ret) {
+ ath12k_warn(ab,
+ "failed to configure mon_status_refill_ring%d %d\n",
+ i, ret);
+ return ret;
+ }
+ }
}
ret = ab->hw_params->hw_ops->rxdma_ring_sel_config(ab);
@@ -4299,6 +4483,7 @@ int ath12k_dp_rx_htt_setup(struct ath12k_base *ab)
int ath12k_dp_rx_alloc(struct ath12k_base *ab)
{
struct ath12k_dp *dp = &ab->dp;
+ struct dp_srng *srng;
int i, ret;
idr_init(&dp->rxdma_mon_buf_ring.bufs_idr);
@@ -4346,6 +4531,23 @@ int ath12k_dp_rx_alloc(struct ath12k_base *ab)
ath12k_warn(ab, "failed to setup HAL_RXDMA_MONITOR_BUF\n");
return ret;
}
+ } else {
+ for (i = 0; i < ab->hw_params->num_rxdma_per_pdev; i++) {
+ idr_init(&dp->rx_mon_status_refill_ring[i].bufs_idr);
+ spin_lock_init(&dp->rx_mon_status_refill_ring[i].idr_lock);
+ }
+
+ for (i = 0; i < ab->hw_params->num_rxdma_per_pdev; i++) {
+ srng = &dp->rx_mon_status_refill_ring[i].refill_buf_ring;
+ ret = ath12k_dp_srng_setup(ab, srng,
+ HAL_RXDMA_MONITOR_STATUS, 0, i,
+ DP_RXDMA_MON_STATUS_RING_SIZE);
+ if (ret) {
+ ath12k_warn(ab, "failed to setup mon status ring %d\n",
+ i);
+ return ret;
+ }
+ }
}
ret = ath12k_dp_rxdma_buf_setup(ab);
@@ -4416,15 +4618,15 @@ int ath12k_dp_rx_pdev_mon_attach(struct ath12k *ar)
return ret;
}
- /* if rxdma1_enable is false, no need to setup
- * rxdma_mon_desc_ring.
- */
- if (!ar->ab->hw_params->rxdma1_enable)
- return 0;
-
pmon->mon_last_linkdesc_paddr = 0;
pmon->mon_last_buf_cookie = DP_RX_DESC_COOKIE_MAX + 1;
spin_lock_init(&pmon->mon_lock);
+ if (!ar->ab->hw_params->rxdma1_enable)
+ return 0;
+
+ INIT_LIST_HEAD(&pmon->dp_rx_mon_mpdu_list);
+ pmon->mon_mpdu = NULL;
+
return 0;
}
diff --git a/drivers/net/wireless/ath/ath12k/dp_rx.h b/drivers/net/wireless/ath/ath12k/dp_rx.h
index 1ce82088c954..e971a314bd2d 100644
--- a/drivers/net/wireless/ath/ath12k/dp_rx.h
+++ b/drivers/net/wireless/ath/ath12k/dp_rx.h
@@ -1,7 +1,7 @@
/* SPDX-License-Identifier: BSD-3-Clause-Clear */
/*
* Copyright (c) 2018-2021 The Linux Foundation. All rights reserved.
- * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#ifndef ATH12K_DP_RX_H
#define ATH12K_DP_RX_H
@@ -14,11 +14,9 @@
struct ath12k_dp_rx_tid {
u8 tid;
- u32 *vaddr;
- dma_addr_t paddr;
- u32 size;
u32 ba_win_sz;
bool active;
+ struct ath12k_reoq_buf qbuf;
/* Info related to rx fragments */
u32 cur_sn;
@@ -65,6 +63,24 @@ struct ath12k_dp_rx_rfc1042_hdr {
__be16 snap_type;
} __packed;
+struct ath12k_dp_rx_info {
+ struct ieee80211_rx_status *rx_status;
+ u32 phy_meta_data;
+ u16 peer_id;
+ u8 decap_type;
+ u8 pkt_type;
+ u8 sgi;
+ u8 rate_mcs;
+ u8 bw;
+ u8 nss;
+ u8 addr2[ETH_ALEN];
+ u8 tid;
+ bool ip_csum_fail;
+ bool l4_csum_fail;
+ bool is_mcbc;
+ bool addr2_present;
+};
+
static inline u32 ath12k_he_gi_to_nl80211_he_gi(u8 sgi)
{
u32 ret = 0;
@@ -79,6 +95,9 @@ static inline u32 ath12k_he_gi_to_nl80211_he_gi(u8 sgi)
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;
@@ -128,16 +147,13 @@ int ath12k_dp_rx_peer_frag_setup(struct ath12k *ar, const u8 *peer_mac, int vdev
u8 ath12k_dp_rx_h_l3pad(struct ath12k_base *ab,
struct hal_rx_desc *desc);
struct ath12k_peer *
-ath12k_dp_rx_h_find_peer(struct ath12k_base *ab, struct sk_buff *msdu);
+ath12k_dp_rx_h_find_peer(struct ath12k_base *ab, struct sk_buff *msdu,
+ struct ath12k_dp_rx_info *rx_info);
u8 ath12k_dp_rx_h_decap_type(struct ath12k_base *ab,
struct hal_rx_desc *desc);
u32 ath12k_dp_rx_h_mpdu_err(struct ath12k_base *ab,
struct hal_rx_desc *desc);
-void ath12k_dp_rx_h_ppdu(struct ath12k *ar, struct hal_rx_desc *rx_desc,
- struct ieee80211_rx_status *rx_status);
-struct ath12k_peer *
-ath12k_dp_rx_h_find_peer(struct ath12k_base *ab, struct sk_buff *msdu);
-
+void ath12k_dp_rx_h_ppdu(struct ath12k *ar, struct ath12k_dp_rx_info *rx_info);
int ath12k_dp_rxdma_ring_sel_config_qcn9274(struct ath12k_base *ab);
int ath12k_dp_rxdma_ring_sel_config_wcn7850(struct ath12k_base *ab);
@@ -145,4 +161,17 @@ int ath12k_dp_htt_tlv_iter(struct ath12k_base *ab, const void *ptr, size_t len,
int (*iter)(struct ath12k_base *ar, u16 tag, u16 len,
const void *ptr, void *data),
void *data);
+void ath12k_dp_rx_h_fetch_info(struct ath12k_base *ab, struct hal_rx_desc *rx_desc,
+ struct ath12k_dp_rx_info *rx_info);
+
+int ath12k_dp_rx_crypto_mic_len(struct ath12k *ar, enum hal_encrypt_type enctype);
+u32 ath12k_dp_rxdesc_get_ppduid(struct ath12k_base *ab,
+ struct hal_rx_desc *rx_desc);
+bool ath12k_dp_rxdesc_mpdu_valid(struct ath12k_base *ab,
+ struct hal_rx_desc *rx_desc);
+int ath12k_dp_rx_link_desc_return(struct ath12k_base *ab,
+ struct ath12k_buffer_addr *buf_addr_info,
+ enum hal_wbm_rel_bm_act action);
+bool ath12k_dp_rxdesc_mpdu_valid(struct ath12k_base *ab,
+ struct hal_rx_desc *rx_desc);
#endif /* ATH12K_DP_RX_H */
diff --git a/drivers/net/wireless/ath/ath12k/dp_tx.c b/drivers/net/wireless/ath/ath12k/dp_tx.c
index a8d341a6df01..b6816b6c2c04 100644
--- a/drivers/net/wireless/ath/ath12k/dp_tx.c
+++ b/drivers/net/wireless/ath/ath12k/dp_tx.c
@@ -1,13 +1,16 @@
// SPDX-License-Identifier: BSD-3-Clause-Clear
/*
* Copyright (c) 2018-2021 The Linux Foundation. All rights reserved.
- * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#include "core.h"
#include "dp_tx.h"
#include "debug.h"
+#include "debugfs.h"
#include "hw.h"
+#include "peer.h"
+#include "mac.h"
static enum hal_tcl_encap_type
ath12k_dp_tx_get_encap_type(struct ath12k_link_vif *arvif, struct sk_buff *skb)
@@ -81,6 +84,7 @@ static void ath12k_dp_tx_release_txbuf(struct ath12k_dp *dp,
u8 pool_id)
{
spin_lock_bh(&dp->tx_desc_lock[pool_id]);
+ tx_desc->skb_ext_desc = NULL;
list_move_tail(&tx_desc->list, &dp->tx_desc_free_list[pool_id]);
spin_unlock_bh(&dp->tx_desc_lock[pool_id]);
}
@@ -117,7 +121,7 @@ static void ath12k_hal_tx_cmd_ext_desc_setup(struct ath12k_base *ab,
le32_encode_bits(ti->data_len,
HAL_TX_MSDU_EXT_INFO1_BUF_LEN);
- tcl_ext_cmd->info1 = le32_encode_bits(1, HAL_TX_MSDU_EXT_INFO1_EXTN_OVERRIDE) |
+ tcl_ext_cmd->info1 |= le32_encode_bits(1, HAL_TX_MSDU_EXT_INFO1_EXTN_OVERRIDE) |
le32_encode_bits(ti->encap_type,
HAL_TX_MSDU_EXT_INFO1_ENCAP_TYPE) |
le32_encode_bits(ti->encrypt_type,
@@ -217,7 +221,8 @@ out:
}
int ath12k_dp_tx(struct ath12k *ar, struct ath12k_link_vif *arvif,
- struct sk_buff *skb)
+ struct sk_buff *skb, bool gsn_valid, int mcbc_gsn,
+ bool is_mcast)
{
struct ath12k_base *ab = ar->ab;
struct ath12k_dp *dp = &ab->dp;
@@ -227,7 +232,7 @@ int ath12k_dp_tx(struct ath12k *ar, struct ath12k_link_vif *arvif,
struct ath12k_skb_cb *skb_cb = ATH12K_SKB_CB(skb);
struct hal_tcl_data_cmd *hal_tcl_desc;
struct hal_tx_msdu_ext_desc *msg;
- struct sk_buff *skb_ext_desc;
+ struct sk_buff *skb_ext_desc = NULL;
struct hal_srng *tcl_ring;
struct ieee80211_hdr *hdr = (void *)skb->data;
struct ath12k_vif *ahvif = arvif->ahvif;
@@ -290,13 +295,27 @@ tcl_ring_sel:
msdu_ext_desc = true;
}
+ if (gsn_valid) {
+ /* Reset and Initialize meta_data_flags with Global Sequence
+ * Number (GSN) info.
+ */
+ ti.meta_data_flags =
+ u32_encode_bits(HTT_TCL_META_DATA_TYPE_GLOBAL_SEQ_NUM,
+ HTT_TCL_META_DATA_TYPE) |
+ u32_encode_bits(mcbc_gsn, HTT_TCL_META_DATA_GLOBAL_SEQ_NUM);
+ }
+
ti.encap_type = ath12k_dp_tx_get_encap_type(arvif, skb);
ti.addr_search_flags = arvif->hal_addr_search_flags;
ti.search_type = arvif->search_type;
ti.type = HAL_TCL_DESC_TYPE_BUFFER;
ti.pkt_offset = 0;
ti.lmac_id = ar->lmac_id;
+
ti.vdev_id = arvif->vdev_id;
+ if (gsn_valid)
+ ti.vdev_id += HTT_TX_MLO_MCAST_HOST_REINJECT_BASE_VDEV_ID;
+
ti.bss_ast_hash = arvif->ast_hash;
ti.bss_ast_idx = arvif->ast_idx;
ti.dscp_tid_tbl_idx = 0;
@@ -331,7 +350,7 @@ tcl_ring_sel:
default:
/* TODO: Take care of other encap modes as well */
ret = -EINVAL;
- atomic_inc(&ab->soc_stats.tx_err.misc_fail);
+ atomic_inc(&ab->device_stats.tx_err.misc_fail);
goto fail_remove_tx_buf;
}
@@ -354,7 +373,7 @@ tcl_ring_sel:
map:
ti.paddr = dma_map_single(ab->dev, skb->data, skb->len, DMA_TO_DEVICE);
if (dma_mapping_error(ab->dev, ti.paddr)) {
- atomic_inc(&ab->soc_stats.tx_err.misc_fail);
+ atomic_inc(&ab->device_stats.tx_err.misc_fail);
ath12k_warn(ab, "failed to DMA map data Tx buffer\n");
ret = -ENOMEM;
goto fail_remove_tx_buf;
@@ -368,6 +387,7 @@ map:
add_htt_metadata = true;
msdu_ext_desc = true;
ti.flags0 |= u32_encode_bits(1, HAL_TCL_DATA_CMD_INFO2_TO_FW);
+ ti.meta_data_flags |= HTT_TCL_META_DATA_VALID_HTT;
ti.encap_type = HAL_TCL_ENCAP_TYPE_RAW;
ti.encrypt_type = HAL_ENCRYPT_TYPE_OPEN;
}
@@ -398,22 +418,21 @@ map:
if (ret < 0) {
ath12k_dbg(ab, ATH12K_DBG_DP_TX,
"Failed to add HTT meta data, dropping packet\n");
- goto fail_unmap_dma;
+ goto fail_free_ext_skb;
}
}
ti.paddr = dma_map_single(ab->dev, skb_ext_desc->data,
skb_ext_desc->len, DMA_TO_DEVICE);
ret = dma_mapping_error(ab->dev, ti.paddr);
- if (ret) {
- kfree_skb(skb_ext_desc);
- goto fail_unmap_dma;
- }
+ if (ret)
+ goto fail_free_ext_skb;
ti.data_len = skb_ext_desc->len;
ti.type = HAL_TCL_DESC_TYPE_EXT_DESC;
skb_cb->paddr_ext_desc = ti.paddr;
+ tx_desc->skb_ext_desc = skb_ext_desc;
}
hal_ring_id = tx_ring->tcl_data_ring.ring_id;
@@ -429,7 +448,7 @@ map:
* desc because the desc is directly enqueued onto hw queue.
*/
ath12k_hal_srng_access_end(ab, tcl_ring);
- ab->soc_stats.tx_err.desc_na[ti.ring_id]++;
+ ab->device_stats.tx_err.desc_na[ti.ring_id]++;
spin_unlock_bh(&tcl_ring->lock);
ret = -ENOMEM;
@@ -444,9 +463,22 @@ map:
ring_selector++;
}
- goto fail_unmap_dma;
+ goto fail_unmap_dma_ext;
}
+ spin_lock_bh(&arvif->link_stats_lock);
+ arvif->link_stats.tx_encap_type[ti.encap_type]++;
+ arvif->link_stats.tx_encrypt_type[ti.encrypt_type]++;
+ arvif->link_stats.tx_desc_type[ti.type]++;
+
+ if (is_mcast)
+ arvif->link_stats.tx_bcast_mcast++;
+ else
+ arvif->link_stats.tx_enqueued++;
+ spin_unlock_bh(&arvif->link_stats_lock);
+
+ ab->device_stats.tx_enqueued[ti.ring_id]++;
+
ath12k_hal_tx_cmd_desc_setup(ab, hal_tcl_desc, &ti);
ath12k_hal_srng_access_end(ab, tcl_ring);
@@ -460,16 +492,24 @@ map:
return 0;
-fail_unmap_dma:
- dma_unmap_single(ab->dev, ti.paddr, ti.data_len, DMA_TO_DEVICE);
-
+fail_unmap_dma_ext:
if (skb_cb->paddr_ext_desc)
dma_unmap_single(ab->dev, skb_cb->paddr_ext_desc,
- sizeof(struct hal_tx_msdu_ext_desc),
+ skb_ext_desc->len,
DMA_TO_DEVICE);
+fail_free_ext_skb:
+ kfree_skb(skb_ext_desc);
+
+fail_unmap_dma:
+ dma_unmap_single(ab->dev, ti.paddr, ti.data_len, DMA_TO_DEVICE);
fail_remove_tx_buf:
ath12k_dp_tx_release_txbuf(dp, tx_desc, pool_id);
+
+ spin_lock_bh(&arvif->link_stats_lock);
+ arvif->link_stats.tx_dropped++;
+ spin_unlock_bh(&arvif->link_stats_lock);
+
if (tcl_ring_retry)
goto tcl_ring_sel;
@@ -477,20 +517,23 @@ fail_remove_tx_buf:
}
static void ath12k_dp_tx_free_txbuf(struct ath12k_base *ab,
- struct sk_buff *msdu, u8 mac_id,
- struct dp_tx_ring *tx_ring)
+ struct dp_tx_ring *tx_ring,
+ struct ath12k_tx_desc_params *desc_params)
{
struct ath12k *ar;
+ struct sk_buff *msdu = desc_params->skb;
struct ath12k_skb_cb *skb_cb;
- u8 pdev_id = ath12k_hw_mac_id_to_pdev_id(ab->hw_params, mac_id);
+ u8 pdev_id = ath12k_hw_mac_id_to_pdev_id(ab->hw_params, desc_params->mac_id);
skb_cb = ATH12K_SKB_CB(msdu);
ar = ab->pdevs[pdev_id].ar;
dma_unmap_single(ab->dev, skb_cb->paddr, msdu->len, DMA_TO_DEVICE);
- if (skb_cb->paddr_ext_desc)
+ if (skb_cb->paddr_ext_desc) {
dma_unmap_single(ab->dev, skb_cb->paddr_ext_desc,
- sizeof(struct hal_tx_msdu_ext_desc), DMA_TO_DEVICE);
+ desc_params->skb_ext_desc->len, DMA_TO_DEVICE);
+ dev_kfree_skb_any(desc_params->skb_ext_desc);
+ }
ieee80211_free_txskb(ar->ah->hw, msdu);
@@ -500,26 +543,46 @@ static void ath12k_dp_tx_free_txbuf(struct ath12k_base *ab,
static void
ath12k_dp_tx_htt_tx_complete_buf(struct ath12k_base *ab,
- struct sk_buff *msdu,
+ struct ath12k_tx_desc_params *desc_params,
struct dp_tx_ring *tx_ring,
struct ath12k_dp_htt_wbm_tx_status *ts)
{
struct ieee80211_tx_info *info;
+ struct ath12k_link_vif *arvif;
struct ath12k_skb_cb *skb_cb;
+ struct ieee80211_vif *vif;
+ struct ath12k_vif *ahvif;
struct ath12k *ar;
+ struct sk_buff *msdu = desc_params->skb;
skb_cb = ATH12K_SKB_CB(msdu);
info = IEEE80211_SKB_CB(msdu);
ar = skb_cb->ar;
+ ab->device_stats.tx_completed[tx_ring->tcl_data_ring_id]++;
if (atomic_dec_and_test(&ar->dp.num_tx_pending))
wake_up(&ar->dp.tx_empty_waitq);
dma_unmap_single(ab->dev, skb_cb->paddr, msdu->len, DMA_TO_DEVICE);
- if (skb_cb->paddr_ext_desc)
+ if (skb_cb->paddr_ext_desc) {
dma_unmap_single(ab->dev, skb_cb->paddr_ext_desc,
- sizeof(struct hal_tx_msdu_ext_desc), DMA_TO_DEVICE);
+ desc_params->skb_ext_desc->len, DMA_TO_DEVICE);
+ dev_kfree_skb_any(desc_params->skb_ext_desc);
+ }
+
+ vif = skb_cb->vif;
+ if (vif) {
+ ahvif = ath12k_vif_to_ahvif(vif);
+ rcu_read_lock();
+ arvif = rcu_dereference(ahvif->link[skb_cb->link_id]);
+ if (arvif) {
+ spin_lock_bh(&arvif->link_stats_lock);
+ arvif->link_stats.tx_completed++;
+ spin_unlock_bh(&arvif->link_stats_lock);
+ }
+ rcu_read_unlock();
+ }
memset(&info->status, 0, sizeof(info->status));
@@ -542,10 +605,9 @@ ath12k_dp_tx_htt_tx_complete_buf(struct ath12k_base *ab,
}
static void
-ath12k_dp_tx_process_htt_tx_complete(struct ath12k_base *ab,
- void *desc, u8 mac_id,
- struct sk_buff *msdu,
- struct dp_tx_ring *tx_ring)
+ath12k_dp_tx_process_htt_tx_complete(struct ath12k_base *ab, void *desc,
+ struct dp_tx_ring *tx_ring,
+ struct ath12k_tx_desc_params *desc_params)
{
struct htt_tx_wbm_completion *status_desc;
struct ath12k_dp_htt_wbm_tx_status ts = {0};
@@ -555,19 +617,21 @@ ath12k_dp_tx_process_htt_tx_complete(struct ath12k_base *ab,
wbm_status = le32_get_bits(status_desc->info0,
HTT_TX_WBM_COMP_INFO0_STATUS);
+ ab->device_stats.fw_tx_status[wbm_status]++;
switch (wbm_status) {
case HAL_WBM_REL_HTT_TX_COMP_STATUS_OK:
- case HAL_WBM_REL_HTT_TX_COMP_STATUS_DROP:
- case HAL_WBM_REL_HTT_TX_COMP_STATUS_TTL:
ts.acked = (wbm_status == HAL_WBM_REL_HTT_TX_COMP_STATUS_OK);
ts.ack_rssi = le32_get_bits(status_desc->info2,
HTT_TX_WBM_COMP_INFO2_ACK_RSSI);
- ath12k_dp_tx_htt_tx_complete_buf(ab, msdu, tx_ring, &ts);
+ ath12k_dp_tx_htt_tx_complete_buf(ab, desc_params, tx_ring, &ts);
break;
+ case HAL_WBM_REL_HTT_TX_COMP_STATUS_DROP:
+ case HAL_WBM_REL_HTT_TX_COMP_STATUS_TTL:
case HAL_WBM_REL_HTT_TX_COMP_STATUS_REINJ:
case HAL_WBM_REL_HTT_TX_COMP_STATUS_INSPECT:
- ath12k_dp_tx_free_txbuf(ab, msdu, mac_id, tx_ring);
+ case HAL_WBM_REL_HTT_TX_COMP_STATUS_VDEVID_MISMATCH:
+ ath12k_dp_tx_free_txbuf(ab, tx_ring, desc_params);
break;
case HAL_WBM_REL_HTT_TX_COMP_STATUS_MEC_NOTIFY:
/* This event is to be handled only when the driver decides to
@@ -575,19 +639,142 @@ ath12k_dp_tx_process_htt_tx_complete(struct ath12k_base *ab,
*/
break;
default:
- ath12k_warn(ab, "Unknown htt tx status %d\n", wbm_status);
+ ath12k_warn(ab, "Unknown htt wbm tx status %d\n", wbm_status);
break;
}
}
+static void ath12k_dp_tx_update_txcompl(struct ath12k *ar, struct hal_tx_status *ts)
+{
+ struct ath12k_base *ab = ar->ab;
+ struct ath12k_peer *peer;
+ struct ieee80211_sta *sta;
+ struct ath12k_sta *ahsta;
+ struct ath12k_link_sta *arsta;
+ struct rate_info txrate = {0};
+ u16 rate, ru_tones;
+ u8 rate_idx = 0;
+ int ret;
+
+ spin_lock_bh(&ab->base_lock);
+ peer = ath12k_peer_find_by_id(ab, ts->peer_id);
+ if (!peer || !peer->sta) {
+ ath12k_dbg(ab, ATH12K_DBG_DP_TX,
+ "failed to find the peer by id %u\n", ts->peer_id);
+ spin_unlock_bh(&ab->base_lock);
+ return;
+ }
+ sta = peer->sta;
+ ahsta = ath12k_sta_to_ahsta(sta);
+ arsta = &ahsta->deflink;
+
+ /* 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)
+ txrate.nss = arsta->last_txrate.nss;
+ else
+ txrate.nss = arsta->peer_nss;
+ spin_unlock_bh(&ab->base_lock);
+
+ switch (ts->pkt_type) {
+ case HAL_TX_RATE_STATS_PKT_TYPE_11A:
+ case HAL_TX_RATE_STATS_PKT_TYPE_11B:
+ ret = ath12k_mac_hw_ratecode_to_legacy_rate(ts->mcs,
+ ts->pkt_type,
+ &rate_idx,
+ &rate);
+ if (ret < 0) {
+ ath12k_warn(ab, "Invalid tx legacy rate %d\n", ret);
+ return;
+ }
+
+ txrate.legacy = rate;
+ break;
+ case HAL_TX_RATE_STATS_PKT_TYPE_11N:
+ if (ts->mcs > ATH12K_HT_MCS_MAX) {
+ ath12k_warn(ab, "Invalid HT mcs index %d\n", ts->mcs);
+ return;
+ }
+
+ if (txrate.nss != 0)
+ txrate.mcs = ts->mcs + 8 * (txrate.nss - 1);
+
+ txrate.flags = RATE_INFO_FLAGS_MCS;
+
+ if (ts->sgi)
+ txrate.flags |= RATE_INFO_FLAGS_SHORT_GI;
+ break;
+ case HAL_TX_RATE_STATS_PKT_TYPE_11AC:
+ if (ts->mcs > ATH12K_VHT_MCS_MAX) {
+ ath12k_warn(ab, "Invalid VHT mcs index %d\n", ts->mcs);
+ return;
+ }
+
+ txrate.mcs = ts->mcs;
+ txrate.flags = RATE_INFO_FLAGS_VHT_MCS;
+
+ if (ts->sgi)
+ txrate.flags |= RATE_INFO_FLAGS_SHORT_GI;
+ break;
+ case HAL_TX_RATE_STATS_PKT_TYPE_11AX:
+ if (ts->mcs > ATH12K_HE_MCS_MAX) {
+ ath12k_warn(ab, "Invalid HE mcs index %d\n", ts->mcs);
+ return;
+ }
+
+ txrate.mcs = ts->mcs;
+ txrate.flags = RATE_INFO_FLAGS_HE_MCS;
+ txrate.he_gi = ath12k_he_gi_to_nl80211_he_gi(ts->sgi);
+ break;
+ case HAL_TX_RATE_STATS_PKT_TYPE_11BE:
+ if (ts->mcs > ATH12K_EHT_MCS_MAX) {
+ ath12k_warn(ab, "Invalid EHT mcs index %d\n", ts->mcs);
+ return;
+ }
+
+ txrate.mcs = ts->mcs;
+ txrate.flags = RATE_INFO_FLAGS_EHT_MCS;
+ txrate.eht_gi = ath12k_mac_eht_gi_to_nl80211_eht_gi(ts->sgi);
+ break;
+ default:
+ ath12k_warn(ab, "Invalid tx pkt type: %d\n", ts->pkt_type);
+ return;
+ }
+
+ txrate.bw = ath12k_mac_bw_to_mac80211_bw(ts->bw);
+
+ if (ts->ofdma && ts->pkt_type == HAL_TX_RATE_STATS_PKT_TYPE_11AX) {
+ txrate.bw = RATE_INFO_BW_HE_RU;
+ ru_tones = ath12k_mac_he_convert_tones_to_ru_tones(ts->tones);
+ txrate.he_ru_alloc =
+ ath12k_he_ru_tones_to_nl80211_he_ru_alloc(ru_tones);
+ }
+
+ if (ts->ofdma && ts->pkt_type == HAL_TX_RATE_STATS_PKT_TYPE_11BE) {
+ txrate.bw = RATE_INFO_BW_EHT_RU;
+ txrate.eht_ru_alloc =
+ ath12k_mac_eht_ru_tones_to_nl80211_eht_ru_alloc(ts->tones);
+ }
+
+ spin_lock_bh(&ab->base_lock);
+ arsta->txrate = txrate;
+ spin_unlock_bh(&ab->base_lock);
+}
+
static void ath12k_dp_tx_complete_msdu(struct ath12k *ar,
- struct sk_buff *msdu,
- struct hal_tx_status *ts)
+ struct ath12k_tx_desc_params *desc_params,
+ struct hal_tx_status *ts,
+ int ring)
{
struct ath12k_base *ab = ar->ab;
struct ath12k_hw *ah = ar->ah;
struct ieee80211_tx_info *info;
+ struct ath12k_link_vif *arvif;
struct ath12k_skb_cb *skb_cb;
+ struct ieee80211_vif *vif;
+ struct ath12k_vif *ahvif;
+ struct sk_buff *msdu = desc_params->skb;
if (WARN_ON_ONCE(ts->buf_rel_source != HAL_WBM_REL_SRC_MODULE_TQM)) {
/* Must not happen */
@@ -595,11 +782,14 @@ static void ath12k_dp_tx_complete_msdu(struct ath12k *ar,
}
skb_cb = ATH12K_SKB_CB(msdu);
+ ab->device_stats.tx_completed[ring]++;
dma_unmap_single(ab->dev, skb_cb->paddr, msdu->len, DMA_TO_DEVICE);
- if (skb_cb->paddr_ext_desc)
+ if (skb_cb->paddr_ext_desc) {
dma_unmap_single(ab->dev, skb_cb->paddr_ext_desc,
- sizeof(struct hal_tx_msdu_ext_desc), DMA_TO_DEVICE);
+ desc_params->skb_ext_desc->len, DMA_TO_DEVICE);
+ dev_kfree_skb_any(desc_params->skb_ext_desc);
+ }
rcu_read_lock();
@@ -613,6 +803,17 @@ static void ath12k_dp_tx_complete_msdu(struct ath12k *ar,
goto exit;
}
+ vif = skb_cb->vif;
+ if (vif) {
+ ahvif = ath12k_vif_to_ahvif(vif);
+ arvif = rcu_dereference(ahvif->link[skb_cb->link_id]);
+ if (arvif) {
+ spin_lock_bh(&arvif->link_stats_lock);
+ arvif->link_stats.tx_completed++;
+ spin_unlock_bh(&arvif->link_stats_lock);
+ }
+ }
+
info = IEEE80211_SKB_CB(msdu);
memset(&info->status, 0, sizeof(info->status));
@@ -658,6 +859,8 @@ static void ath12k_dp_tx_complete_msdu(struct ath12k *ar,
* Might end up reporting it out-of-band from HTT stats.
*/
+ ath12k_dp_tx_update_txcompl(ar, ts);
+
ieee80211_tx_status_skb(ath12k_ar_to_hw(ar), msdu);
exit:
@@ -668,6 +871,8 @@ static void ath12k_dp_tx_status_parse(struct ath12k_base *ab,
struct hal_wbm_completion_ring_tx *desc,
struct hal_tx_status *ts)
{
+ u32 info0 = le32_to_cpu(desc->rate_stats.info0);
+
ts->buf_rel_source =
le32_get_bits(desc->info0, HAL_WBM_COMPL_TX_INFO0_REL_SRC_MODULE);
if (ts->buf_rel_source != HAL_WBM_REL_SRC_MODULE_FW &&
@@ -682,10 +887,17 @@ static void ath12k_dp_tx_status_parse(struct ath12k_base *ab,
ts->ppdu_id = le32_get_bits(desc->info1,
HAL_WBM_COMPL_TX_INFO1_TQM_STATUS_NUMBER);
- if (le32_to_cpu(desc->rate_stats.info0) & HAL_TX_RATE_STATS_INFO0_VALID)
- ts->rate_stats = le32_to_cpu(desc->rate_stats.info0);
- else
- ts->rate_stats = 0;
+
+ ts->peer_id = le32_get_bits(desc->info3, HAL_WBM_COMPL_TX_INFO3_PEER_ID);
+
+ if (info0 & HAL_TX_RATE_STATS_INFO0_VALID) {
+ ts->pkt_type = u32_get_bits(info0, HAL_TX_RATE_STATS_INFO0_PKT_TYPE);
+ ts->mcs = u32_get_bits(info0, HAL_TX_RATE_STATS_INFO0_MCS);
+ ts->sgi = u32_get_bits(info0, HAL_TX_RATE_STATS_INFO0_SGI);
+ ts->bw = u32_get_bits(info0, HAL_TX_RATE_STATS_INFO0_BW);
+ ts->tones = u32_get_bits(info0, HAL_TX_RATE_STATS_INFO0_TONES_IN_RU);
+ ts->ofdma = u32_get_bits(info0, HAL_TX_RATE_STATS_INFO0_OFDMA_TX);
+ }
}
void ath12k_dp_tx_completion_handler(struct ath12k_base *ab, int ring_id)
@@ -695,12 +907,14 @@ void ath12k_dp_tx_completion_handler(struct ath12k_base *ab, int ring_id)
int hal_ring_id = dp->tx_ring[ring_id].tcl_comp_ring.ring_id;
struct hal_srng *status_ring = &ab->hal.srng_list[hal_ring_id];
struct ath12k_tx_desc_info *tx_desc = NULL;
- struct sk_buff *msdu;
struct hal_tx_status ts = { 0 };
+ struct ath12k_tx_desc_params desc_params;
struct dp_tx_ring *tx_ring = &dp->tx_ring[ring_id];
struct hal_wbm_release_ring *desc;
- u8 mac_id, pdev_id;
+ u8 pdev_id;
u64 desc_va;
+ enum hal_wbm_rel_src_module buf_rel_source;
+ enum hal_wbm_tqm_rel_reason rel_status;
spin_lock_bh(&status_ring->lock);
@@ -753,28 +967,37 @@ void ath12k_dp_tx_completion_handler(struct ath12k_base *ab, int ring_id)
continue;
}
- msdu = tx_desc->skb;
- mac_id = tx_desc->mac_id;
+ desc_params.mac_id = tx_desc->mac_id;
+ desc_params.skb = tx_desc->skb;
+ desc_params.skb_ext_desc = tx_desc->skb_ext_desc;
+
+ /* Find the HAL_WBM_RELEASE_INFO0_REL_SRC_MODULE value */
+ buf_rel_source = le32_get_bits(tx_status->info0,
+ HAL_WBM_RELEASE_INFO0_REL_SRC_MODULE);
+ ab->device_stats.tx_wbm_rel_source[buf_rel_source]++;
+
+ rel_status = le32_get_bits(tx_status->info0,
+ HAL_WBM_COMPL_TX_INFO0_TQM_RELEASE_REASON);
+ ab->device_stats.tqm_rel_reason[rel_status]++;
/* Release descriptor as soon as extracting necessary info
* to reduce contention
*/
ath12k_dp_tx_release_txbuf(dp, tx_desc, tx_desc->pool_id);
if (ts.buf_rel_source == HAL_WBM_REL_SRC_MODULE_FW) {
- ath12k_dp_tx_process_htt_tx_complete(ab,
- (void *)tx_status,
- mac_id, msdu,
- tx_ring);
+ ath12k_dp_tx_process_htt_tx_complete(ab, (void *)tx_status,
+ tx_ring, &desc_params);
continue;
}
- pdev_id = ath12k_hw_mac_id_to_pdev_id(ab->hw_params, mac_id);
+ pdev_id = ath12k_hw_mac_id_to_pdev_id(ab->hw_params, desc_params.mac_id);
ar = ab->pdevs[pdev_id].ar;
if (atomic_dec_and_test(&ar->dp.num_tx_pending))
wake_up(&ar->dp.tx_empty_waitq);
- ath12k_dp_tx_complete_msdu(ar, msdu, &ts);
+ ath12k_dp_tx_complete_msdu(ar, &desc_params, &ts,
+ tx_ring->tcl_data_ring_id);
}
}
@@ -814,7 +1037,7 @@ ath12k_dp_tx_get_ring_id_type(struct ath12k_base *ab,
*htt_ring_type = HTT_HW_TO_SW_RING;
break;
case HAL_RXDMA_MONITOR_BUF:
- *htt_ring_id = HTT_RXDMA_MONITOR_BUF_RING;
+ *htt_ring_id = HTT_RX_MON_HOST2MON_BUF_RING;
*htt_ring_type = HTT_SW_TO_HW_RING;
break;
case HAL_RXDMA_MONITOR_STATUS:
@@ -822,7 +1045,7 @@ ath12k_dp_tx_get_ring_id_type(struct ath12k_base *ab,
*htt_ring_type = HTT_SW_TO_HW_RING;
break;
case HAL_RXDMA_MONITOR_DST:
- *htt_ring_id = HTT_RXDMA_MONITOR_DEST_RING;
+ *htt_ring_id = HTT_RX_MON_MON2HOST_DEST_RING;
*htt_ring_type = HTT_HW_TO_SW_RING;
break;
case HAL_RXDMA_MONITOR_DESC:
@@ -971,7 +1194,14 @@ int ath12k_dp_tx_htt_h2t_ver_req_msg(struct ath12k_base *ab)
skb_put(skb, len);
cmd = (struct htt_ver_req_cmd *)skb->data;
cmd->ver_reg_info = le32_encode_bits(HTT_H2T_MSG_TYPE_VERSION_REQ,
- HTT_VER_REQ_INFO_MSG_ID);
+ HTT_OPTION_TAG);
+
+ cmd->tcl_metadata_version = le32_encode_bits(HTT_TAG_TCL_METADATA_VERSION,
+ HTT_OPTION_TAG) |
+ le32_encode_bits(HTT_TCL_METADATA_VER_SZ,
+ HTT_OPTION_LEN) |
+ le32_encode_bits(HTT_OPTION_TCL_METADATA_VER_V2,
+ HTT_OPTION_VALUE);
ret = ath12k_htc_send(&ab->htc, dp->eid, skb);
if (ret) {
@@ -1077,15 +1307,46 @@ int ath12k_dp_tx_htt_rx_filter_setup(struct ath12k_base *ab, u32 ring_id,
cmd->info0 |= le32_encode_bits(!!(params.flags & HAL_SRNG_FLAGS_DATA_TLV_SWAP),
HTT_RX_RING_SELECTION_CFG_CMD_INFO0_PS);
cmd->info0 |= le32_encode_bits(tlv_filter->offset_valid,
- HTT_RX_RING_SELECTION_CFG_CMD_OFFSET_VALID);
+ HTT_RX_RING_SELECTION_CFG_CMD_INFO0_OFFSET_VALID);
+ cmd->info0 |=
+ le32_encode_bits(tlv_filter->drop_threshold_valid,
+ HTT_RX_RING_SELECTION_CFG_CMD_INFO0_DROP_THRES_VAL);
+ cmd->info0 |= le32_encode_bits(!tlv_filter->rxmon_disable,
+ HTT_RX_RING_SELECTION_CFG_CMD_INFO0_EN_RXMON);
+
cmd->info1 = le32_encode_bits(rx_buf_size,
HTT_RX_RING_SELECTION_CFG_CMD_INFO1_BUF_SIZE);
+ cmd->info1 |= le32_encode_bits(tlv_filter->conf_len_mgmt,
+ HTT_RX_RING_SELECTION_CFG_CMD_INFO1_CONF_LEN_MGMT);
+ cmd->info1 |= le32_encode_bits(tlv_filter->conf_len_ctrl,
+ HTT_RX_RING_SELECTION_CFG_CMD_INFO1_CONF_LEN_CTRL);
+ cmd->info1 |= le32_encode_bits(tlv_filter->conf_len_data,
+ HTT_RX_RING_SELECTION_CFG_CMD_INFO1_CONF_LEN_DATA);
cmd->pkt_type_en_flags0 = cpu_to_le32(tlv_filter->pkt_filter_flags0);
cmd->pkt_type_en_flags1 = cpu_to_le32(tlv_filter->pkt_filter_flags1);
cmd->pkt_type_en_flags2 = cpu_to_le32(tlv_filter->pkt_filter_flags2);
cmd->pkt_type_en_flags3 = cpu_to_le32(tlv_filter->pkt_filter_flags3);
cmd->rx_filter_tlv = cpu_to_le32(tlv_filter->rx_filter);
+ cmd->info2 = le32_encode_bits(tlv_filter->rx_drop_threshold,
+ HTT_RX_RING_SELECTION_CFG_CMD_INFO2_DROP_THRESHOLD);
+ cmd->info2 |=
+ le32_encode_bits(tlv_filter->enable_log_mgmt_type,
+ HTT_RX_RING_SELECTION_CFG_CMD_INFO2_EN_LOG_MGMT_TYPE);
+ cmd->info2 |=
+ le32_encode_bits(tlv_filter->enable_log_ctrl_type,
+ HTT_RX_RING_SELECTION_CFG_CMD_INFO2_EN_CTRL_TYPE);
+ cmd->info2 |=
+ le32_encode_bits(tlv_filter->enable_log_data_type,
+ HTT_RX_RING_SELECTION_CFG_CMD_INFO2_EN_LOG_DATA_TYPE);
+
+ cmd->info3 =
+ le32_encode_bits(tlv_filter->enable_rx_tlv_offset,
+ HTT_RX_RING_SELECTION_CFG_CMD_INFO3_EN_TLV_PKT_OFFSET);
+ cmd->info3 |=
+ le32_encode_bits(tlv_filter->rx_tlv_offset,
+ HTT_RX_RING_SELECTION_CFG_CMD_INFO3_PKT_TLV_OFFSET);
+
if (tlv_filter->offset_valid) {
cmd->rx_packet_offset =
le32_encode_bits(tlv_filter->rx_packet_offset,
@@ -1210,15 +1471,28 @@ int ath12k_dp_tx_htt_monitor_mode_ring_config(struct ath12k *ar, bool reset)
int ath12k_dp_tx_htt_rx_monitor_mode_ring_config(struct ath12k *ar, bool reset)
{
struct ath12k_base *ab = ar->ab;
- struct ath12k_dp *dp = &ab->dp;
struct htt_rx_ring_tlv_filter tlv_filter = {0};
- int ret, ring_id;
+ int ret, ring_id, i;
- ring_id = dp->rxdma_mon_buf_ring.refill_buf_ring.ring_id;
tlv_filter.offset_valid = false;
if (!reset) {
- tlv_filter.rx_filter = HTT_RX_MON_FILTER_TLV_FLAGS_MON_BUF_RING;
+ tlv_filter.rx_filter = HTT_RX_MON_FILTER_TLV_FLAGS_MON_DEST_RING;
+
+ tlv_filter.drop_threshold_valid = true;
+ tlv_filter.rx_drop_threshold = HTT_RX_RING_TLV_DROP_THRESHOLD_VALUE;
+
+ tlv_filter.enable_log_mgmt_type = true;
+ tlv_filter.enable_log_ctrl_type = true;
+ tlv_filter.enable_log_data_type = true;
+
+ tlv_filter.conf_len_ctrl = HTT_RX_RING_DEFAULT_DMA_LENGTH;
+ tlv_filter.conf_len_mgmt = HTT_RX_RING_DEFAULT_DMA_LENGTH;
+ tlv_filter.conf_len_data = HTT_RX_RING_DEFAULT_DMA_LENGTH;
+
+ tlv_filter.enable_rx_tlv_offset = true;
+ tlv_filter.rx_tlv_offset = HTT_RX_RING_PKT_TLV_OFFSET;
+
tlv_filter.pkt_filter_flags0 =
HTT_RX_MON_FP_MGMT_FILTER_FLAGS0 |
HTT_RX_MON_MO_MGMT_FILTER_FLAGS0;
@@ -1233,16 +1507,64 @@ int ath12k_dp_tx_htt_rx_monitor_mode_ring_config(struct ath12k *ar, bool reset)
HTT_RX_MON_MO_CTRL_FILTER_FLASG3 |
HTT_RX_MON_FP_DATA_FILTER_FLASG3 |
HTT_RX_MON_MO_DATA_FILTER_FLASG3;
+ } else {
+ tlv_filter = ath12k_mac_mon_status_filter_default;
+
+ if (ath12k_debugfs_is_extd_rx_stats_enabled(ar))
+ tlv_filter.rx_filter = ath12k_debugfs_rx_filter(ar);
}
if (ab->hw_params->rxdma1_enable) {
- ret = ath12k_dp_tx_htt_rx_filter_setup(ar->ab, ring_id, 0,
- HAL_RXDMA_MONITOR_BUF,
- DP_RXDMA_REFILL_RING_SIZE,
+ for (i = 0; i < ab->hw_params->num_rxdma_per_pdev; i++) {
+ ring_id = ar->dp.rxdma_mon_dst_ring[i].ring_id;
+ ret = ath12k_dp_tx_htt_rx_filter_setup(ar->ab, ring_id,
+ ar->dp.mac_id + i,
+ HAL_RXDMA_MONITOR_DST,
+ DP_RXDMA_REFILL_RING_SIZE,
+ &tlv_filter);
+ if (ret) {
+ ath12k_err(ab,
+ "failed to setup filter for monitor buf %d\n",
+ ret);
+ return ret;
+ }
+ }
+ return 0;
+ }
+
+ if (!reset) {
+ for (i = 0; i < ab->hw_params->num_rxdma_per_pdev; i++) {
+ ring_id = ab->dp.rx_mac_buf_ring[i].ring_id;
+ ret = ath12k_dp_tx_htt_rx_filter_setup(ar->ab, ring_id,
+ i,
+ HAL_RXDMA_BUF,
+ DP_RXDMA_REFILL_RING_SIZE,
+ &tlv_filter);
+ if (ret) {
+ ath12k_err(ab,
+ "failed to setup filter for mon rx buf %d\n",
+ ret);
+ return ret;
+ }
+ }
+ }
+
+ for (i = 0; i < ab->hw_params->num_rxdma_per_pdev; i++) {
+ ring_id = ab->dp.rx_mon_status_refill_ring[i].refill_buf_ring.ring_id;
+ if (!reset) {
+ tlv_filter.rx_filter =
+ HTT_RX_MON_FILTER_TLV_FLAGS_MON_STATUS_RING;
+ }
+
+ ret = ath12k_dp_tx_htt_rx_filter_setup(ab, ring_id,
+ i,
+ HAL_RXDMA_MONITOR_STATUS,
+ RX_MON_STATUS_BUF_SIZE,
&tlv_filter);
if (ret) {
ath12k_err(ab,
- "failed to setup filter for monitor buf %d\n", ret);
+ "failed to setup filter for mon status buf %d\n",
+ ret);
return ret;
}
}
diff --git a/drivers/net/wireless/ath/ath12k/dp_tx.h b/drivers/net/wireless/ath/ath12k/dp_tx.h
index 46dce23501f3..10acdcf1fa8f 100644
--- a/drivers/net/wireless/ath/ath12k/dp_tx.h
+++ b/drivers/net/wireless/ath/ath12k/dp_tx.h
@@ -1,7 +1,7 @@
/* SPDX-License-Identifier: BSD-3-Clause-Clear */
/*
* Copyright (c) 2018-2021 The Linux Foundation. All rights reserved.
- * Copyright (c) 2021-2022, 2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) 2021-2022, 2024-2025 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#ifndef ATH12K_DP_TX_H
@@ -17,7 +17,8 @@ struct ath12k_dp_htt_wbm_tx_status {
int ath12k_dp_tx_htt_h2t_ver_req_msg(struct ath12k_base *ab);
int ath12k_dp_tx(struct ath12k *ar, struct ath12k_link_vif *arvif,
- struct sk_buff *skb);
+ struct sk_buff *skb, bool gsn_valid, int mcbc_gsn,
+ bool is_mcast);
void ath12k_dp_tx_completion_handler(struct ath12k_base *ab, int ring_id);
int ath12k_dp_tx_htt_h2t_ppdu_stats_req(struct ath12k *ar, u32 mask);
diff --git a/drivers/net/wireless/ath/ath12k/fw.c b/drivers/net/wireless/ath/ath12k/fw.c
index 5be4b2d4a19d..5ac497f80cad 100644
--- a/drivers/net/wireless/ath/ath12k/fw.c
+++ b/drivers/net/wireless/ath/ath12k/fw.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: BSD-3-Clause-Clear
/*
- * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) 2022-2025 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#include "core.h"
@@ -99,6 +99,8 @@ static int ath12k_fw_request_firmware_api_n(struct ath12k_base *ab,
__set_bit(i, ab->fw.fw_features);
}
+ ab->fw.fw_features_valid = true;
+
ath12k_dbg_dump(ab, ATH12K_DBG_BOOT, "features", "",
ab->fw.fw_features,
sizeof(ab->fw.fw_features));
@@ -169,3 +171,8 @@ void ath12k_fw_unmap(struct ath12k_base *ab)
release_firmware(ab->fw.fw);
memset(&ab->fw, 0, sizeof(ab->fw));
}
+
+bool ath12k_fw_feature_supported(struct ath12k_base *ab, enum ath12k_fw_features feat)
+{
+ return ab->fw.fw_features_valid && test_bit(feat, ab->fw.fw_features);
+}
diff --git a/drivers/net/wireless/ath/ath12k/fw.h b/drivers/net/wireless/ath/ath12k/fw.h
index 273c003eff3b..7afaefed5086 100644
--- a/drivers/net/wireless/ath/ath12k/fw.h
+++ b/drivers/net/wireless/ath/ath12k/fw.h
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: BSD-3-Clause-Clear */
/*
- * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) 2022-2025 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#ifndef ATH12K_FW_H
@@ -32,5 +32,6 @@ enum ath12k_fw_features {
void ath12k_fw_map(struct ath12k_base *ab);
void ath12k_fw_unmap(struct ath12k_base *ab);
+bool ath12k_fw_feature_supported(struct ath12k_base *ab, enum ath12k_fw_features feat);
#endif /* ATH12K_FW_H */
diff --git a/drivers/net/wireless/ath/ath12k/hal.c b/drivers/net/wireless/ath/ath12k/hal.c
index cd59ff8e6c7b..a301898e5849 100644
--- a/drivers/net/wireless/ath/ath12k/hal.c
+++ b/drivers/net/wireless/ath/ath12k/hal.c
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: BSD-3-Clause-Clear
/*
* Copyright (c) 2018-2021 The Linux Foundation. All rights reserved.
- * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#include <linux/dma-mapping.h>
#include "hal_tx.h"
@@ -154,7 +154,14 @@ static const struct hal_srng_config hw_srng_config_template[] = {
.ring_dir = HAL_SRNG_DIR_SRC,
.max_size = HAL_RXDMA_RING_MAX_SIZE_BE,
},
- [HAL_RXDMA_MONITOR_STATUS] = { 0, },
+ [HAL_RXDMA_MONITOR_STATUS] = {
+ .start_ring_id = HAL_SRNG_RING_ID_WMAC1_SW2RXDMA1_STATBUF,
+ .max_rings = 1,
+ .entry_size = sizeof(struct hal_wbm_buffer_ring) >> 2,
+ .mac_type = ATH12K_HAL_SRNG_PMAC,
+ .ring_dir = HAL_SRNG_DIR_SRC,
+ .max_size = HAL_RXDMA_RING_MAX_SIZE_BE,
+ },
[HAL_RXDMA_MONITOR_DESC] = { 0, },
[HAL_RXDMA_DIR_BUF] = {
.start_ring_id = HAL_SRNG_RING_ID_RXDMA_DIR_BUF,
@@ -449,8 +456,8 @@ static u8 *ath12k_hw_qcn9274_rx_desc_mpdu_start_addr2(struct hal_rx_desc *desc)
static bool ath12k_hw_qcn9274_rx_desc_is_da_mcbc(struct hal_rx_desc *desc)
{
- return __le32_to_cpu(desc->u.qcn9274.mpdu_start.info6) &
- RX_MPDU_START_INFO6_MCAST_BCAST;
+ return __le16_to_cpu(desc->u.qcn9274.msdu_end.info5) &
+ RX_MSDU_END_INFO5_DA_IS_MCBC;
}
static void ath12k_hw_qcn9274_rx_desc_get_dot11_hdr(struct hal_rx_desc *desc,
@@ -511,11 +518,6 @@ static void ath12k_hw_qcn9274_rx_desc_get_crypto_hdr(struct hal_rx_desc *desc,
crypto_hdr[7] = HAL_RX_MPDU_INFO_PN_GET_BYTE2(desc->u.qcn9274.mpdu_start.pn[1]);
}
-static u16 ath12k_hw_qcn9274_rx_desc_get_mpdu_frame_ctl(struct hal_rx_desc *desc)
-{
- return __le16_to_cpu(desc->u.qcn9274.mpdu_start.frame_ctrl);
-}
-
static int ath12k_hal_srng_create_config_qcn9274(struct ath12k_base *ab)
{
struct ath12k_hal *hal = &ab->hal;
@@ -552,9 +554,9 @@ static int ath12k_hal_srng_create_config_qcn9274(struct ath12k_base *ab)
s->reg_start[1] = HAL_SEQ_WCSS_UMAC_REO_REG + HAL_REO_STATUS_HP;
s = &hal->srng_config[HAL_TCL_DATA];
- s->reg_start[0] = HAL_SEQ_WCSS_UMAC_TCL_REG + HAL_TCL1_RING_BASE_LSB;
+ s->reg_start[0] = HAL_SEQ_WCSS_UMAC_TCL_REG + HAL_TCL1_RING_BASE_LSB(ab);
s->reg_start[1] = HAL_SEQ_WCSS_UMAC_TCL_REG + HAL_TCL1_RING_HP;
- s->reg_size[0] = HAL_TCL2_RING_BASE_LSB - HAL_TCL1_RING_BASE_LSB;
+ s->reg_size[0] = HAL_TCL2_RING_BASE_LSB(ab) - HAL_TCL1_RING_BASE_LSB(ab);
s->reg_size[1] = HAL_TCL2_RING_HP - HAL_TCL1_RING_HP;
s = &hal->srng_config[HAL_TCL_CMD];
@@ -566,29 +568,29 @@ static int ath12k_hal_srng_create_config_qcn9274(struct ath12k_base *ab)
s->reg_start[1] = HAL_SEQ_WCSS_UMAC_TCL_REG + HAL_TCL_STATUS_RING_HP;
s = &hal->srng_config[HAL_CE_SRC];
- s->reg_start[0] = HAL_SEQ_WCSS_UMAC_CE0_SRC_REG + HAL_CE_DST_RING_BASE_LSB;
- s->reg_start[1] = HAL_SEQ_WCSS_UMAC_CE0_SRC_REG + HAL_CE_DST_RING_HP;
- s->reg_size[0] = HAL_SEQ_WCSS_UMAC_CE1_SRC_REG -
- HAL_SEQ_WCSS_UMAC_CE0_SRC_REG;
- s->reg_size[1] = HAL_SEQ_WCSS_UMAC_CE1_SRC_REG -
- HAL_SEQ_WCSS_UMAC_CE0_SRC_REG;
+ s->reg_start[0] = HAL_SEQ_WCSS_UMAC_CE0_SRC_REG(ab) + HAL_CE_DST_RING_BASE_LSB;
+ s->reg_start[1] = HAL_SEQ_WCSS_UMAC_CE0_SRC_REG(ab) + HAL_CE_DST_RING_HP;
+ s->reg_size[0] = HAL_SEQ_WCSS_UMAC_CE1_SRC_REG(ab) -
+ HAL_SEQ_WCSS_UMAC_CE0_SRC_REG(ab);
+ s->reg_size[1] = HAL_SEQ_WCSS_UMAC_CE1_SRC_REG(ab) -
+ HAL_SEQ_WCSS_UMAC_CE0_SRC_REG(ab);
s = &hal->srng_config[HAL_CE_DST];
- s->reg_start[0] = HAL_SEQ_WCSS_UMAC_CE0_DST_REG + HAL_CE_DST_RING_BASE_LSB;
- s->reg_start[1] = HAL_SEQ_WCSS_UMAC_CE0_DST_REG + HAL_CE_DST_RING_HP;
- s->reg_size[0] = HAL_SEQ_WCSS_UMAC_CE1_DST_REG -
- HAL_SEQ_WCSS_UMAC_CE0_DST_REG;
- s->reg_size[1] = HAL_SEQ_WCSS_UMAC_CE1_DST_REG -
- HAL_SEQ_WCSS_UMAC_CE0_DST_REG;
+ s->reg_start[0] = HAL_SEQ_WCSS_UMAC_CE0_DST_REG(ab) + HAL_CE_DST_RING_BASE_LSB;
+ s->reg_start[1] = HAL_SEQ_WCSS_UMAC_CE0_DST_REG(ab) + HAL_CE_DST_RING_HP;
+ s->reg_size[0] = HAL_SEQ_WCSS_UMAC_CE1_DST_REG(ab) -
+ HAL_SEQ_WCSS_UMAC_CE0_DST_REG(ab);
+ s->reg_size[1] = HAL_SEQ_WCSS_UMAC_CE1_DST_REG(ab) -
+ HAL_SEQ_WCSS_UMAC_CE0_DST_REG(ab);
s = &hal->srng_config[HAL_CE_DST_STATUS];
- s->reg_start[0] = HAL_SEQ_WCSS_UMAC_CE0_DST_REG +
+ s->reg_start[0] = HAL_SEQ_WCSS_UMAC_CE0_DST_REG(ab) +
HAL_CE_DST_STATUS_RING_BASE_LSB;
- s->reg_start[1] = HAL_SEQ_WCSS_UMAC_CE0_DST_REG + HAL_CE_DST_STATUS_RING_HP;
- s->reg_size[0] = HAL_SEQ_WCSS_UMAC_CE1_DST_REG -
- HAL_SEQ_WCSS_UMAC_CE0_DST_REG;
- s->reg_size[1] = HAL_SEQ_WCSS_UMAC_CE1_DST_REG -
- HAL_SEQ_WCSS_UMAC_CE0_DST_REG;
+ s->reg_start[1] = HAL_SEQ_WCSS_UMAC_CE0_DST_REG(ab) + HAL_CE_DST_STATUS_RING_HP;
+ s->reg_size[0] = HAL_SEQ_WCSS_UMAC_CE1_DST_REG(ab) -
+ HAL_SEQ_WCSS_UMAC_CE0_DST_REG(ab);
+ s->reg_size[1] = HAL_SEQ_WCSS_UMAC_CE1_DST_REG(ab) -
+ HAL_SEQ_WCSS_UMAC_CE0_DST_REG(ab);
s = &hal->srng_config[HAL_WBM_IDLE_LINK];
s->reg_start[0] = HAL_SEQ_WCSS_UMAC_WBM_REG + HAL_WBM_IDLE_LINK_RING_BASE_LSB(ab);
@@ -736,7 +738,6 @@ const struct hal_rx_ops hal_rx_qcn9274_ops = {
.rx_desc_is_da_mcbc = ath12k_hw_qcn9274_rx_desc_is_da_mcbc,
.rx_desc_get_dot11_hdr = ath12k_hw_qcn9274_rx_desc_get_dot11_hdr,
.rx_desc_get_crypto_header = ath12k_hw_qcn9274_rx_desc_get_crypto_hdr,
- .rx_desc_get_mpdu_frame_ctl = ath12k_hw_qcn9274_rx_desc_get_mpdu_frame_ctl,
.dp_rx_h_msdu_done = ath12k_hw_qcn9274_dp_rx_h_msdu_done,
.dp_rx_h_l4_cksum_fail = ath12k_hw_qcn9274_dp_rx_h_l4_cksum_fail,
.dp_rx_h_ip_cksum_fail = ath12k_hw_qcn9274_dp_rx_h_ip_cksum_fail,
@@ -908,8 +909,8 @@ static u8 *ath12k_hw_qcn9274_compact_rx_desc_mpdu_start_addr2(struct hal_rx_desc
static bool ath12k_hw_qcn9274_compact_rx_desc_is_da_mcbc(struct hal_rx_desc *desc)
{
- return __le32_to_cpu(desc->u.qcn9274_compact.mpdu_start.info6) &
- RX_MPDU_START_INFO6_MCAST_BCAST;
+ return __le16_to_cpu(desc->u.qcn9274_compact.msdu_end.info5) &
+ RX_MSDU_END_INFO5_DA_IS_MCBC;
}
static void ath12k_hw_qcn9274_compact_rx_desc_get_dot11_hdr(struct hal_rx_desc *desc,
@@ -975,11 +976,6 @@ ath12k_hw_qcn9274_compact_rx_desc_get_crypto_hdr(struct hal_rx_desc *desc,
HAL_RX_MPDU_INFO_PN_GET_BYTE2(desc->u.qcn9274_compact.mpdu_start.pn[1]);
}
-static u16 ath12k_hw_qcn9274_compact_rx_desc_get_mpdu_frame_ctl(struct hal_rx_desc *desc)
-{
- return __le16_to_cpu(desc->u.qcn9274_compact.mpdu_start.frame_ctrl);
-}
-
static bool ath12k_hw_qcn9274_compact_dp_rx_h_msdu_done(struct hal_rx_desc *desc)
{
return !!le32_get_bits(desc->u.qcn9274_compact.msdu_end.info14,
@@ -1080,8 +1076,6 @@ const struct hal_rx_ops hal_rx_qcn9274_compact_ops = {
.rx_desc_is_da_mcbc = ath12k_hw_qcn9274_compact_rx_desc_is_da_mcbc,
.rx_desc_get_dot11_hdr = ath12k_hw_qcn9274_compact_rx_desc_get_dot11_hdr,
.rx_desc_get_crypto_header = ath12k_hw_qcn9274_compact_rx_desc_get_crypto_hdr,
- .rx_desc_get_mpdu_frame_ctl =
- ath12k_hw_qcn9274_compact_rx_desc_get_mpdu_frame_ctl,
.dp_rx_h_msdu_done = ath12k_hw_qcn9274_compact_dp_rx_h_msdu_done,
.dp_rx_h_l4_cksum_fail = ath12k_hw_qcn9274_compact_dp_rx_h_l4_cksum_fail,
.dp_rx_h_ip_cksum_fail = ath12k_hw_qcn9274_compact_dp_rx_h_ip_cksum_fail,
@@ -1330,11 +1324,6 @@ static void ath12k_hw_wcn7850_rx_desc_get_crypto_hdr(struct hal_rx_desc *desc,
crypto_hdr[7] = HAL_RX_MPDU_INFO_PN_GET_BYTE2(desc->u.wcn7850.mpdu_start.pn[1]);
}
-static u16 ath12k_hw_wcn7850_rx_desc_get_mpdu_frame_ctl(struct hal_rx_desc *desc)
-{
- return __le16_to_cpu(desc->u.wcn7850.mpdu_start.frame_ctrl);
-}
-
static int ath12k_hal_srng_create_config_wcn7850(struct ath12k_base *ab)
{
struct ath12k_hal *hal = &ab->hal;
@@ -1371,9 +1360,9 @@ static int ath12k_hal_srng_create_config_wcn7850(struct ath12k_base *ab)
s = &hal->srng_config[HAL_TCL_DATA];
s->max_rings = 5;
- s->reg_start[0] = HAL_SEQ_WCSS_UMAC_TCL_REG + HAL_TCL1_RING_BASE_LSB;
+ s->reg_start[0] = HAL_SEQ_WCSS_UMAC_TCL_REG + HAL_TCL1_RING_BASE_LSB(ab);
s->reg_start[1] = HAL_SEQ_WCSS_UMAC_TCL_REG + HAL_TCL1_RING_HP;
- s->reg_size[0] = HAL_TCL2_RING_BASE_LSB - HAL_TCL1_RING_BASE_LSB;
+ s->reg_size[0] = HAL_TCL2_RING_BASE_LSB(ab) - HAL_TCL1_RING_BASE_LSB(ab);
s->reg_size[1] = HAL_TCL2_RING_HP - HAL_TCL1_RING_HP;
s = &hal->srng_config[HAL_TCL_CMD];
@@ -1386,31 +1375,31 @@ static int ath12k_hal_srng_create_config_wcn7850(struct ath12k_base *ab)
s = &hal->srng_config[HAL_CE_SRC];
s->max_rings = 12;
- s->reg_start[0] = HAL_SEQ_WCSS_UMAC_CE0_SRC_REG + HAL_CE_DST_RING_BASE_LSB;
- s->reg_start[1] = HAL_SEQ_WCSS_UMAC_CE0_SRC_REG + HAL_CE_DST_RING_HP;
- s->reg_size[0] = HAL_SEQ_WCSS_UMAC_CE1_SRC_REG -
- HAL_SEQ_WCSS_UMAC_CE0_SRC_REG;
- s->reg_size[1] = HAL_SEQ_WCSS_UMAC_CE1_SRC_REG -
- HAL_SEQ_WCSS_UMAC_CE0_SRC_REG;
+ s->reg_start[0] = HAL_SEQ_WCSS_UMAC_CE0_SRC_REG(ab) + HAL_CE_DST_RING_BASE_LSB;
+ s->reg_start[1] = HAL_SEQ_WCSS_UMAC_CE0_SRC_REG(ab) + HAL_CE_DST_RING_HP;
+ s->reg_size[0] = HAL_SEQ_WCSS_UMAC_CE1_SRC_REG(ab) -
+ HAL_SEQ_WCSS_UMAC_CE0_SRC_REG(ab);
+ s->reg_size[1] = HAL_SEQ_WCSS_UMAC_CE1_SRC_REG(ab) -
+ HAL_SEQ_WCSS_UMAC_CE0_SRC_REG(ab);
s = &hal->srng_config[HAL_CE_DST];
s->max_rings = 12;
- s->reg_start[0] = HAL_SEQ_WCSS_UMAC_CE0_DST_REG + HAL_CE_DST_RING_BASE_LSB;
- s->reg_start[1] = HAL_SEQ_WCSS_UMAC_CE0_DST_REG + HAL_CE_DST_RING_HP;
- s->reg_size[0] = HAL_SEQ_WCSS_UMAC_CE1_DST_REG -
- HAL_SEQ_WCSS_UMAC_CE0_DST_REG;
- s->reg_size[1] = HAL_SEQ_WCSS_UMAC_CE1_DST_REG -
- HAL_SEQ_WCSS_UMAC_CE0_DST_REG;
+ s->reg_start[0] = HAL_SEQ_WCSS_UMAC_CE0_DST_REG(ab) + HAL_CE_DST_RING_BASE_LSB;
+ s->reg_start[1] = HAL_SEQ_WCSS_UMAC_CE0_DST_REG(ab) + HAL_CE_DST_RING_HP;
+ s->reg_size[0] = HAL_SEQ_WCSS_UMAC_CE1_DST_REG(ab) -
+ HAL_SEQ_WCSS_UMAC_CE0_DST_REG(ab);
+ s->reg_size[1] = HAL_SEQ_WCSS_UMAC_CE1_DST_REG(ab) -
+ HAL_SEQ_WCSS_UMAC_CE0_DST_REG(ab);
s = &hal->srng_config[HAL_CE_DST_STATUS];
s->max_rings = 12;
- s->reg_start[0] = HAL_SEQ_WCSS_UMAC_CE0_DST_REG +
+ s->reg_start[0] = HAL_SEQ_WCSS_UMAC_CE0_DST_REG(ab) +
HAL_CE_DST_STATUS_RING_BASE_LSB;
- s->reg_start[1] = HAL_SEQ_WCSS_UMAC_CE0_DST_REG + HAL_CE_DST_STATUS_RING_HP;
- s->reg_size[0] = HAL_SEQ_WCSS_UMAC_CE1_DST_REG -
- HAL_SEQ_WCSS_UMAC_CE0_DST_REG;
- s->reg_size[1] = HAL_SEQ_WCSS_UMAC_CE1_DST_REG -
- HAL_SEQ_WCSS_UMAC_CE0_DST_REG;
+ s->reg_start[1] = HAL_SEQ_WCSS_UMAC_CE0_DST_REG(ab) + HAL_CE_DST_STATUS_RING_HP;
+ s->reg_size[0] = HAL_SEQ_WCSS_UMAC_CE1_DST_REG(ab) -
+ HAL_SEQ_WCSS_UMAC_CE0_DST_REG(ab);
+ s->reg_size[1] = HAL_SEQ_WCSS_UMAC_CE1_DST_REG(ab) -
+ HAL_SEQ_WCSS_UMAC_CE0_DST_REG(ab);
s = &hal->srng_config[HAL_WBM_IDLE_LINK];
s->reg_start[0] = HAL_SEQ_WCSS_UMAC_WBM_REG + HAL_WBM_IDLE_LINK_RING_BASE_LSB(ab);
@@ -1555,7 +1544,6 @@ const struct hal_rx_ops hal_rx_wcn7850_ops = {
.rx_desc_is_da_mcbc = ath12k_hw_wcn7850_rx_desc_is_da_mcbc,
.rx_desc_get_dot11_hdr = ath12k_hw_wcn7850_rx_desc_get_dot11_hdr,
.rx_desc_get_crypto_header = ath12k_hw_wcn7850_rx_desc_get_crypto_hdr,
- .rx_desc_get_mpdu_frame_ctl = ath12k_hw_wcn7850_rx_desc_get_mpdu_frame_ctl,
.dp_rx_h_msdu_done = ath12k_hw_wcn7850_dp_rx_h_msdu_done,
.dp_rx_h_l4_cksum_fail = ath12k_hw_wcn7850_dp_rx_h_l4_cksum_fail,
.dp_rx_h_ip_cksum_fail = ath12k_hw_wcn7850_dp_rx_h_ip_cksum_fail,
@@ -1756,7 +1744,7 @@ static void ath12k_hal_srng_src_hw_init(struct ath12k_base *ab,
HAL_TCL1_RING_BASE_MSB_RING_BASE_ADDR_MSB) |
u32_encode_bits((srng->entry_size * srng->num_entries),
HAL_TCL1_RING_BASE_MSB_RING_SIZE);
- ath12k_hif_write32(ab, reg_base + HAL_TCL1_RING_BASE_MSB_OFFSET, val);
+ ath12k_hif_write32(ab, reg_base + HAL_TCL1_RING_BASE_MSB_OFFSET(ab), val);
val = u32_encode_bits(srng->entry_size, HAL_REO1_RING_ID_ENTRY_SIZE);
ath12k_hif_write32(ab, reg_base + HAL_TCL1_RING_ID_OFFSET(ab), val);
@@ -1962,7 +1950,7 @@ u32 ath12k_hal_ce_dst_status_get_length(struct hal_ce_srng_dst_status_desc *desc
{
u32 len;
- len = le32_get_bits(desc->flags, HAL_CE_DST_STATUS_DESC_FLAGS_LEN);
+ len = le32_get_bits(READ_ONCE(desc->flags), HAL_CE_DST_STATUS_DESC_FLAGS_LEN);
desc->flags &= ~cpu_to_le32(HAL_CE_DST_STATUS_DESC_FLAGS_LEN);
return len;
@@ -2054,6 +2042,24 @@ int ath12k_hal_srng_src_num_free(struct ath12k_base *ab, struct hal_srng *srng,
return ((srng->ring_size - hp + tp) / srng->entry_size) - 1;
}
+void *ath12k_hal_srng_src_next_peek(struct ath12k_base *ab,
+ struct hal_srng *srng)
+{
+ void *desc;
+ u32 next_hp;
+
+ lockdep_assert_held(&srng->lock);
+
+ next_hp = (srng->u.src_ring.hp + srng->entry_size) % srng->ring_size;
+
+ if (next_hp == srng->u.src_ring.cached_tp)
+ return NULL;
+
+ desc = srng->ring_base_vaddr + next_hp;
+
+ return desc;
+}
+
void *ath12k_hal_srng_src_get_next_entry(struct ath12k_base *ab,
struct hal_srng *srng)
{
@@ -2087,6 +2093,17 @@ void *ath12k_hal_srng_src_get_next_entry(struct ath12k_base *ab,
return desc;
}
+void *ath12k_hal_srng_src_peek(struct ath12k_base *ab, struct hal_srng *srng)
+{
+ lockdep_assert_held(&srng->lock);
+
+ if (((srng->u.src_ring.hp + srng->entry_size) % srng->ring_size) ==
+ srng->u.src_ring.cached_tp)
+ return NULL;
+
+ return srng->ring_base_vaddr + srng->u.src_ring.hp;
+}
+
void *ath12k_hal_srng_src_reap_next(struct ath12k_base *ab,
struct hal_srng *srng)
{
@@ -2132,7 +2149,7 @@ void ath12k_hal_srng_access_begin(struct ath12k_base *ab, struct hal_srng *srng)
srng->u.src_ring.cached_tp =
*(volatile u32 *)srng->u.src_ring.tp_addr;
else
- srng->u.dst_ring.cached_hp = *srng->u.dst_ring.hp_addr;
+ srng->u.dst_ring.cached_hp = READ_ONCE(*srng->u.dst_ring.hp_addr);
}
/* Update cached ring head/tail pointers to HW. ath12k_hal_srng_access_begin()
diff --git a/drivers/net/wireless/ath/ath12k/hal.h b/drivers/net/wireless/ath/ath12k/hal.h
index 94e2e8735958..0ee9c6b26dab 100644
--- a/drivers/net/wireless/ath/ath12k/hal.h
+++ b/drivers/net/wireless/ath/ath12k/hal.h
@@ -1,7 +1,7 @@
/* SPDX-License-Identifier: BSD-3-Clause-Clear */
/*
* Copyright (c) 2018-2021 The Linux Foundation. All rights reserved.
- * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#ifndef ATH12K_HAL_H
@@ -11,6 +11,7 @@
#include "rx_desc.h"
struct ath12k_base;
+#define HAL_CE_REMAP_REG_BASE (ab->ce_remap_base_addr)
#define HAL_LINK_DESC_SIZE (32 << 2)
#define HAL_LINK_DESC_ALIGN 128
@@ -21,6 +22,7 @@ struct ath12k_base;
#define HAL_MAX_AVAIL_BLK_RES 3
#define HAL_RING_BASE_ALIGN 8
+#define HAL_REO_QLUT_ADDR_ALIGN 256
#define HAL_WBM_IDLE_SCATTER_BUF_SIZE_MAX 32704
/* TODO: Check with hw team on the supported scatter buf size */
@@ -39,15 +41,21 @@ struct ath12k_base;
#define HAL_OFFSET_FROM_HP_TO_TP 4
#define HAL_SHADOW_REG(x) (HAL_SHADOW_BASE_ADDR + (4 * (x)))
+#define HAL_REO_QDESC_MAX_PEERID 8191
/* WCSS Relative address */
+#define HAL_SEQ_WCSS_CMEM_OFFSET 0x00100000
#define HAL_SEQ_WCSS_UMAC_OFFSET 0x00a00000
#define HAL_SEQ_WCSS_UMAC_REO_REG 0x00a38000
#define HAL_SEQ_WCSS_UMAC_TCL_REG 0x00a44000
-#define HAL_SEQ_WCSS_UMAC_CE0_SRC_REG 0x01b80000
-#define HAL_SEQ_WCSS_UMAC_CE0_DST_REG 0x01b81000
-#define HAL_SEQ_WCSS_UMAC_CE1_SRC_REG 0x01b82000
-#define HAL_SEQ_WCSS_UMAC_CE1_DST_REG 0x01b83000
+#define HAL_SEQ_WCSS_UMAC_CE0_SRC_REG(ab) \
+ ((ab)->hw_params->regs->hal_umac_ce0_src_reg_base)
+#define HAL_SEQ_WCSS_UMAC_CE0_DST_REG(ab) \
+ ((ab)->hw_params->regs->hal_umac_ce0_dest_reg_base)
+#define HAL_SEQ_WCSS_UMAC_CE1_SRC_REG(ab) \
+ ((ab)->hw_params->regs->hal_umac_ce1_src_reg_base)
+#define HAL_SEQ_WCSS_UMAC_CE1_DST_REG(ab) \
+ ((ab)->hw_params->regs->hal_umac_ce1_dest_reg_base)
#define HAL_SEQ_WCSS_UMAC_WBM_REG 0x00a34000
#define HAL_CE_WFSS_CE_REG_BASE 0x01b80000
@@ -57,8 +65,10 @@ struct ath12k_base;
/* SW2TCL(x) R0 ring configuration address */
#define HAL_TCL1_RING_CMN_CTRL_REG 0x00000020
#define HAL_TCL1_RING_DSCP_TID_MAP 0x00000240
-#define HAL_TCL1_RING_BASE_LSB 0x00000900
-#define HAL_TCL1_RING_BASE_MSB 0x00000904
+#define HAL_TCL1_RING_BASE_LSB(ab) \
+ ((ab)->hw_params->regs->hal_tcl1_ring_base_lsb)
+#define HAL_TCL1_RING_BASE_MSB(ab) \
+ ((ab)->hw_params->regs->hal_tcl1_ring_base_msb)
#define HAL_TCL1_RING_ID(ab) ((ab)->hw_params->regs->hal_tcl1_ring_id)
#define HAL_TCL1_RING_MISC(ab) \
((ab)->hw_params->regs->hal_tcl1_ring_misc)
@@ -76,30 +86,31 @@ struct ath12k_base;
((ab)->hw_params->regs->hal_tcl1_ring_msi1_base_msb)
#define HAL_TCL1_RING_MSI1_DATA(ab) \
((ab)->hw_params->regs->hal_tcl1_ring_msi1_data)
-#define HAL_TCL2_RING_BASE_LSB 0x00000978
+#define HAL_TCL2_RING_BASE_LSB(ab) \
+ ((ab)->hw_params->regs->hal_tcl2_ring_base_lsb)
#define HAL_TCL_RING_BASE_LSB(ab) \
((ab)->hw_params->regs->hal_tcl_ring_base_lsb)
-#define HAL_TCL1_RING_MSI1_BASE_LSB_OFFSET(ab) \
- (HAL_TCL1_RING_MSI1_BASE_LSB(ab) - HAL_TCL1_RING_BASE_LSB)
-#define HAL_TCL1_RING_MSI1_BASE_MSB_OFFSET(ab) \
- (HAL_TCL1_RING_MSI1_BASE_MSB(ab) - HAL_TCL1_RING_BASE_LSB)
-#define HAL_TCL1_RING_MSI1_DATA_OFFSET(ab) \
- (HAL_TCL1_RING_MSI1_DATA(ab) - HAL_TCL1_RING_BASE_LSB)
-#define HAL_TCL1_RING_BASE_MSB_OFFSET \
- (HAL_TCL1_RING_BASE_MSB - HAL_TCL1_RING_BASE_LSB)
-#define HAL_TCL1_RING_ID_OFFSET(ab) \
- (HAL_TCL1_RING_ID(ab) - HAL_TCL1_RING_BASE_LSB)
-#define HAL_TCL1_RING_CONSR_INT_SETUP_IX0_OFFSET(ab) \
- (HAL_TCL1_RING_CONSUMER_INT_SETUP_IX0(ab) - HAL_TCL1_RING_BASE_LSB)
-#define HAL_TCL1_RING_CONSR_INT_SETUP_IX1_OFFSET(ab) \
- (HAL_TCL1_RING_CONSUMER_INT_SETUP_IX1(ab) - HAL_TCL1_RING_BASE_LSB)
-#define HAL_TCL1_RING_TP_ADDR_LSB_OFFSET(ab) \
- (HAL_TCL1_RING_TP_ADDR_LSB(ab) - HAL_TCL1_RING_BASE_LSB)
-#define HAL_TCL1_RING_TP_ADDR_MSB_OFFSET(ab) \
- (HAL_TCL1_RING_TP_ADDR_MSB(ab) - HAL_TCL1_RING_BASE_LSB)
-#define HAL_TCL1_RING_MISC_OFFSET(ab) \
- (HAL_TCL1_RING_MISC(ab) - HAL_TCL1_RING_BASE_LSB)
+#define HAL_TCL1_RING_MSI1_BASE_LSB_OFFSET(ab) ({ typeof(ab) _ab = (ab); \
+ (HAL_TCL1_RING_MSI1_BASE_LSB(_ab) - HAL_TCL1_RING_BASE_LSB(_ab)); })
+#define HAL_TCL1_RING_MSI1_BASE_MSB_OFFSET(ab) ({ typeof(ab) _ab = (ab); \
+ (HAL_TCL1_RING_MSI1_BASE_MSB(_ab) - HAL_TCL1_RING_BASE_LSB(_ab)); })
+#define HAL_TCL1_RING_MSI1_DATA_OFFSET(ab) ({ typeof(ab) _ab = (ab); \
+ (HAL_TCL1_RING_MSI1_DATA(_ab) - HAL_TCL1_RING_BASE_LSB(_ab)); })
+#define HAL_TCL1_RING_BASE_MSB_OFFSET(ab) ({ typeof(ab) _ab = (ab); \
+ (HAL_TCL1_RING_BASE_MSB(_ab) - HAL_TCL1_RING_BASE_LSB(_ab)); })
+#define HAL_TCL1_RING_ID_OFFSET(ab) ({ typeof(ab) _ab = (ab); \
+ (HAL_TCL1_RING_ID(_ab) - HAL_TCL1_RING_BASE_LSB(_ab)); })
+#define HAL_TCL1_RING_CONSR_INT_SETUP_IX0_OFFSET(ab) ({ typeof(ab) _ab = (ab); \
+ (HAL_TCL1_RING_CONSUMER_INT_SETUP_IX0(_ab) - HAL_TCL1_RING_BASE_LSB(_ab)); })
+#define HAL_TCL1_RING_CONSR_INT_SETUP_IX1_OFFSET(ab) ({ typeof(ab) _ab = (ab); \
+ (HAL_TCL1_RING_CONSUMER_INT_SETUP_IX1(_ab) - HAL_TCL1_RING_BASE_LSB(_ab)); })
+#define HAL_TCL1_RING_TP_ADDR_LSB_OFFSET(ab) ({ typeof(ab) _ab = (ab); \
+ (HAL_TCL1_RING_TP_ADDR_LSB(_ab) - HAL_TCL1_RING_BASE_LSB(_ab)); })
+#define HAL_TCL1_RING_TP_ADDR_MSB_OFFSET(ab) ({ typeof(ab) _ab = (ab); \
+ (HAL_TCL1_RING_TP_ADDR_MSB(_ab) - HAL_TCL1_RING_BASE_LSB(_ab)); })
+#define HAL_TCL1_RING_MISC_OFFSET(ab) ({ typeof(ab) _ab = (ab); \
+ (HAL_TCL1_RING_MISC(_ab) - HAL_TCL1_RING_BASE_LSB(_ab)); })
/* SW2TCL(x) R2 ring pointers (head/tail) address */
#define HAL_TCL1_RING_HP 0x00002000
@@ -132,6 +143,8 @@ struct ath12k_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_QDESC_ADDR(ab) ((ab)->hw_params->regs->hal_reo1_qdesc_addr)
+#define HAL_REO1_QDESC_MAX_PEERID(ab) ((ab)->hw_params->regs->hal_reo1_qdesc_max_peerid)
#define HAL_REO1_SW_COOKIE_CFG0(ab) ((ab)->hw_params->regs->hal_reo1_sw_cookie_cfg0)
#define HAL_REO1_SW_COOKIE_CFG1(ab) ((ab)->hw_params->regs->hal_reo1_sw_cookie_cfg1)
#define HAL_REO1_QDESC_LUT_BASE0(ab) ((ab)->hw_params->regs->hal_reo1_qdesc_lut_base0)
@@ -319,6 +332,8 @@ struct ath12k_base;
#define HAL_REO1_SW_COOKIE_CFG_ALIGN BIT(18)
#define HAL_REO1_SW_COOKIE_CFG_ENABLE BIT(19)
#define HAL_REO1_SW_COOKIE_CFG_GLOBAL_ENABLE BIT(20)
+#define HAL_REO_QDESC_ADDR_READ_LUT_ENABLE BIT(7)
+#define HAL_REO_QDESC_ADDR_READ_CLEAR_QDESC_ARRAY BIT(6)
/* CE ring bit field mask and shift */
#define HAL_CE_DST_R0_DEST_CTRL_MAX_LEN GENMASK(15, 0)
@@ -365,6 +380,9 @@ struct ath12k_base;
* ath12k_hal_rx_desc_get_err().
*/
+#define HAL_IPQ5332_CE_WFSS_REG_BASE 0x740000
+#define HAL_IPQ5332_CE_SIZE 0x100000
+
enum hal_srng_ring_id {
HAL_SRNG_RING_ID_REO2SW0 = 0,
HAL_SRNG_RING_ID_REO2SW1,
@@ -480,6 +498,7 @@ enum hal_srng_ring_id {
HAL_SRNG_RING_ID_WMAC1_SW2RXMON_BUF0 = HAL_SRNG_RING_ID_PMAC1_ID_START,
+ HAL_SRNG_RING_ID_WMAC1_SW2RXDMA1_STATBUF,
HAL_SRNG_RING_ID_WMAC1_RXDMA2SW0,
HAL_SRNG_RING_ID_WMAC1_RXDMA2SW1,
HAL_SRNG_RING_ID_WMAC1_RXMON2SW0 = HAL_SRNG_RING_ID_WMAC1_RXDMA2SW1,
@@ -1068,7 +1087,6 @@ struct hal_rx_ops {
bool (*rx_desc_is_da_mcbc)(struct hal_rx_desc *desc);
void (*rx_desc_get_dot11_hdr)(struct hal_rx_desc *desc,
struct ieee80211_hdr *hdr);
- u16 (*rx_desc_get_mpdu_frame_ctl)(struct hal_rx_desc *desc);
void (*rx_desc_get_crypto_header)(struct hal_rx_desc *desc,
u8 *crypto_hdr,
enum hal_encrypt_type enctype);
@@ -1126,6 +1144,7 @@ void ath12k_hal_srng_get_params(struct ath12k_base *ab, struct hal_srng *srng,
struct hal_srng_params *params);
void *ath12k_hal_srng_dst_get_next_entry(struct ath12k_base *ab,
struct hal_srng *srng);
+void *ath12k_hal_srng_src_peek(struct ath12k_base *ab, struct hal_srng *srng);
void *ath12k_hal_srng_dst_peek(struct ath12k_base *ab, struct hal_srng *srng);
int ath12k_hal_srng_dst_num_free(struct ath12k_base *ab, struct hal_srng *srng,
bool sync_hw_ptr);
@@ -1133,6 +1152,8 @@ void *ath12k_hal_srng_src_get_next_reaped(struct ath12k_base *ab,
struct hal_srng *srng);
void *ath12k_hal_srng_src_reap_next(struct ath12k_base *ab,
struct hal_srng *srng);
+void *ath12k_hal_srng_src_next_peek(struct ath12k_base *ab,
+ struct hal_srng *srng);
void *ath12k_hal_srng_src_get_next_entry(struct ath12k_base *ab,
struct hal_srng *srng);
int ath12k_hal_srng_src_num_free(struct ath12k_base *ab, struct hal_srng *srng,
@@ -1154,4 +1175,5 @@ int ath12k_hal_srng_update_shadow_config(struct ath12k_base *ab,
void ath12k_hal_srng_shadow_config(struct ath12k_base *ab);
void ath12k_hal_srng_shadow_update_hp_tp(struct ath12k_base *ab,
struct hal_srng *srng);
+void ath12k_hal_reo_shared_qaddr_cache_clear(struct ath12k_base *ab);
#endif
diff --git a/drivers/net/wireless/ath/ath12k/hal_desc.h b/drivers/net/wireless/ath/ath12k/hal_desc.h
index 7b0403d245e5..0173f731bfef 100644
--- a/drivers/net/wireless/ath/ath12k/hal_desc.h
+++ b/drivers/net/wireless/ath/ath12k/hal_desc.h
@@ -1,7 +1,7 @@
/* SPDX-License-Identifier: BSD-3-Clause-Clear */
/*
* Copyright (c) 2018-2021 The Linux Foundation. All rights reserved.
- * Copyright (c) 2021-2022, 2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) 2021-2022, 2024-2025 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#include "core.h"
@@ -707,7 +707,7 @@ enum hal_rx_msdu_desc_reo_dest_ind {
#define RX_MSDU_DESC_INFO0_DECAP_FORMAT GENMASK(30, 29)
#define HAL_RX_MSDU_PKT_LENGTH_GET(val) \
- (u32_get_bits((val), RX_MSDU_DESC_INFO0_MSDU_LENGTH))
+ (le32_get_bits((val), RX_MSDU_DESC_INFO0_MSDU_LENGTH))
struct rx_msdu_desc {
__le32 info0;
@@ -1008,6 +1008,10 @@ enum hal_reo_entr_rxdma_ecode {
HAL_REO_ENTR_RING_RXDMA_ECODE_FLOW_TIMEOUT_ERR,
HAL_REO_ENTR_RING_RXDMA_ECODE_FLUSH_REQUEST_ERR,
HAL_REO_ENTR_RING_RXDMA_ECODE_AMSDU_FRAG_ERR,
+ HAL_REO_ENTR_RING_RXDMA_ECODE_MULTICAST_ECHO_ERR,
+ HAL_REO_ENTR_RING_RXDMA_ECODE_AMSDU_MISMATCH_ERR,
+ HAL_REO_ENTR_RING_RXDMA_ECODE_UNAUTH_WDS_ERR,
+ HAL_REO_ENTR_RING_RXDMA_ECODE_GRPCAST_AMSDU_WDS_ERR,
HAL_REO_ENTR_RING_RXDMA_ECODE_MAX,
};
@@ -1284,11 +1288,13 @@ enum hal_tcl_encap_type {
HAL_TCL_ENCAP_TYPE_NATIVE_WIFI,
HAL_TCL_ENCAP_TYPE_ETHERNET,
HAL_TCL_ENCAP_TYPE_802_3 = 3,
+ HAL_TCL_ENCAP_TYPE_MAX
};
enum hal_tcl_desc_type {
HAL_TCL_DESC_TYPE_BUFFER,
HAL_TCL_DESC_TYPE_EXT_DESC,
+ HAL_TCL_DESC_TYPE_MAX,
};
enum hal_wbm_htt_tx_comp_status {
@@ -1298,6 +1304,7 @@ enum hal_wbm_htt_tx_comp_status {
HAL_WBM_REL_HTT_TX_COMP_STATUS_REINJ,
HAL_WBM_REL_HTT_TX_COMP_STATUS_INSPECT,
HAL_WBM_REL_HTT_TX_COMP_STATUS_MEC_NOTIFY,
+ HAL_WBM_REL_HTT_TX_COMP_STATUS_VDEVID_MISMATCH,
HAL_WBM_REL_HTT_TX_COMP_STATUS_MAX,
};
@@ -1806,6 +1813,7 @@ enum hal_wbm_rel_src_module {
HAL_WBM_REL_SRC_MODULE_REO,
HAL_WBM_REL_SRC_MODULE_FW,
HAL_WBM_REL_SRC_MODULE_SW,
+ HAL_WBM_REL_SRC_MODULE_MAX,
};
enum hal_wbm_rel_desc_type {
@@ -1998,6 +2006,7 @@ struct hal_wbm_release_ring_cc_rx {
#define HAL_WBM_RELEASE_INFO3_CONTINUATION BIT(2)
#define HAL_WBM_RELEASE_INFO5_LOOPING_COUNT GENMASK(31, 28)
+#define HAL_ENCRYPT_TYPE_MAX 12
struct hal_wbm_release_ring {
struct ath12k_buffer_addr buf_addr_info;
@@ -2968,9 +2977,8 @@ struct hal_mon_buf_ring {
#define HAL_MON_DEST_COOKIE_BUF_ID GENMASK(17, 0)
-#define HAL_MON_DEST_INFO0_END_OFFSET GENMASK(15, 0)
-#define HAL_MON_DEST_INFO0_FLUSH_DETECTED BIT(16)
-#define HAL_MON_DEST_INFO0_END_OF_PPDU BIT(17)
+#define HAL_MON_DEST_INFO0_END_OFFSET GENMASK(11, 0)
+#define HAL_MON_DEST_INFO0_END_REASON GENMASK(17, 16)
#define HAL_MON_DEST_INFO0_INITIATOR BIT(18)
#define HAL_MON_DEST_INFO0_EMPTY_DESC BIT(19)
#define HAL_MON_DEST_INFO0_RING_ID GENMASK(27, 20)
diff --git a/drivers/net/wireless/ath/ath12k/hal_rx.c b/drivers/net/wireless/ath/ath12k/hal_rx.c
index ac17d6223fa7..48aa48c48606 100644
--- a/drivers/net/wireless/ath/ath12k/hal_rx.c
+++ b/drivers/net/wireless/ath/ath12k/hal_rx.c
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: BSD-3-Clause-Clear
/*
* Copyright (c) 2018-2021 The Linux Foundation. All rights reserved.
- * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#include "debug.h"
@@ -326,7 +326,7 @@ int ath12k_hal_desc_reo_parse_err(struct ath12k_base *ab,
HAL_REO_DEST_RING_INFO0_PUSH_REASON);
err_code = le32_get_bits(desc->info0,
HAL_REO_DEST_RING_INFO0_ERROR_CODE);
- ab->soc_stats.reo_error[err_code]++;
+ ab->device_stats.reo_error[err_code]++;
if (push_reason != HAL_REO_DEST_RING_PUSH_REASON_ERR_DETECTED &&
push_reason != HAL_REO_DEST_RING_PUSH_REASON_ROUTING_INSTRUCTION) {
@@ -381,7 +381,7 @@ int ath12k_hal_wbm_desc_parse_err(struct ath12k_base *ab, void *desc,
val = le32_get_bits(wbm_desc->buf_addr_info.info1,
BUFFER_ADDR_INFO1_RET_BUF_MGR);
if (val != HAL_RX_BUF_RBM_SW3_BM) {
- ab->soc_stats.invalid_rbm++;
+ ab->device_stats.invalid_rbm++;
return -EINVAL;
}
@@ -393,7 +393,7 @@ int ath12k_hal_wbm_desc_parse_err(struct ath12k_base *ab, void *desc,
val = le32_get_bits(wbm_cc_desc->info0,
HAL_WBM_RELEASE_RX_CC_INFO0_RBM);
if (val != HAL_RX_BUF_RBM_SW3_BM) {
- ab->soc_stats.invalid_rbm++;
+ ab->device_stats.invalid_rbm++;
return -EINVAL;
}
@@ -446,17 +446,97 @@ void ath12k_hal_rx_reo_ent_paddr_get(struct ath12k_base *ab,
*cookie = le32_get_bits(buff_addr->info1, BUFFER_ADDR_INFO1_SW_COOKIE);
}
+void ath12k_hal_rx_reo_ent_buf_paddr_get(void *rx_desc, dma_addr_t *paddr,
+ u32 *sw_cookie,
+ struct ath12k_buffer_addr **pp_buf_addr,
+ u8 *rbm, u32 *msdu_cnt)
+{
+ struct hal_reo_entrance_ring *reo_ent_ring =
+ (struct hal_reo_entrance_ring *)rx_desc;
+ struct ath12k_buffer_addr *buf_addr_info;
+ struct rx_mpdu_desc *rx_mpdu_desc_info_details;
+
+ rx_mpdu_desc_info_details =
+ (struct rx_mpdu_desc *)&reo_ent_ring->rx_mpdu_info;
+
+ *msdu_cnt = le32_get_bits(rx_mpdu_desc_info_details->info0,
+ RX_MPDU_DESC_INFO0_MSDU_COUNT);
+
+ buf_addr_info = (struct ath12k_buffer_addr *)&reo_ent_ring->buf_addr_info;
+
+ *paddr = (((u64)le32_get_bits(buf_addr_info->info1,
+ BUFFER_ADDR_INFO1_ADDR)) << 32) |
+ le32_get_bits(buf_addr_info->info0,
+ BUFFER_ADDR_INFO0_ADDR);
+
+ *sw_cookie = le32_get_bits(buf_addr_info->info1,
+ BUFFER_ADDR_INFO1_SW_COOKIE);
+ *rbm = le32_get_bits(buf_addr_info->info1,
+ BUFFER_ADDR_INFO1_RET_BUF_MGR);
+
+ *pp_buf_addr = (void *)buf_addr_info;
+}
+
+void ath12k_hal_rx_msdu_list_get(struct ath12k *ar,
+ struct hal_rx_msdu_link *link_desc,
+ struct hal_rx_msdu_list *msdu_list,
+ u16 *num_msdus)
+{
+ struct hal_rx_msdu_details *msdu_details = NULL;
+ struct rx_msdu_desc *msdu_desc_info = NULL;
+ u32 last = 0, first = 0;
+ u8 tmp = 0;
+ int i;
+
+ last = u32_encode_bits(last, RX_MSDU_DESC_INFO0_LAST_MSDU_IN_MPDU);
+ first = u32_encode_bits(first, RX_MSDU_DESC_INFO0_FIRST_MSDU_IN_MPDU);
+ msdu_details = &link_desc->msdu_link[0];
+
+ for (i = 0; i < HAL_RX_NUM_MSDU_DESC; i++) {
+ if (!i && le32_get_bits(msdu_details[i].buf_addr_info.info0,
+ BUFFER_ADDR_INFO0_ADDR) == 0)
+ break;
+ if (le32_get_bits(msdu_details[i].buf_addr_info.info0,
+ BUFFER_ADDR_INFO0_ADDR) == 0) {
+ msdu_desc_info = &msdu_details[i - 1].rx_msdu_info;
+ msdu_desc_info->info0 |= cpu_to_le32(last);
+ break;
+ }
+ msdu_desc_info = &msdu_details[i].rx_msdu_info;
+
+ if (!i)
+ msdu_desc_info->info0 |= cpu_to_le32(first);
+ else if (i == (HAL_RX_NUM_MSDU_DESC - 1))
+ msdu_desc_info->info0 |= cpu_to_le32(last);
+ msdu_list->msdu_info[i].msdu_flags = le32_to_cpu(msdu_desc_info->info0);
+ msdu_list->msdu_info[i].msdu_len =
+ HAL_RX_MSDU_PKT_LENGTH_GET(msdu_desc_info->info0);
+ msdu_list->sw_cookie[i] =
+ le32_get_bits(msdu_details[i].buf_addr_info.info1,
+ BUFFER_ADDR_INFO1_SW_COOKIE);
+ tmp = le32_get_bits(msdu_details[i].buf_addr_info.info1,
+ BUFFER_ADDR_INFO1_RET_BUF_MGR);
+ msdu_list->paddr[i] =
+ ((u64)(le32_get_bits(msdu_details[i].buf_addr_info.info1,
+ BUFFER_ADDR_INFO1_ADDR)) << 32) |
+ le32_get_bits(msdu_details[i].buf_addr_info.info0,
+ BUFFER_ADDR_INFO0_ADDR);
+ msdu_list->rbm[i] = tmp;
+ }
+ *num_msdus = i;
+}
+
void ath12k_hal_rx_msdu_link_desc_set(struct ath12k_base *ab,
- struct hal_wbm_release_ring *dst_desc,
- struct hal_wbm_release_ring *src_desc,
+ struct hal_wbm_release_ring *desc,
+ struct ath12k_buffer_addr *buf_addr_info,
enum hal_wbm_rel_bm_act action)
{
- dst_desc->buf_addr_info = src_desc->buf_addr_info;
- dst_desc->info0 |= le32_encode_bits(HAL_WBM_REL_SRC_MODULE_SW,
- HAL_WBM_RELEASE_INFO0_REL_SRC_MODULE) |
- le32_encode_bits(action, HAL_WBM_RELEASE_INFO0_BM_ACTION) |
- le32_encode_bits(HAL_WBM_REL_DESC_TYPE_MSDU_LINK,
- HAL_WBM_RELEASE_INFO0_DESC_TYPE);
+ desc->buf_addr_info = *buf_addr_info;
+ desc->info0 |= le32_encode_bits(HAL_WBM_REL_SRC_MODULE_SW,
+ HAL_WBM_RELEASE_INFO0_REL_SRC_MODULE) |
+ le32_encode_bits(action, HAL_WBM_RELEASE_INFO0_BM_ACTION) |
+ le32_encode_bits(HAL_WBM_REL_DESC_TYPE_MSDU_LINK,
+ HAL_WBM_RELEASE_INFO0_DESC_TYPE);
}
void ath12k_hal_reo_status_queue_stats(struct ath12k_base *ab, struct hal_tlv_64_hdr *tlv,
@@ -851,3 +931,20 @@ void ath12k_hal_reo_hw_setup(struct ath12k_base *ab, u32 ring_hash_map)
ath12k_hif_write32(ab, reo_base + HAL_REO1_DEST_RING_CTRL_IX_3,
ring_hash_map);
}
+
+void ath12k_hal_reo_shared_qaddr_cache_clear(struct ath12k_base *ab)
+{
+ u32 val;
+
+ lockdep_assert_held(&ab->base_lock);
+ val = ath12k_hif_read32(ab, HAL_SEQ_WCSS_UMAC_REO_REG +
+ HAL_REO1_QDESC_ADDR(ab));
+
+ val |= u32_encode_bits(1, HAL_REO_QDESC_ADDR_READ_CLEAR_QDESC_ARRAY);
+ ath12k_hif_write32(ab, HAL_SEQ_WCSS_UMAC_REO_REG +
+ HAL_REO1_QDESC_ADDR(ab), val);
+
+ val &= ~HAL_REO_QDESC_ADDR_READ_CLEAR_QDESC_ARRAY;
+ ath12k_hif_write32(ab, HAL_SEQ_WCSS_UMAC_REO_REG +
+ HAL_REO1_QDESC_ADDR(ab), val);
+}
diff --git a/drivers/net/wireless/ath/ath12k/hal_rx.h b/drivers/net/wireless/ath/ath12k/hal_rx.h
index b08aa2e79f41..a3ab588aae19 100644
--- a/drivers/net/wireless/ath/ath12k/hal_rx.h
+++ b/drivers/net/wireless/ath/ath12k/hal_rx.h
@@ -1,7 +1,7 @@
/* SPDX-License-Identifier: BSD-3-Clause-Clear */
/*
* Copyright (c) 2018-2021 The Linux Foundation. All rights reserved.
- * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#ifndef ATH12K_HAL_RX_H
@@ -22,9 +22,6 @@ struct hal_rx_wbm_rel_info {
#define HAL_INVALID_PEERID 0x3fff
#define VHT_SIG_SU_NSS_MASK 0x7
-#define HAL_RX_MAX_MCS 12
-#define HAL_RX_MAX_NSS 8
-
#define HAL_RX_MPDU_INFO_PN_GET_BYTE1(__val) \
le32_get_bits((__val), GENMASK(7, 0))
@@ -71,6 +68,8 @@ enum hal_rx_preamble {
HAL_RX_PREAMBLE_11N,
HAL_RX_PREAMBLE_11AC,
HAL_RX_PREAMBLE_11AX,
+ HAL_RX_PREAMBLE_11BA,
+ HAL_RX_PREAMBLE_11BE,
HAL_RX_PREAMBLE_MAX,
};
@@ -108,9 +107,13 @@ enum hal_rx_mon_status {
HAL_RX_MON_STATUS_PPDU_NOT_DONE,
HAL_RX_MON_STATUS_PPDU_DONE,
HAL_RX_MON_STATUS_BUF_DONE,
+ HAL_RX_MON_STATUS_BUF_ADDR,
+ HAL_RX_MON_STATUS_MPDU_START,
+ HAL_RX_MON_STATUS_MPDU_END,
+ HAL_RX_MON_STATUS_MSDU_END,
};
-#define HAL_RX_MAX_MPDU 256
+#define HAL_RX_MAX_MPDU 1024
#define HAL_RX_NUM_WORDS_PER_PPDU_BITMAP (HAL_RX_MAX_MPDU >> 5)
struct hal_rx_user_status {
@@ -143,10 +146,43 @@ struct hal_rx_user_status {
u32 mpdu_fcs_ok_bitmap[HAL_RX_NUM_WORDS_PER_PPDU_BITMAP];
u32 mpdu_ok_byte_count;
u32 mpdu_err_byte_count;
+ bool ampdu_present;
+ u16 ampdu_id;
};
#define HAL_MAX_UL_MU_USERS 37
+struct hal_rx_u_sig_info {
+ bool ul_dl;
+ u8 bw;
+ u8 ppdu_type_comp_mode;
+ u8 eht_sig_mcs;
+ u8 num_eht_sig_sym;
+ struct ieee80211_radiotap_eht_usig usig;
+};
+
+#define HAL_RX_MON_MAX_AGGR_SIZE 128
+
+struct hal_rx_tlv_aggr_info {
+ bool in_progress;
+ u16 cur_len;
+ u16 tlv_tag;
+ u8 buf[HAL_RX_MON_MAX_AGGR_SIZE];
+};
+
+struct hal_rx_radiotap_eht {
+ __le32 known;
+ __le32 data[9];
+};
+
+#define EHT_MAX_USER_INFO 4
+
+struct hal_rx_eht_info {
+ u8 num_user_info;
+ struct hal_rx_radiotap_eht eht;
+ u32 user_info[EHT_MAX_USER_INFO];
+};
+
struct hal_rx_mon_ppdu_info {
u32 ppdu_id;
u32 last_ppdu_id;
@@ -227,10 +263,15 @@ struct hal_rx_mon_ppdu_info {
u8 addr4[ETH_ALEN];
struct hal_rx_user_status userstats[HAL_MAX_UL_MU_USERS];
u8 userid;
- u16 ampdu_id[HAL_MAX_UL_MU_USERS];
bool first_msdu_in_mpdu;
bool is_ampdu;
u8 medium_prot_type;
+ bool ppdu_continuation;
+ bool eht_usig;
+ struct hal_rx_u_sig_info u_sig_info;
+ bool is_eht;
+ struct hal_rx_eht_info eht_info;
+ struct hal_rx_tlv_aggr_info tlv_aggr;
};
#define HAL_RX_PPDU_START_INFO0_PPDU_ID GENMASK(15, 0)
@@ -466,6 +507,18 @@ struct hal_rx_mpdu_start {
__le32 rsvd2[16];
} __packed;
+struct hal_rx_msdu_end {
+ __le32 info0;
+ __le32 rsvd0[9];
+ __le16 info00;
+ __le16 info01;
+ __le32 rsvd00[8];
+ __le32 info1;
+ __le32 rsvd1[10];
+ __le32 info2;
+ __le32 rsvd2;
+} __packed;
+
#define HAL_RX_PPDU_END_DURATION GENMASK(23, 0)
struct hal_rx_ppdu_end_duration {
__le32 rsvd0[9];
@@ -485,6 +538,7 @@ struct hal_rx_msdu_desc_info {
#define HAL_RX_NUM_MSDU_DESC 6
struct hal_rx_msdu_list {
struct hal_rx_msdu_desc_info msdu_info[HAL_RX_NUM_MSDU_DESC];
+ u64 paddr[HAL_RX_NUM_MSDU_DESC];
u32 sw_cookie[HAL_RX_NUM_MSDU_DESC];
u8 rbm[HAL_RX_NUM_MSDU_DESC];
};
@@ -641,6 +695,395 @@ struct hal_rx_resp_req_info {
#define HAL_RX_MPDU_ERR_MPDU_LEN BIT(6)
#define HAL_RX_MPDU_ERR_UNENCRYPTED_FRAME BIT(7)
+#define HAL_RX_PHY_CMN_USER_INFO0_GI GENMASK(17, 16)
+
+struct hal_phyrx_common_user_info {
+ __le32 rsvd[2];
+ __le32 info0;
+ __le32 rsvd1;
+} __packed;
+
+#define HAL_RX_EHT_SIG_NDP_CMN_INFO0_SPATIAL_REUSE GENMASK(3, 0)
+#define HAL_RX_EHT_SIG_NDP_CMN_INFO0_GI_LTF GENMASK(5, 4)
+#define HAL_RX_EHT_SIG_NDP_CMN_INFO0_NUM_LTF_SYM GENMASK(8, 6)
+#define HAL_RX_EHT_SIG_NDP_CMN_INFO0_NSS GENMASK(10, 7)
+#define HAL_RX_EHT_SIG_NDP_CMN_INFO0_BEAMFORMED BIT(11)
+#define HAL_RX_EHT_SIG_NDP_CMN_INFO0_DISREGARD GENMASK(13, 12)
+#define HAL_RX_EHT_SIG_NDP_CMN_INFO0_CRC GENMASK(17, 14)
+
+struct hal_eht_sig_ndp_cmn_eb {
+ __le32 info0;
+} __packed;
+
+#define HAL_RX_EHT_SIG_OVERFLOW_INFO0_SPATIAL_REUSE GENMASK(3, 0)
+#define HAL_RX_EHT_SIG_OVERFLOW_INFO0_GI_LTF GENMASK(5, 4)
+#define HAL_RX_EHT_SIG_OVERFLOW_INFO0_NUM_LTF_SYM GENMASK(8, 6)
+#define HAL_RX_EHT_SIG_OVERFLOW_INFO0_LDPC_EXTA_SYM BIT(9)
+#define HAL_RX_EHT_SIG_OVERFLOW_INFO0_PRE_FEC_PAD_FACTOR GENMASK(11, 10)
+#define HAL_RX_EHT_SIG_OVERFLOW_INFO0_DISAMBIGUITY BIT(12)
+#define HAL_RX_EHT_SIG_OVERFLOW_INFO0_DISREGARD GENMASK(16, 13)
+
+struct hal_eht_sig_usig_overflow {
+ __le32 info0;
+} __packed;
+
+#define HAL_RX_EHT_SIG_NON_MUMIMO_USER_INFO0_STA_ID GENMASK(10, 0)
+#define HAL_RX_EHT_SIG_NON_MUMIMO_USER_INFO0_MCS GENMASK(14, 11)
+#define HAL_RX_EHT_SIG_NON_MUMIMO_USER_INFO0_VALIDATE BIT(15)
+#define HAL_RX_EHT_SIG_NON_MUMIMO_USER_INFO0_NSS GENMASK(19, 16)
+#define HAL_RX_EHT_SIG_NON_MUMIMO_USER_INFO0_BEAMFORMED BIT(20)
+#define HAL_RX_EHT_SIG_NON_MUMIMO_USER_INFO0_CODING BIT(21)
+#define HAL_RX_EHT_SIG_NON_MUMIMO_USER_INFO0_CRC GENMASK(25, 22)
+
+struct hal_eht_sig_non_mu_mimo {
+ __le32 info0;
+} __packed;
+
+#define HAL_RX_EHT_SIG_MUMIMO_USER_INFO0_STA_ID GENMASK(10, 0)
+#define HAL_RX_EHT_SIG_MUMIMO_USER_INFO0_MCS GENMASK(14, 11)
+#define HAL_RX_EHT_SIG_MUMIMO_USER_INFO0_CODING BIT(15)
+#define HAL_RX_EHT_SIG_MUMIMO_USER_INFO0_SPATIAL_CODING GENMASK(22, 16)
+#define HAL_RX_EHT_SIG_MUMIMO_USER_INFO0_CRC GENMASK(26, 23)
+
+struct hal_eht_sig_mu_mimo {
+ __le32 info0;
+} __packed;
+
+union hal_eht_sig_user_field {
+ struct hal_eht_sig_mu_mimo mu_mimo;
+ struct hal_eht_sig_non_mu_mimo n_mu_mimo;
+};
+
+#define HAL_RX_EHT_SIG_NON_OFDMA_INFO0_SPATIAL_REUSE GENMASK(3, 0)
+#define HAL_RX_EHT_SIG_NON_OFDMA_INFO0_GI_LTF GENMASK(5, 4)
+#define HAL_RX_EHT_SIG_NON_OFDMA_INFO0_NUM_LTF_SYM GENMASK(8, 6)
+#define HAL_RX_EHT_SIG_NON_OFDMA_INFO0_LDPC_EXTA_SYM BIT(9)
+#define HAL_RX_EHT_SIG_NON_OFDMA_INFO0_PRE_FEC_PAD_FACTOR GENMASK(11, 10)
+#define HAL_RX_EHT_SIG_NON_OFDMA_INFO0_DISAMBIGUITY BIT(12)
+#define HAL_RX_EHT_SIG_NON_OFDMA_INFO0_DISREGARD GENMASK(16, 13)
+#define HAL_RX_EHT_SIG_NON_OFDMA_INFO0_NUM_USERS GENMASK(19, 17)
+
+struct hal_eht_sig_non_ofdma_cmn_eb {
+ __le32 info0;
+ union hal_eht_sig_user_field user_field;
+} __packed;
+
+#define HAL_RX_EHT_SIG_OFDMA_EB1_SPATIAL_REUSE GENMASK_ULL(3, 0)
+#define HAL_RX_EHT_SIG_OFDMA_EB1_GI_LTF GENMASK_ULL(5, 4)
+#define HAL_RX_EHT_SIG_OFDMA_EB1_NUM_LFT_SYM GENMASK_ULL(8, 6)
+#define HAL_RX_EHT_SIG_OFDMA_EB1_LDPC_EXTRA_SYM BIT(9)
+#define HAL_RX_EHT_SIG_OFDMA_EB1_PRE_FEC_PAD_FACTOR GENMASK_ULL(11, 10)
+#define HAL_RX_EHT_SIG_OFDMA_EB1_PRE_DISAMBIGUITY BIT(12)
+#define HAL_RX_EHT_SIG_OFDMA_EB1_DISREGARD GENMASK_ULL(16, 13)
+#define HAL_RX_EHT_SIG_OFDMA_EB1_RU_ALLOC_1_1 GENMASK_ULL(25, 17)
+#define HAL_RX_EHT_SIG_OFDMA_EB1_RU_ALLOC_1_2 GENMASK_ULL(34, 26)
+#define HAL_RX_EHT_SIG_OFDMA_EB1_CRC GENMASK_ULL(30, 27)
+
+struct hal_eht_sig_ofdma_cmn_eb1 {
+ __le64 info0;
+} __packed;
+
+#define HAL_RX_EHT_SIG_OFDMA_EB2_RU_ALLOC_2_1 GENMASK_ULL(8, 0)
+#define HAL_RX_EHT_SIG_OFDMA_EB2_RU_ALLOC_2_2 GENMASK_ULL(17, 9)
+#define HAL_RX_EHT_SIG_OFDMA_EB2_RU_ALLOC_2_3 GENMASK_ULL(26, 18)
+#define HAL_RX_EHT_SIG_OFDMA_EB2_RU_ALLOC_2_4 GENMASK_ULL(35, 27)
+#define HAL_RX_EHT_SIG_OFDMA_EB2_RU_ALLOC_2_5 GENMASK_ULL(44, 36)
+#define HAL_RX_EHT_SIG_OFDMA_EB2_RU_ALLOC_2_6 GENMASK_ULL(53, 45)
+#define HAL_RX_EHT_SIG_OFDMA_EB2_MCS GNEMASK_ULL(57, 54)
+
+struct hal_eht_sig_ofdma_cmn_eb2 {
+ __le64 info0;
+} __packed;
+
+struct hal_eht_sig_ofdma_cmn_eb {
+ struct hal_eht_sig_ofdma_cmn_eb1 eb1;
+ struct hal_eht_sig_ofdma_cmn_eb2 eb2;
+ union hal_eht_sig_user_field user_field;
+} __packed;
+
+enum hal_eht_bw {
+ HAL_EHT_BW_20,
+ HAL_EHT_BW_40,
+ HAL_EHT_BW_80,
+ HAL_EHT_BW_160,
+ HAL_EHT_BW_320_1,
+ HAL_EHT_BW_320_2,
+};
+
+#define HAL_RX_USIG_CMN_INFO0_PHY_VERSION GENMASK(2, 0)
+#define HAL_RX_USIG_CMN_INFO0_BW GENMASK(5, 3)
+#define HAL_RX_USIG_CMN_INFO0_UL_DL BIT(6)
+#define HAL_RX_USIG_CMN_INFO0_BSS_COLOR GENMASK(12, 7)
+#define HAL_RX_USIG_CMN_INFO0_TXOP GENMASK(19, 13)
+#define HAL_RX_USIG_CMN_INFO0_DISREGARD GENMASK(25, 20)
+#define HAL_RX_USIG_CMN_INFO0_VALIDATE BIT(26)
+
+struct hal_mon_usig_cmn {
+ __le32 info0;
+} __packed;
+
+#define HAL_RX_USIG_TB_INFO0_PPDU_TYPE_COMP_MODE GENMASK(1, 0)
+#define HAL_RX_USIG_TB_INFO0_VALIDATE BIT(2)
+#define HAL_RX_USIG_TB_INFO0_SPATIAL_REUSE_1 GENMASK(6, 3)
+#define HAL_RX_USIG_TB_INFO0_SPATIAL_REUSE_2 GENMASK(10, 7)
+#define HAL_RX_USIG_TB_INFO0_DISREGARD_1 GENMASK(15, 11)
+#define HAL_RX_USIG_TB_INFO0_CRC GENMASK(19, 16)
+#define HAL_RX_USIG_TB_INFO0_TAIL GENMASK(25, 20)
+#define HAL_RX_USIG_TB_INFO0_RX_INTEG_CHECK_PASS BIT(31)
+
+struct hal_mon_usig_tb {
+ __le32 info0;
+} __packed;
+
+#define HAL_RX_USIG_MU_INFO0_PPDU_TYPE_COMP_MODE GENMASK(1, 0)
+#define HAL_RX_USIG_MU_INFO0_VALIDATE_1 BIT(2)
+#define HAL_RX_USIG_MU_INFO0_PUNC_CH_INFO GENMASK(7, 3)
+#define HAL_RX_USIG_MU_INFO0_VALIDATE_2 BIT(8)
+#define HAL_RX_USIG_MU_INFO0_EHT_SIG_MCS GENMASK(10, 9)
+#define HAL_RX_USIG_MU_INFO0_NUM_EHT_SIG_SYM GENMASK(15, 11)
+#define HAL_RX_USIG_MU_INFO0_CRC GENMASK(20, 16)
+#define HAL_RX_USIG_MU_INFO0_TAIL GENMASK(26, 21)
+#define HAL_RX_USIG_MU_INFO0_RX_INTEG_CHECK_PASS BIT(31)
+
+struct hal_mon_usig_mu {
+ __le32 info0;
+} __packed;
+
+union hal_mon_usig_non_cmn {
+ struct hal_mon_usig_tb tb;
+ struct hal_mon_usig_mu mu;
+};
+
+struct hal_mon_usig_hdr {
+ struct hal_mon_usig_cmn cmn;
+ union hal_mon_usig_non_cmn non_cmn;
+} __packed;
+
+#define HAL_RX_USR_INFO0_PHY_PPDU_ID GENMASK(15, 0)
+#define HAL_RX_USR_INFO0_USR_RSSI GENMASK(23, 16)
+#define HAL_RX_USR_INFO0_PKT_TYPE GENMASK(27, 24)
+#define HAL_RX_USR_INFO0_STBC BIT(28)
+#define HAL_RX_USR_INFO0_RECEPTION_TYPE GENMASK(31, 29)
+
+#define HAL_RX_USR_INFO1_MCS GENMASK(3, 0)
+#define HAL_RX_USR_INFO1_SGI GENMASK(5, 4)
+#define HAL_RX_USR_INFO1_HE_RANGING_NDP BIT(6)
+#define HAL_RX_USR_INFO1_MIMO_SS_BITMAP GENMASK(15, 8)
+#define HAL_RX_USR_INFO1_RX_BW GENMASK(18, 16)
+#define HAL_RX_USR_INFO1_DL_OFMDA_USR_IDX GENMASK(31, 24)
+
+#define HAL_RX_USR_INFO2_DL_OFDMA_CONTENT_CHAN BIT(0)
+#define HAL_RX_USR_INFO2_NSS GENMASK(10, 8)
+#define HAL_RX_USR_INFO2_STREAM_OFFSET GENMASK(13, 11)
+#define HAL_RX_USR_INFO2_STA_DCM BIT(14)
+#define HAL_RX_USR_INFO2_LDPC BIT(15)
+#define HAL_RX_USR_INFO2_RU_TYPE_80_0 GENMASK(19, 16)
+#define HAL_RX_USR_INFO2_RU_TYPE_80_1 GENMASK(23, 20)
+#define HAL_RX_USR_INFO2_RU_TYPE_80_2 GENMASK(27, 24)
+#define HAL_RX_USR_INFO2_RU_TYPE_80_3 GENMASK(31, 28)
+
+#define HAL_RX_USR_INFO3_RU_START_IDX_80_0 GENMASK(5, 0)
+#define HAL_RX_USR_INFO3_RU_START_IDX_80_1 GENMASK(13, 8)
+#define HAL_RX_USR_INFO3_RU_START_IDX_80_2 GENMASK(21, 16)
+#define HAL_RX_USR_INFO3_RU_START_IDX_80_3 GENMASK(29, 24)
+
+struct hal_receive_user_info {
+ __le32 info0;
+ __le32 info1;
+ __le32 info2;
+ __le32 info3;
+ __le32 user_fd_rssi_seg0;
+ __le32 user_fd_rssi_seg1;
+ __le32 user_fd_rssi_seg2;
+ __le32 user_fd_rssi_seg3;
+} __packed;
+
+enum hal_mon_reception_type {
+ HAL_RECEPTION_TYPE_SU,
+ HAL_RECEPTION_TYPE_DL_MU_MIMO,
+ HAL_RECEPTION_TYPE_DL_MU_OFMA,
+ HAL_RECEPTION_TYPE_DL_MU_OFDMA_MIMO,
+ HAL_RECEPTION_TYPE_UL_MU_MIMO,
+ HAL_RECEPTION_TYPE_UL_MU_OFDMA,
+ HAL_RECEPTION_TYPE_UL_MU_OFDMA_MIMO,
+};
+
+/* Different allowed RU in 11BE */
+#define HAL_EHT_RU_26 0ULL
+#define HAL_EHT_RU_52 1ULL
+#define HAL_EHT_RU_78 2ULL
+#define HAL_EHT_RU_106 3ULL
+#define HAL_EHT_RU_132 4ULL
+#define HAL_EHT_RU_242 5ULL
+#define HAL_EHT_RU_484 6ULL
+#define HAL_EHT_RU_726 7ULL
+#define HAL_EHT_RU_996 8ULL
+#define HAL_EHT_RU_996x2 9ULL
+#define HAL_EHT_RU_996x3 10ULL
+#define HAL_EHT_RU_996x4 11ULL
+#define HAL_EHT_RU_NONE 15ULL
+#define HAL_EHT_RU_INVALID 31ULL
+/* MRUs spanning above 80Mhz
+ * HAL_EHT_RU_996_484 = HAL_EHT_RU_484 + HAL_EHT_RU_996 + 4 (reserved)
+ */
+#define HAL_EHT_RU_996_484 18ULL
+#define HAL_EHT_RU_996x2_484 28ULL
+#define HAL_EHT_RU_996x3_484 40ULL
+#define HAL_EHT_RU_996_484_242 23ULL
+
+#define NUM_RU_BITS_PER80 16
+#define NUM_RU_BITS_PER20 4
+
+/* Different per_80Mhz band in 320Mhz bandwidth */
+#define HAL_80_0 0
+#define HAL_80_1 1
+#define HAL_80_2 2
+#define HAL_80_3 3
+
+#define HAL_RU_80MHZ(num_band) ((num_band) * NUM_RU_BITS_PER80)
+#define HAL_RU_20MHZ(idx_per_80) ((idx_per_80) * NUM_RU_BITS_PER20)
+
+#define HAL_RU_SHIFT(num_band, idx_per_80) \
+ (HAL_RU_80MHZ(num_band) + HAL_RU_20MHZ(idx_per_80))
+
+#define HAL_RU(ru, num_band, idx_per_80) \
+ ((u64)(ru) << HAL_RU_SHIFT(num_band, idx_per_80))
+
+/* MRU-996+484 */
+#define HAL_EHT_RU_996_484_0 (HAL_RU(HAL_EHT_RU_484, HAL_80_0, 1) | \
+ HAL_RU(HAL_EHT_RU_996, HAL_80_1, 0))
+#define HAL_EHT_RU_996_484_1 (HAL_RU(HAL_EHT_RU_484, HAL_80_0, 0) | \
+ HAL_RU(HAL_EHT_RU_996, HAL_80_1, 0))
+#define HAL_EHT_RU_996_484_2 (HAL_RU(HAL_EHT_RU_996, HAL_80_0, 0) | \
+ HAL_RU(HAL_EHT_RU_484, HAL_80_1, 1))
+#define HAL_EHT_RU_996_484_3 (HAL_RU(HAL_EHT_RU_996, HAL_80_0, 0) | \
+ HAL_RU(HAL_EHT_RU_484, HAL_80_1, 0))
+#define HAL_EHT_RU_996_484_4 (HAL_RU(HAL_EHT_RU_484, HAL_80_2, 1) | \
+ HAL_RU(HAL_EHT_RU_996, HAL_80_3, 0))
+#define HAL_EHT_RU_996_484_5 (HAL_RU(HAL_EHT_RU_484, HAL_80_2, 0) | \
+ HAL_RU(HAL_EHT_RU_996, HAL_80_3, 0))
+#define HAL_EHT_RU_996_484_6 (HAL_RU(HAL_EHT_RU_996, HAL_80_2, 0) | \
+ HAL_RU(HAL_EHT_RU_484, HAL_80_3, 1))
+#define HAL_EHT_RU_996_484_7 (HAL_RU(HAL_EHT_RU_996, HAL_80_2, 0) | \
+ HAL_RU(HAL_EHT_RU_484, HAL_80_3, 0))
+
+/* MRU-996x2+484 */
+#define HAL_EHT_RU_996x2_484_0 (HAL_RU(HAL_EHT_RU_484, HAL_80_0, 1) | \
+ HAL_RU(HAL_EHT_RU_996x2, HAL_80_1, 0) | \
+ HAL_RU(HAL_EHT_RU_996x2, HAL_80_2, 0))
+#define HAL_EHT_RU_996x2_484_1 (HAL_RU(HAL_EHT_RU_484, HAL_80_0, 0) | \
+ HAL_RU(HAL_EHT_RU_996x2, HAL_80_1, 0) | \
+ HAL_RU(HAL_EHT_RU_996x2, HAL_80_2, 0))
+#define HAL_EHT_RU_996x2_484_2 (HAL_RU(HAL_EHT_RU_996x2, HAL_80_0, 0) | \
+ HAL_RU(HAL_EHT_RU_484, HAL_80_1, 1) | \
+ HAL_RU(HAL_EHT_RU_996x2, HAL_80_2, 0))
+#define HAL_EHT_RU_996x2_484_3 (HAL_RU(HAL_EHT_RU_996x2, HAL_80_0, 0) | \
+ HAL_RU(HAL_EHT_RU_484, HAL_80_1, 0) | \
+ HAL_RU(HAL_EHT_RU_996x2, HAL_80_2, 0))
+#define HAL_EHT_RU_996x2_484_4 (HAL_RU(HAL_EHT_RU_996x2, HAL_80_0, 0) | \
+ HAL_RU(HAL_EHT_RU_996x2, HAL_80_1, 0) | \
+ HAL_RU(HAL_EHT_RU_484, HAL_80_2, 1))
+#define HAL_EHT_RU_996x2_484_5 (HAL_RU(HAL_EHT_RU_996x2, HAL_80_0, 0) | \
+ HAL_RU(HAL_EHT_RU_996x2, HAL_80_1, 0) | \
+ HAL_RU(HAL_EHT_RU_484, HAL_80_2, 0))
+#define HAL_EHT_RU_996x2_484_6 (HAL_RU(HAL_EHT_RU_484, HAL_80_1, 1) | \
+ HAL_RU(HAL_EHT_RU_996x2, HAL_80_2, 0) | \
+ HAL_RU(HAL_EHT_RU_996x2, HAL_80_3, 0))
+#define HAL_EHT_RU_996x2_484_7 (HAL_RU(HAL_EHT_RU_484, HAL_80_1, 0) | \
+ HAL_RU(HAL_EHT_RU_996x2, HAL_80_2, 0) | \
+ HAL_RU(HAL_EHT_RU_996x2, HAL_80_3, 0))
+#define HAL_EHT_RU_996x2_484_8 (HAL_RU(HAL_EHT_RU_996x2, HAL_80_1, 0) | \
+ HAL_RU(HAL_EHT_RU_484, HAL_80_2, 1) | \
+ HAL_RU(HAL_EHT_RU_996x2, HAL_80_3, 0))
+#define HAL_EHT_RU_996x2_484_9 (HAL_RU(HAL_EHT_RU_996x2, HAL_80_1, 0) | \
+ HAL_RU(HAL_EHT_RU_484, HAL_80_2, 0) | \
+ HAL_RU(HAL_EHT_RU_996x2, HAL_80_3, 0))
+#define HAL_EHT_RU_996x2_484_10 (HAL_RU(HAL_EHT_RU_996x2, HAL_80_1, 0) | \
+ HAL_RU(HAL_EHT_RU_996x2, HAL_80_2, 0) | \
+ HAL_RU(HAL_EHT_RU_484, HAL_80_3, 1))
+#define HAL_EHT_RU_996x2_484_11 (HAL_RU(HAL_EHT_RU_996x2, HAL_80_1, 0) | \
+ HAL_RU(HAL_EHT_RU_996x2, HAL_80_2, 0) | \
+ HAL_RU(HAL_EHT_RU_484, HAL_80_3, 0))
+
+/* MRU-996x3+484 */
+#define HAL_EHT_RU_996x3_484_0 (HAL_RU(HAL_EHT_RU_484, HAL_80_0, 1) | \
+ HAL_RU(HAL_EHT_RU_996x3, HAL_80_1, 0) | \
+ HAL_RU(HAL_EHT_RU_996x3, HAL_80_2, 0) | \
+ HAL_RU(HAL_EHT_RU_996x3, HAL_80_3, 0))
+#define HAL_EHT_RU_996x3_484_1 (HAL_RU(HAL_EHT_RU_484, HAL_80_0, 0) | \
+ HAL_RU(HAL_EHT_RU_996x3, HAL_80_1, 0) | \
+ HAL_RU(HAL_EHT_RU_996x3, HAL_80_2, 0) | \
+ HAL_RU(HAL_EHT_RU_996x3, HAL_80_3, 0))
+#define HAL_EHT_RU_996x3_484_2 (HAL_RU(HAL_EHT_RU_996x3, HAL_80_0, 0) | \
+ HAL_RU(HAL_EHT_RU_484, HAL_80_1, 1) | \
+ HAL_RU(HAL_EHT_RU_996x3, HAL_80_2, 0) | \
+ HAL_RU(HAL_EHT_RU_996x3, HAL_80_3, 0))
+#define HAL_EHT_RU_996x3_484_3 (HAL_RU(HAL_EHT_RU_996x3, HAL_80_0, 0) | \
+ HAL_RU(HAL_EHT_RU_484, HAL_80_1, 0) | \
+ HAL_RU(HAL_EHT_RU_996x3, HAL_80_2, 0) | \
+ HAL_RU(HAL_EHT_RU_996x3, HAL_80_3, 0))
+#define HAL_EHT_RU_996x3_484_4 (HAL_RU(HAL_EHT_RU_996x3, HAL_80_0, 0) | \
+ HAL_RU(HAL_EHT_RU_996x3, HAL_80_1, 0) | \
+ HAL_RU(HAL_EHT_RU_484, HAL_80_2, 1) | \
+ HAL_RU(HAL_EHT_RU_996x3, HAL_80_3, 0))
+#define HAL_EHT_RU_996x3_484_5 (HAL_RU(HAL_EHT_RU_996x3, HAL_80_0, 0) | \
+ HAL_RU(HAL_EHT_RU_996x3, HAL_80_1, 0) | \
+ HAL_RU(HAL_EHT_RU_484, HAL_80_2, 0) | \
+ HAL_RU(HAL_EHT_RU_996x3, HAL_80_3, 0))
+#define HAL_EHT_RU_996x3_484_6 (HAL_RU(HAL_EHT_RU_996x3, HAL_80_0, 0) | \
+ HAL_RU(HAL_EHT_RU_996x3, HAL_80_1, 0) | \
+ HAL_RU(HAL_EHT_RU_996x3, HAL_80_2, 0) | \
+ HAL_RU(HAL_EHT_RU_484, HAL_80_3, 1))
+#define HAL_EHT_RU_996x3_484_7 (HAL_RU(HAL_EHT_RU_996x3, HAL_80_0, 0) | \
+ HAL_RU(HAL_EHT_RU_996x3, HAL_80_1, 0) | \
+ HAL_RU(HAL_EHT_RU_996x3, HAL_80_2, 0) | \
+ HAL_RU(HAL_EHT_RU_484, HAL_80_3, 0))
+
+#define HAL_RU_PER80(ru_per80, num_80mhz, ru_idx_per80mhz) \
+ (HAL_RU(ru_per80, num_80mhz, ru_idx_per80mhz))
+
+#define RU_INVALID 0
+#define RU_26 1
+#define RU_52 2
+#define RU_106 4
+#define RU_242 9
+#define RU_484 18
+#define RU_996 37
+#define RU_2X996 74
+#define RU_3X996 111
+#define RU_4X996 148
+#define RU_52_26 (RU_52 + RU_26)
+#define RU_106_26 (RU_106 + RU_26)
+#define RU_484_242 (RU_484 + RU_242)
+#define RU_996_484 (RU_996 + RU_484)
+#define RU_996_484_242 (RU_996 + RU_484_242)
+#define RU_2X996_484 (RU_2X996 + RU_484)
+#define RU_3X996_484 (RU_3X996 + RU_484)
+
+enum ath12k_eht_ru_size {
+ ATH12K_EHT_RU_26,
+ ATH12K_EHT_RU_52,
+ ATH12K_EHT_RU_106,
+ ATH12K_EHT_RU_242,
+ ATH12K_EHT_RU_484,
+ ATH12K_EHT_RU_996,
+ ATH12K_EHT_RU_996x2,
+ ATH12K_EHT_RU_996x4,
+ ATH12K_EHT_RU_52_26,
+ ATH12K_EHT_RU_106_26,
+ ATH12K_EHT_RU_484_242,
+ ATH12K_EHT_RU_996_484,
+ ATH12K_EHT_RU_996_484_242,
+ ATH12K_EHT_RU_996x2_484,
+ ATH12K_EHT_RU_996x3,
+ ATH12K_EHT_RU_996x3_484,
+
+ /* Keep last */
+ ATH12K_EHT_RU_INVALID,
+};
+
+#define HAL_RX_RU_ALLOC_TYPE_MAX ATH12K_EHT_RU_INVALID
+
static inline
enum nl80211_he_ru_alloc ath12k_he_ru_tones_to_nl80211_he_ru_alloc(u16 ru_tones)
{
@@ -662,6 +1105,9 @@ enum nl80211_he_ru_alloc ath12k_he_ru_tones_to_nl80211_he_ru_alloc(u16 ru_tones)
case RU_996:
ret = NL80211_RATE_INFO_HE_RU_ALLOC_996;
break;
+ case RU_2X996:
+ ret = NL80211_RATE_INFO_HE_RU_ALLOC_2x996;
+ break;
case RU_26:
fallthrough;
default:
@@ -696,8 +1142,8 @@ void ath12k_hal_rx_msdu_link_info_get(struct hal_rx_msdu_link *link, u32 *num_ms
u32 *msdu_cookies,
enum hal_rx_buf_return_buf_manager *rbm);
void ath12k_hal_rx_msdu_link_desc_set(struct ath12k_base *ab,
- struct hal_wbm_release_ring *dst_desc,
- struct hal_wbm_release_ring *src_desc,
+ struct hal_wbm_release_ring *desc,
+ struct ath12k_buffer_addr *buf_addr_info,
enum hal_wbm_rel_bm_act action);
void ath12k_hal_rx_buf_addr_info_set(struct ath12k_buffer_addr *binfo,
dma_addr_t paddr, u32 cookie, u8 manager);
@@ -712,5 +1158,12 @@ int ath12k_hal_wbm_desc_parse_err(struct ath12k_base *ab, void *desc,
void ath12k_hal_rx_reo_ent_paddr_get(struct ath12k_base *ab,
struct ath12k_buffer_addr *buff_addr,
dma_addr_t *paddr, u32 *cookie);
+void ath12k_hal_rx_reo_ent_buf_paddr_get(void *rx_desc, dma_addr_t *paddr, u32 *sw_cookie,
+ struct ath12k_buffer_addr **pp_buf_addr,
+ u8 *rbm, u32 *msdu_cnt);
+void ath12k_hal_rx_msdu_list_get(struct ath12k *ar,
+ struct hal_rx_msdu_link *link_desc,
+ struct hal_rx_msdu_list *msdu_list,
+ u16 *num_msdus);
#endif
diff --git a/drivers/net/wireless/ath/ath12k/hal_tx.h b/drivers/net/wireless/ath/ath12k/hal_tx.h
index 3cf5973771d7..eb065a79f6c6 100644
--- a/drivers/net/wireless/ath/ath12k/hal_tx.h
+++ b/drivers/net/wireless/ath/ath12k/hal_tx.h
@@ -1,7 +1,8 @@
/* SPDX-License-Identifier: BSD-3-Clause-Clear */
/*
* Copyright (c) 2018-2021 The Linux Foundation. All rights reserved.
- * Copyright (c) 2021-2022, 2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) 2021-2022, 2024-2025 Qualcomm Innovation Center, Inc.
+ * All rights reserved.
*/
#ifndef ATH12K_HAL_TX_H
@@ -63,7 +64,12 @@ struct hal_tx_status {
u8 try_cnt;
u8 tid;
u16 peer_id;
- u32 rate_stats;
+ enum hal_tx_rate_stats_pkt_type pkt_type;
+ enum hal_tx_rate_stats_sgi sgi;
+ enum ath12k_supported_bw bw;
+ u8 mcs;
+ u16 tones;
+ u8 ofdma;
};
#define HAL_TX_PHY_DESC_INFO0_BF_TYPE GENMASK(17, 16)
diff --git a/drivers/net/wireless/ath/ath12k/hw.c b/drivers/net/wireless/ath/ath12k/hw.c
index b7b583fadb5a..7e2cf0fb2085 100644
--- a/drivers/net/wireless/ath/ath12k/hw.c
+++ b/drivers/net/wireless/ath/ath12k/hw.c
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: BSD-3-Clause-Clear
/*
* Copyright (c) 2018-2021 The Linux Foundation. All rights reserved.
- * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#include <linux/types.h>
@@ -118,6 +118,10 @@ static const struct ath12k_hw_ops wcn7850_ops = {
#define ATH12K_TX_MON_RING_MASK_0 0x1
#define ATH12K_TX_MON_RING_MASK_1 0x2
+#define ATH12K_RX_MON_STATUS_RING_MASK_0 0x1
+#define ATH12K_RX_MON_STATUS_RING_MASK_1 0x2
+#define ATH12K_RX_MON_STATUS_RING_MASK_2 0x4
+
/* Target firmware's Copy Engine configuration. */
static const struct ce_pipe_config ath12k_target_ce_config_wlan_qcn9274[] = {
/* CE0: host->target HTC control and raw streams */
@@ -535,6 +539,217 @@ static const struct service_to_pipe ath12k_target_service_to_ce_map_wlan_wcn7850
},
};
+static const struct ce_pipe_config ath12k_target_ce_config_wlan_ipq5332[] = {
+ /* host->target HTC control and raw streams */
+ {
+ .pipenum = __cpu_to_le32(0),
+ .pipedir = __cpu_to_le32(PIPEDIR_OUT),
+ .nentries = __cpu_to_le32(32),
+ .nbytes_max = __cpu_to_le32(2048),
+ .flags = __cpu_to_le32(CE_ATTR_FLAGS),
+ .reserved = __cpu_to_le32(0),
+ },
+ /* target->host HTT */
+ {
+ .pipenum = __cpu_to_le32(1),
+ .pipedir = __cpu_to_le32(PIPEDIR_IN),
+ .nentries = __cpu_to_le32(32),
+ .nbytes_max = __cpu_to_le32(2048),
+ .flags = __cpu_to_le32(CE_ATTR_FLAGS),
+ .reserved = __cpu_to_le32(0),
+ },
+ /* target->host WMI + HTC control */
+ {
+ .pipenum = __cpu_to_le32(2),
+ .pipedir = __cpu_to_le32(PIPEDIR_IN),
+ .nentries = __cpu_to_le32(32),
+ .nbytes_max = __cpu_to_le32(2048),
+ .flags = __cpu_to_le32(CE_ATTR_FLAGS),
+ .reserved = __cpu_to_le32(0),
+ },
+ /* host->target WMI */
+ {
+ .pipenum = __cpu_to_le32(3),
+ .pipedir = __cpu_to_le32(PIPEDIR_OUT),
+ .nentries = __cpu_to_le32(32),
+ .nbytes_max = __cpu_to_le32(2048),
+ .flags = __cpu_to_le32(CE_ATTR_FLAGS),
+ .reserved = __cpu_to_le32(0),
+ },
+ /* host->target HTT */
+ {
+ .pipenum = __cpu_to_le32(4),
+ .pipedir = __cpu_to_le32(PIPEDIR_OUT),
+ .nentries = __cpu_to_le32(256),
+ .nbytes_max = __cpu_to_le32(256),
+ .flags = __cpu_to_le32(CE_ATTR_FLAGS | CE_ATTR_DIS_INTR),
+ .reserved = __cpu_to_le32(0),
+ },
+ /* Target -> host PKTLOG */
+ {
+ .pipenum = __cpu_to_le32(5),
+ .pipedir = __cpu_to_le32(PIPEDIR_IN),
+ .nentries = __cpu_to_le32(32),
+ .nbytes_max = __cpu_to_le32(2048),
+ .flags = __cpu_to_le32(CE_ATTR_FLAGS),
+ .reserved = __cpu_to_le32(0),
+ },
+ /* Reserved for target autonomous HIF_memcpy */
+ {
+ .pipenum = __cpu_to_le32(6),
+ .pipedir = __cpu_to_le32(PIPEDIR_INOUT),
+ .nentries = __cpu_to_le32(32),
+ .nbytes_max = __cpu_to_le32(16384),
+ .flags = __cpu_to_le32(CE_ATTR_FLAGS),
+ .reserved = __cpu_to_le32(0),
+ },
+ /* CE7 Reserved for CV Prefetch */
+ {
+ .pipenum = __cpu_to_le32(7),
+ .pipedir = __cpu_to_le32(PIPEDIR_OUT),
+ .nentries = __cpu_to_le32(32),
+ .nbytes_max = __cpu_to_le32(2048),
+ .flags = __cpu_to_le32(CE_ATTR_FLAGS),
+ .reserved = __cpu_to_le32(0),
+ },
+ /* CE8 Reserved for target generic HIF memcpy */
+ {
+ .pipenum = __cpu_to_le32(8),
+ .pipedir = __cpu_to_le32(PIPEDIR_INOUT),
+ .nentries = __cpu_to_le32(32),
+ .nbytes_max = __cpu_to_le32(16384),
+ .flags = __cpu_to_le32(CE_ATTR_FLAGS),
+ .reserved = __cpu_to_le32(0),
+ },
+ /* CE9 WMI logging/CFR/Spectral/Radar/ */
+ {
+ .pipenum = __cpu_to_le32(9),
+ .pipedir = __cpu_to_le32(PIPEDIR_IN),
+ .nentries = __cpu_to_le32(32),
+ .nbytes_max = __cpu_to_le32(2048),
+ .flags = __cpu_to_le32(CE_ATTR_FLAGS),
+ .reserved = __cpu_to_le32(0),
+ },
+ /* Unused TBD */
+ {
+ .pipenum = __cpu_to_le32(10),
+ .pipedir = __cpu_to_le32(PIPEDIR_NONE),
+ .nentries = __cpu_to_le32(0),
+ .nbytes_max = __cpu_to_le32(0),
+ .flags = __cpu_to_le32(0),
+ .reserved = __cpu_to_le32(0),
+ },
+ /* Unused TBD */
+ {
+ .pipenum = __cpu_to_le32(11),
+ .pipedir = __cpu_to_le32(PIPEDIR_NONE),
+ .nentries = __cpu_to_le32(0),
+ .nbytes_max = __cpu_to_le32(0),
+ .flags = __cpu_to_le32(0),
+ .reserved = __cpu_to_le32(0),
+ },
+};
+
+static const struct service_to_pipe ath12k_target_service_to_ce_map_wlan_ipq5332[] = {
+ {
+ __cpu_to_le32(ATH12K_HTC_SVC_ID_WMI_DATA_VO),
+ __cpu_to_le32(PIPEDIR_OUT),
+ __cpu_to_le32(3),
+ },
+ {
+ __cpu_to_le32(ATH12K_HTC_SVC_ID_WMI_DATA_VO),
+ __cpu_to_le32(PIPEDIR_IN),
+ __cpu_to_le32(2),
+ },
+ {
+ __cpu_to_le32(ATH12K_HTC_SVC_ID_WMI_DATA_BK),
+ __cpu_to_le32(PIPEDIR_OUT),
+ __cpu_to_le32(3),
+ },
+ {
+ __cpu_to_le32(ATH12K_HTC_SVC_ID_WMI_DATA_BK),
+ __cpu_to_le32(PIPEDIR_IN),
+ __cpu_to_le32(2),
+ },
+ {
+ __cpu_to_le32(ATH12K_HTC_SVC_ID_WMI_DATA_BE),
+ __cpu_to_le32(PIPEDIR_OUT),
+ __cpu_to_le32(3),
+ },
+ {
+ __cpu_to_le32(ATH12K_HTC_SVC_ID_WMI_DATA_BE),
+ __cpu_to_le32(PIPEDIR_IN),
+ __cpu_to_le32(2),
+ },
+ {
+ __cpu_to_le32(ATH12K_HTC_SVC_ID_WMI_DATA_VI),
+ __cpu_to_le32(PIPEDIR_OUT),
+ __cpu_to_le32(3),
+ },
+ {
+ __cpu_to_le32(ATH12K_HTC_SVC_ID_WMI_DATA_VI),
+ __cpu_to_le32(PIPEDIR_IN),
+ __cpu_to_le32(2),
+ },
+ {
+ __cpu_to_le32(ATH12K_HTC_SVC_ID_WMI_CONTROL),
+ __cpu_to_le32(PIPEDIR_OUT),
+ __cpu_to_le32(3),
+ },
+ {
+ __cpu_to_le32(ATH12K_HTC_SVC_ID_WMI_CONTROL),
+ __cpu_to_le32(PIPEDIR_IN),
+ __cpu_to_le32(2),
+ },
+ {
+ __cpu_to_le32(ATH12K_HTC_SVC_ID_RSVD_CTRL),
+ __cpu_to_le32(PIPEDIR_OUT),
+ __cpu_to_le32(0),
+ },
+ {
+ __cpu_to_le32(ATH12K_HTC_SVC_ID_RSVD_CTRL),
+ __cpu_to_le32(PIPEDIR_IN),
+ __cpu_to_le32(1),
+ },
+ {
+ __cpu_to_le32(ATH12K_HTC_SVC_ID_TEST_RAW_STREAMS),
+ __cpu_to_le32(PIPEDIR_OUT),
+ __cpu_to_le32(0),
+ },
+ {
+ __cpu_to_le32(ATH12K_HTC_SVC_ID_TEST_RAW_STREAMS),
+ __cpu_to_le32(PIPEDIR_IN),
+ __cpu_to_le32(1),
+ },
+ {
+ __cpu_to_le32(ATH12K_HTC_SVC_ID_HTT_DATA_MSG),
+ __cpu_to_le32(PIPEDIR_OUT),
+ __cpu_to_le32(4),
+ },
+ {
+ __cpu_to_le32(ATH12K_HTC_SVC_ID_HTT_DATA_MSG),
+ __cpu_to_le32(PIPEDIR_IN),
+ __cpu_to_le32(1),
+ },
+ {
+ __cpu_to_le32(ATH12K_HTC_SVC_ID_PKT_LOG),
+ __cpu_to_le32(PIPEDIR_IN),
+ __cpu_to_le32(5),
+ },
+ {
+ __cpu_to_le32(ATH12K_HTC_SVC_ID_WMI_CONTROL_DIAG),
+ __cpu_to_le32(PIPEDIR_IN),
+ __cpu_to_le32(9),
+ },
+ /* (Additions here) */
+
+ { /* must be last */
+ __cpu_to_le32(0),
+ __cpu_to_le32(0),
+ __cpu_to_le32(0),
+ },
+};
+
static const struct ath12k_hw_ring_mask ath12k_hw_ring_mask_qcn9274 = {
.tx = {
ATH12K_TX_RING_MASK_0,
@@ -543,7 +758,11 @@ static const struct ath12k_hw_ring_mask ath12k_hw_ring_mask_qcn9274 = {
ATH12K_TX_RING_MASK_3,
},
.rx_mon_dest = {
- 0, 0, 0,
+ 0, 0, 0, 0,
+ 0, 0, 0, 0,
+ ATH12K_RX_MON_RING_MASK_0,
+ ATH12K_RX_MON_RING_MASK_1,
+ ATH12K_RX_MON_RING_MASK_2,
},
.rx = {
0, 0, 0, 0,
@@ -573,6 +792,46 @@ static const struct ath12k_hw_ring_mask ath12k_hw_ring_mask_qcn9274 = {
},
};
+static const struct ath12k_hw_ring_mask ath12k_hw_ring_mask_ipq5332 = {
+ .tx = {
+ ATH12K_TX_RING_MASK_0,
+ ATH12K_TX_RING_MASK_1,
+ ATH12K_TX_RING_MASK_2,
+ ATH12K_TX_RING_MASK_3,
+ },
+ .rx_mon_dest = {
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ ATH12K_RX_MON_RING_MASK_0,
+ },
+ .rx = {
+ 0, 0, 0, 0,
+ ATH12K_RX_RING_MASK_0,
+ ATH12K_RX_RING_MASK_1,
+ ATH12K_RX_RING_MASK_2,
+ ATH12K_RX_RING_MASK_3,
+ },
+ .rx_err = {
+ 0, 0, 0,
+ ATH12K_RX_ERR_RING_MASK_0,
+ },
+ .rx_wbm_rel = {
+ 0, 0, 0,
+ ATH12K_RX_WBM_REL_RING_MASK_0,
+ },
+ .reo_status = {
+ 0, 0, 0,
+ ATH12K_REO_STATUS_RING_MASK_0,
+ },
+ .host2rxdma = {
+ 0, 0, 0,
+ ATH12K_HOST2RXDMA_RING_MASK_0,
+ },
+ .tx_mon_dest = {
+ ATH12K_TX_MON_RING_MASK_0,
+ ATH12K_TX_MON_RING_MASK_1,
+ },
+};
+
static const struct ath12k_hw_ring_mask ath12k_hw_ring_mask_wcn7850 = {
.tx = {
ATH12K_TX_RING_MASK_0,
@@ -581,6 +840,12 @@ static const struct ath12k_hw_ring_mask ath12k_hw_ring_mask_wcn7850 = {
},
.rx_mon_dest = {
},
+ .rx_mon_status = {
+ 0, 0, 0, 0,
+ ATH12K_RX_MON_STATUS_RING_MASK_0,
+ ATH12K_RX_MON_STATUS_RING_MASK_1,
+ ATH12K_RX_MON_STATUS_RING_MASK_2,
+ },
.rx = {
0, 0, 0,
ATH12K_RX_RING_MASK_0,
@@ -615,6 +880,9 @@ static const struct ath12k_hw_regs qcn9274_v1_regs = {
.hal_tcl1_ring_msi1_base_msb = 0x0000094c,
.hal_tcl1_ring_msi1_data = 0x00000950,
.hal_tcl_ring_base_lsb = 0x00000b58,
+ .hal_tcl1_ring_base_lsb = 0x00000900,
+ .hal_tcl1_ring_base_msb = 0x00000904,
+ .hal_tcl2_ring_base_lsb = 0x00000978,
/* TCL STATUS ring address */
.hal_tcl_status_ring_base_lsb = 0x00000d38,
@@ -677,6 +945,12 @@ static const struct ath12k_hw_regs qcn9274_v1_regs = {
/* REO status ring address */
.hal_reo_status_ring_base = 0x00000a84,
+
+ /* CE base address */
+ .hal_umac_ce0_src_reg_base = 0x01b80000,
+ .hal_umac_ce0_dest_reg_base = 0x01b81000,
+ .hal_umac_ce1_src_reg_base = 0x01b82000,
+ .hal_umac_ce1_dest_reg_base = 0x01b83000,
};
static const struct ath12k_hw_regs qcn9274_v2_regs = {
@@ -691,6 +965,9 @@ static const struct ath12k_hw_regs qcn9274_v2_regs = {
.hal_tcl1_ring_msi1_base_msb = 0x0000094c,
.hal_tcl1_ring_msi1_data = 0x00000950,
.hal_tcl_ring_base_lsb = 0x00000b58,
+ .hal_tcl1_ring_base_lsb = 0x00000900,
+ .hal_tcl1_ring_base_msb = 0x00000904,
+ .hal_tcl2_ring_base_lsb = 0x00000978,
/* TCL STATUS ring address */
.hal_tcl_status_ring_base_lsb = 0x00000d38,
@@ -730,6 +1007,8 @@ static const struct ath12k_hw_regs qcn9274_v2_regs = {
.hal_reo1_sw_cookie_cfg1 = 0x00000070,
.hal_reo1_qdesc_lut_base0 = 0x00000074,
.hal_reo1_qdesc_lut_base1 = 0x00000078,
+ .hal_reo1_qdesc_addr = 0x0000007c,
+ .hal_reo1_qdesc_max_peerid = 0x00000088,
.hal_reo1_ring_base_lsb = 0x00000500,
.hal_reo1_ring_base_msb = 0x00000504,
.hal_reo1_ring_id = 0x00000508,
@@ -757,6 +1036,100 @@ static const struct ath12k_hw_regs qcn9274_v2_regs = {
/* REO status ring address */
.hal_reo_status_ring_base = 0x00000aa0,
+
+ /* CE base address */
+ .hal_umac_ce0_src_reg_base = 0x01b80000,
+ .hal_umac_ce0_dest_reg_base = 0x01b81000,
+ .hal_umac_ce1_src_reg_base = 0x01b82000,
+ .hal_umac_ce1_dest_reg_base = 0x01b83000,
+};
+
+static const struct ath12k_hw_regs ipq5332_regs = {
+ /* SW2TCL(x) R0 ring configuration address */
+ .hal_tcl1_ring_id = 0x00000918,
+ .hal_tcl1_ring_misc = 0x00000920,
+ .hal_tcl1_ring_tp_addr_lsb = 0x0000092c,
+ .hal_tcl1_ring_tp_addr_msb = 0x00000930,
+ .hal_tcl1_ring_consumer_int_setup_ix0 = 0x00000940,
+ .hal_tcl1_ring_consumer_int_setup_ix1 = 0x00000944,
+ .hal_tcl1_ring_msi1_base_lsb = 0x00000958,
+ .hal_tcl1_ring_msi1_base_msb = 0x0000095c,
+ .hal_tcl1_ring_base_lsb = 0x00000910,
+ .hal_tcl1_ring_base_msb = 0x00000914,
+ .hal_tcl1_ring_msi1_data = 0x00000960,
+ .hal_tcl2_ring_base_lsb = 0x00000988,
+ .hal_tcl_ring_base_lsb = 0x00000b68,
+
+ /* TCL STATUS ring address */
+ .hal_tcl_status_ring_base_lsb = 0x00000d48,
+
+ /* REO DEST ring address */
+ .hal_reo2_ring_base = 0x00000578,
+ .hal_reo1_misc_ctrl_addr = 0x00000b9c,
+ .hal_reo1_sw_cookie_cfg0 = 0x0000006c,
+ .hal_reo1_sw_cookie_cfg1 = 0x00000070,
+ .hal_reo1_qdesc_lut_base0 = 0x00000074,
+ .hal_reo1_qdesc_lut_base1 = 0x00000078,
+ .hal_reo1_ring_base_lsb = 0x00000500,
+ .hal_reo1_ring_base_msb = 0x00000504,
+ .hal_reo1_ring_id = 0x00000508,
+ .hal_reo1_ring_misc = 0x00000510,
+ .hal_reo1_ring_hp_addr_lsb = 0x00000514,
+ .hal_reo1_ring_hp_addr_msb = 0x00000518,
+ .hal_reo1_ring_producer_int_setup = 0x00000524,
+ .hal_reo1_ring_msi1_base_lsb = 0x00000548,
+ .hal_reo1_ring_msi1_base_msb = 0x0000054C,
+ .hal_reo1_ring_msi1_data = 0x00000550,
+ .hal_reo1_aging_thres_ix0 = 0x00000B28,
+ .hal_reo1_aging_thres_ix1 = 0x00000B2C,
+ .hal_reo1_aging_thres_ix2 = 0x00000B30,
+ .hal_reo1_aging_thres_ix3 = 0x00000B34,
+
+ /* REO Exception ring address */
+ .hal_reo2_sw0_ring_base = 0x000008c0,
+
+ /* REO Reinject ring address */
+ .hal_sw2reo_ring_base = 0x00000320,
+ .hal_sw2reo1_ring_base = 0x00000398,
+
+ /* REO cmd ring address */
+ .hal_reo_cmd_ring_base = 0x000002A8,
+
+ /* REO status ring address */
+ .hal_reo_status_ring_base = 0x00000aa0,
+
+ /* WBM idle link ring address */
+ .hal_wbm_idle_ring_base_lsb = 0x00000d3c,
+ .hal_wbm_idle_ring_misc_addr = 0x00000d4c,
+ .hal_wbm_r0_idle_list_cntl_addr = 0x00000240,
+ .hal_wbm_r0_idle_list_size_addr = 0x00000244,
+ .hal_wbm_scattered_ring_base_lsb = 0x00000250,
+ .hal_wbm_scattered_ring_base_msb = 0x00000254,
+ .hal_wbm_scattered_desc_head_info_ix0 = 0x00000260,
+ .hal_wbm_scattered_desc_head_info_ix1 = 0x00000264,
+ .hal_wbm_scattered_desc_tail_info_ix0 = 0x00000270,
+ .hal_wbm_scattered_desc_tail_info_ix1 = 0x00000274,
+ .hal_wbm_scattered_desc_ptr_hp_addr = 0x0000027c,
+
+ /* SW2WBM release ring address */
+ .hal_wbm_sw_release_ring_base_lsb = 0x0000037c,
+
+ /* WBM2SW release ring address */
+ .hal_wbm0_release_ring_base_lsb = 0x00000e08,
+ .hal_wbm1_release_ring_base_lsb = 0x00000e80,
+
+ /* PPE release ring address */
+ .hal_ppe_rel_ring_base = 0x0000046c,
+
+ /* CE address */
+ .hal_umac_ce0_src_reg_base = 0x00740000 -
+ HAL_IPQ5332_CE_WFSS_REG_BASE,
+ .hal_umac_ce0_dest_reg_base = 0x00741000 -
+ HAL_IPQ5332_CE_WFSS_REG_BASE,
+ .hal_umac_ce1_src_reg_base = 0x00742000 -
+ HAL_IPQ5332_CE_WFSS_REG_BASE,
+ .hal_umac_ce1_dest_reg_base = 0x00743000 -
+ HAL_IPQ5332_CE_WFSS_REG_BASE,
};
static const struct ath12k_hw_regs wcn7850_regs = {
@@ -771,6 +1144,9 @@ static const struct ath12k_hw_regs wcn7850_regs = {
.hal_tcl1_ring_msi1_base_msb = 0x0000094c,
.hal_tcl1_ring_msi1_data = 0x00000950,
.hal_tcl_ring_base_lsb = 0x00000b58,
+ .hal_tcl1_ring_base_lsb = 0x00000900,
+ .hal_tcl1_ring_base_msb = 0x00000904,
+ .hal_tcl2_ring_base_lsb = 0x00000978,
/* TCL STATUS ring address */
.hal_tcl_status_ring_base_lsb = 0x00000d38,
@@ -833,6 +1209,12 @@ static const struct ath12k_hw_regs wcn7850_regs = {
/* REO status ring address */
.hal_reo_status_ring_base = 0x00000a84,
+
+ /* CE base address */
+ .hal_umac_ce0_src_reg_base = 0x01b80000,
+ .hal_umac_ce0_dest_reg_base = 0x01b81000,
+ .hal_umac_ce1_src_reg_base = 0x01b82000,
+ .hal_umac_ce1_dest_reg_base = 0x01b83000,
};
static const struct ath12k_hw_hal_params ath12k_hw_hal_params_qcn9274 = {
@@ -852,6 +1234,26 @@ static const struct ath12k_hw_hal_params ath12k_hw_hal_params_wcn7850 = {
HAL_WBM_SW_COOKIE_CONV_CFG_WBM2SW4_EN,
};
+static const struct ath12k_hw_hal_params ath12k_hw_hal_params_ipq5332 = {
+ .rx_buf_rbm = HAL_RX_BUF_RBM_SW3_BM,
+ .wbm2sw_cc_enable = HAL_WBM_SW_COOKIE_CONV_CFG_WBM2SW0_EN |
+ HAL_WBM_SW_COOKIE_CONV_CFG_WBM2SW1_EN |
+ HAL_WBM_SW_COOKIE_CONV_CFG_WBM2SW2_EN |
+ HAL_WBM_SW_COOKIE_CONV_CFG_WBM2SW3_EN |
+ HAL_WBM_SW_COOKIE_CONV_CFG_WBM2SW4_EN,
+};
+
+static const struct ce_ie_addr ath12k_ce_ie_addr_ipq5332 = {
+ .ie1_reg_addr = CE_HOST_IE_ADDRESS - HAL_IPQ5332_CE_WFSS_REG_BASE,
+ .ie2_reg_addr = CE_HOST_IE_2_ADDRESS - HAL_IPQ5332_CE_WFSS_REG_BASE,
+ .ie3_reg_addr = CE_HOST_IE_3_ADDRESS - HAL_IPQ5332_CE_WFSS_REG_BASE,
+};
+
+static const struct ce_remap ath12k_ce_remap_ipq5332 = {
+ .base = HAL_IPQ5332_CE_WFSS_REG_BASE,
+ .size = HAL_IPQ5332_CE_SIZE,
+};
+
static const struct ath12k_hw_params ath12k_hw_params[] = {
{
.name = "qcn9274 hw1.0",
@@ -860,6 +1262,7 @@ static const struct ath12k_hw_params ath12k_hw_params[] = {
.dir = "QCN9274/hw1.0",
.board_size = 256 * 1024,
.cal_offset = 128 * 1024,
+ .m3_loader = ath12k_m3_fw_loader_driver,
},
.max_radios = 1,
.single_pdev_only = false,
@@ -895,7 +1298,7 @@ static const struct ath12k_hw_params ath12k_hw_params[] = {
.download_calib = true,
.supports_suspend = false,
.tcl_ring_retry = true,
- .reoq_lut_support = false,
+ .reoq_lut_support = true,
.supports_shadow_regs = false,
.num_tcl_banks = 48,
@@ -928,6 +1331,14 @@ static const struct ath12k_hw_params ath12k_hw_params[] = {
.iova_mask = 0,
.supports_aspm = false,
+
+ .ce_ie_addr = NULL,
+ .ce_remap = NULL,
+ .bdf_addr_offset = 0,
+
+ .current_cc_support = false,
+
+ .dp_primary_link_only = true,
},
{
.name = "wcn7850 hw2.0",
@@ -937,6 +1348,7 @@ static const struct ath12k_hw_params ath12k_hw_params[] = {
.dir = "WCN7850/hw2.0",
.board_size = 256 * 1024,
.cal_offset = 256 * 1024,
+ .m3_loader = ath12k_m3_fw_loader_driver,
},
.max_radios = 1,
@@ -968,7 +1380,7 @@ static const struct ath12k_hw_params ath12k_hw_params[] = {
BIT(NL80211_IFTYPE_P2P_DEVICE) |
BIT(NL80211_IFTYPE_P2P_CLIENT) |
BIT(NL80211_IFTYPE_P2P_GO),
- .supports_monitor = false,
+ .supports_monitor = true,
.idle_ps = true,
.download_calib = false,
@@ -1008,6 +1420,14 @@ static const struct ath12k_hw_params ath12k_hw_params[] = {
.iova_mask = ATH12K_PCIE_MAX_PAYLOAD_SIZE - 1,
.supports_aspm = true,
+
+ .ce_ie_addr = NULL,
+ .ce_remap = NULL,
+ .bdf_addr_offset = 0,
+
+ .current_cc_support = true,
+
+ .dp_primary_link_only = false,
},
{
.name = "qcn9274 hw2.0",
@@ -1016,6 +1436,7 @@ static const struct ath12k_hw_params ath12k_hw_params[] = {
.dir = "QCN9274/hw2.0",
.board_size = 256 * 1024,
.cal_offset = 128 * 1024,
+ .m3_loader = ath12k_m3_fw_loader_driver,
},
.max_radios = 2,
.single_pdev_only = false,
@@ -1035,7 +1456,7 @@ static const struct ath12k_hw_params ath12k_hw_params[] = {
.hal_params = &ath12k_hw_hal_params_qcn9274,
- .rxdma1_enable = false,
+ .rxdma1_enable = true,
.num_rxdma_per_pdev = 1,
.num_rxdma_dst_ring = 0,
.rx_mac_buf_ring = false,
@@ -1045,7 +1466,7 @@ static const struct ath12k_hw_params ath12k_hw_params[] = {
BIT(NL80211_IFTYPE_AP) |
BIT(NL80211_IFTYPE_MESH_POINT) |
BIT(NL80211_IFTYPE_AP_VLAN),
- .supports_monitor = false,
+ .supports_monitor = true,
.idle_ps = false,
.download_calib = true,
@@ -1084,6 +1505,92 @@ static const struct ath12k_hw_params ath12k_hw_params[] = {
.iova_mask = 0,
.supports_aspm = false,
+
+ .ce_ie_addr = NULL,
+ .ce_remap = NULL,
+ .bdf_addr_offset = 0,
+
+ .current_cc_support = false,
+
+ .dp_primary_link_only = true,
+ },
+ {
+ .name = "ipq5332 hw1.0",
+ .hw_rev = ATH12K_HW_IPQ5332_HW10,
+ .fw = {
+ .dir = "IPQ5332/hw1.0",
+ .board_size = 256 * 1024,
+ .cal_offset = 128 * 1024,
+ .m3_loader = ath12k_m3_fw_loader_remoteproc,
+ },
+ .max_radios = 1,
+ .single_pdev_only = false,
+ .qmi_service_ins_id = ATH12K_QMI_WLFW_SERVICE_INS_ID_V01_IPQ5332,
+ .internal_sleep_clock = false,
+
+ .hw_ops = &qcn9274_ops,
+ .regs = &ipq5332_regs,
+ .ring_mask = &ath12k_hw_ring_mask_ipq5332,
+
+ .host_ce_config = ath12k_host_ce_config_ipq5332,
+ .ce_count = 12,
+ .target_ce_config = ath12k_target_ce_config_wlan_ipq5332,
+ .target_ce_count = 12,
+ .svc_to_ce_map = ath12k_target_service_to_ce_map_wlan_ipq5332,
+ .svc_to_ce_map_len = 18,
+
+ .hal_params = &ath12k_hw_hal_params_ipq5332,
+
+ .rxdma1_enable = false,
+ .num_rxdma_per_pdev = 1,
+ .num_rxdma_dst_ring = 0,
+ .rx_mac_buf_ring = false,
+ .vdev_start_delay = false,
+
+ .interface_modes = BIT(NL80211_IFTYPE_STATION) |
+ BIT(NL80211_IFTYPE_AP) |
+ BIT(NL80211_IFTYPE_MESH_POINT),
+ .supports_monitor = false,
+
+ .idle_ps = false,
+ .download_calib = true,
+ .supports_suspend = false,
+ .tcl_ring_retry = true,
+ .reoq_lut_support = false,
+ .supports_shadow_regs = false,
+
+ .num_tcl_banks = 48,
+ .max_tx_ring = 4,
+
+ .wmi_init = &ath12k_wmi_init_qcn9274,
+
+ .hal_ops = &hal_qcn9274_ops,
+
+ .qmi_cnss_feature_bitmap = BIT(CNSS_QDSS_CFG_MISS_V01),
+
+ .rfkill_pin = 0,
+ .rfkill_cfg = 0,
+ .rfkill_on_level = 0,
+
+ .rddm_size = 0,
+
+ .def_num_link = 0,
+ .max_mlo_peer = 256,
+
+ .otp_board_id_register = 0,
+
+ .supports_sta_ps = false,
+
+ .acpi_guid = NULL,
+ .supports_dynamic_smps_6ghz = false,
+ .iova_mask = 0,
+ .supports_aspm = false,
+
+ .ce_ie_addr = &ath12k_ce_ie_addr_ipq5332,
+ .ce_remap = &ath12k_ce_remap_ipq5332,
+ .bdf_addr_offset = 0xC00000,
+
+ .dp_primary_link_only = true,
},
};
diff --git a/drivers/net/wireless/ath/ath12k/hw.h b/drivers/net/wireless/ath/ath12k/hw.h
index 8d52182e28ae..0fbc17649df4 100644
--- a/drivers/net/wireless/ath/ath12k/hw.h
+++ b/drivers/net/wireless/ath/ath12k/hw.h
@@ -1,7 +1,7 @@
/* SPDX-License-Identifier: BSD-3-Clause-Clear */
/*
* Copyright (c) 2018-2021 The Linux Foundation. All rights reserved.
- * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#ifndef ATH12K_HW_H
@@ -97,6 +97,7 @@
#define ATH12K_REGDB_FILE_NAME "regdb.bin"
#define ATH12K_PCIE_MAX_PAYLOAD_SIZE 128
+#define ATH12K_IPQ5332_USERPD_ID 1
enum ath12k_hw_rate_cck {
ATH12K_HW_RATE_CCK_LP_11M = 0,
@@ -121,6 +122,7 @@ enum ath12k_hw_rate_ofdm {
enum ath12k_bus {
ATH12K_BUS_PCI,
+ ATH12K_BUS_AHB,
};
#define ATH12K_EXT_IRQ_GRP_NUM_MAX 11
@@ -133,6 +135,7 @@ enum hal_encrypt_type;
struct ath12k_hw_ring_mask {
u8 tx[ATH12K_EXT_IRQ_GRP_NUM_MAX];
u8 rx_mon_dest[ATH12K_EXT_IRQ_GRP_NUM_MAX];
+ u8 rx_mon_status[ATH12K_EXT_IRQ_GRP_NUM_MAX];
u8 rx[ATH12K_EXT_IRQ_GRP_NUM_MAX];
u8 rx_err[ATH12K_EXT_IRQ_GRP_NUM_MAX];
u8 rx_wbm_rel[ATH12K_EXT_IRQ_GRP_NUM_MAX];
@@ -146,6 +149,11 @@ struct ath12k_hw_hal_params {
u32 wbm2sw_cc_enable;
};
+enum ath12k_m3_fw_loaders {
+ ath12k_m3_fw_loader_driver,
+ ath12k_m3_fw_loader_remoteproc,
+};
+
struct ath12k_hw_params {
const char *name;
u16 hw_rev;
@@ -154,6 +162,7 @@ struct ath12k_hw_params {
const char *dir;
size_t board_size;
size_t cal_offset;
+ enum ath12k_m3_fw_loaders m3_loader;
} fw;
u8 max_radios;
@@ -190,6 +199,7 @@ struct ath12k_hw_params {
bool reoq_lut_support:1;
bool supports_shadow_regs:1;
bool supports_aspm:1;
+ bool current_cc_support:1;
u32 num_tcl_banks;
u32 max_tx_ring;
@@ -220,6 +230,13 @@ struct ath12k_hw_params {
bool supports_dynamic_smps_6ghz;
u32 iova_mask;
+
+ const struct ce_ie_addr *ce_ie_addr;
+ const struct ce_remap *ce_remap;
+ u32 bdf_addr_offset;
+
+ /* setup REO queue, frag etc only for primary link peer */
+ bool dp_primary_link_only:1;
};
struct ath12k_hw_ops {
@@ -293,9 +310,15 @@ struct ath12k_hw_regs {
u32 hal_tcl1_ring_msi1_base_msb;
u32 hal_tcl1_ring_msi1_data;
u32 hal_tcl_ring_base_lsb;
+ u32 hal_tcl1_ring_base_lsb;
+ u32 hal_tcl1_ring_base_msb;
+ u32 hal_tcl2_ring_base_lsb;
u32 hal_tcl_status_ring_base_lsb;
+ u32 hal_reo1_qdesc_addr;
+ u32 hal_reo1_qdesc_max_peerid;
+
u32 hal_wbm_idle_ring_base_lsb;
u32 hal_wbm_idle_ring_misc_addr;
u32 hal_wbm_r0_idle_list_cntl_addr;
@@ -316,6 +339,11 @@ struct ath12k_hw_regs {
u32 pcie_qserdes_sysclk_en_sel;
u32 pcie_pcs_osc_dtct_config_base;
+ u32 hal_umac_ce0_src_reg_base;
+ u32 hal_umac_ce0_dest_reg_base;
+ u32 hal_umac_ce1_src_reg_base;
+ u32 hal_umac_ce1_dest_reg_base;
+
u32 hal_ppe_rel_ring_base;
u32 hal_reo2_ring_base;
diff --git a/drivers/net/wireless/ath/ath12k/mac.c b/drivers/net/wireless/ath/ath12k/mac.c
index 2d062b5904a8..88b59f3ff87a 100644
--- a/drivers/net/wireless/ath/ath12k/mac.c
+++ b/drivers/net/wireless/ath/ath12k/mac.c
@@ -15,10 +15,12 @@
#include "hw.h"
#include "dp_tx.h"
#include "dp_rx.h"
+#include "testmode.h"
#include "peer.h"
#include "debugfs.h"
#include "hif.h"
#include "wow.h"
+#include "debugfs_sta.h"
#define CHAN2G(_channel, _freq, _flags) { \
.band = NL80211_BAND_2GHZ, \
@@ -227,7 +229,8 @@ ath12k_phymodes[NUM_NL80211_BANDS][ATH12K_CHAN_WIDTH_NUM] = {
const struct htt_rx_ring_tlv_filter ath12k_mac_mon_status_filter_default = {
.rx_filter = HTT_RX_FILTER_TLV_FLAGS_MPDU_START |
HTT_RX_FILTER_TLV_FLAGS_PPDU_END |
- HTT_RX_FILTER_TLV_FLAGS_PPDU_END_STATUS_DONE,
+ HTT_RX_FILTER_TLV_FLAGS_PPDU_END_STATUS_DONE |
+ HTT_RX_FILTER_TLV_FLAGS_PPDU_START_USER_INFO,
.pkt_filter_flags0 = HTT_RX_FP_MGMT_FILTER_FLAGS0,
.pkt_filter_flags1 = HTT_RX_FP_MGMT_FILTER_FLAGS1,
.pkt_filter_flags2 = HTT_RX_FP_CTRL_FILTER_FLASG2,
@@ -337,6 +340,82 @@ static const char *ath12k_mac_phymode_str(enum wmi_phy_mode mode)
return "<unknown>";
}
+u16 ath12k_mac_he_convert_tones_to_ru_tones(u16 tones)
+{
+ switch (tones) {
+ case 26:
+ return RU_26;
+ case 52:
+ return RU_52;
+ case 106:
+ return RU_106;
+ case 242:
+ return RU_242;
+ case 484:
+ return RU_484;
+ case 996:
+ return RU_996;
+ case (996 * 2):
+ return RU_2X996;
+ default:
+ return RU_26;
+ }
+}
+
+enum nl80211_eht_gi ath12k_mac_eht_gi_to_nl80211_eht_gi(u8 sgi)
+{
+ switch (sgi) {
+ case RX_MSDU_START_SGI_0_8_US:
+ return NL80211_RATE_INFO_EHT_GI_0_8;
+ case RX_MSDU_START_SGI_1_6_US:
+ return NL80211_RATE_INFO_EHT_GI_1_6;
+ case RX_MSDU_START_SGI_3_2_US:
+ return NL80211_RATE_INFO_EHT_GI_3_2;
+ default:
+ return NL80211_RATE_INFO_EHT_GI_0_8;
+ }
+}
+
+enum nl80211_eht_ru_alloc ath12k_mac_eht_ru_tones_to_nl80211_eht_ru_alloc(u16 ru_tones)
+{
+ switch (ru_tones) {
+ case 26:
+ return NL80211_RATE_INFO_EHT_RU_ALLOC_26;
+ case 52:
+ return NL80211_RATE_INFO_EHT_RU_ALLOC_52;
+ case (52 + 26):
+ return NL80211_RATE_INFO_EHT_RU_ALLOC_52P26;
+ case 106:
+ return NL80211_RATE_INFO_EHT_RU_ALLOC_106;
+ case (106 + 26):
+ return NL80211_RATE_INFO_EHT_RU_ALLOC_106P26;
+ case 242:
+ return NL80211_RATE_INFO_EHT_RU_ALLOC_242;
+ case 484:
+ return NL80211_RATE_INFO_EHT_RU_ALLOC_484;
+ case (484 + 242):
+ return NL80211_RATE_INFO_EHT_RU_ALLOC_484P242;
+ case 996:
+ return NL80211_RATE_INFO_EHT_RU_ALLOC_996;
+ case (996 + 484):
+ return NL80211_RATE_INFO_EHT_RU_ALLOC_996P484;
+ case (996 + 484 + 242):
+ return NL80211_RATE_INFO_EHT_RU_ALLOC_996P484P242;
+ case (2 * 996):
+ return NL80211_RATE_INFO_EHT_RU_ALLOC_2x996;
+ case (2 * 996 + 484):
+ return NL80211_RATE_INFO_EHT_RU_ALLOC_2x996P484;
+ case (3 * 996):
+ return NL80211_RATE_INFO_EHT_RU_ALLOC_3x996;
+ case (3 * 996 + 484):
+ return NL80211_RATE_INFO_EHT_RU_ALLOC_3x996P484;
+ case (4 * 996):
+ return NL80211_RATE_INFO_EHT_RU_ALLOC_4x996;
+ default:
+ return NL80211_RATE_INFO_EHT_RU_ALLOC_26;
+ }
+}
+
enum rate_info_bw
ath12k_mac_bw_to_mac80211_bw(enum ath12k_supported_bw bw)
{
@@ -502,7 +581,28 @@ static int ath12k_mac_vif_link_chan(struct ieee80211_vif *vif, u8 link_id,
return 0;
}
-static struct ieee80211_bss_conf *
+static struct ath12k_link_vif *
+ath12k_mac_get_tx_arvif(struct ath12k_link_vif *arvif,
+ struct ieee80211_bss_conf *link_conf)
+{
+ struct ieee80211_bss_conf *tx_bss_conf;
+ struct ath12k *ar = arvif->ar;
+ struct ath12k_vif *tx_ahvif;
+
+ lockdep_assert_wiphy(ath12k_ar_to_hw(ar)->wiphy);
+
+ tx_bss_conf = wiphy_dereference(ath12k_ar_to_hw(ar)->wiphy,
+ link_conf->tx_bss_conf);
+ if (tx_bss_conf) {
+ tx_ahvif = ath12k_vif_to_ahvif(tx_bss_conf->vif);
+ return wiphy_dereference(tx_ahvif->ah->hw->wiphy,
+ tx_ahvif->link[tx_bss_conf->link_id]);
+ }
+
+ return NULL;
+}
+
+struct ieee80211_bss_conf *
ath12k_mac_get_link_bss_conf(struct ath12k_link_vif *arvif)
{
struct ieee80211_vif *vif = arvif->ahvif->vif;
@@ -675,7 +775,10 @@ struct ath12k *ath12k_mac_get_ar_by_pdev_id(struct ath12k_base *ab, u32 pdev_id)
return NULL;
for (i = 0; i < ab->num_radios; i++) {
- pdev = rcu_dereference(ab->pdevs_active[i]);
+ if (ab->fw_mode == ATH12K_FIRMWARE_MODE_FTM)
+ pdev = &ab->pdevs[i];
+ else
+ pdev = rcu_dereference(ab->pdevs_active[i]);
if (pdev && pdev->pdev_id == pdev_id)
return (pdev->ar ? pdev->ar : NULL);
@@ -725,9 +828,9 @@ static struct ath12k *ath12k_get_ar_by_ctx(struct ieee80211_hw *hw,
return ath12k_mac_get_ar_by_chan(hw, ctx->def.chan);
}
-static struct ath12k *ath12k_get_ar_by_vif(struct ieee80211_hw *hw,
- struct ieee80211_vif *vif,
- u8 link_id)
+struct ath12k *ath12k_get_ar_by_vif(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ u8 link_id)
{
struct ath12k_vif *ahvif = ath12k_vif_to_ahvif(vif);
struct ath12k_hw *ah = ath12k_hw_to_ah(hw);
@@ -780,12 +883,12 @@ static bool ath12k_mac_band_match(enum nl80211_band band1, enum WMI_HOST_WLAN_BA
{
switch (band1) {
case NL80211_BAND_2GHZ:
- if (band2 & WMI_HOST_WLAN_2G_CAP)
+ if (band2 & WMI_HOST_WLAN_2GHZ_CAP)
return true;
break;
case NL80211_BAND_5GHZ:
case NL80211_BAND_6GHZ:
- if (band2 & WMI_HOST_WLAN_5G_CAP)
+ if (band2 & WMI_HOST_WLAN_5GHZ_CAP)
return true;
break;
default:
@@ -886,7 +989,7 @@ static int ath12k_mac_txpower_recalc(struct ath12k *ar)
ath12k_dbg(ar->ab, ATH12K_DBG_MAC, "txpower to set in hw %d\n",
txpower / 2);
- if ((pdev->cap.supported_bands & WMI_HOST_WLAN_2G_CAP) &&
+ if ((pdev->cap.supported_bands & WMI_HOST_WLAN_2GHZ_CAP) &&
ar->txpower_limit_2g != txpower) {
param = WMI_PDEV_PARAM_TXPOWER_LIMIT2G;
ret = ath12k_wmi_pdev_set_param(ar, param,
@@ -896,7 +999,7 @@ static int ath12k_mac_txpower_recalc(struct ath12k *ar)
ar->txpower_limit_2g = txpower;
}
- if ((pdev->cap.supported_bands & WMI_HOST_WLAN_5G_CAP) &&
+ if ((pdev->cap.supported_bands & WMI_HOST_WLAN_5GHZ_CAP) &&
ar->txpower_limit_5g != txpower) {
param = WMI_PDEV_PARAM_TXPOWER_LIMIT5G;
ret = ath12k_wmi_pdev_set_param(ar, param,
@@ -1151,61 +1254,6 @@ static int ath12k_mac_monitor_vdev_stop(struct ath12k *ar)
return ret;
}
-static int ath12k_mac_monitor_vdev_create(struct ath12k *ar)
-{
- struct ath12k_pdev *pdev = ar->pdev;
- struct ath12k_wmi_vdev_create_arg arg = {};
- int bit, ret;
- u8 tmp_addr[6];
-
- lockdep_assert_wiphy(ath12k_ar_to_hw(ar)->wiphy);
-
- if (ar->monitor_vdev_created)
- return 0;
-
- if (ar->ab->free_vdev_map == 0) {
- ath12k_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;
-
- arg.if_id = ar->monitor_vdev_id;
- arg.type = WMI_VDEV_TYPE_MONITOR;
- arg.subtype = WMI_VDEV_SUBTYPE_NONE;
- arg.pdev_id = pdev->pdev_id;
- arg.if_stats_id = ATH12K_INVAL_VDEV_STATS_ID;
-
- if (pdev->cap.supported_bands & WMI_HOST_WLAN_2G_CAP) {
- arg.chains[NL80211_BAND_2GHZ].tx = ar->num_tx_chains;
- arg.chains[NL80211_BAND_2GHZ].rx = ar->num_rx_chains;
- }
-
- if (pdev->cap.supported_bands & WMI_HOST_WLAN_5G_CAP) {
- arg.chains[NL80211_BAND_5GHZ].tx = ar->num_tx_chains;
- arg.chains[NL80211_BAND_5GHZ].rx = ar->num_rx_chains;
- }
-
- ret = ath12k_wmi_vdev_create(ar, tmp_addr, &arg);
- if (ret) {
- ath12k_warn(ar->ab, "failed to request monitor vdev %i creation: %d\n",
- ar->monitor_vdev_id, ret);
- ar->monitor_vdev_id = -1;
- return ret;
- }
-
- 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_created = true;
- ath12k_dbg(ar->ab, ATH12K_DBG_MAC, "mac monitor vdev %d created\n",
- ar->monitor_vdev_id);
-
- return 0;
-}
-
static int ath12k_mac_monitor_vdev_delete(struct ath12k *ar)
{
int ret;
@@ -1242,19 +1290,9 @@ static int ath12k_mac_monitor_vdev_delete(struct ath12k *ar)
return ret;
}
-static void
-ath12k_mac_get_any_chandef_iter(struct ieee80211_hw *hw,
- struct ieee80211_chanctx_conf *conf,
- void *data)
-{
- struct cfg80211_chan_def **def = data;
-
- *def = &conf->def;
-}
-
static int ath12k_mac_monitor_start(struct ath12k *ar)
{
- struct cfg80211_chan_def *chandef = NULL;
+ struct ath12k_mac_get_any_chanctx_conf_arg arg;
int ret;
lockdep_assert_wiphy(ath12k_ar_to_hw(ar)->wiphy);
@@ -1262,25 +1300,33 @@ static int ath12k_mac_monitor_start(struct ath12k *ar)
if (ar->monitor_started)
return 0;
+ arg.ar = ar;
+ arg.chanctx_conf = NULL;
ieee80211_iter_chan_contexts_atomic(ath12k_ar_to_hw(ar),
- ath12k_mac_get_any_chandef_iter,
- &chandef);
- if (!chandef)
+ ath12k_mac_get_any_chanctx_conf_iter,
+ &arg);
+ if (!arg.chanctx_conf)
return 0;
- ret = ath12k_mac_monitor_vdev_start(ar, ar->monitor_vdev_id, chandef);
+ ret = ath12k_mac_monitor_vdev_start(ar, ar->monitor_vdev_id,
+ &arg.chanctx_conf->def);
if (ret) {
ath12k_warn(ar->ab, "failed to start monitor vdev: %d\n", ret);
- ath12k_mac_monitor_vdev_delete(ar);
+ return ret;
+ }
+
+ ret = ath12k_dp_tx_htt_monitor_mode_ring_config(ar, false);
+ if (ret) {
+ ath12k_warn(ar->ab, "fail to set monitor filter: %d\n", ret);
return ret;
}
ar->monitor_started = true;
ar->num_started_vdevs++;
- ret = ath12k_dp_tx_htt_monitor_mode_ring_config(ar, false);
- ath12k_dbg(ar->ab, ATH12K_DBG_MAC, "mac monitor started ret %d\n", ret);
- return ret;
+ ath12k_dbg(ar->ab, ATH12K_DBG_MAC, "mac monitor started\n");
+
+ return 0;
}
static int ath12k_mac_monitor_stop(struct ath12k *ar)
@@ -1346,58 +1392,9 @@ err:
return ret;
}
-static int ath12k_mac_config(struct ath12k *ar, u32 changed)
-{
- struct ieee80211_hw *hw = ath12k_ar_to_hw(ar);
- struct ieee80211_conf *conf = &hw->conf;
- int ret = 0;
-
- lockdep_assert_wiphy(hw->wiphy);
-
- if (changed & IEEE80211_CONF_CHANGE_MONITOR) {
- ar->monitor_conf_enabled = conf->flags & IEEE80211_CONF_MONITOR;
- if (ar->monitor_conf_enabled) {
- if (ar->monitor_vdev_created)
- return ret;
- ret = ath12k_mac_monitor_vdev_create(ar);
- if (ret)
- return ret;
- ret = ath12k_mac_monitor_start(ar);
- if (ret)
- goto err_mon_del;
- } else {
- if (!ar->monitor_vdev_created)
- return ret;
- ret = ath12k_mac_monitor_stop(ar);
- if (ret)
- return ret;
- ath12k_mac_monitor_vdev_delete(ar);
- }
- }
-
- return ret;
-
-err_mon_del:
- ath12k_mac_monitor_vdev_delete(ar);
- return ret;
-}
-
static int ath12k_mac_op_config(struct ieee80211_hw *hw, u32 changed)
{
- struct ath12k_hw *ah = ath12k_hw_to_ah(hw);
- struct ath12k *ar;
- int ret;
-
- lockdep_assert_wiphy(hw->wiphy);
-
- ar = ath12k_ah_to_ar(ah, 0);
-
- ret = ath12k_mac_config(ar, changed);
- if (ret)
- ath12k_warn(ar->ab, "failed to update config pdev idx %d: %d\n",
- ar->pdev_idx, ret);
-
- return ret;
+ return 0;
}
static int ath12k_mac_setup_bcn_p2p_ie(struct ath12k_link_vif *arvif,
@@ -1549,30 +1546,18 @@ static void ath12k_mac_set_arvif_ies(struct ath12k_link_vif *arvif, struct sk_bu
}
}
-static int ath12k_mac_setup_bcn_tmpl_ema(struct ath12k_link_vif *arvif)
+static int ath12k_mac_setup_bcn_tmpl_ema(struct ath12k_link_vif *arvif,
+ struct ath12k_link_vif *tx_arvif,
+ u8 bssid_index)
{
- struct ath12k_vif *ahvif = arvif->ahvif;
- struct ieee80211_bss_conf *bss_conf;
struct ath12k_wmi_bcn_tmpl_ema_arg ema_args;
struct ieee80211_ema_beacons *beacons;
- struct ath12k_link_vif *tx_arvif;
bool nontx_profile_found = false;
- struct ath12k_vif *tx_ahvif;
int ret = 0;
u8 i;
- bss_conf = ath12k_mac_get_link_bss_conf(arvif);
- if (!bss_conf) {
- ath12k_warn(arvif->ar->ab,
- "failed to get link bss conf to update bcn tmpl for vif %pM link %u\n",
- ahvif->vif->addr, arvif->link_id);
- return -ENOLINK;
- }
-
- tx_ahvif = ath12k_vif_to_ahvif(ahvif->vif->mbssid_tx_vif);
- tx_arvif = &tx_ahvif->deflink;
beacons = ieee80211_beacon_get_template_ema_list(ath12k_ar_to_hw(tx_arvif->ar),
- tx_ahvif->vif,
+ tx_arvif->ahvif->vif,
tx_arvif->link_id);
if (!beacons || !beacons->cnt) {
ath12k_warn(arvif->ar->ab,
@@ -1586,13 +1571,12 @@ static int ath12k_mac_setup_bcn_tmpl_ema(struct ath12k_link_vif *arvif)
for (i = 0; i < beacons->cnt; i++) {
if (tx_arvif != arvif && !nontx_profile_found)
ath12k_mac_set_arvif_ies(arvif, beacons->bcn[i].skb,
- bss_conf->bssid_index,
+ bssid_index,
&nontx_profile_found);
ema_args.bcn_cnt = beacons->cnt;
ema_args.bcn_index = i;
- ret = ath12k_wmi_bcn_tmpl(tx_arvif->ar, tx_arvif->vdev_id,
- &beacons->bcn[i].offs,
+ ret = ath12k_wmi_bcn_tmpl(tx_arvif, &beacons->bcn[i].offs,
beacons->bcn[i].skb, &ema_args);
if (ret) {
ath12k_warn(tx_arvif->ar->ab,
@@ -1605,7 +1589,7 @@ static int ath12k_mac_setup_bcn_tmpl_ema(struct ath12k_link_vif *arvif)
if (tx_arvif != arvif && !nontx_profile_found)
ath12k_warn(arvif->ar->ab,
"nontransmitted bssid index %u not found in beacon template\n",
- bss_conf->bssid_index);
+ bssid_index);
ieee80211_beacon_free_ema_list(beacons);
return ret;
@@ -1616,11 +1600,10 @@ static int ath12k_mac_setup_bcn_tmpl(struct ath12k_link_vif *arvif)
struct ath12k_vif *ahvif = arvif->ahvif;
struct ieee80211_vif *vif = ath12k_ahvif_to_vif(ahvif);
struct ieee80211_bss_conf *link_conf;
- struct ath12k_link_vif *tx_arvif = arvif;
+ struct ath12k_link_vif *tx_arvif;
struct ath12k *ar = arvif->ar;
struct ath12k_base *ab = ar->ab;
struct ieee80211_mutable_offsets offs = {};
- struct ath12k_vif *tx_ahvif = ahvif;
bool nontx_profile_found = false;
struct sk_buff *bcn;
int ret;
@@ -1635,17 +1618,20 @@ static int ath12k_mac_setup_bcn_tmpl(struct ath12k_link_vif *arvif)
return -ENOLINK;
}
- if (vif->mbssid_tx_vif) {
- tx_ahvif = ath12k_vif_to_ahvif(vif->mbssid_tx_vif);
- tx_arvif = &tx_ahvif->deflink;
+ tx_arvif = ath12k_mac_get_tx_arvif(arvif, link_conf);
+ if (tx_arvif) {
if (tx_arvif != arvif && arvif->is_up)
return 0;
if (link_conf->ema_ap)
- return ath12k_mac_setup_bcn_tmpl_ema(arvif);
+ return ath12k_mac_setup_bcn_tmpl_ema(arvif, tx_arvif,
+ link_conf->bssid_index);
+ } else {
+ tx_arvif = arvif;
}
- bcn = ieee80211_beacon_get_template(ath12k_ar_to_hw(tx_arvif->ar), tx_ahvif->vif,
+ bcn = ieee80211_beacon_get_template(ath12k_ar_to_hw(tx_arvif->ar),
+ tx_arvif->ahvif->vif,
&offs, tx_arvif->link_id);
if (!bcn) {
ath12k_warn(ab, "failed to get beacon template from mac80211\n");
@@ -1686,7 +1672,7 @@ static int ath12k_mac_setup_bcn_tmpl(struct ath12k_link_vif *arvif)
}
}
- ret = ath12k_wmi_bcn_tmpl(ar, arvif->vdev_id, &offs, bcn, NULL);
+ ret = ath12k_wmi_bcn_tmpl(arvif, &offs, bcn, NULL);
if (ret)
ath12k_warn(ab, "failed to submit beacon template command: %d\n",
@@ -1702,6 +1688,8 @@ static void ath12k_control_beaconing(struct ath12k_link_vif *arvif,
{
struct ath12k_wmi_vdev_up_params params = {};
struct ath12k_vif *ahvif = arvif->ahvif;
+ struct ieee80211_bss_conf *link_conf;
+ struct ath12k_link_vif *tx_arvif;
struct ath12k *ar = arvif->ar;
int ret;
@@ -1732,11 +1720,17 @@ static void ath12k_control_beaconing(struct ath12k_link_vif *arvif,
params.vdev_id = arvif->vdev_id;
params.aid = ahvif->aid;
params.bssid = arvif->bssid;
- if (ahvif->vif->mbssid_tx_vif) {
- struct ath12k_vif *tx_ahvif =
- ath12k_vif_to_ahvif(ahvif->vif->mbssid_tx_vif);
- struct ath12k_link_vif *tx_arvif = &tx_ahvif->deflink;
+ link_conf = ath12k_mac_get_link_bss_conf(arvif);
+ if (!link_conf) {
+ ath12k_warn(ar->ab,
+ "unable to access bss link conf for link %u required to retrieve transmitting link conf\n",
+ arvif->link_id);
+ return;
+ }
+
+ tx_arvif = ath12k_mac_get_tx_arvif(arvif, link_conf);
+ if (tx_arvif) {
params.tx_bssid = tx_arvif->bssid;
params.nontx_profile_idx = info->bssid_index;
params.nontx_profile_cnt = 1 << info->bssid_indicator;
@@ -2945,6 +2939,7 @@ static void ath12k_peer_assoc_h_eht(struct ath12k *ar,
const struct ieee80211_sta_eht_cap *eht_cap;
const struct ieee80211_sta_he_cap *he_cap;
struct ieee80211_link_sta *link_sta;
+ struct ieee80211_bss_conf *link_conf;
u32 *rx_mcs, *tx_mcs;
lockdep_assert_wiphy(ath12k_ar_to_hw(ar)->wiphy);
@@ -2956,6 +2951,12 @@ static void ath12k_peer_assoc_h_eht(struct ath12k *ar,
return;
}
+ link_conf = ath12k_mac_get_link_bss_conf(arvif);
+ if (!link_conf) {
+ ath12k_warn(ar->ab, "unable to access link_conf in peer assoc eht set\n");
+ return;
+ }
+
eht_cap = &link_sta->eht_cap;
he_cap = &link_sta->he_cap;
if (!he_cap->has_he || !eht_cap->has_eht)
@@ -3027,6 +3028,7 @@ static void ath12k_peer_assoc_h_eht(struct ath12k *ar,
}
arg->punct_bitmap = ~arvif->punct_bitmap;
+ arg->eht_disable_mcs15 = link_conf->eht_disable_mcs15;
}
static void ath12k_peer_assoc_h_mlo(struct ath12k_link_sta *arsta,
@@ -3057,6 +3059,7 @@ static void ath12k_peer_assoc_h_mlo(struct ath12k_link_sta *arsta,
ml->ml_peer_id = ahsta->ml_peer_id;
ml->ieee_link_id = arsta->link_id;
ml->num_partner_links = 0;
+ ml->eml_cap = sta->eml_cap;
links = ahsta->links_map;
rcu_read_lock();
@@ -3116,6 +3119,7 @@ static void ath12k_peer_assoc_prepare(struct ath12k *ar,
ath12k_peer_assoc_h_smps(arsta, arg);
ath12k_peer_assoc_h_mlo(arsta, arg);
+ arsta->peer_nss = arg->peer_nss;
/* TODO: amsdu_disable req? */
}
@@ -3138,6 +3142,37 @@ static int ath12k_setup_peer_smps(struct ath12k *ar, struct ath12k_link_vif *arv
ath12k_smps_map[smps]);
}
+static u32 ath12k_mac_ieee80211_sta_bw_to_wmi(struct ath12k *ar,
+ struct ieee80211_link_sta *link_sta)
+{
+ u32 bw;
+
+ switch (link_sta->bandwidth) {
+ case IEEE80211_STA_RX_BW_20:
+ bw = WMI_PEER_CHWIDTH_20MHZ;
+ break;
+ case IEEE80211_STA_RX_BW_40:
+ bw = WMI_PEER_CHWIDTH_40MHZ;
+ break;
+ case IEEE80211_STA_RX_BW_80:
+ bw = WMI_PEER_CHWIDTH_80MHZ;
+ break;
+ case IEEE80211_STA_RX_BW_160:
+ bw = WMI_PEER_CHWIDTH_160MHZ;
+ break;
+ case IEEE80211_STA_RX_BW_320:
+ bw = WMI_PEER_CHWIDTH_320MHZ;
+ break;
+ default:
+ ath12k_warn(ar->ab, "Invalid bandwidth %d for link station %pM\n",
+ link_sta->bandwidth, link_sta->addr);
+ bw = WMI_PEER_CHWIDTH_20MHZ;
+ break;
+ }
+
+ return bw;
+}
+
static void ath12k_bss_assoc(struct ath12k *ar,
struct ath12k_link_vif *arvif,
struct ieee80211_bss_conf *bss_conf)
@@ -3263,6 +3298,11 @@ static void ath12k_bss_assoc(struct ath12k *ar,
if (ret)
ath12k_warn(ar->ab, "failed to set vdev %i OBSS PD parameters: %d\n",
arvif->vdev_id, ret);
+
+ if (test_bit(WMI_TLV_SERVICE_11D_OFFLOAD, ar->ab->wmi_ab.svc_map) &&
+ ahvif->vdev_type == WMI_VDEV_TYPE_STA &&
+ ahvif->vdev_subtype == WMI_VDEV_SUBTYPE_NONE)
+ ath12k_mac_11d_scan_stop_all(ar->ab);
}
static void ath12k_bss_disassoc(struct ath12k *ar,
@@ -3336,7 +3376,10 @@ static void ath12k_recalculate_mgmt_rate(struct ath12k *ar,
}
sband = hw->wiphy->bands[def->chan->band];
- basic_rate_idx = ffs(bss_conf->basic_rates) - 1;
+ if (bss_conf->basic_rates)
+ basic_rate_idx = __ffs(bss_conf->basic_rates);
+ else
+ basic_rate_idx = 0;
bitrate = sband->bitrates[basic_rate_idx].bitrate;
hw_rate_code = ath12k_mac_get_rate_hw_value(bitrate);
@@ -3358,12 +3401,181 @@ static void ath12k_recalculate_mgmt_rate(struct ath12k *ar,
ath12k_warn(ar->ab, "failed to set beacon tx rate %d\n", ret);
}
+static void ath12k_mac_init_arvif(struct ath12k_vif *ahvif,
+ struct ath12k_link_vif *arvif, int link_id)
+{
+ struct ath12k_hw *ah = ahvif->ah;
+ u8 _link_id;
+ int i;
+
+ lockdep_assert_wiphy(ah->hw->wiphy);
+
+ if (WARN_ON(!arvif))
+ return;
+
+ if (WARN_ON(link_id >= ATH12K_NUM_MAX_LINKS))
+ return;
+
+ if (link_id < 0)
+ _link_id = 0;
+ else
+ _link_id = link_id;
+
+ arvif->ahvif = ahvif;
+ arvif->link_id = _link_id;
+
+ /* Protects the datapath stats update on a per link basis */
+ spin_lock_init(&arvif->link_stats_lock);
+
+ INIT_LIST_HEAD(&arvif->list);
+ INIT_DELAYED_WORK(&arvif->connection_loss_work,
+ ath12k_mac_vif_sta_connection_loss_work);
+
+ for (i = 0; i < ARRAY_SIZE(arvif->bitrate_mask.control); i++) {
+ arvif->bitrate_mask.control[i].legacy = 0xffffffff;
+ 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));
+ }
+
+ /* Handle MLO related assignments */
+ if (link_id >= 0) {
+ rcu_assign_pointer(ahvif->link[arvif->link_id], arvif);
+ ahvif->links_map |= BIT(_link_id);
+ }
+
+ ath12k_generic_dbg(ATH12K_DBG_MAC,
+ "mac init link arvif (link_id %d%s) for vif %pM. links_map 0x%x",
+ _link_id, (link_id < 0) ? " deflink" : "", ahvif->vif->addr,
+ ahvif->links_map);
+}
+
+static void ath12k_mac_remove_link_interface(struct ieee80211_hw *hw,
+ struct ath12k_link_vif *arvif)
+{
+ struct ath12k_vif *ahvif = arvif->ahvif;
+ struct ath12k_hw *ah = hw->priv;
+ struct ath12k *ar = arvif->ar;
+ int ret;
+
+ lockdep_assert_wiphy(ah->hw->wiphy);
+
+ cancel_delayed_work_sync(&arvif->connection_loss_work);
+
+ ath12k_dbg(ar->ab, ATH12K_DBG_MAC, "mac remove link interface (vdev %d link id %d)",
+ arvif->vdev_id, arvif->link_id);
+
+ if (test_bit(WMI_TLV_SERVICE_11D_OFFLOAD, ar->ab->wmi_ab.svc_map) &&
+ ahvif->vdev_type == WMI_VDEV_TYPE_STA &&
+ ahvif->vdev_subtype == WMI_VDEV_SUBTYPE_NONE)
+ ath12k_mac_11d_scan_stop(ar);
+
+ if (ahvif->vdev_type == WMI_VDEV_TYPE_AP) {
+ ret = ath12k_peer_delete(ar, arvif->vdev_id, arvif->bssid);
+ if (ret)
+ ath12k_warn(ar->ab, "failed to submit AP self-peer removal on vdev %d link id %d: %d",
+ arvif->vdev_id, arvif->link_id, ret);
+ }
+ ath12k_mac_vdev_delete(ar, arvif);
+}
+
+static struct ath12k_link_vif *ath12k_mac_assign_link_vif(struct ath12k_hw *ah,
+ struct ieee80211_vif *vif,
+ u8 link_id)
+{
+ struct ath12k_vif *ahvif = ath12k_vif_to_ahvif(vif);
+ struct ath12k_link_vif *arvif;
+
+ lockdep_assert_wiphy(ah->hw->wiphy);
+
+ arvif = wiphy_dereference(ah->hw->wiphy, ahvif->link[link_id]);
+ if (arvif)
+ return arvif;
+
+ /* If this is the first link arvif being created for an ML VIF
+ * use the preallocated deflink memory except for scan arvifs
+ */
+ if (!ahvif->links_map && link_id != ATH12K_DEFAULT_SCAN_LINK) {
+ arvif = &ahvif->deflink;
+
+ if (vif->type == NL80211_IFTYPE_STATION)
+ arvif->is_sta_assoc_link = true;
+ } else {
+ arvif = kzalloc(sizeof(*arvif), GFP_KERNEL);
+ if (!arvif)
+ return NULL;
+ }
+
+ ath12k_mac_init_arvif(ahvif, arvif, link_id);
+
+ return arvif;
+}
+
+static void ath12k_mac_unassign_link_vif(struct ath12k_link_vif *arvif)
+{
+ struct ath12k_vif *ahvif = arvif->ahvif;
+ struct ath12k_hw *ah = ahvif->ah;
+
+ lockdep_assert_wiphy(ah->hw->wiphy);
+
+ rcu_assign_pointer(ahvif->link[arvif->link_id], NULL);
+ synchronize_rcu();
+ ahvif->links_map &= ~BIT(arvif->link_id);
+
+ if (arvif != &ahvif->deflink)
+ kfree(arvif);
+ else
+ memset(arvif, 0, sizeof(*arvif));
+}
+
static int
ath12k_mac_op_change_vif_links(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
u16 old_links, u16 new_links,
struct ieee80211_bss_conf *ol[IEEE80211_MLD_MAX_NUM_LINKS])
{
+ struct ath12k_vif *ahvif = ath12k_vif_to_ahvif(vif);
+ unsigned long to_remove = old_links & ~new_links;
+ unsigned long to_add = ~old_links & new_links;
+ struct ath12k_hw *ah = ath12k_hw_to_ah(hw);
+ struct ath12k_link_vif *arvif;
+ u8 link_id;
+
+ lockdep_assert_wiphy(hw->wiphy);
+
+ ath12k_generic_dbg(ATH12K_DBG_MAC,
+ "mac vif link changed for MLD %pM old_links 0x%x new_links 0x%x\n",
+ vif->addr, old_links, new_links);
+
+ for_each_set_bit(link_id, &to_add, IEEE80211_MLD_MAX_NUM_LINKS) {
+ arvif = wiphy_dereference(hw->wiphy, ahvif->link[link_id]);
+ /* mac80211 wants to add link but driver already has the
+ * link. This should not happen ideally.
+ */
+ if (WARN_ON(arvif))
+ return -EINVAL;
+
+ arvif = ath12k_mac_assign_link_vif(ah, vif, link_id);
+ if (WARN_ON(!arvif))
+ return -EINVAL;
+ }
+
+ for_each_set_bit(link_id, &to_remove, IEEE80211_MLD_MAX_NUM_LINKS) {
+ arvif = wiphy_dereference(hw->wiphy, ahvif->link[link_id]);
+ if (WARN_ON(!arvif))
+ return -EINVAL;
+
+ if (!arvif->is_created)
+ continue;
+
+ if (WARN_ON(!arvif->ar))
+ return -EINVAL;
+
+ ath12k_mac_remove_link_interface(hw, arvif);
+ ath12k_mac_unassign_link_vif(arvif);
+ }
+
return 0;
}
@@ -3422,6 +3634,8 @@ static void ath12k_mac_op_vif_cfg_changed(struct ieee80211_hw *hw,
unsigned long links = ahvif->links_map;
struct ieee80211_bss_conf *info;
struct ath12k_link_vif *arvif;
+ struct ieee80211_sta *sta;
+ struct ath12k_sta *ahsta;
struct ath12k *ar;
u8 link_id;
@@ -3434,6 +3648,35 @@ static void ath12k_mac_op_vif_cfg_changed(struct ieee80211_hw *hw,
}
if (changed & BSS_CHANGED_ASSOC) {
+ if (vif->cfg.assoc) {
+ /* only in station mode we can get here, so it's safe
+ * to use ap_addr
+ */
+ rcu_read_lock();
+ sta = ieee80211_find_sta(vif, vif->cfg.ap_addr);
+ if (!sta) {
+ rcu_read_unlock();
+ WARN_ONCE(1, "failed to find sta with addr %pM\n",
+ vif->cfg.ap_addr);
+ return;
+ }
+
+ ahsta = ath12k_sta_to_ahsta(sta);
+ arvif = wiphy_dereference(hw->wiphy,
+ ahvif->link[ahsta->assoc_link_id]);
+ rcu_read_unlock();
+
+ ar = arvif->ar;
+ /* there is no reason for which an assoc link's
+ * bss info does not exist
+ */
+ info = ath12k_mac_get_link_bss_conf(arvif);
+ ath12k_bss_assoc(ar, arvif, info);
+
+ /* exclude assoc link as it is done above */
+ links &= ~BIT(ahsta->assoc_link_id);
+ }
+
for_each_set_bit(link_id, &links, IEEE80211_MLD_MAX_NUM_LINKS) {
arvif = wiphy_dereference(hw->wiphy, ahvif->link[link_id]);
if (!arvif || !arvif->ar)
@@ -3509,6 +3752,18 @@ static void ath12k_mac_vif_setup_ps(struct ath12k_link_vif *arvif)
psmode, arvif->vdev_id, ret);
}
+static bool ath12k_mac_supports_station_tpc(struct ath12k *ar,
+ struct ath12k_vif *ahvif,
+ const struct cfg80211_chan_def *chandef)
+{
+ return ath12k_wmi_supports_6ghz_cc_ext(ar) &&
+ test_bit(WMI_TLV_SERVICE_EXT_TPC_REG_SUPPORT, ar->ab->wmi_ab.svc_map) &&
+ ahvif->vdev_type == WMI_VDEV_TYPE_STA &&
+ ahvif->vdev_subtype == WMI_VDEV_SUBTYPE_NONE &&
+ chandef->chan &&
+ chandef->chan->band == NL80211_BAND_6GHZ;
+}
+
static void ath12k_mac_bss_info_changed(struct ath12k *ar,
struct ath12k_link_vif *arvif,
struct ieee80211_bss_conf *info,
@@ -3703,12 +3958,16 @@ static void ath12k_mac_bss_info_changed(struct ath12k *ar,
band = def.chan->band;
mcast_rate = info->mcast_rate[band];
- if (mcast_rate > 0)
+ if (mcast_rate > 0) {
rateidx = mcast_rate - 1;
- else
- rateidx = ffs(info->basic_rates) - 1;
+ } else {
+ if (info->basic_rates)
+ rateidx = __ffs(info->basic_rates);
+ else
+ rateidx = 0;
+ }
- if (ar->pdev->cap.supported_bands & WMI_HOST_WLAN_5G_CAP)
+ if (ar->pdev->cap.supported_bands & WMI_HOST_WLAN_5GHZ_CAP)
rateidx += ATH12K_MAC_FIRST_OFDM_RATE_IDX;
bitrate = ath12k_legacy_rates[rateidx].bitrate;
@@ -3862,109 +4121,6 @@ static void ath12k_mac_op_link_info_changed(struct ieee80211_hw *hw,
ath12k_mac_bss_info_changed(ar, arvif, info, changed);
}
-static struct ath12k_link_vif *ath12k_mac_assign_link_vif(struct ath12k_hw *ah,
- struct ieee80211_vif *vif,
- u8 link_id)
-{
- struct ath12k_vif *ahvif = ath12k_vif_to_ahvif(vif);
- struct ath12k_link_vif *arvif;
- int i;
-
- lockdep_assert_wiphy(ah->hw->wiphy);
-
- arvif = wiphy_dereference(ah->hw->wiphy, ahvif->link[link_id]);
- if (arvif)
- return arvif;
-
- if (!vif->valid_links) {
- /* Use deflink for Non-ML VIFs and mark the link id as 0
- */
- link_id = 0;
- arvif = &ahvif->deflink;
- } else {
- /* If this is the first link arvif being created for an ML VIF
- * use the preallocated deflink memory except for scan arvifs
- */
- if (!ahvif->links_map && link_id != ATH12K_DEFAULT_SCAN_LINK) {
- arvif = &ahvif->deflink;
- } else {
- arvif = (struct ath12k_link_vif *)
- kzalloc(sizeof(struct ath12k_link_vif), GFP_KERNEL);
- if (!arvif)
- return NULL;
- }
- }
-
- arvif->ahvif = ahvif;
- arvif->link_id = link_id;
- ahvif->links_map |= BIT(link_id);
-
- INIT_LIST_HEAD(&arvif->list);
- INIT_DELAYED_WORK(&arvif->connection_loss_work,
- ath12k_mac_vif_sta_connection_loss_work);
-
- for (i = 0; i < ARRAY_SIZE(arvif->bitrate_mask.control); i++) {
- arvif->bitrate_mask.control[i].legacy = 0xffffffff;
- 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));
- }
-
- /* Allocate Default Queue now and reassign during actual vdev create */
- vif->cab_queue = ATH12K_HW_DEFAULT_QUEUE;
- for (i = 0; i < ARRAY_SIZE(vif->hw_queue); i++)
- vif->hw_queue[i] = ATH12K_HW_DEFAULT_QUEUE;
-
- vif->driver_flags |= IEEE80211_VIF_SUPPORTS_UAPSD;
-
- rcu_assign_pointer(ahvif->link[arvif->link_id], arvif);
- ahvif->links_map |= BIT(link_id);
- synchronize_rcu();
- return arvif;
-}
-
-static void ath12k_mac_unassign_link_vif(struct ath12k_link_vif *arvif)
-{
- struct ath12k_vif *ahvif = arvif->ahvif;
- struct ath12k_hw *ah = ahvif->ah;
-
- lockdep_assert_wiphy(ah->hw->wiphy);
-
- rcu_assign_pointer(ahvif->link[arvif->link_id], NULL);
- synchronize_rcu();
- ahvif->links_map &= ~BIT(arvif->link_id);
-
- if (arvif != &ahvif->deflink)
- kfree(arvif);
- else
- memset(arvif, 0, sizeof(*arvif));
-}
-
-static void ath12k_mac_remove_link_interface(struct ieee80211_hw *hw,
- struct ath12k_link_vif *arvif)
-{
- struct ath12k_vif *ahvif = arvif->ahvif;
- struct ath12k_hw *ah = hw->priv;
- struct ath12k *ar = arvif->ar;
- int ret;
-
- lockdep_assert_wiphy(ah->hw->wiphy);
-
- cancel_delayed_work_sync(&arvif->connection_loss_work);
-
- ath12k_dbg(ar->ab, ATH12K_DBG_MAC, "mac remove link interface (vdev %d link id %d)",
- arvif->vdev_id, arvif->link_id);
-
- if (ahvif->vdev_type == WMI_VDEV_TYPE_AP) {
- ret = ath12k_peer_delete(ar, arvif->vdev_id, arvif->bssid);
- if (ret)
- ath12k_warn(ar->ab, "failed to submit AP self-peer removal on vdev %d link id %d: %d",
- arvif->vdev_id, arvif->link_id, ret);
- }
- ath12k_mac_vdev_delete(ar, arvif);
-}
-
static struct ath12k*
ath12k_mac_select_scan_device(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
@@ -3985,9 +4141,9 @@ ath12k_mac_select_scan_device(struct ieee80211_hw *hw,
* split the hw request and perform multiple scans
*/
- if (center_freq < ATH12K_MIN_5G_FREQ)
+ if (center_freq < ATH12K_MIN_5GHZ_FREQ)
band = NL80211_BAND_2GHZ;
- else if (center_freq < ATH12K_MIN_6G_FREQ)
+ else if (center_freq < ATH12K_MIN_6GHZ_FREQ)
band = NL80211_BAND_5GHZ;
else
band = NL80211_BAND_6GHZ;
@@ -4017,7 +4173,7 @@ void __ath12k_mac_scan_finish(struct ath12k *ar)
fallthrough;
case ATH12K_SCAN_STARTING:
cancel_delayed_work(&ar->scan.timeout);
- complete(&ar->scan.completed);
+ complete_all(&ar->scan.completed);
wiphy_work_queue(ar->ah->hw->wiphy, &ar->scan.vdev_clean_wk);
break;
}
@@ -4199,6 +4355,145 @@ static int ath12k_start_scan(struct ath12k *ar,
return 0;
}
+int ath12k_mac_get_fw_stats(struct ath12k *ar,
+ struct ath12k_fw_stats_req_params *param)
+{
+ struct ath12k_base *ab = ar->ab;
+ struct ath12k_hw *ah = ath12k_ar_to_ah(ar);
+ unsigned long timeout, time_left;
+ int ret;
+
+ guard(mutex)(&ah->hw_mutex);
+
+ if (ah->state != ATH12K_HW_STATE_ON)
+ return -ENETDOWN;
+
+ /* FW stats can get split when exceeding the stats data buffer limit.
+ * In that case, since there is no end marking for the back-to-back
+ * 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 * 1000);
+ ath12k_fw_stats_reset(ar);
+
+ reinit_completion(&ar->fw_stats_complete);
+
+ ret = ath12k_wmi_send_stats_request_cmd(ar, param->stats_id,
+ param->vdev_id, param->pdev_id);
+
+ if (ret) {
+ ath12k_warn(ab, "failed to request fw stats: %d\n", ret);
+ return ret;
+ }
+
+ ath12k_dbg(ab, ATH12K_DBG_WMI,
+ "get fw stat pdev id %d vdev id %d stats id 0x%x\n",
+ param->pdev_id, param->vdev_id, param->stats_id);
+
+ time_left = wait_for_completion_timeout(&ar->fw_stats_complete, 1 * HZ);
+
+ if (!time_left) {
+ ath12k_warn(ab, "time out while waiting for get fw stats\n");
+ return -ETIMEDOUT;
+ }
+
+ /* Firmware sends WMI_UPDATE_STATS_EVENTID back-to-back
+ * when stats data buffer limit is reached. fw_stats_complete
+ * is completed once host receives first event from firmware, but
+ * still end might not be marked in the TLV.
+ * Below loop is to confirm that firmware completed sending all the event
+ * and fw_stats_done is marked true when end is marked in the TLV.
+ */
+ for (;;) {
+ if (time_after(jiffies, timeout))
+ break;
+ spin_lock_bh(&ar->data_lock);
+ if (ar->fw_stats.fw_stats_done) {
+ spin_unlock_bh(&ar->data_lock);
+ break;
+ }
+ spin_unlock_bh(&ar->data_lock);
+ }
+ return 0;
+}
+
+static int ath12k_mac_op_get_txpower(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ unsigned int link_id,
+ int *dbm)
+{
+ struct ath12k_vif *ahvif = ath12k_vif_to_ahvif(vif);
+ struct ath12k_fw_stats_req_params params = {};
+ struct ath12k_fw_stats_pdev *pdev;
+ struct ath12k_hw *ah = hw->priv;
+ struct ath12k_link_vif *arvif;
+ struct ath12k_base *ab;
+ struct ath12k *ar;
+ int ret;
+
+ /* Final Tx power is minimum of Target Power, CTL power, Regulatory
+ * Power, PSD EIRP Power. We just know the Regulatory power from the
+ * regulatory rules obtained. FW knows all these power and sets the min
+ * of these. Hence, we request the FW pdev stats in which FW reports
+ * the minimum of all vdev's channel Tx power.
+ */
+ lockdep_assert_wiphy(hw->wiphy);
+
+ arvif = wiphy_dereference(ah->hw->wiphy, ahvif->link[link_id]);
+ if (!arvif || !arvif->ar)
+ return -EINVAL;
+
+ ar = arvif->ar;
+ ab = ar->ab;
+ if (ah->state != ATH12K_HW_STATE_ON)
+ goto err_fallback;
+
+ if (test_bit(ATH12K_FLAG_CAC_RUNNING, &ar->dev_flags))
+ return -EAGAIN;
+
+ /* Limit the requests to Firmware for fetching the tx power */
+ if (ar->chan_tx_pwr != ATH12K_PDEV_TX_POWER_INVALID &&
+ time_before(jiffies,
+ msecs_to_jiffies(ATH12K_PDEV_TX_POWER_REFRESH_TIME_MSECS) +
+ ar->last_tx_power_update))
+ goto send_tx_power;
+
+ params.pdev_id = ar->pdev->pdev_id;
+ params.vdev_id = arvif->vdev_id;
+ params.stats_id = WMI_REQUEST_PDEV_STAT;
+ ret = ath12k_mac_get_fw_stats(ar, &params);
+ if (ret) {
+ ath12k_warn(ab, "failed to request fw pdev stats: %d\n", ret);
+ goto err_fallback;
+ }
+
+ spin_lock_bh(&ar->data_lock);
+ pdev = list_first_entry_or_null(&ar->fw_stats.pdevs,
+ struct ath12k_fw_stats_pdev, list);
+ if (!pdev) {
+ spin_unlock_bh(&ar->data_lock);
+ goto err_fallback;
+ }
+
+ /* tx power reported by firmware is in units of 0.5 dBm */
+ ar->chan_tx_pwr = pdev->chan_tx_power / 2;
+ spin_unlock_bh(&ar->data_lock);
+ ar->last_tx_power_update = jiffies;
+
+send_tx_power:
+ *dbm = ar->chan_tx_pwr;
+ ath12k_dbg(ar->ab, ATH12K_DBG_MAC, "txpower fetched from firmware %d dBm\n",
+ *dbm);
+ return 0;
+
+err_fallback:
+ /* We didn't get txpower from FW. Hence, relying on vif->bss_conf.txpower */
+ *dbm = vif->bss_conf.txpower;
+ ath12k_dbg(ar->ab, ATH12K_DBG_MAC, "txpower from firmware NaN, reported %d dBm\n",
+ *dbm);
+ return 0;
+}
+
static u8
ath12k_mac_find_link_id_by_ar(struct ath12k_vif *ahvif, struct ath12k *ar)
{
@@ -4252,7 +4547,7 @@ static int ath12k_mac_op_hw_scan(struct ieee80211_hw *hw,
return -EINVAL;
/* check if any of the links of ML VIF is already started on
- * radio(ar) correpsondig to given scan frequency and use it,
+ * radio(ar) corresponding to given scan frequency and use it,
* if not use scan link (link 15) for scan purpose.
*/
link_id = ath12k_mac_find_link_id_by_ar(ahvif, ar);
@@ -4361,10 +4656,16 @@ static int ath12k_mac_op_hw_scan(struct ieee80211_hw *hw,
ret = ath12k_start_scan(ar, arg);
if (ret) {
- ath12k_warn(ar->ab, "failed to start hw scan: %d\n", ret);
+ if (ret == -EBUSY)
+ ath12k_dbg(ar->ab, ATH12K_DBG_MAC,
+ "scan engine is busy 11d state %d\n", ar->state_11d);
+ else
+ ath12k_warn(ar->ab, "failed to start hw scan: %d\n", ret);
+
spin_lock_bh(&ar->data_lock);
ar->scan.state = ATH12K_SCAN_IDLE;
spin_unlock_bh(&ar->data_lock);
+ goto exit;
}
ath12k_dbg(ar->ab, ATH12K_DBG_MAC, "mac scan started");
@@ -4388,6 +4689,11 @@ exit:
kfree(arg);
}
+ if (ar->state_11d == ATH12K_11D_PREPARING &&
+ ahvif->vdev_type == WMI_VDEV_TYPE_STA &&
+ ahvif->vdev_subtype == WMI_VDEV_SUBTYPE_NONE)
+ ath12k_mac_11d_scan_start(ar, arvif->vdev_id);
+
return ret;
}
@@ -4428,7 +4734,6 @@ static int ath12k_install_key(struct ath12k_link_vif *arvif,
.macaddr = macaddr,
};
struct ath12k_vif *ahvif = arvif->ahvif;
- struct ieee80211_vif *vif = ath12k_ahvif_to_vif(ahvif);
lockdep_assert_wiphy(ath12k_ar_to_hw(ar)->wiphy);
@@ -4447,8 +4752,8 @@ static int ath12k_install_key(struct ath12k_link_vif *arvif,
switch (key->cipher) {
case WLAN_CIPHER_SUITE_CCMP:
+ case WLAN_CIPHER_SUITE_CCMP_256:
arg.key_cipher = WMI_CIPHER_AES_CCM;
- /* TODO: Re-check if flag is valid */
key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV_MGMT;
break;
case WLAN_CIPHER_SUITE_TKIP:
@@ -4456,12 +4761,10 @@ static int ath12k_install_key(struct ath12k_link_vif *arvif,
arg.key_txmic_len = 8;
arg.key_rxmic_len = 8;
break;
- case WLAN_CIPHER_SUITE_CCMP_256:
- arg.key_cipher = WMI_CIPHER_AES_CCM;
- break;
case WLAN_CIPHER_SUITE_GCMP:
case WLAN_CIPHER_SUITE_GCMP_256:
arg.key_cipher = WMI_CIPHER_AES_GCM;
+ key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV_MGMT;
break;
default:
ath12k_warn(ar->ab, "cipher %d is not supported\n", key->cipher);
@@ -4481,7 +4784,7 @@ install:
if (!wait_for_completion_timeout(&ar->install_key_done, 1 * HZ))
return -ETIMEDOUT;
- if (ether_addr_equal(macaddr, vif->addr))
+ if (ether_addr_equal(macaddr, arvif->bssid))
ahvif->key_cipher = key->cipher;
return ar->install_key_status ? -EINVAL : 0;
@@ -4534,9 +4837,6 @@ static int ath12k_mac_set_key(struct ath12k *ar, enum set_key_cmd cmd,
struct ath12k_link_sta *arsta,
struct ieee80211_key_conf *key)
{
- struct ath12k_vif *ahvif = arvif->ahvif;
- struct ieee80211_vif *vif = ath12k_ahvif_to_vif(ahvif);
- struct ieee80211_bss_conf *link_conf;
struct ieee80211_sta *sta = NULL;
struct ath12k_base *ab = ar->ab;
struct ath12k_peer *peer;
@@ -4553,19 +4853,10 @@ static int ath12k_mac_set_key(struct ath12k *ar, enum set_key_cmd cmd,
if (test_bit(ATH12K_FLAG_HW_CRYPTO_DISABLED, &ab->dev_flags))
return 1;
- link_conf = ath12k_mac_get_link_bss_conf(arvif);
- if (!link_conf) {
- ath12k_warn(ab, "unable to access bss link conf in set key for vif %pM link %u\n",
- vif->addr, arvif->link_id);
- return -ENOLINK;
- }
-
if (sta)
peer_addr = arsta->addr;
- else if (ahvif->vdev_type == WMI_VDEV_TYPE_STA)
- peer_addr = link_conf->bssid;
else
- peer_addr = link_conf->addr;
+ peer_addr = arvif->bssid;
key->hw_key_idx = key->keyidx;
@@ -4909,6 +5200,11 @@ static int ath12k_mac_station_assoc(struct ath12k *ar,
return -EINVAL;
}
+ spin_lock_bh(&ar->data_lock);
+ arsta->bw = ath12k_mac_ieee80211_sta_bw_to_wmi(ar, link_sta);
+ arsta->bw_prev = link_sta->bandwidth;
+ spin_unlock_bh(&ar->data_lock);
+
if (link_sta->vht_cap.vht_supported && num_vht_rates == 1) {
ret = ath12k_mac_set_peer_vht_fixed_rate(arvif, arsta, mask,
band);
@@ -5354,10 +5650,13 @@ static int ath12k_mac_station_add(struct ath12k *ar,
ar->max_num_stations);
goto exit;
}
- arsta->rx_stats = kzalloc(sizeof(*arsta->rx_stats), GFP_KERNEL);
- if (!arsta->rx_stats) {
- ret = -ENOMEM;
- goto dec_num_station;
+
+ if (ath12k_debugfs_is_extd_rx_stats_enabled(ar) && !arsta->rx_stats) {
+ arsta->rx_stats = kzalloc(sizeof(*arsta->rx_stats), GFP_KERNEL);
+ if (!arsta->rx_stats) {
+ ret = -ENOMEM;
+ goto dec_num_station;
+ }
}
peer_param.vdev_id = arvif->vdev_id;
@@ -5403,6 +5702,7 @@ static int ath12k_mac_station_add(struct ath12k *ar,
}
}
+ ewma_avg_rssi_init(&arsta->avg_rssi);
return 0;
free_peer:
@@ -5415,37 +5715,6 @@ exit:
return ret;
}
-static u32 ath12k_mac_ieee80211_sta_bw_to_wmi(struct ath12k *ar,
- struct ieee80211_sta *sta)
-{
- u32 bw = WMI_PEER_CHWIDTH_20MHZ;
-
- switch (sta->deflink.bandwidth) {
- case IEEE80211_STA_RX_BW_20:
- bw = WMI_PEER_CHWIDTH_20MHZ;
- break;
- case IEEE80211_STA_RX_BW_40:
- bw = WMI_PEER_CHWIDTH_40MHZ;
- break;
- case IEEE80211_STA_RX_BW_80:
- bw = WMI_PEER_CHWIDTH_80MHZ;
- break;
- case IEEE80211_STA_RX_BW_160:
- bw = WMI_PEER_CHWIDTH_160MHZ;
- break;
- case IEEE80211_STA_RX_BW_320:
- bw = WMI_PEER_CHWIDTH_320MHZ;
- break;
- default:
- ath12k_warn(ar->ab, "Invalid bandwidth %d in rc update for %pM\n",
- sta->deflink.bandwidth, sta->addr);
- bw = WMI_PEER_CHWIDTH_20MHZ;
- break;
- }
-
- return bw;
-}
-
static int ath12k_mac_assign_link_sta(struct ath12k_hw *ah,
struct ath12k_sta *ahsta,
struct ath12k_link_sta *arsta,
@@ -5529,13 +5798,15 @@ static int ath12k_mac_handle_link_sta_state(struct ieee80211_hw *hw,
enum ieee80211_sta_state new_state)
{
struct ieee80211_vif *vif = ath12k_ahvif_to_vif(arvif->ahvif);
- struct ieee80211_sta *sta = ath12k_ahsta_to_sta(arsta->ahsta);
+ struct ieee80211_bss_conf *link_conf;
struct ath12k *ar = arvif->ar;
+ struct ath12k_reg_info *reg_info;
+ struct ath12k_base *ab = ar->ab;
int ret = 0;
lockdep_assert_wiphy(hw->wiphy);
- ath12k_dbg(ar->ab, ATH12K_DBG_MAC, "mac handle link %u sta %pM state %d -> %d\n",
+ ath12k_dbg(ab, ATH12K_DBG_MAC, "mac handle link %u sta %pM state %d -> %d\n",
arsta->link_id, arsta->addr, old_state, new_state);
/* IEEE80211_STA_NONE -> IEEE80211_STA_NOTEXIST: Remove the station
@@ -5545,7 +5816,7 @@ static int ath12k_mac_handle_link_sta_state(struct ieee80211_hw *hw,
new_state == IEEE80211_STA_NOTEXIST)) {
ret = ath12k_mac_station_remove(ar, arvif, arsta);
if (ret) {
- ath12k_warn(ar->ab, "Failed to remove station: %pM for VDEV: %d\n",
+ ath12k_warn(ab, "Failed to remove station: %pM for VDEV: %d\n",
arsta->addr, arvif->vdev_id);
goto exit;
}
@@ -5556,7 +5827,7 @@ static int ath12k_mac_handle_link_sta_state(struct ieee80211_hw *hw,
new_state == IEEE80211_STA_NONE) {
ret = ath12k_mac_station_add(ar, arvif, arsta);
if (ret)
- ath12k_warn(ar->ab, "Failed to add station: %pM for VDEV: %d\n",
+ ath12k_warn(ab, "Failed to add station: %pM for VDEV: %d\n",
arsta->addr, arvif->vdev_id);
/* IEEE80211_STA_AUTH -> IEEE80211_STA_ASSOC: Send station assoc command for
@@ -5569,25 +5840,30 @@ static int ath12k_mac_handle_link_sta_state(struct ieee80211_hw *hw,
vif->type == NL80211_IFTYPE_ADHOC)) {
ret = ath12k_mac_station_assoc(ar, arvif, arsta, false);
if (ret)
- ath12k_warn(ar->ab, "Failed to associate station: %pM\n",
+ ath12k_warn(ab, "Failed to associate station: %pM\n",
arsta->addr);
- spin_lock_bh(&ar->data_lock);
-
- arsta->bw = ath12k_mac_ieee80211_sta_bw_to_wmi(ar, sta);
- arsta->bw_prev = sta->deflink.bandwidth;
-
- spin_unlock_bh(&ar->data_lock);
-
/* IEEE80211_STA_ASSOC -> IEEE80211_STA_AUTHORIZED: set peer status as
* authorized
*/
} else if (old_state == IEEE80211_STA_ASSOC &&
new_state == IEEE80211_STA_AUTHORIZED) {
ret = ath12k_mac_station_authorize(ar, arvif, arsta);
- if (ret)
- ath12k_warn(ar->ab, "Failed to authorize station: %pM\n",
+ if (ret) {
+ ath12k_warn(ab, "Failed to authorize station: %pM\n",
arsta->addr);
+ goto exit;
+ }
+
+ if (ath12k_wmi_supports_6ghz_cc_ext(ar) &&
+ arvif->ahvif->vdev_type == WMI_VDEV_TYPE_STA) {
+ link_conf = ath12k_mac_get_link_bss_conf(arvif);
+ reg_info = ab->reg_info[ar->pdev_idx];
+ ath12k_dbg(ab, ATH12K_DBG_MAC, "connection done, update reg rules\n");
+ ath12k_hw_to_ah(hw)->regd_updated = false;
+ ath12k_reg_handle_chan_list(ab, reg_info, arvif->ahvif->vdev_type,
+ link_conf->power_type);
+ }
/* IEEE80211_STA_AUTHORIZED -> IEEE80211_STA_ASSOC: station may be in removal,
* deauthorize it.
@@ -5606,7 +5882,7 @@ static int ath12k_mac_handle_link_sta_state(struct ieee80211_hw *hw,
vif->type == NL80211_IFTYPE_ADHOC)) {
ret = ath12k_mac_station_disassoc(ar, arvif, arsta);
if (ret)
- ath12k_warn(ar->ab, "Failed to disassociate station: %pM\n",
+ ath12k_warn(ab, "Failed to disassociate station: %pM\n",
arsta->addr);
}
@@ -5670,6 +5946,17 @@ static int ath12k_mac_op_sta_state(struct ieee80211_hw *hw,
* link sta
*/
if (sta->mlo) {
+ /* For station mode, arvif->is_sta_assoc_link has been set when
+ * vdev starts. Make sure the arvif/arsta pair have same setting
+ */
+ if (vif->type == NL80211_IFTYPE_STATION &&
+ !arsta->arvif->is_sta_assoc_link) {
+ ath12k_hw_warn(ah, "failed to verify assoc link setting with link id %u\n",
+ link_id);
+ ret = -EINVAL;
+ goto exit;
+ }
+
arsta->is_assoc_link = true;
ahsta->assoc_link_id = link_id;
}
@@ -5846,7 +6133,7 @@ static void ath12k_mac_op_link_sta_rc_update(struct ieee80211_hw *hw,
spin_lock_bh(&ar->data_lock);
if (changed & IEEE80211_RC_BW_CHANGED) {
- bw = ath12k_mac_ieee80211_sta_bw_to_wmi(ar, sta);
+ bw = ath12k_mac_ieee80211_sta_bw_to_wmi(ar, link_sta);
arsta->bw_prev = arsta->bw;
arsta->bw = bw;
}
@@ -6343,7 +6630,7 @@ static void ath12k_mac_setup_ht_vht_cap(struct ath12k *ar,
rate_cap_tx_chainmask = ar->cfg_tx_chainmask >> cap->tx_chain_mask_shift;
rate_cap_rx_chainmask = ar->cfg_rx_chainmask >> cap->rx_chain_mask_shift;
- if (cap->supported_bands & WMI_HOST_WLAN_2G_CAP) {
+ if (cap->supported_bands & WMI_HOST_WLAN_2GHZ_CAP) {
band = &ar->mac.sbands[NL80211_BAND_2GHZ];
ht_cap = cap->band[NL80211_BAND_2GHZ].ht_cap_info;
if (ht_cap_info)
@@ -6352,7 +6639,7 @@ static void ath12k_mac_setup_ht_vht_cap(struct ath12k *ar,
rate_cap_rx_chainmask);
}
- if (cap->supported_bands & WMI_HOST_WLAN_5G_CAP &&
+ if (cap->supported_bands & WMI_HOST_WLAN_5GHZ_CAP &&
(ar->ab->hw_params->single_pdev_only ||
!ar->supports_6ghz)) {
band = &ar->mac.sbands[NL80211_BAND_5GHZ];
@@ -6529,6 +6816,8 @@ static void ath12k_mac_copy_he_cap(struct ath12k_band_cap *band_cap,
switch (iftype) {
case NL80211_IFTYPE_AP:
+ he_cap_elem->mac_cap_info[2] &=
+ ~IEEE80211_HE_MAC_CAP2_BCAST_TWT;
he_cap_elem->phy_cap_info[3] &=
~IEEE80211_HE_PHY_CAP3_DCM_MAX_CONST_TX_MASK;
he_cap_elem->phy_cap_info[9] |=
@@ -6674,7 +6963,8 @@ static void ath12k_mac_copy_eht_cap(struct ath12k *ar,
memset(eht_cap, 0, sizeof(struct ieee80211_sta_eht_cap));
- if (!(test_bit(WMI_TLV_SERVICE_11BE, ar->ab->wmi_ab.svc_map)))
+ if (!(test_bit(WMI_TLV_SERVICE_11BE, ar->ab->wmi_ab.svc_map)) ||
+ ath12k_acpi_get_disable_11be(ar->ab))
return;
eht_cap->has_eht = true;
@@ -6760,7 +7050,7 @@ static void ath12k_mac_setup_sband_iftype_data(struct ath12k *ar,
enum nl80211_band band;
int count;
- if (cap->supported_bands & WMI_HOST_WLAN_2G_CAP) {
+ if (cap->supported_bands & WMI_HOST_WLAN_2GHZ_CAP) {
band = NL80211_BAND_2GHZ;
count = ath12k_mac_copy_sband_iftype_data(ar, cap,
ar->mac.iftype[band],
@@ -6770,7 +7060,7 @@ static void ath12k_mac_setup_sband_iftype_data(struct ath12k *ar,
count);
}
- if (cap->supported_bands & WMI_HOST_WLAN_5G_CAP) {
+ if (cap->supported_bands & WMI_HOST_WLAN_5GHZ_CAP) {
band = NL80211_BAND_5GHZ;
count = ath12k_mac_copy_sband_iftype_data(ar, cap,
ar->mac.iftype[band],
@@ -6780,7 +7070,7 @@ static void ath12k_mac_setup_sband_iftype_data(struct ath12k *ar,
count);
}
- if (cap->supported_bands & WMI_HOST_WLAN_5G_CAP &&
+ if (cap->supported_bands & WMI_HOST_WLAN_5GHZ_CAP &&
ar->supports_6ghz) {
band = NL80211_BAND_6GHZ;
count = ath12k_mac_copy_sband_iftype_data(ar, cap,
@@ -6908,14 +7198,17 @@ static int ath12k_mac_mgmt_tx_wmi(struct ath12k *ar, struct ath12k_link_vif *arv
{
struct ath12k_base *ab = ar->ab;
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
+ struct ath12k_skb_cb *skb_cb = ATH12K_SKB_CB(skb);
struct ieee80211_tx_info *info;
+ enum hal_encrypt_type enctype;
+ unsigned int mic_len;
dma_addr_t paddr;
int buf_id;
int ret;
lockdep_assert_wiphy(ath12k_ar_to_hw(ar)->wiphy);
- ATH12K_SKB_CB(skb)->ar = ar;
+ skb_cb->ar = ar;
spin_lock_bh(&ar->txmgmt_idr_lock);
buf_id = idr_alloc(&ar->txmgmt_idr, skb, 0,
ATH12K_TX_MGMT_NUM_PENDING_MAX, GFP_ATOMIC);
@@ -6924,12 +7217,15 @@ static int ath12k_mac_mgmt_tx_wmi(struct ath12k *ar, struct ath12k_link_vif *arv
return -ENOSPC;
info = IEEE80211_SKB_CB(skb);
- if (!(info->flags & IEEE80211_TX_CTL_HW_80211_ENCAP)) {
+ if ((skb_cb->flags & ATH12K_SKB_CIPHER_SET) &&
+ !(info->flags & IEEE80211_TX_CTL_HW_80211_ENCAP)) {
if ((ieee80211_is_action(hdr->frame_control) ||
ieee80211_is_deauth(hdr->frame_control) ||
ieee80211_is_disassoc(hdr->frame_control)) &&
ieee80211_has_protected(hdr->frame_control)) {
- skb_put(skb, IEEE80211_CCMP_MIC_LEN);
+ enctype = ath12k_dp_tx_get_encrypt_type(skb_cb->cipher);
+ mic_len = ath12k_dp_rx_crypto_mic_len(ar, enctype);
+ skb_put(skb, mic_len);
}
}
@@ -6940,7 +7236,7 @@ static int ath12k_mac_mgmt_tx_wmi(struct ath12k *ar, struct ath12k_link_vif *arv
goto err_free_idr;
}
- ATH12K_SKB_CB(skb)->paddr = paddr;
+ skb_cb->paddr = paddr;
ret = ath12k_wmi_mgmt_send(ar, arvif->vdev_id, buf_id, skb);
if (ret) {
@@ -6951,7 +7247,7 @@ static int ath12k_mac_mgmt_tx_wmi(struct ath12k *ar, struct ath12k_link_vif *arv
return 0;
err_unmap_buf:
- dma_unmap_single(ab->dev, ATH12K_SKB_CB(skb)->paddr,
+ dma_unmap_single(ab->dev, skb_cb->paddr,
skb->len, DMA_TO_DEVICE);
err_free_idr:
spin_lock_bh(&ar->txmgmt_idr_lock);
@@ -7071,6 +7367,22 @@ static void ath12k_mac_add_p2p_noa_ie(struct ath12k *ar,
}
/* Note: called under rcu_read_lock() */
+static void ath12k_mlo_mcast_update_tx_link_address(struct ieee80211_vif *vif,
+ u8 link_id, struct sk_buff *skb,
+ u32 info_flags)
+{
+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
+ struct ieee80211_bss_conf *bss_conf;
+
+ if (info_flags & IEEE80211_TX_CTL_HW_80211_ENCAP)
+ return;
+
+ bss_conf = rcu_dereference(vif->link_conf[link_id]);
+ if (bss_conf)
+ ether_addr_copy(hdr->addr2, bss_conf->addr);
+}
+
+/* Note: called under rcu_read_lock() */
static u8 ath12k_mac_get_tx_link(struct ieee80211_sta *sta, struct ieee80211_vif *vif,
u8 link, struct sk_buff *skb, u32 info_flags)
{
@@ -7181,12 +7493,25 @@ static void ath12k_mac_op_tx(struct ieee80211_hw *hw,
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
struct ieee80211_key_conf *key = info->control.hw_key;
struct ieee80211_sta *sta = control->sta;
+ struct ath12k_link_vif *tmp_arvif;
u32 info_flags = info->flags;
- struct ath12k *ar;
+ struct sk_buff *msdu_copied;
+ struct ath12k *ar, *tmp_ar;
+ struct ath12k_peer *peer;
+ unsigned long links_map;
+ bool is_mcast = false;
+ bool is_dvlan = false;
+ struct ethhdr *eth;
bool is_prb_rsp;
+ u16 mcbc_gsn;
u8 link_id;
int ret;
+ if (ahvif->vdev_type == WMI_VDEV_TYPE_MONITOR) {
+ ieee80211_free_txskb(hw, skb);
+ return;
+ }
+
link_id = u32_get_bits(info->control.flags, IEEE80211_TX_CTRL_MLO_LINK);
memset(skb_cb, 0, sizeof(*skb_cb));
skb_cb->vif = vif;
@@ -7220,6 +7545,9 @@ static void ath12k_mac_op_tx(struct ieee80211_hw *hw,
is_prb_rsp = ieee80211_is_probe_resp(hdr->frame_control);
if (info_flags & IEEE80211_TX_CTL_HW_80211_ENCAP) {
+ eth = (struct ethhdr *)skb->data;
+ is_mcast = is_multicast_ether_addr(eth->h_dest);
+
skb_cb->flags |= ATH12K_SKB_HW_80211_ENCAP;
} else if (ieee80211_is_mgmt(hdr->frame_control)) {
ret = ath12k_mac_mgmt_tx(ar, skb, is_prb_rsp);
@@ -7231,14 +7559,104 @@ static void ath12k_mac_op_tx(struct ieee80211_hw *hw,
return;
}
+ if (!(info_flags & IEEE80211_TX_CTL_HW_80211_ENCAP))
+ is_mcast = is_multicast_ether_addr(hdr->addr1);
+
/* This is case only for P2P_GO */
if (vif->type == NL80211_IFTYPE_AP && vif->p2p)
ath12k_mac_add_p2p_noa_ie(ar, vif, skb, is_prb_rsp);
- ret = ath12k_dp_tx(ar, arvif, skb);
- if (ret) {
- ath12k_warn(ar->ab, "failed to transmit frame %d\n", ret);
- ieee80211_free_txskb(hw, skb);
+ /* Checking if it is a DVLAN frame */
+ if (!test_bit(ATH12K_FLAG_HW_CRYPTO_DISABLED, &ar->ab->dev_flags) &&
+ !(skb_cb->flags & ATH12K_SKB_HW_80211_ENCAP) &&
+ !(skb_cb->flags & ATH12K_SKB_CIPHER_SET) &&
+ ieee80211_has_protected(hdr->frame_control))
+ is_dvlan = true;
+
+ if (!vif->valid_links || !is_mcast || is_dvlan ||
+ test_bit(ATH12K_FLAG_RAW_MODE, &ar->ab->dev_flags)) {
+ ret = ath12k_dp_tx(ar, arvif, skb, false, 0, is_mcast);
+ if (unlikely(ret)) {
+ ath12k_warn(ar->ab, "failed to transmit frame %d\n", ret);
+ ieee80211_free_txskb(ar->ah->hw, skb);
+ return;
+ }
+ } else {
+ mcbc_gsn = atomic_inc_return(&ahvif->mcbc_gsn) & 0xfff;
+
+ links_map = ahvif->links_map;
+ for_each_set_bit(link_id, &links_map,
+ IEEE80211_MLD_MAX_NUM_LINKS) {
+ tmp_arvif = rcu_dereference(ahvif->link[link_id]);
+ if (!tmp_arvif || !tmp_arvif->is_up)
+ continue;
+
+ tmp_ar = tmp_arvif->ar;
+ msdu_copied = skb_copy(skb, GFP_ATOMIC);
+ if (!msdu_copied) {
+ ath12k_err(ar->ab,
+ "skb copy failure link_id 0x%X vdevid 0x%X\n",
+ link_id, tmp_arvif->vdev_id);
+ continue;
+ }
+
+ ath12k_mlo_mcast_update_tx_link_address(vif, link_id,
+ msdu_copied,
+ info_flags);
+
+ skb_cb = ATH12K_SKB_CB(msdu_copied);
+ skb_cb->link_id = link_id;
+
+ /* For open mode, skip peer find logic */
+ if (unlikely(!ahvif->key_cipher))
+ goto skip_peer_find;
+
+ spin_lock_bh(&tmp_ar->ab->base_lock);
+ peer = ath12k_peer_find_by_addr(tmp_ar->ab, tmp_arvif->bssid);
+ if (!peer) {
+ spin_unlock_bh(&tmp_ar->ab->base_lock);
+ ath12k_warn(tmp_ar->ab,
+ "failed to find peer for vdev_id 0x%X addr %pM link_map 0x%X\n",
+ tmp_arvif->vdev_id, tmp_arvif->bssid,
+ ahvif->links_map);
+ dev_kfree_skb_any(msdu_copied);
+ continue;
+ }
+
+ key = peer->keys[peer->mcast_keyidx];
+ if (key) {
+ skb_cb->cipher = key->cipher;
+ skb_cb->flags |= ATH12K_SKB_CIPHER_SET;
+
+ hdr = (struct ieee80211_hdr *)msdu_copied->data;
+ if (!ieee80211_has_protected(hdr->frame_control))
+ hdr->frame_control |=
+ cpu_to_le16(IEEE80211_FCTL_PROTECTED);
+ }
+ spin_unlock_bh(&tmp_ar->ab->base_lock);
+
+skip_peer_find:
+ ret = ath12k_dp_tx(tmp_ar, tmp_arvif,
+ msdu_copied, true, mcbc_gsn, is_mcast);
+ if (unlikely(ret)) {
+ if (ret == -ENOMEM) {
+ /* Drops are expected during heavy multicast
+ * frame flood. Print with debug log
+ * level to avoid lot of console prints
+ */
+ ath12k_dbg(ar->ab, ATH12K_DBG_MAC,
+ "failed to transmit frame %d\n",
+ ret);
+ } else {
+ ath12k_warn(ar->ab,
+ "failed to transmit frame %d\n",
+ ret);
+ }
+
+ dev_kfree_skb_any(msdu_copied);
+ }
+ }
+ ieee80211_free_txskb(ar->ah->hw, skb);
}
}
@@ -7255,8 +7673,40 @@ void ath12k_mac_drain_tx(struct ath12k *ar)
static int ath12k_mac_config_mon_status_default(struct ath12k *ar, bool enable)
{
- return -EOPNOTSUPP;
- /* TODO: Need to support new monitor mode */
+ struct htt_rx_ring_tlv_filter tlv_filter = {};
+ struct ath12k_base *ab = ar->ab;
+ u32 ring_id, i;
+ int ret = 0;
+
+ lockdep_assert_wiphy(ath12k_ar_to_hw(ar)->wiphy);
+
+ if (!ab->hw_params->rxdma1_enable)
+ return ret;
+
+ if (enable) {
+ tlv_filter = ath12k_mac_mon_status_filter_default;
+
+ if (ath12k_debugfs_rx_filter(ar))
+ tlv_filter.rx_filter = ath12k_debugfs_rx_filter(ar);
+ } else {
+ tlv_filter.rxmon_disable = true;
+ }
+
+ for (i = 0; i < ab->hw_params->num_rxdma_per_pdev; i++) {
+ ring_id = ar->dp.rxdma_mon_dst_ring[i].ring_id;
+ ret = ath12k_dp_tx_htt_rx_filter_setup(ab, ring_id,
+ ar->dp.mac_id + i,
+ HAL_RXDMA_MONITOR_DST,
+ DP_RXDMA_REFILL_RING_SIZE,
+ &tlv_filter);
+ if (ret) {
+ ath12k_err(ab,
+ "failed to setup filter for monitor buf %d\n",
+ ret);
+ }
+ }
+
+ return ret;
}
static int ath12k_mac_start(struct ath12k *ar)
@@ -7273,7 +7723,7 @@ static int ath12k_mac_start(struct ath12k *ar)
1, pdev->pdev_id);
if (ret) {
- ath12k_err(ab, "failed to enable PMF QOS: (%d\n", ret);
+ ath12k_err(ab, "failed to enable PMF QOS: %d\n", ret);
goto err;
}
@@ -7318,12 +7768,13 @@ static int ath12k_mac_start(struct ath12k *ar)
/* TODO: Do we need to enable ANI? */
- ath12k_reg_update_chan_list(ar);
+ ath12k_reg_update_chan_list(ar, false);
ar->num_started_vdevs = 0;
ar->num_created_vdevs = 0;
ar->num_peers = 0;
ar->allocated_vdev_map = 0;
+ ar->chan_tx_pwr = ATH12K_PDEV_TX_POWER_INVALID;
/* Configure monitor status ring with default rx_filter to get rx status
* such as rssi, rx_duration.
@@ -7363,9 +7814,14 @@ err:
static void ath12k_drain_tx(struct ath12k_hw *ah)
{
- struct ath12k *ar;
+ struct ath12k *ar = ah->radio;
int i;
+ if (ath12k_ftm_mode) {
+ ath12k_err(ar->ab, "fail to start mac operations in ftm mode\n");
+ return;
+ }
+
lockdep_assert_wiphy(ah->hw->wiphy);
for_each_ar(ah, ar, i)
@@ -7394,6 +7850,7 @@ static int ath12k_mac_op_start(struct ieee80211_hw *hw)
case ATH12K_HW_STATE_RESTARTED:
case ATH12K_HW_STATE_WEDGED:
case ATH12K_HW_STATE_ON:
+ case ATH12K_HW_STATE_TM:
ah->state = ATH12K_HW_STATE_OFF;
WARN_ON(1);
@@ -7499,6 +7956,9 @@ static void ath12k_mac_stop(struct ath12k *ar)
wiphy_work_cancel(ath12k_ar_to_hw(ar)->wiphy, &ar->scan.vdev_clean_wk);
cancel_work_sync(&ar->regd_update_work);
cancel_work_sync(&ar->ab->rfkill_work);
+ cancel_work_sync(&ar->ab->update_11d_work);
+ ar->state_11d = ATH12K_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) {
@@ -7561,14 +8021,9 @@ static int ath12k_mac_setup_vdev_params_mbssid(struct ath12k_link_vif *arvif,
u32 *flags, u32 *tx_vdev_id)
{
struct ath12k_vif *ahvif = arvif->ahvif;
- struct ieee80211_vif *tx_vif = ahvif->vif->mbssid_tx_vif;
struct ieee80211_bss_conf *link_conf;
struct ath12k *ar = arvif->ar;
struct ath12k_link_vif *tx_arvif;
- struct ath12k_vif *tx_ahvif;
-
- if (!tx_vif)
- return 0;
link_conf = ath12k_mac_get_link_bss_conf(arvif);
if (!link_conf) {
@@ -7577,11 +8032,13 @@ static int ath12k_mac_setup_vdev_params_mbssid(struct ath12k_link_vif *arvif,
return -ENOLINK;
}
- tx_ahvif = ath12k_vif_to_ahvif(tx_vif);
- tx_arvif = &tx_ahvif->deflink;
+ tx_arvif = ath12k_mac_get_tx_arvif(arvif, link_conf);
+ if (!tx_arvif)
+ return 0;
if (link_conf->nontransmitted) {
- if (ar->ah->hw->wiphy != ieee80211_vif_to_wdev(tx_vif)->wiphy)
+ if (ath12k_ar_to_hw(ar)->wiphy !=
+ ath12k_ar_to_hw(tx_arvif->ar)->wiphy)
return -EINVAL;
*flags = WMI_VDEV_MBSSID_FLAGS_NON_TRANSMIT_AP;
@@ -7624,15 +8081,15 @@ static int ath12k_mac_setup_vdev_create_arg(struct ath12k_link_vif *arvif,
return ret;
}
- if (pdev->cap.supported_bands & WMI_HOST_WLAN_2G_CAP) {
+ if (pdev->cap.supported_bands & WMI_HOST_WLAN_2GHZ_CAP) {
arg->chains[NL80211_BAND_2GHZ].tx = ar->num_tx_chains;
arg->chains[NL80211_BAND_2GHZ].rx = ar->num_rx_chains;
}
- if (pdev->cap.supported_bands & WMI_HOST_WLAN_5G_CAP) {
+ if (pdev->cap.supported_bands & WMI_HOST_WLAN_5GHZ_CAP) {
arg->chains[NL80211_BAND_5GHZ].tx = ar->num_tx_chains;
arg->chains[NL80211_BAND_5GHZ].rx = ar->num_rx_chains;
}
- if (pdev->cap.supported_bands & WMI_HOST_WLAN_5G_CAP &&
+ if (pdev->cap.supported_bands & WMI_HOST_WLAN_5GHZ_CAP &&
ar->supports_6ghz) {
arg->chains[NL80211_BAND_6GHZ].tx = ar->num_tx_chains;
arg->chains[NL80211_BAND_6GHZ].rx = ar->num_rx_chains;
@@ -7661,7 +8118,7 @@ ath12k_mac_prepare_he_mode(struct ath12k_pdev *pdev, u32 viftype)
u32 *hecap_phy_ptr = NULL;
u32 hemode;
- if (pdev->cap.supported_bands & WMI_HOST_WLAN_2G_CAP)
+ if (pdev->cap.supported_bands & WMI_HOST_WLAN_2GHZ_CAP)
cap_band = &pdev_cap->band[NL80211_BAND_2GHZ];
else
cap_band = &pdev_cap->band[NL80211_BAND_5GHZ];
@@ -7792,44 +8249,121 @@ static void ath12k_mac_op_update_vif_offload(struct ieee80211_hw *hw,
ath12k_mac_update_vif_offload(&ahvif->deflink);
}
-int ath12k_mac_vdev_create(struct ath12k *ar, struct ath12k_link_vif *arvif)
+static bool ath12k_mac_vif_ap_active_any(struct ath12k_base *ab)
{
- struct ath12k_hw *ah = ar->ah;
- struct ath12k_base *ab = ar->ab;
- struct ieee80211_hw *hw = ah->hw;
- struct ath12k_vif *ahvif = arvif->ahvif;
- struct ieee80211_vif *vif = ath12k_ahvif_to_vif(ahvif);
- struct ath12k_wmi_vdev_create_arg vdev_arg = {0};
- struct ath12k_wmi_peer_create_arg peer_param = {0};
- struct ieee80211_bss_conf *link_conf;
- u32 param_id, param_value;
- u16 nss;
+ struct ath12k *ar;
+ struct ath12k_pdev *pdev;
+ struct ath12k_link_vif *arvif;
int i;
- int ret, vdev_id;
- u8 link_id;
- lockdep_assert_wiphy(hw->wiphy);
+ 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->ahvif->vdev_type == WMI_VDEV_TYPE_AP)
+ return true;
+ }
+ }
+ return false;
+}
- /* If no link is active and scan vdev is requested
- * use a default link conf for scan address purpose.
- */
- if (arvif->link_id == ATH12K_DEFAULT_SCAN_LINK && vif->valid_links)
- link_id = ffs(vif->valid_links) - 1;
- else
- link_id = arvif->link_id;
+void ath12k_mac_11d_scan_start(struct ath12k *ar, u32 vdev_id)
+{
+ struct wmi_11d_scan_start_arg arg;
+ int ret;
- link_conf = wiphy_dereference(hw->wiphy, vif->link_conf[link_id]);
- if (!link_conf) {
- ath12k_warn(ar->ab, "unable to access bss link conf in vdev create for vif %pM link %u\n",
- vif->addr, arvif->link_id);
- return -ENOLINK;
+ lockdep_assert_wiphy(ath12k_ar_to_hw(ar)->wiphy);
+
+ if (ar->regdom_set_by_user)
+ goto fin;
+
+ if (ar->vdev_id_11d_scan != ATH12K_11D_INVALID_VDEV_ID)
+ goto fin;
+
+ if (!test_bit(WMI_TLV_SERVICE_11D_OFFLOAD, ar->ab->wmi_ab.svc_map))
+ goto fin;
+
+ if (ath12k_mac_vif_ap_active_any(ar->ab))
+ goto fin;
+
+ arg.vdev_id = vdev_id;
+ arg.start_interval_msec = 0;
+ arg.scan_period_msec = ATH12K_SCAN_11D_INTERVAL;
+
+ ath12k_dbg(ar->ab, ATH12K_DBG_MAC,
+ "mac start 11d scan for vdev %d\n", vdev_id);
+
+ ret = ath12k_wmi_send_11d_scan_start_cmd(ar, &arg);
+ if (ret) {
+ ath12k_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 (ar->state_11d == ATH12K_11D_PREPARING)
+ ar->state_11d = ATH12K_11D_RUNNING;
}
- memcpy(arvif->bssid, link_conf->addr, ETH_ALEN);
+fin:
+ if (ar->state_11d == ATH12K_11D_PREPARING) {
+ ar->state_11d = ATH12K_11D_IDLE;
+ complete(&ar->completed_11d_scan);
+ }
+}
- arvif->ar = ar;
- vdev_id = __ffs64(ab->free_vdev_map);
- arvif->vdev_id = vdev_id;
+void ath12k_mac_11d_scan_stop(struct ath12k *ar)
+{
+ int ret;
+ u32 vdev_id;
+
+ lockdep_assert_wiphy(ath12k_ar_to_hw(ar)->wiphy);
+
+ if (!test_bit(WMI_TLV_SERVICE_11D_OFFLOAD, ar->ab->wmi_ab.svc_map))
+ return;
+
+ ath12k_dbg(ar->ab, ATH12K_DBG_MAC, "mac stop 11d for vdev %d\n",
+ ar->vdev_id_11d_scan);
+
+ if (ar->state_11d == ATH12K_11D_PREPARING) {
+ ar->state_11d = ATH12K_11D_IDLE;
+ complete(&ar->completed_11d_scan);
+ }
+
+ if (ar->vdev_id_11d_scan != ATH12K_11D_INVALID_VDEV_ID) {
+ vdev_id = ar->vdev_id_11d_scan;
+
+ ret = ath12k_wmi_send_11d_scan_stop_cmd(ar, vdev_id);
+ if (ret) {
+ ath12k_warn(ar->ab,
+ "failed to stopt 11d scan vdev %d ret: %d\n",
+ vdev_id, ret);
+ } else {
+ ar->vdev_id_11d_scan = ATH12K_11D_INVALID_VDEV_ID;
+ ar->state_11d = ATH12K_11D_IDLE;
+ complete(&ar->completed_11d_scan);
+ }
+ }
+}
+
+void ath12k_mac_11d_scan_stop_all(struct ath12k_base *ab)
+{
+ struct ath12k *ar;
+ struct ath12k_pdev *pdev;
+ int i;
+
+ ath12k_dbg(ab, ATH12K_DBG_MAC, "mac stop soc 11d scan\n");
+
+ for (i = 0; i < ab->num_radios; i++) {
+ pdev = &ab->pdevs[i];
+ ar = pdev->ar;
+
+ ath12k_mac_11d_scan_stop(ar);
+ }
+}
+
+static void ath12k_mac_determine_vdev_type(struct ieee80211_vif *vif,
+ struct ath12k_vif *ahvif)
+{
ahvif->vdev_subtype = WMI_VDEV_SUBTYPE_NONE;
switch (vif->type) {
@@ -7853,7 +8387,6 @@ int ath12k_mac_vdev_create(struct ath12k *ar, struct ath12k_link_vif *arvif)
break;
case NL80211_IFTYPE_MONITOR:
ahvif->vdev_type = WMI_VDEV_TYPE_MONITOR;
- ar->monitor_vdev_id = vdev_id;
break;
case NL80211_IFTYPE_P2P_DEVICE:
ahvif->vdev_type = WMI_VDEV_TYPE_STA;
@@ -7863,6 +8396,53 @@ int ath12k_mac_vdev_create(struct ath12k *ar, struct ath12k_link_vif *arvif)
WARN_ON(1);
break;
}
+}
+
+int ath12k_mac_vdev_create(struct ath12k *ar, struct ath12k_link_vif *arvif)
+{
+ struct ath12k_hw *ah = ar->ah;
+ struct ath12k_base *ab = ar->ab;
+ struct ieee80211_hw *hw = ah->hw;
+ struct ath12k_vif *ahvif = arvif->ahvif;
+ struct ieee80211_vif *vif = ath12k_ahvif_to_vif(ahvif);
+ struct ath12k_wmi_vdev_create_arg vdev_arg = {0};
+ struct ath12k_wmi_peer_create_arg peer_param = {0};
+ struct ieee80211_bss_conf *link_conf = NULL;
+ u32 param_id, param_value;
+ u16 nss;
+ int i;
+ int ret, vdev_id;
+ u8 link_id;
+
+ lockdep_assert_wiphy(hw->wiphy);
+
+ /* In NO_VIRTUAL_MONITOR, its necessary to restrict only one monitor
+ * interface in each radio
+ */
+ if (vif->type == NL80211_IFTYPE_MONITOR && ar->monitor_vdev_created)
+ return -EINVAL;
+
+ link_id = arvif->link_id;
+
+ if (link_id < IEEE80211_MLD_MAX_NUM_LINKS) {
+ link_conf = wiphy_dereference(hw->wiphy, vif->link_conf[link_id]);
+ if (!link_conf) {
+ ath12k_warn(ar->ab, "unable to access bss link conf in vdev create for vif %pM link %u\n",
+ vif->addr, arvif->link_id);
+ return -ENOLINK;
+ }
+ }
+
+ if (link_conf)
+ memcpy(arvif->bssid, link_conf->addr, ETH_ALEN);
+ else
+ memcpy(arvif->bssid, vif->addr, ETH_ALEN);
+
+ arvif->ar = ar;
+ vdev_id = __ffs64(ab->free_vdev_map);
+ arvif->vdev_id = vdev_id;
+ if (vif->type == NL80211_IFTYPE_MONITOR)
+ ar->monitor_vdev_id = vdev_id;
ath12k_dbg(ar->ab, ATH12K_DBG_MAC, "mac vdev create id %d type %d subtype %d map %llx\n",
arvif->vdev_id, ahvif->vdev_type, ahvif->vdev_subtype,
@@ -7926,6 +8506,7 @@ int ath12k_mac_vdev_create(struct ath12k *ar, struct ath12k_link_vif *arvif)
arvif->vdev_id, ret);
goto err_peer_del;
}
+ ath12k_mac_11d_scan_stop_all(ar->ab);
break;
case WMI_VDEV_TYPE_STA:
param_id = WMI_STA_PS_PARAM_RX_WAKE_POLICY;
@@ -7964,12 +8545,26 @@ int ath12k_mac_vdev_create(struct ath12k *ar, struct ath12k_link_vif *arvif)
arvif->vdev_id, ret);
goto err_peer_del;
}
+
+ if (test_bit(WMI_TLV_SERVICE_11D_OFFLOAD, ab->wmi_ab.svc_map) &&
+ ahvif->vdev_type == WMI_VDEV_TYPE_STA &&
+ ahvif->vdev_subtype == WMI_VDEV_SUBTYPE_NONE) {
+ reinit_completion(&ar->completed_11d_scan);
+ ar->state_11d = ATH12K_11D_PREPARING;
+ }
+ break;
+ case WMI_VDEV_TYPE_MONITOR:
+ ar->monitor_vdev_created = true;
break;
default:
break;
}
- arvif->txpower = link_conf->txpower;
+ if (link_conf)
+ arvif->txpower = link_conf->txpower;
+ else
+ arvif->txpower = NL80211_TX_POWER_AUTOMATIC;
+
ret = ath12k_mac_txpower_recalc(ar);
if (ret)
goto err_peer_del;
@@ -7984,8 +8579,6 @@ int ath12k_mac_vdev_create(struct ath12k *ar, struct ath12k_link_vif *arvif)
}
ath12k_dp_vdev_tx_attach(ar, arvif);
- if (vif->type != NL80211_IFTYPE_MONITOR && ar->monitor_conf_enabled)
- ath12k_mac_monitor_vdev_create(ar);
return ret;
@@ -8010,6 +8603,11 @@ err_peer_del:
}
err_vdev_del:
+ if (ahvif->vdev_type == WMI_VDEV_TYPE_MONITOR) {
+ ar->monitor_vdev_id = -1;
+ ar->monitor_vdev_created = false;
+ }
+
ath12k_wmi_vdev_delete(ar, arvif->vdev_id);
ar->num_created_vdevs--;
arvif->is_created = false;
@@ -8066,6 +8664,7 @@ static void ath12k_mac_vif_cache_flush(struct ath12k *ar, struct ath12k_link_vif
struct ieee80211_vif *vif = ath12k_ahvif_to_vif(ahvif);
struct ath12k_vif_cache *cache = ahvif->cache[arvif->link_id];
struct ath12k_base *ab = ar->ab;
+ struct ieee80211_bss_conf *link_conf;
int ret;
@@ -8084,7 +8683,13 @@ static void ath12k_mac_vif_cache_flush(struct ath12k *ar, struct ath12k_link_vif
}
if (cache->bss_conf_changed) {
- ath12k_mac_bss_info_changed(ar, arvif, &vif->bss_conf,
+ link_conf = ath12k_mac_get_link_bss_conf(arvif);
+ if (!link_conf) {
+ ath12k_warn(ar->ab, "unable to access bss link conf in cache flush for vif %pM link %u\n",
+ vif->addr, arvif->link_id);
+ return;
+ }
+ ath12k_mac_bss_info_changed(ar, arvif, link_conf,
cache->bss_conf_changed);
}
@@ -8197,7 +8802,10 @@ static int ath12k_mac_op_add_interface(struct ieee80211_hw *hw,
{
struct ath12k_hw *ah = ath12k_hw_to_ah(hw);
struct ath12k_vif *ahvif = ath12k_vif_to_ahvif(vif);
+ struct ath12k_reg_info *reg_info;
struct ath12k_link_vif *arvif;
+ struct ath12k_base *ab;
+ struct ath12k *ar;
int i;
lockdep_assert_wiphy(hw->wiphy);
@@ -8207,19 +8815,8 @@ static int ath12k_mac_op_add_interface(struct ieee80211_hw *hw,
ahvif->ah = ah;
ahvif->vif = vif;
arvif = &ahvif->deflink;
- arvif->ahvif = ahvif;
- INIT_LIST_HEAD(&arvif->list);
- INIT_DELAYED_WORK(&arvif->connection_loss_work,
- ath12k_mac_vif_sta_connection_loss_work);
-
- for (i = 0; i < ARRAY_SIZE(arvif->bitrate_mask.control); i++) {
- arvif->bitrate_mask.control[i].legacy = 0xffffffff;
- 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));
- }
+ ath12k_mac_init_arvif(ahvif, arvif, -1);
/* Allocate Default Queue now and reassign during actual vdev create */
vif->cab_queue = ATH12K_HW_DEFAULT_QUEUE;
@@ -8227,6 +8824,22 @@ static int ath12k_mac_op_add_interface(struct ieee80211_hw *hw,
vif->hw_queue[i] = ATH12K_HW_DEFAULT_QUEUE;
vif->driver_flags |= IEEE80211_VIF_SUPPORTS_UAPSD;
+
+ ath12k_mac_determine_vdev_type(vif, ahvif);
+
+ for_each_ar(ah, ar, i) {
+ if (!ath12k_wmi_supports_6ghz_cc_ext(ar))
+ continue;
+
+ ab = ar->ab;
+ reg_info = ab->reg_info[ar->pdev_idx];
+ ath12k_dbg(ab, ATH12K_DBG_MAC, "interface added to change reg rules\n");
+ ah->regd_updated = false;
+ ath12k_reg_handle_chan_list(ab, reg_info, ahvif->vdev_type,
+ IEEE80211_REG_UNSET_AP);
+ break;
+ }
+
/* Defer vdev creation until assign_chanctx or hw_scan is initiated as driver
* will not know if this interface is an ML vif at this point.
*/
@@ -8291,8 +8904,6 @@ static int ath12k_mac_vdev_delete(struct ath12k *ar, struct ath12k_link_vif *arv
if (ahvif->vdev_type == WMI_VDEV_TYPE_MONITOR) {
ar->monitor_vdev_id = -1;
ar->monitor_vdev_created = false;
- } else if (ar->monitor_vdev_created && !ar->monitor_started) {
- ret = ath12k_mac_monitor_vdev_delete(ar);
}
ath12k_dbg(ab, ATH12K_DBG_MAC, "vdev %pM deleted, vdev_id %d\n",
@@ -8381,29 +8992,6 @@ static void ath12k_mac_op_remove_interface(struct ieee80211_hw *hw,
FIF_PROBE_REQ | \
FIF_FCSFAIL)
-static void ath12k_mac_configure_filter(struct ath12k *ar,
- unsigned int total_flags)
-{
- bool reset_flag;
- int ret;
-
- lockdep_assert_wiphy(ath12k_ar_to_hw(ar)->wiphy);
-
- ar->filter_flags = total_flags;
-
- /* For monitor mode */
- reset_flag = !(ar->filter_flags & FIF_BCN_PRBRESP_PROMISC);
-
- ret = ath12k_dp_tx_htt_monitor_mode_ring_config(ar, reset_flag);
- if (ret)
- ath12k_warn(ar->ab,
- "fail to set monitor filter: %d\n", ret);
-
- ath12k_dbg(ar->ab, ATH12K_DBG_MAC,
- "total_flags:0x%x, reset_flag:%d\n",
- total_flags, reset_flag);
-}
-
static void ath12k_mac_op_configure_filter(struct ieee80211_hw *hw,
unsigned int changed_flags,
unsigned int *total_flags,
@@ -8417,7 +9005,7 @@ static void ath12k_mac_op_configure_filter(struct ieee80211_hw *hw,
ar = ath12k_ah_to_ar(ah, 0);
*total_flags &= SUPPORTED_FILTERS;
- ath12k_mac_configure_filter(ar, *total_flags);
+ ar->filter_flags = *total_flags;
}
static int ath12k_mac_op_get_antenna(struct ieee80211_hw *hw, u32 *tx_ant, u32 *rx_ant)
@@ -8546,6 +9134,7 @@ static int ath12k_mac_op_add_chanctx(struct ieee80211_hw *hw,
*/
ar->rx_channel = ctx->def.chan;
spin_unlock_bh(&ar->data_lock);
+ ar->chan_tx_pwr = ATH12K_PDEV_TX_POWER_INVALID;
return 0;
}
@@ -8574,6 +9163,7 @@ static void ath12k_mac_op_remove_chanctx(struct ieee80211_hw *hw,
*/
ar->rx_channel = NULL;
spin_unlock_bh(&ar->data_lock);
+ ar->chan_tx_pwr = ATH12K_PDEV_TX_POWER_INVALID;
}
static enum wmi_phy_mode
@@ -8665,6 +9255,9 @@ ath12k_mac_mlo_get_vdev_args(struct ath12k_link_vif *arvif,
* link vdevs which are advertised as partners below
*/
ml_arg->link_add = true;
+
+ ml_arg->assoc_link = arvif->is_sta_assoc_link;
+
partner_info = ml_arg->partner_info;
links = ahvif->links_map;
@@ -8677,6 +9270,9 @@ ath12k_mac_mlo_get_vdev_args(struct ath12k_link_vif *arvif,
if (arvif == arvif_p)
continue;
+ if (!arvif_p->is_created)
+ continue;
+
link_conf = wiphy_dereference(ahvif->ah->hw->wiphy,
ahvif->vif->link_conf[arvif_p->link_id]);
@@ -8800,6 +9396,15 @@ ath12k_mac_vdev_start_restart(struct ath12k_link_vif *arvif,
return ret;
}
+ /* TODO: For now we only set TPC power here. However when
+ * channel changes, say CSA, it should be updated again.
+ */
+ if (ath12k_mac_supports_station_tpc(ar, ahvif, chandef)) {
+ ath12k_mac_fill_reg_tpc_info(ar, arvif, ctx);
+ ath12k_wmi_send_vdev_set_tpc_power(ar, arvif->vdev_id,
+ &arvif->reg_tpc_info);
+ }
+
ar->num_started_vdevs++;
ath12k_dbg(ab, ATH12K_DBG_MAC, "vdev %pM started, vdev_id %d\n",
ahvif->vif->addr, arvif->vdev_id);
@@ -8982,9 +9587,9 @@ ath12k_mac_update_vif_chan(struct ath12k *ar,
int n_vifs)
{
struct ath12k_wmi_vdev_up_params params = {};
+ struct ath12k_link_vif *arvif, *tx_arvif;
struct ieee80211_bss_conf *link_conf;
struct ath12k_base *ab = ar->ab;
- struct ath12k_link_vif *arvif;
struct ieee80211_vif *vif;
struct ath12k_vif *ahvif;
u8 link_id;
@@ -9002,8 +9607,10 @@ ath12k_mac_update_vif_chan(struct ath12k *ar,
arvif = wiphy_dereference(ath12k_ar_to_hw(ar)->wiphy,
ahvif->link[link_id]);
- if (vif->type == NL80211_IFTYPE_MONITOR)
+ if (vif->type == NL80211_IFTYPE_MONITOR) {
monitor_vif = true;
+ continue;
+ }
ath12k_dbg(ab, ATH12K_DBG_MAC,
"mac chanctx switch vdev_id %i freq %u->%u width %d->%d\n",
@@ -9052,11 +9659,9 @@ ath12k_mac_update_vif_chan(struct ath12k *ar,
params.vdev_id = arvif->vdev_id;
params.aid = ahvif->aid;
params.bssid = arvif->bssid;
- if (vif->mbssid_tx_vif) {
- struct ath12k_vif *tx_ahvif =
- ath12k_vif_to_ahvif(vif->mbssid_tx_vif);
- struct ath12k_link_vif *tx_arvif = &tx_ahvif->deflink;
+ tx_arvif = ath12k_mac_get_tx_arvif(arvif, link_conf);
+ if (tx_arvif) {
params.tx_bssid = tx_arvif->bssid;
params.nontx_profile_idx = link_conf->bssid_index;
params.nontx_profile_cnt = 1 << link_conf->bssid_indicator;
@@ -9155,16 +9760,26 @@ static int ath12k_start_vdev_delay(struct ath12k *ar,
struct ath12k_base *ab = ar->ab;
struct ath12k_vif *ahvif = arvif->ahvif;
struct ieee80211_vif *vif = ath12k_ahvif_to_vif(arvif->ahvif);
+ struct ieee80211_chanctx_conf *chanctx;
+ struct ieee80211_bss_conf *link_conf;
int ret;
if (WARN_ON(arvif->is_started))
return -EBUSY;
- ret = ath12k_mac_vdev_start(arvif, &arvif->chanctx);
+ link_conf = ath12k_mac_get_link_bss_conf(arvif);
+ if (!link_conf) {
+ ath12k_warn(ab, "failed to get link conf for vdev %u\n", arvif->vdev_id);
+ return -EINVAL;
+ }
+
+ chanctx = wiphy_dereference(ath12k_ar_to_hw(arvif->ar)->wiphy,
+ link_conf->chanctx_conf);
+ ret = ath12k_mac_vdev_start(arvif, chanctx);
if (ret) {
ath12k_warn(ab, "failed to start vdev %i addr %pM on freq %d: %d\n",
arvif->vdev_id, vif->addr,
- arvif->chanctx.def.chan->center_freq, ret);
+ chanctx->def.chan->center_freq, ret);
return ret;
}
@@ -9182,6 +9797,391 @@ static int ath12k_start_vdev_delay(struct ath12k *ar,
return 0;
}
+static u8 ath12k_mac_get_num_pwr_levels(struct cfg80211_chan_def *chan_def)
+{
+ if (chan_def->chan->flags & IEEE80211_CHAN_PSD) {
+ switch (chan_def->width) {
+ case NL80211_CHAN_WIDTH_20:
+ return 1;
+ case NL80211_CHAN_WIDTH_40:
+ return 2;
+ case NL80211_CHAN_WIDTH_80:
+ return 4;
+ case NL80211_CHAN_WIDTH_160:
+ return 8;
+ case NL80211_CHAN_WIDTH_320:
+ return 16;
+ default:
+ return 1;
+ }
+ } else {
+ switch (chan_def->width) {
+ case NL80211_CHAN_WIDTH_20:
+ return 1;
+ case NL80211_CHAN_WIDTH_40:
+ return 2;
+ case NL80211_CHAN_WIDTH_80:
+ return 3;
+ case NL80211_CHAN_WIDTH_160:
+ return 4;
+ case NL80211_CHAN_WIDTH_320:
+ return 5;
+ default:
+ return 1;
+ }
+ }
+}
+
+static u16 ath12k_mac_get_6ghz_start_frequency(struct cfg80211_chan_def *chan_def)
+{
+ u16 diff_seq;
+
+ /* It is to get the lowest channel number's center frequency of the chan.
+ * For example,
+ * bandwidth=40 MHz, center frequency is 5965, lowest channel is 1
+ * with center frequency 5955, its diff is 5965 - 5955 = 10.
+ * bandwidth=80 MHz, center frequency is 5985, lowest channel is 1
+ * with center frequency 5955, its diff is 5985 - 5955 = 30.
+ * bandwidth=160 MHz, center frequency is 6025, lowest channel is 1
+ * with center frequency 5955, its diff is 6025 - 5955 = 70.
+ * bandwidth=320 MHz, center frequency is 6105, lowest channel is 1
+ * with center frequency 5955, its diff is 6105 - 5955 = 70.
+ */
+ switch (chan_def->width) {
+ case NL80211_CHAN_WIDTH_320:
+ diff_seq = 150;
+ break;
+ case NL80211_CHAN_WIDTH_160:
+ diff_seq = 70;
+ break;
+ case NL80211_CHAN_WIDTH_80:
+ diff_seq = 30;
+ break;
+ case NL80211_CHAN_WIDTH_40:
+ diff_seq = 10;
+ break;
+ default:
+ diff_seq = 0;
+ }
+
+ return chan_def->center_freq1 - diff_seq;
+}
+
+static u16 ath12k_mac_get_seg_freq(struct cfg80211_chan_def *chan_def,
+ u16 start_seq, u8 seq)
+{
+ u16 seg_seq;
+
+ /* It is to get the center frequency of the specific bandwidth.
+ * start_seq means the lowest channel number's center frequency.
+ * seq 0/1/2/3 means 20 MHz/40 MHz/80 MHz/160 MHz.
+ * For example,
+ * lowest channel is 1, its center frequency 5955,
+ * center frequency is 5955 when bandwidth=20 MHz, its diff is 5955 - 5955 = 0.
+ * lowest channel is 1, its center frequency 5955,
+ * center frequency is 5965 when bandwidth=40 MHz, its diff is 5965 - 5955 = 10.
+ * lowest channel is 1, its center frequency 5955,
+ * center frequency is 5985 when bandwidth=80 MHz, its diff is 5985 - 5955 = 30.
+ * lowest channel is 1, its center frequency 5955,
+ * center frequency is 6025 when bandwidth=160 MHz, its diff is 6025 - 5955 = 70.
+ */
+ seg_seq = 10 * (BIT(seq) - 1);
+ return seg_seq + start_seq;
+}
+
+static void ath12k_mac_get_psd_channel(struct ath12k *ar,
+ u16 step_freq,
+ u16 *start_freq,
+ u16 *center_freq,
+ u8 i,
+ struct ieee80211_channel **temp_chan,
+ s8 *tx_power)
+{
+ /* It is to get the center frequency for each 20 MHz.
+ * For example, if the chan is 160 MHz and center frequency is 6025,
+ * then it include 8 channels, they are 1/5/9/13/17/21/25/29,
+ * channel number 1's center frequency is 5955, it is parameter start_freq.
+ * parameter i is the step of the 8 channels. i is 0~7 for the 8 channels.
+ * the channel 1/5/9/13/17/21/25/29 maps i=0/1/2/3/4/5/6/7,
+ * and maps its center frequency is 5955/5975/5995/6015/6035/6055/6075/6095,
+ * the gap is 20 for each channel, parameter step_freq means the gap.
+ * after get the center frequency of each channel, it is easy to find the
+ * struct ieee80211_channel of it and get the max_reg_power.
+ */
+ *center_freq = *start_freq + i * step_freq;
+ *temp_chan = ieee80211_get_channel(ar->ah->hw->wiphy, *center_freq);
+ *tx_power = (*temp_chan)->max_reg_power;
+}
+
+static void ath12k_mac_get_eirp_power(struct ath12k *ar,
+ u16 *start_freq,
+ u16 *center_freq,
+ u8 i,
+ struct ieee80211_channel **temp_chan,
+ struct cfg80211_chan_def *def,
+ s8 *tx_power)
+{
+ /* It is to get the center frequency for 20 MHz/40 MHz/80 MHz/
+ * 160 MHz bandwidth, and then plus 10 to the center frequency,
+ * it is the center frequency of a channel number.
+ * For example, when configured channel number is 1.
+ * center frequency is 5965 when bandwidth=40 MHz, after plus 10, it is 5975,
+ * then it is channel number 5.
+ * center frequency is 5985 when bandwidth=80 MHz, after plus 10, it is 5995,
+ * then it is channel number 9.
+ * center frequency is 6025 when bandwidth=160 MHz, after plus 10, it is 6035,
+ * then it is channel number 17.
+ * after get the center frequency of each channel, it is easy to find the
+ * struct ieee80211_channel of it and get the max_reg_power.
+ */
+ *center_freq = ath12k_mac_get_seg_freq(def, *start_freq, i);
+
+ /* For the 20 MHz, its center frequency is same with same channel */
+ if (i != 0)
+ *center_freq += 10;
+
+ *temp_chan = ieee80211_get_channel(ar->ah->hw->wiphy, *center_freq);
+ *tx_power = (*temp_chan)->max_reg_power;
+}
+
+void ath12k_mac_fill_reg_tpc_info(struct ath12k *ar,
+ struct ath12k_link_vif *arvif,
+ struct ieee80211_chanctx_conf *ctx)
+{
+ struct ath12k_base *ab = ar->ab;
+ struct ath12k_reg_tpc_power_info *reg_tpc_info = &arvif->reg_tpc_info;
+ struct ieee80211_bss_conf *bss_conf = ath12k_mac_get_link_bss_conf(arvif);
+ struct ieee80211_channel *chan, *temp_chan;
+ u8 pwr_lvl_idx, num_pwr_levels, pwr_reduction;
+ bool is_psd_power = false, is_tpe_present = false;
+ s8 max_tx_power[ATH12K_NUM_PWR_LEVELS],
+ psd_power, tx_power, eirp_power;
+ u16 start_freq, center_freq;
+
+ chan = ctx->def.chan;
+ start_freq = ath12k_mac_get_6ghz_start_frequency(&ctx->def);
+ pwr_reduction = bss_conf->pwr_reduction;
+
+ if (arvif->reg_tpc_info.num_pwr_levels) {
+ is_tpe_present = true;
+ num_pwr_levels = arvif->reg_tpc_info.num_pwr_levels;
+ } else {
+ num_pwr_levels = ath12k_mac_get_num_pwr_levels(&ctx->def);
+ }
+
+ for (pwr_lvl_idx = 0; pwr_lvl_idx < num_pwr_levels; pwr_lvl_idx++) {
+ /* STA received TPE IE*/
+ if (is_tpe_present) {
+ /* local power is PSD power*/
+ if (chan->flags & IEEE80211_CHAN_PSD) {
+ /* Connecting AP is psd power */
+ if (reg_tpc_info->is_psd_power) {
+ is_psd_power = true;
+ ath12k_mac_get_psd_channel(ar, 20,
+ &start_freq,
+ &center_freq,
+ pwr_lvl_idx,
+ &temp_chan,
+ &tx_power);
+ psd_power = temp_chan->psd;
+ eirp_power = tx_power;
+ max_tx_power[pwr_lvl_idx] =
+ min_t(s8,
+ psd_power,
+ reg_tpc_info->tpe[pwr_lvl_idx]);
+ /* Connecting AP is not psd power */
+ } else {
+ ath12k_mac_get_eirp_power(ar,
+ &start_freq,
+ &center_freq,
+ pwr_lvl_idx,
+ &temp_chan,
+ &ctx->def,
+ &tx_power);
+ psd_power = temp_chan->psd;
+ /* convert psd power to EIRP power based
+ * on channel width
+ */
+ tx_power =
+ min_t(s8, tx_power,
+ psd_power + 13 + pwr_lvl_idx * 3);
+ max_tx_power[pwr_lvl_idx] =
+ min_t(s8,
+ tx_power,
+ reg_tpc_info->tpe[pwr_lvl_idx]);
+ }
+ /* local power is not PSD power */
+ } else {
+ /* Connecting AP is psd power */
+ if (reg_tpc_info->is_psd_power) {
+ is_psd_power = true;
+ ath12k_mac_get_psd_channel(ar, 20,
+ &start_freq,
+ &center_freq,
+ pwr_lvl_idx,
+ &temp_chan,
+ &tx_power);
+ eirp_power = tx_power;
+ max_tx_power[pwr_lvl_idx] =
+ reg_tpc_info->tpe[pwr_lvl_idx];
+ /* Connecting AP is not psd power */
+ } else {
+ ath12k_mac_get_eirp_power(ar,
+ &start_freq,
+ &center_freq,
+ pwr_lvl_idx,
+ &temp_chan,
+ &ctx->def,
+ &tx_power);
+ max_tx_power[pwr_lvl_idx] =
+ min_t(s8,
+ tx_power,
+ reg_tpc_info->tpe[pwr_lvl_idx]);
+ }
+ }
+ /* STA not received TPE IE */
+ } else {
+ /* local power is PSD power*/
+ if (chan->flags & IEEE80211_CHAN_PSD) {
+ is_psd_power = true;
+ ath12k_mac_get_psd_channel(ar, 20,
+ &start_freq,
+ &center_freq,
+ pwr_lvl_idx,
+ &temp_chan,
+ &tx_power);
+ psd_power = temp_chan->psd;
+ eirp_power = tx_power;
+ max_tx_power[pwr_lvl_idx] = psd_power;
+ } else {
+ ath12k_mac_get_eirp_power(ar,
+ &start_freq,
+ &center_freq,
+ pwr_lvl_idx,
+ &temp_chan,
+ &ctx->def,
+ &tx_power);
+ max_tx_power[pwr_lvl_idx] = tx_power;
+ }
+ }
+
+ if (is_psd_power) {
+ /* If AP local power constraint is present */
+ if (pwr_reduction)
+ eirp_power = eirp_power - pwr_reduction;
+
+ /* If firmware updated max tx power is non zero, then take
+ * the min of firmware updated ap tx power
+ * and max power derived from above mentioned parameters.
+ */
+ ath12k_dbg(ab, ATH12K_DBG_MAC,
+ "eirp power : %d firmware report power : %d\n",
+ eirp_power, ar->max_allowed_tx_power);
+ /* Firmware reports lower max_allowed_tx_power during vdev
+ * start response. In case of 6 GHz, firmware is not aware
+ * of EIRP power unless driver sets EIRP power through WMI
+ * TPC command. So radio which does not support idle power
+ * save can set maximum calculated EIRP power directly to
+ * firmware through TPC command without min comparison with
+ * vdev start response's max_allowed_tx_power.
+ */
+ if (ar->max_allowed_tx_power && ab->hw_params->idle_ps)
+ eirp_power = min_t(s8,
+ eirp_power,
+ ar->max_allowed_tx_power);
+ } else {
+ /* If AP local power constraint is present */
+ if (pwr_reduction)
+ max_tx_power[pwr_lvl_idx] =
+ max_tx_power[pwr_lvl_idx] - pwr_reduction;
+ /* If firmware updated max tx power is non zero, then take
+ * the min of firmware updated ap tx power
+ * and max power derived from above mentioned parameters.
+ */
+ if (ar->max_allowed_tx_power && ab->hw_params->idle_ps)
+ max_tx_power[pwr_lvl_idx] =
+ min_t(s8,
+ max_tx_power[pwr_lvl_idx],
+ ar->max_allowed_tx_power);
+ }
+ reg_tpc_info->chan_power_info[pwr_lvl_idx].chan_cfreq = center_freq;
+ reg_tpc_info->chan_power_info[pwr_lvl_idx].tx_power =
+ max_tx_power[pwr_lvl_idx];
+ }
+
+ reg_tpc_info->num_pwr_levels = num_pwr_levels;
+ reg_tpc_info->is_psd_power = is_psd_power;
+ reg_tpc_info->eirp_power = eirp_power;
+ reg_tpc_info->ap_power_type =
+ ath12k_reg_ap_pwr_convert(bss_conf->power_type);
+}
+
+static void ath12k_mac_parse_tx_pwr_env(struct ath12k *ar,
+ struct ath12k_link_vif *arvif)
+{
+ struct ieee80211_bss_conf *bss_conf = ath12k_mac_get_link_bss_conf(arvif);
+ struct ath12k_reg_tpc_power_info *tpc_info = &arvif->reg_tpc_info;
+ struct ieee80211_parsed_tpe_eirp *local_non_psd, *reg_non_psd;
+ struct ieee80211_parsed_tpe_psd *local_psd, *reg_psd;
+ struct ieee80211_parsed_tpe *tpe = &bss_conf->tpe;
+ enum wmi_reg_6g_client_type client_type;
+ struct ath12k_reg_info *reg_info;
+ struct ath12k_base *ab = ar->ab;
+ bool psd_valid, non_psd_valid;
+ int i;
+
+ reg_info = ab->reg_info[ar->pdev_idx];
+ client_type = reg_info->client_type;
+
+ local_psd = &tpe->psd_local[client_type];
+ reg_psd = &tpe->psd_reg_client[client_type];
+ local_non_psd = &tpe->max_local[client_type];
+ reg_non_psd = &tpe->max_reg_client[client_type];
+
+ psd_valid = local_psd->valid | reg_psd->valid;
+ non_psd_valid = local_non_psd->valid | reg_non_psd->valid;
+
+ if (!psd_valid && !non_psd_valid) {
+ ath12k_warn(ab,
+ "no transmit power envelope match client power type %d\n",
+ client_type);
+ return;
+ };
+
+ if (psd_valid) {
+ tpc_info->is_psd_power = true;
+
+ tpc_info->num_pwr_levels = max(local_psd->count,
+ reg_psd->count);
+ if (tpc_info->num_pwr_levels > ATH12K_NUM_PWR_LEVELS)
+ tpc_info->num_pwr_levels = ATH12K_NUM_PWR_LEVELS;
+
+ for (i = 0; i < tpc_info->num_pwr_levels; i++) {
+ tpc_info->tpe[i] = min(local_psd->power[i],
+ reg_psd->power[i]) / 2;
+ ath12k_dbg(ab, ATH12K_DBG_MAC,
+ "TPE PSD power[%d] : %d\n",
+ i, tpc_info->tpe[i]);
+ }
+ } else {
+ tpc_info->is_psd_power = false;
+ tpc_info->eirp_power = 0;
+
+ tpc_info->num_pwr_levels = max(local_non_psd->count,
+ reg_non_psd->count);
+ if (tpc_info->num_pwr_levels > ATH12K_NUM_PWR_LEVELS)
+ tpc_info->num_pwr_levels = ATH12K_NUM_PWR_LEVELS;
+
+ for (i = 0; i < tpc_info->num_pwr_levels; i++) {
+ tpc_info->tpe[i] = min(local_non_psd->power[i],
+ reg_non_psd->power[i]) / 2;
+ ath12k_dbg(ab, ATH12K_DBG_MAC,
+ "non PSD power[%d] : %d\n",
+ i, tpc_info->tpe[i]);
+ }
+ }
+}
+
static int
ath12k_mac_op_assign_vif_chanctx(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
@@ -9209,8 +10209,8 @@ ath12k_mac_op_assign_vif_chanctx(struct ieee80211_hw *hw,
ar = ath12k_mac_assign_vif_to_vdev(hw, arvif, ctx);
if (!ar) {
- ath12k_warn(arvif->ar->ab, "failed to assign chanctx for vif %pM link id %u link vif is already started",
- vif->addr, link_id);
+ ath12k_hw_warn(ah, "failed to assign chanctx for vif %pM link id %u link vif is already started",
+ vif->addr, link_id);
return -EINVAL;
}
@@ -9220,6 +10220,11 @@ ath12k_mac_op_assign_vif_chanctx(struct ieee80211_hw *hw,
"mac chanctx assign ptr %p vdev_id %i\n",
ctx, arvif->vdev_id);
+ if (ath12k_wmi_supports_6ghz_cc_ext(ar) &&
+ ctx->def.chan->band == NL80211_BAND_6GHZ &&
+ ahvif->vdev_type == WMI_VDEV_TYPE_STA)
+ ath12k_mac_parse_tx_pwr_env(ar, arvif);
+
arvif->punct_bitmap = ctx->def.punctured;
/* for some targets bss peer must be created before vdev_start */
@@ -9227,7 +10232,6 @@ ath12k_mac_op_assign_vif_chanctx(struct ieee80211_hw *hw,
ahvif->vdev_type != WMI_VDEV_TYPE_AP &&
ahvif->vdev_type != WMI_VDEV_TYPE_MONITOR &&
!ath12k_peer_exist_by_vdev_id(ab, arvif->vdev_id)) {
- memcpy(&arvif->chanctx, ctx, sizeof(*ctx));
ret = 0;
goto out;
}
@@ -9239,8 +10243,10 @@ ath12k_mac_op_assign_vif_chanctx(struct ieee80211_hw *hw,
if (ahvif->vdev_type == WMI_VDEV_TYPE_MONITOR) {
ret = ath12k_mac_monitor_start(ar);
- if (ret)
+ if (ret) {
+ ath12k_mac_monitor_vdev_delete(ar);
goto out;
+ }
arvif->is_started = true;
goto out;
@@ -9254,9 +10260,6 @@ ath12k_mac_op_assign_vif_chanctx(struct ieee80211_hw *hw,
goto out;
}
- if (ahvif->vdev_type != WMI_VDEV_TYPE_MONITOR && ar->monitor_vdev_created)
- ath12k_mac_monitor_start(ar);
-
arvif->is_started = true;
/* TODO: Setup ps and cts/rts protection */
@@ -9319,12 +10322,18 @@ ath12k_mac_op_unassign_vif_chanctx(struct ieee80211_hw *hw,
}
arvif->is_started = false;
- if (ahvif->vdev_type != WMI_VDEV_TYPE_MONITOR &&
- ar->num_started_vdevs == 1 && ar->monitor_vdev_created)
- ath12k_mac_monitor_stop(ar);
+ if (test_bit(WMI_TLV_SERVICE_11D_OFFLOAD, ab->wmi_ab.svc_map) &&
+ ahvif->vdev_type == WMI_VDEV_TYPE_STA &&
+ ahvif->vdev_subtype == WMI_VDEV_SUBTYPE_NONE &&
+ ar->state_11d != ATH12K_11D_PREPARING) {
+ reinit_completion(&ar->completed_11d_scan);
+ ar->state_11d = ATH12K_11D_PREPARING;
+ }
- ath12k_mac_remove_link_interface(hw, arvif);
- ath12k_mac_unassign_link_vif(arvif);
+ if (ar->scan.arvif == arvif && ar->scan.state == ATH12K_SCAN_RUNNING) {
+ ath12k_scan_abort(ar);
+ ar->scan.arvif = NULL;
+ }
}
static int
@@ -9886,6 +10895,14 @@ ath12k_mac_op_reconfig_complete(struct ieee80211_hw *hw,
ath12k_warn(ar->ab, "pdev %d successfully recovered\n",
ar->pdev->pdev_id);
+ if (ar->ab->hw_params->current_cc_support &&
+ ar->alpha2[0] != 0 && ar->alpha2[1] != 0) {
+ struct wmi_set_current_country_arg arg = {};
+
+ memcpy(&arg.alpha2, ar->alpha2, 2);
+ ath12k_wmi_send_set_current_country_cmd(ar, &arg);
+ }
+
if (ab->is_reset) {
recovery_count = atomic_inc_return(&ab->recovery_count);
@@ -10023,11 +11040,21 @@ static void ath12k_mac_op_sta_statistics(struct ieee80211_hw *hw,
struct station_info *sinfo)
{
struct ath12k_sta *ahsta = ath12k_sta_to_ahsta(sta);
+ struct ath12k_fw_stats_req_params params = {};
struct ath12k_link_sta *arsta;
+ struct ath12k *ar;
+ s8 signal;
+ bool db2dbm;
lockdep_assert_wiphy(hw->wiphy);
arsta = &ahsta->deflink;
+ ar = ath12k_get_ar_by_vif(hw, vif, arsta->link_id);
+ if (!ar)
+ return;
+
+ 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);
@@ -10035,25 +11062,46 @@ static void ath12k_mac_op_sta_statistics(struct ieee80211_hw *hw,
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.eht_gi = arsta->txrate.eht_gi;
+ sinfo->txrate.eht_ru_alloc = arsta->txrate.eht_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 + ATH12K_DEFAULT_NOISE_FLOOR;
- sinfo->filled |= BIT_ULL(NL80211_STA_INFO_SIGNAL);
+ signal = arsta->rssi_comb;
+
+ params.pdev_id = ar->pdev->pdev_id;
+ params.vdev_id = 0;
+ params.stats_id = WMI_REQUEST_VDEV_STAT;
+
+ if (!signal &&
+ ahsta->ahvif->vdev_type == WMI_VDEV_TYPE_STA &&
+ !(ath12k_mac_get_fw_stats(ar, &params)))
+ signal = arsta->rssi_beacon;
+
+ if (signal) {
+ sinfo->signal = db2dbm ? signal : signal + ATH12K_DEFAULT_NOISE_FLOOR;
+ sinfo->filled |= BIT_ULL(NL80211_STA_INFO_SIGNAL);
+ }
+
+ sinfo->signal_avg = ewma_avg_rssi_read(&arsta->avg_rssi);
+
+ if (!db2dbm)
+ sinfo->signal_avg += ATH12K_DEFAULT_NOISE_FLOOR;
+
+ sinfo->filled |= BIT_ULL(NL80211_STA_INFO_SIGNAL_AVG);
}
static int ath12k_mac_op_cancel_remain_on_channel(struct ieee80211_hw *hw,
@@ -10100,7 +11148,7 @@ static int ath12k_mac_op_remain_on_channel(struct ieee80211_hw *hw,
return -EINVAL;
/* check if any of the links of ML VIF is already started on
- * radio(ar) correpsondig to given scan frequency and use it,
+ * radio(ar) corresponding to given scan frequency and use it,
* if not use deflink(link 0) for scan purpose.
*/
@@ -10284,6 +11332,7 @@ static const struct ieee80211_ops ath12k_ops = {
.assign_vif_chanctx = ath12k_mac_op_assign_vif_chanctx,
.unassign_vif_chanctx = ath12k_mac_op_unassign_vif_chanctx,
.switch_vif_chanctx = ath12k_mac_op_switch_vif_chanctx,
+ .get_txpower = ath12k_mac_op_get_txpower,
.set_rts_threshold = ath12k_mac_op_set_rts_threshold,
.set_frag_threshold = ath12k_mac_op_set_frag_threshold,
.set_bitrate_mask = ath12k_mac_op_set_bitrate_mask,
@@ -10299,8 +11348,37 @@ static const struct ieee80211_ops ath12k_ops = {
.resume = ath12k_wow_op_resume,
.set_wakeup = ath12k_wow_op_set_wakeup,
#endif
+#ifdef CONFIG_ATH12K_DEBUGFS
+ .vif_add_debugfs = ath12k_debugfs_op_vif_add,
+#endif
+ CFG80211_TESTMODE_CMD(ath12k_tm_cmd)
+#ifdef CONFIG_ATH12K_DEBUGFS
+ .link_sta_add_debugfs = ath12k_debugfs_link_sta_op_add,
+#endif
};
+void ath12k_mac_update_freq_range(struct ath12k *ar,
+ u32 freq_low, u32 freq_high)
+{
+ if (!(freq_low && freq_high))
+ return;
+
+ if (ar->freq_range.start_freq || ar->freq_range.end_freq) {
+ ar->freq_range.start_freq = min(ar->freq_range.start_freq,
+ MHZ_TO_KHZ(freq_low));
+ ar->freq_range.end_freq = max(ar->freq_range.end_freq,
+ MHZ_TO_KHZ(freq_high));
+ } else {
+ ar->freq_range.start_freq = MHZ_TO_KHZ(freq_low);
+ ar->freq_range.end_freq = MHZ_TO_KHZ(freq_high);
+ }
+
+ ath12k_dbg(ar->ab, ATH12K_DBG_MAC,
+ "mac pdev %u freq limit updated. New range %u->%u MHz\n",
+ ar->pdev->pdev_id, KHZ_TO_MHZ(ar->freq_range.start_freq),
+ KHZ_TO_MHZ(ar->freq_range.end_freq));
+}
+
static void ath12k_mac_update_ch_list(struct ath12k *ar,
struct ieee80211_supported_band *band,
u32 freq_low, u32 freq_high)
@@ -10315,9 +11393,6 @@ static void ath12k_mac_update_ch_list(struct ath12k *ar,
band->channels[i].center_freq > freq_high)
band->channels[i].flags |= IEEE80211_CHAN_DISABLED;
}
-
- ar->freq_range.start_freq = MHZ_TO_KHZ(freq_low);
- ar->freq_range.end_freq = MHZ_TO_KHZ(freq_high);
}
static u32 ath12k_get_phy_id(struct ath12k *ar, u32 band)
@@ -10325,10 +11400,10 @@ static u32 ath12k_get_phy_id(struct ath12k *ar, u32 band)
struct ath12k_pdev *pdev = ar->pdev;
struct ath12k_pdev_cap *pdev_cap = &pdev->cap;
- if (band == WMI_HOST_WLAN_2G_CAP)
+ if (band == WMI_HOST_WLAN_2GHZ_CAP)
return pdev_cap->band[NL80211_BAND_2GHZ].phy_id;
- if (band == WMI_HOST_WLAN_5G_CAP)
+ if (band == WMI_HOST_WLAN_5GHZ_CAP)
return pdev_cap->band[NL80211_BAND_5GHZ].phy_id;
ath12k_warn(ar->ab, "unsupported phy cap:%d\n", band);
@@ -10342,18 +11417,19 @@ static int ath12k_mac_setup_channels_rates(struct ath12k *ar,
{
struct ieee80211_supported_band *band;
struct ath12k_wmi_hal_reg_capabilities_ext_arg *reg_cap;
+ struct ath12k_base *ab = ar->ab;
+ u32 phy_id, freq_low, freq_high;
struct ath12k_hw *ah = ar->ah;
void *channels;
- u32 phy_id;
BUILD_BUG_ON((ARRAY_SIZE(ath12k_2ghz_channels) +
ARRAY_SIZE(ath12k_5ghz_channels) +
ARRAY_SIZE(ath12k_6ghz_channels)) !=
ATH12K_NUM_CHANS);
- reg_cap = &ar->ab->hal_reg_cap[ar->pdev_idx];
+ reg_cap = &ab->hal_reg_cap[ar->pdev_idx];
- if (supported_bands & WMI_HOST_WLAN_2G_CAP) {
+ if (supported_bands & WMI_HOST_WLAN_2GHZ_CAP) {
channels = kmemdup(ath12k_2ghz_channels,
sizeof(ath12k_2ghz_channels),
GFP_KERNEL);
@@ -10368,17 +11444,25 @@ static int ath12k_mac_setup_channels_rates(struct ath12k *ar,
band->bitrates = ath12k_g_rates;
bands[NL80211_BAND_2GHZ] = band;
- if (ar->ab->hw_params->single_pdev_only) {
- phy_id = ath12k_get_phy_id(ar, WMI_HOST_WLAN_2G_CAP);
- reg_cap = &ar->ab->hal_reg_cap[phy_id];
+ if (ab->hw_params->single_pdev_only) {
+ phy_id = ath12k_get_phy_id(ar, WMI_HOST_WLAN_2GHZ_CAP);
+ reg_cap = &ab->hal_reg_cap[phy_id];
}
+
+ freq_low = max(reg_cap->low_2ghz_chan,
+ ab->reg_freq_2ghz.start_freq);
+ freq_high = min(reg_cap->high_2ghz_chan,
+ ab->reg_freq_2ghz.end_freq);
+
ath12k_mac_update_ch_list(ar, band,
reg_cap->low_2ghz_chan,
reg_cap->high_2ghz_chan);
+
+ ath12k_mac_update_freq_range(ar, freq_low, freq_high);
}
- if (supported_bands & WMI_HOST_WLAN_5G_CAP) {
- if (reg_cap->high_5ghz_chan >= ATH12K_MIN_6G_FREQ) {
+ if (supported_bands & WMI_HOST_WLAN_5GHZ_CAP) {
+ if (reg_cap->high_5ghz_chan >= ATH12K_MIN_6GHZ_FREQ) {
channels = kmemdup(ath12k_6ghz_channels,
sizeof(ath12k_6ghz_channels), GFP_KERNEL);
if (!channels) {
@@ -10394,13 +11478,21 @@ static int ath12k_mac_setup_channels_rates(struct ath12k *ar,
band->n_bitrates = ath12k_a_rates_size;
band->bitrates = ath12k_a_rates;
bands[NL80211_BAND_6GHZ] = band;
+
+ freq_low = max(reg_cap->low_5ghz_chan,
+ ab->reg_freq_6ghz.start_freq);
+ freq_high = min(reg_cap->high_5ghz_chan,
+ ab->reg_freq_6ghz.end_freq);
+
ath12k_mac_update_ch_list(ar, band,
reg_cap->low_5ghz_chan,
reg_cap->high_5ghz_chan);
+
+ ath12k_mac_update_freq_range(ar, freq_low, freq_high);
ah->use_6ghz_regd = true;
}
- if (reg_cap->low_5ghz_chan < ATH12K_MIN_6G_FREQ) {
+ if (reg_cap->low_5ghz_chan < ATH12K_MIN_6GHZ_FREQ) {
channels = kmemdup(ath12k_5ghz_channels,
sizeof(ath12k_5ghz_channels),
GFP_KERNEL);
@@ -10418,14 +11510,21 @@ static int ath12k_mac_setup_channels_rates(struct ath12k *ar,
band->bitrates = ath12k_a_rates;
bands[NL80211_BAND_5GHZ] = band;
- if (ar->ab->hw_params->single_pdev_only) {
- phy_id = ath12k_get_phy_id(ar, WMI_HOST_WLAN_5G_CAP);
- reg_cap = &ar->ab->hal_reg_cap[phy_id];
+ if (ab->hw_params->single_pdev_only) {
+ phy_id = ath12k_get_phy_id(ar, WMI_HOST_WLAN_5GHZ_CAP);
+ reg_cap = &ab->hal_reg_cap[phy_id];
}
+ freq_low = max(reg_cap->low_5ghz_chan,
+ ab->reg_freq_5ghz.start_freq);
+ freq_high = min(reg_cap->high_5ghz_chan,
+ ab->reg_freq_5ghz.end_freq);
+
ath12k_mac_update_ch_list(ar, band,
reg_cap->low_5ghz_chan,
reg_cap->high_5ghz_chan);
+
+ ath12k_mac_update_freq_range(ar, freq_low, freq_high);
}
}
@@ -10525,13 +11624,18 @@ ath12k_mac_setup_radio_iface_comb(struct ath12k *ar,
comb[0].limits = limits;
comb[0].n_limits = n_limits;
comb[0].max_interfaces = max_interfaces;
- comb[0].num_different_channels = 1;
comb[0].beacon_int_infra_match = true;
comb[0].beacon_int_min_gcd = 100;
- comb[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);
+
+ if (ar->ab->hw_params->single_pdev_only) {
+ comb[0].num_different_channels = 2;
+ } else {
+ comb[0].num_different_channels = 1;
+ comb[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);
+ }
return 0;
}
@@ -10756,6 +11860,7 @@ static void ath12k_mac_hw_unregister(struct ath12k_hw *ah)
for_each_ar(ah, ar, i) {
cancel_work_sync(&ar->regd_update_work);
ath12k_debugfs_unregister(ar);
+ ath12k_fw_stats_reset(ar);
}
ieee80211_unregister_hw(hw);
@@ -10904,6 +12009,7 @@ static int ath12k_mac_hw_register(struct ath12k_hw *ah)
ieee80211_hw_set(hw, QUEUE_CONTROL);
ieee80211_hw_set(hw, SUPPORTS_TX_FRAG);
ieee80211_hw_set(hw, REPORTS_LOW_ACK);
+ ieee80211_hw_set(hw, NO_VIRTUAL_MONITOR);
if ((ht_cap & WMI_HT_CAP_ENABLED) || is_6ghz) {
ieee80211_hw_set(hw, AMPDU_AGGREGATION);
@@ -10950,6 +12056,8 @@ static int ath12k_mac_hw_register(struct ath12k_hw *ah)
ath12k_iftypes_ext_capa[2].eml_capabilities = cap->eml_cap;
ath12k_iftypes_ext_capa[2].mld_capa_and_ops = cap->mld_cap;
wiphy->flags |= WIPHY_FLAG_SUPPORTS_MLO;
+
+ ieee80211_hw_set(hw, MLO_MCAST_MULTI_LINK_TX);
}
hw->queues = ATH12K_HW_MAX_QUEUES;
@@ -11030,6 +12138,19 @@ static int ath12k_mac_hw_register(struct ath12k_hw *ah)
goto err_unregister_hw;
}
+ if (ar->ab->hw_params->current_cc_support && ab->new_alpha2[0]) {
+ struct wmi_set_current_country_arg current_cc = {};
+
+ memcpy(&current_cc.alpha2, ab->new_alpha2, 2);
+ memcpy(&ar->alpha2, ab->new_alpha2, 2);
+ ret = ath12k_wmi_send_set_current_country_cmd(ar, &current_cc);
+ if (ret)
+ ath12k_warn(ar->ab,
+ "failed set cc code for mac register: %d\n",
+ ret);
+ }
+
+ ath12k_fw_stats_init(ar);
ath12k_debugfs_register(ar);
}
@@ -11077,6 +12198,7 @@ static void ath12k_mac_setup(struct ath12k *ar)
ar->num_tx_chains = hweight32(pdev->cap.tx_chain_mask);
ar->num_rx_chains = hweight32(pdev->cap.rx_chain_mask);
ar->scan.arvif = NULL;
+ ar->vdev_id_11d_scan = ATH12K_11D_INVALID_VDEV_ID;
spin_lock_init(&ar->data_lock);
INIT_LIST_HEAD(&ar->arvifs);
@@ -11092,6 +12214,7 @@ static void ath12k_mac_setup(struct ath12k *ar)
init_completion(&ar->scan.completed);
init_completion(&ar->scan.on_channel);
init_completion(&ar->mlo_setup_done);
+ init_completion(&ar->completed_11d_scan);
INIT_DELAYED_WORK(&ar->scan.timeout, ath12k_scan_timeout_work);
wiphy_work_init(&ar->scan.vdev_clean_wk, ath12k_scan_vdev_clean_work);
@@ -11099,6 +12222,10 @@ static void ath12k_mac_setup(struct ath12k *ar)
wiphy_work_init(&ar->wmi_mgmt_tx_work, ath12k_mgmt_over_wmi_tx_work);
skb_queue_head_init(&ar->wmi_mgmt_tx_queue);
+
+ ar->monitor_vdev_id = -1;
+ ar->monitor_vdev_created = false;
+ ar->monitor_started = false;
}
static int __ath12k_mac_mlo_setup(struct ath12k *ar)
@@ -11133,6 +12260,9 @@ static int __ath12k_mac_mlo_setup(struct ath12k *ar)
}
}
+ if (num_link == 0)
+ return 0;
+
mlo.group_id = cpu_to_le32(ag->id);
mlo.partner_link_id = partner_link_id;
mlo.num_partner_links = num_link;
@@ -11162,10 +12292,16 @@ static int __ath12k_mac_mlo_teardown(struct ath12k *ar)
{
struct ath12k_base *ab = ar->ab;
int ret;
+ u8 num_link;
if (test_bit(ATH12K_FLAG_RECOVERY, &ab->dev_flags))
return 0;
+ num_link = ath12k_get_num_partner_link(ar);
+
+ if (num_link == 0)
+ return 0;
+
ret = ath12k_wmi_mlo_teardown(ar);
if (ret) {
ath12k_warn(ab, "failed to send MLO teardown WMI command for pdev %d: %d\n",
@@ -11244,7 +12380,6 @@ void ath12k_mac_mlo_teardown(struct ath12k_hw_group *ag)
int ath12k_mac_register(struct ath12k_hw_group *ag)
{
- struct ath12k_base *ab = ag->ab[0];
struct ath12k_hw *ah;
int i;
int ret;
@@ -11257,8 +12392,6 @@ int ath12k_mac_register(struct ath12k_hw_group *ag)
goto err;
}
- set_bit(ATH12K_FLAG_REGISTERED, &ab->dev_flags);
-
return 0;
err:
@@ -11275,12 +12408,9 @@ err:
void ath12k_mac_unregister(struct ath12k_hw_group *ag)
{
- struct ath12k_base *ab = ag->ab[0];
struct ath12k_hw *ah;
int i;
- clear_bit(ATH12K_FLAG_REGISTERED, &ab->dev_flags);
-
for (i = ag->num_hw - 1; i >= 0; i--) {
ah = ath12k_ag_to_ah(ag, i);
if (!ah)
@@ -11394,6 +12524,7 @@ int ath12k_mac_allocate(struct ath12k_hw_group *ag)
if (!ab)
continue;
+ ath12k_debugfs_pdev_create(ab);
ath12k_mac_set_device_defaults(ab);
total_radio += ab->num_radios;
}
diff --git a/drivers/net/wireless/ath/ath12k/mac.h b/drivers/net/wireless/ath/ath12k/mac.h
index 3594729b6397..e6e74b45bfa4 100644
--- a/drivers/net/wireless/ath/ath12k/mac.h
+++ b/drivers/net/wireless/ath/ath12k/mac.h
@@ -1,7 +1,7 @@
/* SPDX-License-Identifier: BSD-3-Clause-Clear */
/*
* Copyright (c) 2018-2021 The Linux Foundation. All rights reserved.
- * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#ifndef ATH12K_MAC_H
@@ -33,6 +33,9 @@ struct ath12k_generic_iter {
#define ATH12K_KEEPALIVE_MAX_IDLE 3895
#define ATH12K_KEEPALIVE_MAX_UNRESPONSIVE 3900
+#define ATH12K_PDEV_TX_POWER_INVALID ((u32)-1)
+#define ATH12K_PDEV_TX_POWER_REFRESH_TIME_MSECS 5000 /* msecs */
+
/* FIXME: should these be in ieee80211.h? */
#define IEEE80211_VHT_MCS_SUPPORT_0_11_MASK GENMASK(23, 16)
#define IEEE80211_DISABLE_VHT_MCS_SUPPORT_0_11 BIT(24)
@@ -64,8 +67,55 @@ struct ath12k_mac_get_any_chanctx_conf_arg {
struct ieee80211_chanctx_conf *chanctx_conf;
};
+/**
+ * struct ath12k_chan_power_info - TPE containing power info per channel chunk
+ * @chan_cfreq: channel center freq (MHz)
+ * e.g.
+ * channel 37/20 MHz, it is 6135
+ * channel 37/40 MHz, it is 6125
+ * channel 37/80 MHz, it is 6145
+ * channel 37/160 MHz, it is 6185
+ * @tx_power: transmit power (dBm)
+ */
+struct ath12k_chan_power_info {
+ u16 chan_cfreq;
+ s8 tx_power;
+};
+
+/* ath12k only deals with 320 MHz, so 16 subchannels */
+#define ATH12K_NUM_PWR_LEVELS 16
+
+/**
+ * struct ath12k_reg_tpc_power_info - regulatory TPC power info
+ * @is_psd_power: is PSD power or not
+ * @eirp_power: Maximum EIRP power (dBm), valid only if power is PSD
+ * @ap_power_type: type of power (SP/LPI/VLP)
+ * @num_pwr_levels: number of power levels
+ * @reg_max: Array of maximum TX power (dBm) per PSD value
+ * @ap_constraint_power: AP constraint power (dBm)
+ * @tpe: TPE values processed from TPE IE
+ * @chan_power_info: power info to send to firmware
+ */
+struct ath12k_reg_tpc_power_info {
+ bool is_psd_power;
+ u8 eirp_power;
+ enum wmi_reg_6g_ap_type ap_power_type;
+ u8 num_pwr_levels;
+ u8 reg_max[ATH12K_NUM_PWR_LEVELS];
+ u8 ap_constraint_power;
+ s8 tpe[ATH12K_NUM_PWR_LEVELS];
+ struct ath12k_chan_power_info chan_power_info[ATH12K_NUM_PWR_LEVELS];
+};
+
extern const struct htt_rx_ring_tlv_filter ath12k_mac_mon_status_filter_default;
+#define ATH12K_SCAN_11D_INTERVAL 600000
+#define ATH12K_11D_INVALID_VDEV_ID 0xFFFF
+
+void ath12k_mac_11d_scan_start(struct ath12k *ar, u32 vdev_id);
+void ath12k_mac_11d_scan_stop(struct ath12k *ar);
+void ath12k_mac_11d_scan_stop_all(struct ath12k_base *ab);
+
void ath12k_mac_destroy(struct ath12k_hw_group *ag);
void ath12k_mac_unregister(struct ath12k_hw_group *ag);
int ath12k_mac_register(struct ath12k_hw_group *ag);
@@ -108,5 +158,17 @@ int ath12k_mac_vdev_stop(struct ath12k_link_vif *arvif);
void ath12k_mac_get_any_chanctx_conf_iter(struct ieee80211_hw *hw,
struct ieee80211_chanctx_conf *conf,
void *data);
-
+u16 ath12k_mac_he_convert_tones_to_ru_tones(u16 tones);
+enum nl80211_eht_ru_alloc ath12k_mac_eht_ru_tones_to_nl80211_eht_ru_alloc(u16 ru_tones);
+enum nl80211_eht_gi ath12k_mac_eht_gi_to_nl80211_eht_gi(u8 sgi);
+struct ieee80211_bss_conf *ath12k_mac_get_link_bss_conf(struct ath12k_link_vif *arvif);
+struct ath12k *ath12k_get_ar_by_vif(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ u8 link_id);
+int ath12k_mac_get_fw_stats(struct ath12k *ar, struct ath12k_fw_stats_req_params *param);
+void ath12k_mac_update_freq_range(struct ath12k *ar,
+ u32 freq_low, u32 freq_high);
+void ath12k_mac_fill_reg_tpc_info(struct ath12k *ar,
+ struct ath12k_link_vif *arvif,
+ struct ieee80211_chanctx_conf *ctx);
#endif
diff --git a/drivers/net/wireless/ath/ath12k/mhi.c b/drivers/net/wireless/ath/ath12k/mhi.c
index 2f6d14382ed7..08f44baf182a 100644
--- a/drivers/net/wireless/ath/ath12k/mhi.c
+++ b/drivers/net/wireless/ath/ath12k/mhi.c
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: BSD-3-Clause-Clear
/*
* Copyright (c) 2020-2021 The Linux Foundation. All rights reserved.
- * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#include <linux/msi.h>
@@ -285,8 +285,11 @@ static void ath12k_mhi_op_status_cb(struct mhi_controller *mhi_cntrl,
break;
}
- if (!(test_bit(ATH12K_FLAG_UNREGISTERING, &ab->dev_flags)))
+ if (!(test_bit(ATH12K_FLAG_UNREGISTERING, &ab->dev_flags))) {
+ set_bit(ATH12K_FLAG_CRASH_FLUSH, &ab->dev_flags);
+ set_bit(ATH12K_FLAG_RECOVERY, &ab->dev_flags);
queue_work(ab->workqueue_aux, &ab->reset_work);
+ }
break;
default:
break;
@@ -379,7 +382,7 @@ int ath12k_mhi_register(struct ath12k_pci *ab_pci)
mhi_ctrl->irq_flags = IRQF_SHARED | IRQF_NOBALANCING;
mhi_ctrl->iova_start = 0;
- mhi_ctrl->iova_stop = 0xffffffff;
+ mhi_ctrl->iova_stop = ab_pci->dma_mask;
mhi_ctrl->sbl_size = SZ_512K;
mhi_ctrl->seg_len = SZ_512K;
mhi_ctrl->fbc_download = true;
diff --git a/drivers/net/wireless/ath/ath12k/pci.c b/drivers/net/wireless/ath/ath12k/pci.c
index 06cff3849ab8..489d546390fc 100644
--- a/drivers/net/wireless/ath/ath12k/pci.c
+++ b/drivers/net/wireless/ath/ath12k/pci.c
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: BSD-3-Clause-Clear
/*
* Copyright (c) 2019-2021 The Linux Foundation. All rights reserved.
- * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#include <linux/module.h>
@@ -17,7 +17,7 @@
#include "debug.h"
#define ATH12K_PCI_BAR_NUM 0
-#define ATH12K_PCI_DMA_MASK 32
+#define ATH12K_PCI_DMA_MASK 36
#define ATH12K_PCI_IRQ_CE0_OFFSET 3
@@ -483,8 +483,11 @@ static void __ath12k_pci_ext_irq_disable(struct ath12k_base *ab)
ath12k_pci_ext_grp_disable(irq_grp);
- napi_synchronize(&irq_grp->napi);
- napi_disable(&irq_grp->napi);
+ if (irq_grp->napi_enabled) {
+ napi_synchronize(&irq_grp->napi);
+ napi_disable(&irq_grp->napi);
+ irq_grp->napi_enabled = false;
+ }
}
}
@@ -597,7 +600,8 @@ static int ath12k_pci_ext_irq_config(struct ath12k_base *ab)
ab->hw_params->ring_mask->rx_wbm_rel[i] ||
ab->hw_params->ring_mask->reo_status[i] ||
ab->hw_params->ring_mask->host2rxdma[i] ||
- ab->hw_params->ring_mask->rx_mon_dest[i]) {
+ ab->hw_params->ring_mask->rx_mon_dest[i] ||
+ ab->hw_params->ring_mask->rx_mon_status[i]) {
num_irq = 1;
}
@@ -646,7 +650,7 @@ static int ath12k_pci_set_irq_affinity_hint(struct ath12k_pci *ab_pci,
if (test_bit(ATH12K_PCI_FLAG_MULTI_MSI_VECTORS, &ab_pci->flags))
return 0;
- return irq_set_affinity_hint(ab_pci->pdev->irq, m);
+ return irq_set_affinity_and_hint(ab_pci->pdev->irq, m);
}
static int ath12k_pci_config_irq(struct ath12k_base *ab)
@@ -715,7 +719,7 @@ static void ath12k_pci_init_qmi_ce_config(struct ath12k_base *ab)
cfg->svc_to_ce_map_len = ab->hw_params->svc_to_ce_map_len;
ab->qmi.service_ins_id = ab->hw_params->qmi_service_ins_id;
- if (test_bit(ATH12K_FW_FEATURE_MULTI_QRTR_ID, ab->fw.fw_features)) {
+ if (ath12k_fw_feature_supported(ab, ATH12K_FW_FEATURE_MULTI_QRTR_ID)) {
ab_pci->qmi_instance =
u32_encode_bits(pci_domain_nr(bus), DOMAIN_NUMBER_MASK) |
u32_encode_bits(bus->number, BUS_NUMBER_MASK);
@@ -874,13 +878,9 @@ static int ath12k_pci_claim(struct ath12k_pci *ab_pci, struct pci_dev *pdev)
goto disable_device;
}
- ret = dma_set_mask_and_coherent(&pdev->dev,
- DMA_BIT_MASK(ATH12K_PCI_DMA_MASK));
- if (ret) {
- ath12k_err(ab, "failed to set pci dma mask to %d: %d\n",
- ATH12K_PCI_DMA_MASK, ret);
- goto release_region;
- }
+ ab_pci->dma_mask = DMA_BIT_MASK(ATH12K_PCI_DMA_MASK);
+ dma_set_mask(&pdev->dev, ab_pci->dma_mask);
+ dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32));
pci_set_master(pdev);
@@ -1114,7 +1114,11 @@ void ath12k_pci_ext_irq_enable(struct ath12k_base *ab)
for (i = 0; i < ATH12K_EXT_IRQ_GRP_NUM_MAX; i++) {
struct ath12k_ext_irq_grp *irq_grp = &ab->ext_irq_grp[i];
- napi_enable(&irq_grp->napi);
+ if (!irq_grp->napi_enabled) {
+ napi_enable(&irq_grp->napi);
+ irq_grp->napi_enabled = true;
+ }
+
ath12k_pci_ext_grp_enable(irq_grp);
}
@@ -1465,7 +1469,7 @@ int ath12k_pci_power_up(struct ath12k_base *ab)
ath12k_pci_msi_enable(ab_pci);
- if (test_bit(ATH12K_FW_FEATURE_MULTI_QRTR_ID, ab->fw.fw_features))
+ if (ath12k_fw_feature_supported(ab, ATH12K_FW_FEATURE_MULTI_QRTR_ID))
ath12k_pci_update_qrtr_node_id(ab);
ret = ath12k_mhi_start(ab_pci);
@@ -1484,6 +1488,9 @@ void ath12k_pci_power_down(struct ath12k_base *ab, bool is_suspend)
{
struct ath12k_pci *ab_pci = ath12k_pci_priv(ab);
+ if (!test_bit(ATH12K_PCI_FLAG_INIT_DONE, &ab_pci->flags))
+ return;
+
/* restore aspm in case firmware bootup fails */
ath12k_pci_aspm_restore(ab_pci);
@@ -1561,6 +1568,7 @@ static int ath12k_pci_probe(struct pci_dev *pdev,
ab_pci->ab = ab;
ab_pci->pdev = pdev;
ab->hif.ops = &ath12k_pci_hif_ops;
+ ab->fw_mode = ATH12K_FIRMWARE_MODE_NORMAL;
pci_set_drvdata(pdev, ab);
spin_lock_init(&ab_pci->window_lock);
@@ -1689,6 +1697,8 @@ static int ath12k_pci_probe(struct pci_dev *pdev,
return 0;
err_free_irq:
+ /* __free_irq() expects the caller to have cleared the affinity hint */
+ ath12k_pci_set_irq_affinity_hint(ab_pci, NULL);
ath12k_pci_free_irq(ab);
err_ce_free:
@@ -1700,12 +1710,12 @@ err_hal_srng_deinit:
err_mhi_unregister:
ath12k_mhi_unregister(ab_pci);
-err_pci_msi_free:
- ath12k_pci_msi_free(ab_pci);
-
err_irq_affinity_cleanup:
ath12k_pci_set_irq_affinity_hint(ab_pci, NULL);
+err_pci_msi_free:
+ ath12k_pci_msi_free(ab_pci);
+
err_pci_free_region:
ath12k_pci_free_region(ab_pci);
@@ -1724,8 +1734,6 @@ static void ath12k_pci_remove(struct pci_dev *pdev)
if (test_bit(ATH12K_FLAG_QMI_FAIL, &ab->dev_flags)) {
ath12k_pci_power_down(ab, false);
- ath12k_qmi_deinit_service(ab);
- ath12k_core_hw_group_unassign(ab);
goto qmi_fail;
}
@@ -1733,10 +1741,11 @@ static void ath12k_pci_remove(struct pci_dev *pdev)
cancel_work_sync(&ab->reset_work);
cancel_work_sync(&ab->dump_work);
- ath12k_core_deinit(ab);
- ath12k_fw_unmap(ab);
+ ath12k_core_hw_group_cleanup(ab->ag);
qmi_fail:
+ ath12k_core_deinit(ab);
+ ath12k_fw_unmap(ab);
ath12k_mhi_unregister(ab_pci);
ath12k_pci_free_irq(ab);
@@ -1748,13 +1757,34 @@ qmi_fail:
ath12k_core_free(ab);
}
+static void ath12k_pci_hw_group_power_down(struct ath12k_hw_group *ag)
+{
+ struct ath12k_base *ab;
+ int i;
+
+ if (!ag)
+ return;
+
+ mutex_lock(&ag->mutex);
+
+ for (i = 0; i < ag->num_devices; i++) {
+ ab = ag->ab[i];
+ if (!ab)
+ continue;
+
+ ath12k_pci_power_down(ab, false);
+ }
+
+ mutex_unlock(&ag->mutex);
+}
+
static void ath12k_pci_shutdown(struct pci_dev *pdev)
{
struct ath12k_base *ab = pci_get_drvdata(pdev);
struct ath12k_pci *ab_pci = ath12k_pci_priv(ab);
ath12k_pci_set_irq_affinity_hint(ab_pci, NULL);
- ath12k_pci_power_down(ab, false);
+ ath12k_pci_hw_group_power_down(ab->ag);
}
static __maybe_unused int ath12k_pci_pm_suspend(struct device *dev)
@@ -1821,7 +1851,7 @@ static struct pci_driver ath12k_pci_driver = {
.driver.pm = &ath12k_pci_pm_ops,
};
-static int ath12k_pci_init(void)
+int ath12k_pci_init(void)
{
int ret;
@@ -1834,14 +1864,8 @@ static int ath12k_pci_init(void)
return 0;
}
-module_init(ath12k_pci_init);
-static void ath12k_pci_exit(void)
+void ath12k_pci_exit(void)
{
pci_unregister_driver(&ath12k_pci_driver);
}
-
-module_exit(ath12k_pci_exit);
-
-MODULE_DESCRIPTION("Driver support for Qualcomm Technologies PCIe 802.11be WLAN devices");
-MODULE_LICENSE("Dual BSD/GPL");
diff --git a/drivers/net/wireless/ath/ath12k/pci.h b/drivers/net/wireless/ath/ath12k/pci.h
index 31584a7ad80e..0b4c459d6d8e 100644
--- a/drivers/net/wireless/ath/ath12k/pci.h
+++ b/drivers/net/wireless/ath/ath12k/pci.h
@@ -1,7 +1,7 @@
/* SPDX-License-Identifier: BSD-3-Clause-Clear */
/*
* Copyright (c) 2019-2021 The Linux Foundation. All rights reserved.
- * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#ifndef ATH12K_PCI_H
#define ATH12K_PCI_H
@@ -116,6 +116,7 @@ struct ath12k_pci {
unsigned long irq_flags;
const struct ath12k_pci_ops *pci_ops;
u32 qmi_instance;
+ u64 dma_mask;
};
static inline struct ath12k_pci *ath12k_pci_priv(struct ath12k_base *ab)
@@ -145,4 +146,6 @@ void ath12k_pci_stop(struct ath12k_base *ab);
int ath12k_pci_start(struct ath12k_base *ab);
int ath12k_pci_power_up(struct ath12k_base *ab);
void ath12k_pci_power_down(struct ath12k_base *ab, bool is_suspend);
+int ath12k_pci_init(void);
+void ath12k_pci_exit(void);
#endif /* ATH12K_PCI_H */
diff --git a/drivers/net/wireless/ath/ath12k/peer.c b/drivers/net/wireless/ath/ath12k/peer.c
index 792cca8a3fb1..ec7236bbccc0 100644
--- a/drivers/net/wireless/ath/ath12k/peer.c
+++ b/drivers/net/wireless/ath/ath12k/peer.c
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: BSD-3-Clause-Clear
/*
* Copyright (c) 2018-2021 The Linux Foundation. All rights reserved.
- * Copyright (c) 2021-2022, 2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) 2021-2022, 2024-2025 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#include "core.h"
@@ -383,6 +383,9 @@ int ath12k_peer_create(struct ath12k *ar, struct ath12k_link_vif *arvif,
arvif->ast_idx = peer->hw_peer_id;
}
+ if (vif->type == NL80211_IFTYPE_AP)
+ peer->ucast_ra_only = true;
+
if (sta) {
ahsta = ath12k_sta_to_ahsta(sta);
arsta = wiphy_dereference(ath12k_ar_to_hw(ar)->wiphy,
diff --git a/drivers/net/wireless/ath/ath12k/peer.h b/drivers/net/wireless/ath/ath12k/peer.h
index 5870ee11a8c7..f3a5e054d2b5 100644
--- a/drivers/net/wireless/ath/ath12k/peer.h
+++ b/drivers/net/wireless/ath/ath12k/peer.h
@@ -1,7 +1,7 @@
/* SPDX-License-Identifier: BSD-3-Clause-Clear */
/*
* Copyright (c) 2018-2021 The Linux Foundation. All rights reserved.
- * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#ifndef ATH12K_PEER_H
@@ -62,6 +62,7 @@ struct ath12k_peer {
/* for reference to ath12k_link_sta */
u8 link_id;
+ bool ucast_ra_only;
};
struct ath12k_ml_peer {
diff --git a/drivers/net/wireless/ath/ath12k/qmi.c b/drivers/net/wireless/ath/ath12k/qmi.c
index 5c3563383fab..99e1fb2910d0 100644
--- a/drivers/net/wireless/ath/ath12k/qmi.c
+++ b/drivers/net/wireless/ath/ath12k/qmi.c
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: BSD-3-Clause-Clear
/*
* Copyright (c) 2018-2021 The Linux Foundation. All rights reserved.
- * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#include <linux/elf.h>
@@ -11,6 +11,8 @@
#include "debug.h"
#include <linux/of.h>
#include <linux/firmware.h>
+#include <linux/of_address.h>
+#include <linux/ioport.h>
#define SLEEP_CLOCK_SELECT_INTERNAL_BIT 0x02
#define HOST_CSTATE_BIT 0x04
@@ -2056,8 +2058,7 @@ static int ath12k_host_cap_parse_mlo(struct ath12k_base *ab,
}
if (!ab->qmi.num_radios || ab->qmi.num_radios == U8_MAX) {
- ab->single_chip_mlo_supp = false;
-
+ ag->mlo_capable = false;
ath12k_dbg(ab, ATH12K_DBG_QMI,
"skip QMI MLO cap due to invalid num_radio %d\n",
ab->qmi.num_radios);
@@ -2169,10 +2170,12 @@ int ath12k_qmi_host_cap_send(struct ath12k_base *ab)
req.bdf_support_valid = 1;
req.bdf_support = 1;
- req.m3_support_valid = 1;
- req.m3_support = 1;
- req.m3_cache_support_valid = 1;
- req.m3_cache_support = 1;
+ if (ab->hw_params->fw.m3_loader == ath12k_m3_fw_loader_driver) {
+ req.m3_support_valid = 1;
+ req.m3_support = 1;
+ req.m3_cache_support_valid = 1;
+ req.m3_cache_support = 1;
+ }
req.cal_done_valid = 1;
req.cal_done = ab->qmi.cal_done;
@@ -2265,9 +2268,8 @@ static void ath12k_qmi_phy_cap_send(struct ath12k_base *ab)
goto out;
}
- if (resp.single_chip_mlo_support_valid &&
- resp.single_chip_mlo_support)
- ab->single_chip_mlo_supp = true;
+ if (resp.single_chip_mlo_support_valid && resp.single_chip_mlo_support)
+ ab->single_chip_mlo_support = true;
if (!resp.num_phy_valid) {
ret = -ENODATA;
@@ -2277,10 +2279,10 @@ static void ath12k_qmi_phy_cap_send(struct ath12k_base *ab)
ab->qmi.num_radios = resp.num_phy;
ath12k_dbg(ab, ATH12K_DBG_QMI,
- "phy capability resp valid %d num_phy %d valid %d board_id %d valid %d single_chip_mlo_support %d\n",
+ "phy capability resp valid %d single_chip_mlo_support %d valid %d num_phy %d valid %d board_id %d\n",
+ resp.single_chip_mlo_support_valid, resp.single_chip_mlo_support,
resp.num_phy_valid, resp.num_phy,
- resp.board_id_valid, resp.board_id,
- resp.single_chip_mlo_support_valid, resp.single_chip_mlo_support);
+ resp.board_id_valid, resp.board_id);
return;
@@ -2382,7 +2384,8 @@ int ath12k_qmi_respond_fw_mem_request(struct ath12k_base *ab)
* failure to firmware and firmware then request multiple blocks of
* small chunk size memory.
*/
- if (ab->qmi.target_mem_delayed) {
+ if (!test_bit(ATH12K_FLAG_FIXED_MEM_REGION, &ab->dev_flags) &&
+ ab->qmi.target_mem_delayed) {
delayed = true;
ath12k_dbg(ab, ATH12K_DBG_QMI, "qmi delays mem_request %d\n",
ab->qmi.mem_seg_count);
@@ -2440,12 +2443,35 @@ out:
return ret;
}
+void ath12k_qmi_reset_mlo_mem(struct ath12k_hw_group *ag)
+{
+ struct target_mem_chunk *mlo_chunk;
+ int i;
+
+ lockdep_assert_held(&ag->mutex);
+
+ if (!ag->mlo_mem.init_done || ag->num_started)
+ return;
+
+ for (i = 0; i < ARRAY_SIZE(ag->mlo_mem.chunk); i++) {
+ mlo_chunk = &ag->mlo_mem.chunk[i];
+
+ if (mlo_chunk->v.addr)
+ /* TODO: Mode 0 recovery is the default mode hence resetting the
+ * whole memory region for now. Once Mode 1 support is added, this
+ * needs to be handled properly
+ */
+ memset(mlo_chunk->v.addr, 0, mlo_chunk->size);
+ }
+}
+
static void ath12k_qmi_free_mlo_mem_chunk(struct ath12k_base *ab,
struct target_mem_chunk *chunk,
int idx)
{
struct ath12k_hw_group *ag = ab->ag;
struct target_mem_chunk *mlo_chunk;
+ bool fixed_mem;
lockdep_assert_held(&ag->mutex);
@@ -2457,8 +2483,13 @@ static void ath12k_qmi_free_mlo_mem_chunk(struct ath12k_base *ab,
return;
}
+ fixed_mem = test_bit(ATH12K_FLAG_FIXED_MEM_REGION, &ab->dev_flags);
mlo_chunk = &ag->mlo_mem.chunk[idx];
- if (mlo_chunk->v.addr) {
+
+ if (fixed_mem && mlo_chunk->v.ioaddr) {
+ iounmap(mlo_chunk->v.ioaddr);
+ mlo_chunk->v.ioaddr = NULL;
+ } else if (mlo_chunk->v.addr) {
dma_free_coherent(ab->dev,
mlo_chunk->size,
mlo_chunk->v.addr,
@@ -2468,7 +2499,10 @@ static void ath12k_qmi_free_mlo_mem_chunk(struct ath12k_base *ab,
mlo_chunk->paddr = 0;
mlo_chunk->size = 0;
- chunk->v.addr = NULL;
+ if (fixed_mem)
+ chunk->v.ioaddr = NULL;
+ else
+ chunk->v.addr = NULL;
chunk->paddr = 0;
chunk->size = 0;
}
@@ -2479,19 +2513,24 @@ static void ath12k_qmi_free_target_mem_chunk(struct ath12k_base *ab)
int i, mlo_idx;
for (i = 0, mlo_idx = 0; i < ab->qmi.mem_seg_count; i++) {
- if (!ab->qmi.target_mem[i].v.addr)
- continue;
-
if (ab->qmi.target_mem[i].type == MLO_GLOBAL_MEM_REGION_TYPE) {
ath12k_qmi_free_mlo_mem_chunk(ab,
&ab->qmi.target_mem[i],
mlo_idx++);
} else {
- dma_free_coherent(ab->dev,
- ab->qmi.target_mem[i].prev_size,
- ab->qmi.target_mem[i].v.addr,
- ab->qmi.target_mem[i].paddr);
- ab->qmi.target_mem[i].v.addr = NULL;
+ if (test_bit(ATH12K_FLAG_FIXED_MEM_REGION, &ab->dev_flags) &&
+ ab->qmi.target_mem[i].v.ioaddr) {
+ iounmap(ab->qmi.target_mem[i].v.ioaddr);
+ ab->qmi.target_mem[i].v.ioaddr = NULL;
+ } else {
+ if (!ab->qmi.target_mem[i].v.addr)
+ continue;
+ dma_free_coherent(ab->dev,
+ ab->qmi.target_mem[i].prev_size,
+ ab->qmi.target_mem[i].v.addr,
+ ab->qmi.target_mem[i].paddr);
+ ab->qmi.target_mem[i].v.addr = NULL;
+ }
}
}
@@ -2644,6 +2683,130 @@ err:
return ret;
}
+static int ath12k_qmi_assign_target_mem_chunk(struct ath12k_base *ab)
+{
+ struct reserved_mem *rmem;
+ size_t avail_rmem_size;
+ 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:
+ rmem = ath12k_core_get_reserved_mem(ab, 0);
+ if (!rmem) {
+ ret = -ENODEV;
+ goto out;
+ }
+
+ avail_rmem_size = rmem->size;
+ if (avail_rmem_size < ab->qmi.target_mem[i].size) {
+ ath12k_dbg(ab, ATH12K_DBG_QMI,
+ "failed to assign mem type %u req size %u avail size %zu\n",
+ ab->qmi.target_mem[i].type,
+ ab->qmi.target_mem[i].size,
+ avail_rmem_size);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ ab->qmi.target_mem[idx].paddr = rmem->base;
+ ab->qmi.target_mem[idx].v.ioaddr =
+ ioremap(ab->qmi.target_mem[idx].paddr,
+ ab->qmi.target_mem[i].size);
+ if (!ab->qmi.target_mem[idx].v.ioaddr) {
+ ret = -EIO;
+ goto out;
+ }
+ ab->qmi.target_mem[idx].size = ab->qmi.target_mem[i].size;
+ ab->qmi.target_mem[idx].type = ab->qmi.target_mem[i].type;
+ idx++;
+ break;
+ case BDF_MEM_REGION_TYPE:
+ rmem = ath12k_core_get_reserved_mem(ab, 0);
+ if (!rmem) {
+ ret = -ENODEV;
+ goto out;
+ }
+
+ avail_rmem_size = rmem->size - ab->hw_params->bdf_addr_offset;
+ if (avail_rmem_size < ab->qmi.target_mem[i].size) {
+ ath12k_dbg(ab, ATH12K_DBG_QMI,
+ "failed to assign mem type %u req size %u avail size %zu\n",
+ ab->qmi.target_mem[i].type,
+ ab->qmi.target_mem[i].size,
+ avail_rmem_size);
+ ret = -EINVAL;
+ goto out;
+ }
+ ab->qmi.target_mem[idx].paddr =
+ rmem->base + ab->hw_params->bdf_addr_offset;
+ ab->qmi.target_mem[idx].v.ioaddr =
+ ioremap(ab->qmi.target_mem[idx].paddr,
+ ab->qmi.target_mem[i].size);
+ if (!ab->qmi.target_mem[idx].v.ioaddr) {
+ ret = -EIO;
+ goto out;
+ }
+ ab->qmi.target_mem[idx].size = ab->qmi.target_mem[i].size;
+ ab->qmi.target_mem[idx].type = ab->qmi.target_mem[i].type;
+ idx++;
+ break;
+ case CALDB_MEM_REGION_TYPE:
+ /* Cold boot calibration is not enabled in Ath12k. Hence,
+ * assign paddr = 0.
+ * Once cold boot calibration is enabled add support to
+ * assign reserved memory from DT.
+ */
+ ab->qmi.target_mem[idx].paddr = 0;
+ ab->qmi.target_mem[idx].v.ioaddr = NULL;
+ ab->qmi.target_mem[idx].size = ab->qmi.target_mem[i].size;
+ ab->qmi.target_mem[idx].type = ab->qmi.target_mem[i].type;
+ idx++;
+ break;
+ case M3_DUMP_REGION_TYPE:
+ rmem = ath12k_core_get_reserved_mem(ab, 1);
+ if (!rmem) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ avail_rmem_size = rmem->size;
+ if (avail_rmem_size < ab->qmi.target_mem[i].size) {
+ ath12k_dbg(ab, ATH12K_DBG_QMI,
+ "failed to assign mem type %u req size %u avail size %zu\n",
+ ab->qmi.target_mem[i].type,
+ ab->qmi.target_mem[i].size,
+ avail_rmem_size);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ ab->qmi.target_mem[idx].paddr = rmem->base;
+ ab->qmi.target_mem[idx].v.ioaddr =
+ ioremap(ab->qmi.target_mem[idx].paddr,
+ ab->qmi.target_mem[i].size);
+ if (!ab->qmi.target_mem[idx].v.ioaddr) {
+ ret = -EIO;
+ goto out;
+ }
+ ab->qmi.target_mem[idx].size = ab->qmi.target_mem[i].size;
+ ab->qmi.target_mem[idx].type = ab->qmi.target_mem[i].type;
+ idx++;
+ break;
+ default:
+ ath12k_warn(ab, "qmi ignore invalid mem req type %u\n",
+ ab->qmi.target_mem[i].type);
+ break;
+ }
+ }
+ ab->qmi.mem_seg_count = idx;
+
+ return 0;
+out:
+ ath12k_qmi_free_target_mem_chunk(ab);
+ return ret;
+}
+
/* clang stack usage explodes if this is inlined */
static noinline_for_stack
int ath12k_qmi_request_target_cap(struct ath12k_base *ab)
@@ -2740,6 +2903,15 @@ int ath12k_qmi_request_target_cap(struct ath12k_base *ab)
if (r)
ath12k_dbg(ab, ATH12K_DBG_QMI, "SMBIOS bdf variant name not set.\n");
+ r = ath12k_acpi_start(ab);
+ if (r)
+ /* ACPI is optional so continue in case of an error */
+ ath12k_dbg(ab, ATH12K_DBG_BOOT, "acpi failed: %d\n", r);
+
+ r = ath12k_acpi_check_bdf_variant_name(ab);
+ if (r)
+ ath12k_dbg(ab, ATH12K_DBG_BOOT, "ACPI bdf variant name not set.\n");
+
out:
return ret;
}
@@ -2936,6 +3108,9 @@ static void ath12k_qmi_m3_free(struct ath12k_base *ab)
{
struct m3_mem_region *m3_mem = &ab->qmi.m3_mem;
+ if (ab->hw_params->fw.m3_loader == ath12k_m3_fw_loader_remoteproc)
+ return;
+
if (!m3_mem->vaddr)
return;
@@ -3016,15 +3191,16 @@ int ath12k_qmi_wlanfw_m3_info_send(struct ath12k_base *ab)
struct qmi_txn txn;
int ret = 0;
- ret = ath12k_qmi_m3_load(ab);
- if (ret) {
- ath12k_err(ab, "failed to load m3 firmware: %d", ret);
- return ret;
+ if (ab->hw_params->fw.m3_loader == ath12k_m3_fw_loader_driver) {
+ ret = ath12k_qmi_m3_load(ab);
+ if (ret) {
+ ath12k_err(ab, "failed to load m3 firmware: %d", ret);
+ return ret;
+ }
+ req.addr = m3_mem->paddr;
+ req.size = m3_mem->size;
}
- req.addr = m3_mem->paddr;
- req.size = m3_mem->size;
-
ret = qmi_txn_init(&ab->qmi.handle, &txn,
qmi_wlanfw_m3_info_resp_msg_v01_ei, &resp);
if (ret < 0)
@@ -3474,11 +3650,20 @@ static void ath12k_qmi_msg_mem_request_cb(struct qmi_handle *qmi_hdl,
msg->mem_seg[i].type, msg->mem_seg[i].size);
}
- ret = ath12k_qmi_alloc_target_mem_chunk(ab);
- if (ret) {
- ath12k_warn(ab, "qmi failed to alloc target memory: %d\n",
- ret);
- return;
+ if (test_bit(ATH12K_FLAG_FIXED_MEM_REGION, &ab->dev_flags)) {
+ ret = ath12k_qmi_assign_target_mem_chunk(ab);
+ if (ret) {
+ ath12k_warn(ab, "failed to assign qmi target memory: %d\n",
+ ret);
+ return;
+ }
+ } else {
+ ret = ath12k_qmi_alloc_target_mem_chunk(ab);
+ if (ret) {
+ ath12k_warn(ab, "qmi failed to alloc target memory: %d\n",
+ ret);
+ return;
+ }
}
ath12k_qmi_driver_event_post(qmi, ATH12K_QMI_EVENT_REQUEST_MEM, NULL);
diff --git a/drivers/net/wireless/ath/ath12k/qmi.h b/drivers/net/wireless/ath/ath12k/qmi.h
index 45d7c3fcafdd..96e6c3daecfe 100644
--- a/drivers/net/wireless/ath/ath12k/qmi.h
+++ b/drivers/net/wireless/ath/ath12k/qmi.h
@@ -1,7 +1,7 @@
/* SPDX-License-Identifier: BSD-3-Clause-Clear */
/*
* Copyright (c) 2018-2021 The Linux Foundation. All rights reserved.
- * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#ifndef ATH12K_QMI_H
@@ -21,6 +21,7 @@
#define ATH12K_QMI_WLFW_SERVICE_INS_ID_V01_WCN7850 0x1
#define ATH12K_QMI_WLFW_SERVICE_INS_ID_V01_QCN9274 0x07
+#define ATH12K_QMI_WLFW_SERVICE_INS_ID_V01_IPQ5332 0x2
#define ATH12K_QMI_WLANFW_MAX_TIMESTAMP_LEN_V01 32
#define ATH12K_QMI_RESP_LEN_MAX 8192
#define ATH12K_QMI_WLANFW_MAX_NUM_MEM_SEG_V01 52
@@ -41,6 +42,7 @@
#define ATH12K_BOARD_ID_DEFAULT 0xFF
struct ath12k_base;
+struct ath12k_hw_group;
enum ath12k_qmi_file_type {
ATH12K_QMI_FILE_TYPE_BDF_GOLDEN = 0,
@@ -621,5 +623,6 @@ void ath12k_qmi_deinit_service(struct ath12k_base *ab);
int ath12k_qmi_init_service(struct ath12k_base *ab);
void ath12k_qmi_free_resource(struct ath12k_base *ab);
void ath12k_qmi_trigger_host_cap(struct ath12k_base *ab);
+void ath12k_qmi_reset_mlo_mem(struct ath12k_hw_group *ag);
#endif
diff --git a/drivers/net/wireless/ath/ath12k/reg.c b/drivers/net/wireless/ath/ath12k/reg.c
index 439d61f284d8..2598b39d5d7e 100644
--- a/drivers/net/wireless/ath/ath12k/reg.c
+++ b/drivers/net/wireless/ath/ath12k/reg.c
@@ -1,11 +1,12 @@
// SPDX-License-Identifier: BSD-3-Clause-Clear
/*
* Copyright (c) 2018-2021 The Linux Foundation. All rights reserved.
- * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#include <linux/rtnetlink.h>
#include "core.h"
#include "debug.h"
+#include "mac.h"
/* World regdom to be used in case default regd from fw is unavailable */
#define ATH12K_2GHZ_CH01_11 REG_RULE(2412 - 10, 2462 + 10, 40, 0, 20, 0)
@@ -48,6 +49,7 @@ ath12k_reg_notifier(struct wiphy *wiphy, struct regulatory_request *request)
{
struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
struct ath12k_wmi_init_country_arg arg;
+ struct wmi_set_current_country_arg current_arg = {};
struct ath12k_hw *ah = ath12k_hw_to_ah(hw);
struct ath12k *ar = ath12k_ah_to_ar(ah, 0);
int ret, i;
@@ -55,6 +57,24 @@ ath12k_reg_notifier(struct wiphy *wiphy, struct regulatory_request *request)
ath12k_dbg(ar->ab, ATH12K_DBG_REG,
"Regulatory Notification received for %s\n", wiphy_name(wiphy));
+ if (request->initiator == NL80211_REGDOM_SET_BY_DRIVER) {
+ ath12k_dbg(ar->ab, ATH12K_DBG_REG,
+ "driver initiated regd update\n");
+ if (ah->state != ATH12K_HW_STATE_ON)
+ return;
+
+ for_each_ar(ah, ar, i) {
+ ret = ath12k_reg_update_chan_list(ar, true);
+ if (ret) {
+ ath12k_warn(ar->ab,
+ "failed to update chan list for pdev %u, ret %d\n",
+ i, ret);
+ break;
+ }
+ }
+ return;
+ }
+
/* Currently supporting only General User Hints. Cell base user
* hints to be handled later.
* Hints from other sources like Core, Beacons are not expected for
@@ -77,27 +97,38 @@ ath12k_reg_notifier(struct wiphy *wiphy, struct regulatory_request *request)
return;
}
- /* Set the country code to the firmware and wait for
- * the WMI_REG_CHAN_LIST_CC EVENT for updating the
- * reg info
- */
- arg.flags = ALPHA_IS_SET;
- memcpy(&arg.cc_info.alpha2, request->alpha2, 2);
- arg.cc_info.alpha2[2] = 0;
-
/* Allow fresh updates to wiphy regd */
ah->regd_updated = false;
/* Send the reg change request to all the radios */
for_each_ar(ah, ar, i) {
- ret = ath12k_wmi_send_init_country_cmd(ar, &arg);
- if (ret)
- ath12k_warn(ar->ab,
- "INIT Country code set to fw failed : %d\n", ret);
+ if (ar->ab->hw_params->current_cc_support) {
+ memcpy(&current_arg.alpha2, request->alpha2, 2);
+ memcpy(&ar->alpha2, &current_arg.alpha2, 2);
+ ret = ath12k_wmi_send_set_current_country_cmd(ar, &current_arg);
+ if (ret)
+ ath12k_warn(ar->ab,
+ "failed set current country code: %d\n", ret);
+ } else {
+ arg.flags = ALPHA_IS_SET;
+ memcpy(&arg.cc_info.alpha2, request->alpha2, 2);
+ arg.cc_info.alpha2[2] = 0;
+
+ ret = ath12k_wmi_send_init_country_cmd(ar, &arg);
+ if (ret)
+ ath12k_warn(ar->ab,
+ "failed set INIT Country code: %d\n", ret);
+ }
+
+ wiphy_lock(wiphy);
+ ath12k_mac_11d_scan_stop(ar);
+ wiphy_unlock(wiphy);
+
+ ar->regdom_set_by_user = true;
}
}
-int ath12k_reg_update_chan_list(struct ath12k *ar)
+int ath12k_reg_update_chan_list(struct ath12k *ar, bool wait)
{
struct ieee80211_supported_band **bands;
struct ath12k_wmi_scan_chan_list_arg *arg;
@@ -106,7 +137,35 @@ int ath12k_reg_update_chan_list(struct ath12k *ar)
struct ath12k_wmi_channel_arg *ch;
enum nl80211_band band;
int num_channels = 0;
- int i, ret;
+ int i, ret, left;
+
+ if (wait && ar->state_11d == ATH12K_11D_RUNNING) {
+ left = wait_for_completion_timeout(&ar->completed_11d_scan,
+ ATH12K_SCAN_TIMEOUT_HZ);
+ if (!left) {
+ ath12k_dbg(ar->ab, ATH12K_DBG_REG,
+ "failed to receive 11d scan complete: timed out\n");
+ ar->state_11d = ATH12K_11D_IDLE;
+ }
+ ath12k_dbg(ar->ab, ATH12K_DBG_REG,
+ "reg 11d scan wait left time %d\n", left);
+ }
+
+ if (wait &&
+ (ar->scan.state == ATH12K_SCAN_STARTING ||
+ ar->scan.state == ATH12K_SCAN_RUNNING)) {
+ left = wait_for_completion_timeout(&ar->scan.completed,
+ ATH12K_SCAN_TIMEOUT_HZ);
+ if (!left)
+ ath12k_dbg(ar->ab, ATH12K_DBG_REG,
+ "failed to receive hw scan complete: timed out\n");
+
+ ath12k_dbg(ar->ab, ATH12K_DBG_REG,
+ "reg hw scan wait left time %d\n", left);
+ }
+
+ if (ar->ah->state == ATH12K_HW_STATE_RESTARTING)
+ return 0;
bands = hw->wiphy->bands;
for (band = 0; band < NUM_NL80211_BANDS; band++) {
@@ -206,15 +265,57 @@ static void ath12k_copy_regd(struct ieee80211_regdomain *regd_orig,
int ath12k_regd_update(struct ath12k *ar, bool init)
{
+ struct ath12k_wmi_hal_reg_capabilities_ext_arg *reg_cap;
+ u32 phy_id, freq_low, freq_high, supported_bands;
struct ath12k_hw *ah = ath12k_ar_to_ah(ar);
struct ieee80211_hw *hw = ah->hw;
struct ieee80211_regdomain *regd, *regd_copy = NULL;
int ret, regd_len, pdev_id;
struct ath12k_base *ab;
- int i;
ab = ar->ab;
+ supported_bands = ar->pdev->cap.supported_bands;
+ reg_cap = &ab->hal_reg_cap[ar->pdev_idx];
+
+ /* Possible that due to reg change, current limits for supported
+ * frequency changed. Update it. As a first step, reset the
+ * previous values and then compute and set the new values.
+ */
+ ar->freq_range.start_freq = 0;
+ ar->freq_range.end_freq = 0;
+
+ if (supported_bands & WMI_HOST_WLAN_2GHZ_CAP) {
+ if (ab->hw_params->single_pdev_only) {
+ phy_id = ar->pdev->cap.band[WMI_HOST_WLAN_2GHZ_CAP].phy_id;
+ reg_cap = &ab->hal_reg_cap[phy_id];
+ }
+
+ freq_low = max(reg_cap->low_2ghz_chan, ab->reg_freq_2ghz.start_freq);
+ freq_high = min(reg_cap->high_2ghz_chan, ab->reg_freq_2ghz.end_freq);
+
+ ath12k_mac_update_freq_range(ar, freq_low, freq_high);
+ }
+
+ if (supported_bands & WMI_HOST_WLAN_5GHZ_CAP && !ar->supports_6ghz) {
+ if (ab->hw_params->single_pdev_only) {
+ phy_id = ar->pdev->cap.band[WMI_HOST_WLAN_5GHZ_CAP].phy_id;
+ reg_cap = &ab->hal_reg_cap[phy_id];
+ }
+
+ freq_low = max(reg_cap->low_5ghz_chan, ab->reg_freq_5ghz.start_freq);
+ freq_high = min(reg_cap->high_5ghz_chan, ab->reg_freq_5ghz.end_freq);
+
+ ath12k_mac_update_freq_range(ar, freq_low, freq_high);
+ }
+
+ if (supported_bands & WMI_HOST_WLAN_5GHZ_CAP && ar->supports_6ghz) {
+ freq_low = max(reg_cap->low_5ghz_chan, ab->reg_freq_6ghz.start_freq);
+ freq_high = min(reg_cap->high_5ghz_chan, ab->reg_freq_6ghz.end_freq);
+
+ ath12k_mac_update_freq_range(ar, freq_low, freq_high);
+ }
+
/* If one of the radios within ah has already updated the regd for
* the wiphy, then avoid setting regd again
*/
@@ -275,11 +376,7 @@ int ath12k_regd_update(struct ath12k *ar, bool init)
goto err;
}
- rtnl_lock();
- wiphy_lock(hw->wiphy);
- ret = regulatory_set_wiphy_regd_sync(hw->wiphy, regd_copy);
- wiphy_unlock(hw->wiphy);
- rtnl_unlock();
+ ret = regulatory_set_wiphy_regd(hw->wiphy, regd_copy);
kfree(regd_copy);
@@ -290,15 +387,7 @@ int ath12k_regd_update(struct ath12k *ar, bool init)
goto skip;
ah->regd_updated = true;
- /* Apply the new regd to all the radios, this is expected to be received only once
- * since we check for ah->regd_updated and allow here only once.
- */
- for_each_ar(ah, ar, i) {
- ab = ar->ab;
- ret = ath12k_reg_update_chan_list(ar);
- if (ret)
- goto err;
- }
+
skip:
return 0;
err:
@@ -365,129 +454,6 @@ static u32 ath12k_map_fw_phy_flags(u32 phy_flags)
return flags;
}
-static bool
-ath12k_reg_can_intersect(struct ieee80211_reg_rule *rule1,
- struct ieee80211_reg_rule *rule2)
-{
- u32 start_freq1, end_freq1;
- u32 start_freq2, end_freq2;
-
- start_freq1 = rule1->freq_range.start_freq_khz;
- start_freq2 = rule2->freq_range.start_freq_khz;
-
- end_freq1 = rule1->freq_range.end_freq_khz;
- end_freq2 = rule2->freq_range.end_freq_khz;
-
- if ((start_freq1 >= start_freq2 &&
- start_freq1 < end_freq2) ||
- (start_freq2 > start_freq1 &&
- start_freq2 < end_freq1))
- return true;
-
- /* TODO: Should we restrict intersection feasibility
- * based on min bandwidth of the intersected region also,
- * say the intersected rule should have a min bandwidth
- * of 20MHz?
- */
-
- return false;
-}
-
-static void ath12k_reg_intersect_rules(struct ieee80211_reg_rule *rule1,
- struct ieee80211_reg_rule *rule2,
- struct ieee80211_reg_rule *new_rule)
-{
- u32 start_freq1, end_freq1;
- u32 start_freq2, end_freq2;
- u32 freq_diff, max_bw;
-
- start_freq1 = rule1->freq_range.start_freq_khz;
- start_freq2 = rule2->freq_range.start_freq_khz;
-
- end_freq1 = rule1->freq_range.end_freq_khz;
- end_freq2 = rule2->freq_range.end_freq_khz;
-
- new_rule->freq_range.start_freq_khz = max_t(u32, start_freq1,
- start_freq2);
- new_rule->freq_range.end_freq_khz = min_t(u32, end_freq1, end_freq2);
-
- freq_diff = new_rule->freq_range.end_freq_khz -
- new_rule->freq_range.start_freq_khz;
- max_bw = min_t(u32, rule1->freq_range.max_bandwidth_khz,
- rule2->freq_range.max_bandwidth_khz);
- new_rule->freq_range.max_bandwidth_khz = min_t(u32, max_bw, freq_diff);
-
- new_rule->power_rule.max_antenna_gain =
- min_t(u32, rule1->power_rule.max_antenna_gain,
- rule2->power_rule.max_antenna_gain);
-
- new_rule->power_rule.max_eirp = min_t(u32, rule1->power_rule.max_eirp,
- rule2->power_rule.max_eirp);
-
- /* Use the flags of both the rules */
- new_rule->flags = rule1->flags | rule2->flags;
-
- /* To be safe, lts use the max cac timeout of both rules */
- new_rule->dfs_cac_ms = max_t(u32, rule1->dfs_cac_ms,
- rule2->dfs_cac_ms);
-}
-
-static struct ieee80211_regdomain *
-ath12k_regd_intersect(struct ieee80211_regdomain *default_regd,
- struct ieee80211_regdomain *curr_regd)
-{
- u8 num_old_regd_rules, num_curr_regd_rules, num_new_regd_rules;
- struct ieee80211_reg_rule *old_rule, *curr_rule, *new_rule;
- struct ieee80211_regdomain *new_regd = NULL;
- u8 i, j, k;
-
- num_old_regd_rules = default_regd->n_reg_rules;
- num_curr_regd_rules = curr_regd->n_reg_rules;
- num_new_regd_rules = 0;
-
- /* Find the number of intersecting rules to allocate new regd memory */
- for (i = 0; i < num_old_regd_rules; i++) {
- old_rule = default_regd->reg_rules + i;
- for (j = 0; j < num_curr_regd_rules; j++) {
- curr_rule = curr_regd->reg_rules + j;
-
- if (ath12k_reg_can_intersect(old_rule, curr_rule))
- num_new_regd_rules++;
- }
- }
-
- if (!num_new_regd_rules)
- return NULL;
-
- new_regd = kzalloc(sizeof(*new_regd) + (num_new_regd_rules *
- sizeof(struct ieee80211_reg_rule)),
- GFP_ATOMIC);
-
- if (!new_regd)
- return NULL;
-
- /* We set the new country and dfs region directly and only trim
- * the freq, power, antenna gain by intersecting with the
- * default regdomain. Also MAX of the dfs cac timeout is selected.
- */
- new_regd->n_reg_rules = num_new_regd_rules;
- memcpy(new_regd->alpha2, curr_regd->alpha2, sizeof(new_regd->alpha2));
- new_regd->dfs_region = curr_regd->dfs_region;
- new_rule = new_regd->reg_rules;
-
- for (i = 0, k = 0; i < num_old_regd_rules; i++) {
- old_rule = default_regd->reg_rules + i;
- for (j = 0; j < num_curr_regd_rules; j++) {
- curr_rule = curr_regd->reg_rules + j;
-
- if (ath12k_reg_can_intersect(old_rule, curr_rule))
- ath12k_reg_intersect_rules(old_rule, curr_rule,
- (new_rule + k++));
- }
- }
- return new_regd;
-}
-
static const char *
ath12k_reg_get_regdom_str(enum nl80211_dfs_regions dfs_region)
{
@@ -524,13 +490,14 @@ ath12k_reg_adjust_bw(u16 start_freq, u16 end_freq, u16 max_bw)
static void
ath12k_reg_update_rule(struct ieee80211_reg_rule *reg_rule, u32 start_freq,
u32 end_freq, u32 bw, u32 ant_gain, u32 reg_pwr,
- u32 reg_flags)
+ s8 psd, u32 reg_flags)
{
reg_rule->freq_range.start_freq_khz = MHZ_TO_KHZ(start_freq);
reg_rule->freq_range.end_freq_khz = MHZ_TO_KHZ(end_freq);
reg_rule->freq_range.max_bandwidth_khz = MHZ_TO_KHZ(bw);
reg_rule->power_rule.max_antenna_gain = DBI_TO_MBI(ant_gain);
reg_rule->power_rule.max_eirp = DBM_TO_MBM(reg_pwr);
+ reg_rule->psd = psd;
reg_rule->flags = reg_flags;
}
@@ -552,7 +519,7 @@ ath12k_reg_update_weather_radar_band(struct ath12k_base *ab,
ath12k_reg_update_rule(regd->reg_rules + i, reg_rule->start_freq,
ETSI_WEATHER_RADAR_BAND_LOW, bw,
reg_rule->ant_gain, reg_rule->reg_power,
- flags);
+ reg_rule->psd_eirp, flags);
ath12k_dbg(ab, ATH12K_DBG_REG,
"\t%d. (%d - %d @ %d) (%d, %d) (%d ms) (FLAGS %d)\n",
@@ -574,7 +541,7 @@ ath12k_reg_update_weather_radar_band(struct ath12k_base *ab,
ath12k_reg_update_rule(regd->reg_rules + i,
ETSI_WEATHER_RADAR_BAND_LOW, end_freq, bw,
reg_rule->ant_gain, reg_rule->reg_power,
- flags);
+ reg_rule->psd_eirp, flags);
regd->reg_rules[i].dfs_cac_ms = ETSI_WEATHER_RADAR_BAND_CAC_TIMEOUT;
@@ -599,7 +566,7 @@ ath12k_reg_update_weather_radar_band(struct ath12k_base *ab,
ath12k_reg_update_rule(regd->reg_rules + i, ETSI_WEATHER_RADAR_BAND_HIGH,
reg_rule->end_freq, bw,
reg_rule->ant_gain, reg_rule->reg_power,
- flags);
+ reg_rule->psd_eirp, flags);
ath12k_dbg(ab, ATH12K_DBG_REG,
"\t%d. (%d - %d @ %d) (%d, %d) (%d ms) (FLAGS %d)\n",
@@ -611,26 +578,77 @@ ath12k_reg_update_weather_radar_band(struct ath12k_base *ab,
*rule_idx = i;
}
+static void ath12k_reg_update_freq_range(struct ath12k_reg_freq *reg_freq,
+ struct ath12k_reg_rule *reg_rule)
+{
+ if (reg_freq->start_freq > reg_rule->start_freq)
+ reg_freq->start_freq = reg_rule->start_freq;
+
+ if (reg_freq->end_freq < reg_rule->end_freq)
+ reg_freq->end_freq = reg_rule->end_freq;
+}
+
+enum wmi_reg_6g_ap_type
+ath12k_reg_ap_pwr_convert(enum ieee80211_ap_reg_power power_type)
+{
+ switch (power_type) {
+ case IEEE80211_REG_LPI_AP:
+ return WMI_REG_INDOOR_AP;
+ case IEEE80211_REG_SP_AP:
+ return WMI_REG_STD_POWER_AP;
+ case IEEE80211_REG_VLP_AP:
+ return WMI_REG_VLP_AP;
+ default:
+ return WMI_REG_MAX_AP_TYPE;
+ }
+}
+
struct ieee80211_regdomain *
ath12k_reg_build_regd(struct ath12k_base *ab,
- struct ath12k_reg_info *reg_info, bool intersect)
+ struct ath12k_reg_info *reg_info,
+ enum wmi_vdev_type vdev_type,
+ enum ieee80211_ap_reg_power power_type)
{
- struct ieee80211_regdomain *tmp_regd, *default_regd, *new_regd = NULL;
- struct ath12k_reg_rule *reg_rule;
+ struct ieee80211_regdomain *new_regd = NULL;
+ struct ath12k_reg_rule *reg_rule, *reg_rule_6ghz;
+ u32 flags, reg_6ghz_number, max_bw_6ghz;
u8 i = 0, j = 0, k = 0;
u8 num_rules;
u16 max_bw;
- u32 flags;
char alpha2[3];
num_rules = reg_info->num_5g_reg_rules + reg_info->num_2g_reg_rules;
- /* FIXME: Currently taking reg rules for 6G only from Indoor AP mode list.
- * This can be updated to choose the combination dynamically based on AP
- * type and client type, after complete 6G regulatory support is added.
- */
- if (reg_info->is_ext_reg_event)
- num_rules += reg_info->num_6g_reg_rules_ap[WMI_REG_INDOOR_AP];
+ if (reg_info->is_ext_reg_event) {
+ if (vdev_type == WMI_VDEV_TYPE_STA) {
+ enum wmi_reg_6g_ap_type ap_type;
+
+ ap_type = ath12k_reg_ap_pwr_convert(power_type);
+ if (ap_type == WMI_REG_MAX_AP_TYPE)
+ ap_type = WMI_REG_INDOOR_AP;
+
+ reg_6ghz_number = reg_info->num_6g_reg_rules_cl
+ [ap_type][WMI_REG_DEFAULT_CLIENT];
+ if (reg_6ghz_number == 0) {
+ ap_type = WMI_REG_INDOOR_AP;
+ reg_6ghz_number = reg_info->num_6g_reg_rules_cl
+ [ap_type][WMI_REG_DEFAULT_CLIENT];
+ }
+
+ reg_rule_6ghz = reg_info->reg_rules_6g_client_ptr
+ [ap_type][WMI_REG_DEFAULT_CLIENT];
+ max_bw_6ghz = reg_info->max_bw_6g_client
+ [ap_type][WMI_REG_DEFAULT_CLIENT];
+ } else {
+ reg_6ghz_number = reg_info->num_6g_reg_rules_ap
+ [WMI_REG_INDOOR_AP];
+ reg_rule_6ghz =
+ reg_info->reg_rules_6g_ap_ptr[WMI_REG_INDOOR_AP];
+ max_bw_6ghz = reg_info->max_bw_6g_ap[WMI_REG_INDOOR_AP];
+ }
+
+ num_rules += reg_6ghz_number;
+ }
if (!num_rules)
goto ret;
@@ -639,21 +657,31 @@ ath12k_reg_build_regd(struct ath12k_base *ab,
if (reg_info->dfs_region == ATH12K_DFS_REG_ETSI)
num_rules += 2;
- tmp_regd = kzalloc(sizeof(*tmp_regd) +
+ new_regd = kzalloc(sizeof(*new_regd) +
(num_rules * sizeof(struct ieee80211_reg_rule)),
GFP_ATOMIC);
- if (!tmp_regd)
+ if (!new_regd)
goto ret;
- memcpy(tmp_regd->alpha2, reg_info->alpha2, REG_ALPHA2_LEN + 1);
+ memcpy(new_regd->alpha2, reg_info->alpha2, REG_ALPHA2_LEN + 1);
memcpy(alpha2, reg_info->alpha2, REG_ALPHA2_LEN + 1);
alpha2[2] = '\0';
- tmp_regd->dfs_region = ath12k_map_fw_dfs_region(reg_info->dfs_region);
+ new_regd->dfs_region = ath12k_map_fw_dfs_region(reg_info->dfs_region);
ath12k_dbg(ab, ATH12K_DBG_REG,
"\r\nCountry %s, CFG Regdomain %s FW Regdomain %d, num_reg_rules %d\n",
- alpha2, ath12k_reg_get_regdom_str(tmp_regd->dfs_region),
+ alpha2, ath12k_reg_get_regdom_str(new_regd->dfs_region),
reg_info->dfs_region, num_rules);
+
+ /* Reset start and end frequency for each band
+ */
+ ab->reg_freq_5ghz.start_freq = INT_MAX;
+ ab->reg_freq_5ghz.end_freq = 0;
+ ab->reg_freq_2ghz.start_freq = INT_MAX;
+ ab->reg_freq_2ghz.end_freq = 0;
+ ab->reg_freq_6ghz.start_freq = INT_MAX;
+ ab->reg_freq_6ghz.end_freq = 0;
+
/* Update reg_rules[] below. Firmware is expected to
* send these rules in order(2G rules first and then 5G)
*/
@@ -664,6 +692,7 @@ ath12k_reg_build_regd(struct ath12k_base *ab,
max_bw = min_t(u16, reg_rule->max_bw,
reg_info->max_bw_2g);
flags = 0;
+ ath12k_reg_update_freq_range(&ab->reg_freq_2ghz, reg_rule);
} else if (reg_info->num_5g_reg_rules &&
(j < reg_info->num_5g_reg_rules)) {
reg_rule = reg_info->reg_rules_5g_ptr + j++;
@@ -677,13 +706,15 @@ ath12k_reg_build_regd(struct ath12k_base *ab,
* per other BW rule flags we pass from here
*/
flags = NL80211_RRF_AUTO_BW;
- } else if (reg_info->is_ext_reg_event &&
- reg_info->num_6g_reg_rules_ap[WMI_REG_INDOOR_AP] &&
- (k < reg_info->num_6g_reg_rules_ap[WMI_REG_INDOOR_AP])) {
- reg_rule = reg_info->reg_rules_6g_ap_ptr[WMI_REG_INDOOR_AP] + k++;
- max_bw = min_t(u16, reg_rule->max_bw,
- reg_info->max_bw_6g_ap[WMI_REG_INDOOR_AP]);
+ ath12k_reg_update_freq_range(&ab->reg_freq_5ghz, reg_rule);
+ } else if (reg_info->is_ext_reg_event && reg_6ghz_number &&
+ (k < reg_6ghz_number)) {
+ reg_rule = reg_rule_6ghz + k++;
+ max_bw = min_t(u16, reg_rule->max_bw, max_bw_6ghz);
flags = NL80211_RRF_AUTO_BW;
+ if (reg_rule->psd_flag)
+ flags |= NL80211_RRF_PSD;
+ ath12k_reg_update_freq_range(&ab->reg_freq_6ghz, reg_rule);
} else {
break;
}
@@ -691,11 +722,11 @@ ath12k_reg_build_regd(struct ath12k_base *ab,
flags |= ath12k_map_fw_reg_flags(reg_rule->flags);
flags |= ath12k_map_fw_phy_flags(reg_info->phybitmap);
- ath12k_reg_update_rule(tmp_regd->reg_rules + i,
+ ath12k_reg_update_rule(new_regd->reg_rules + i,
reg_rule->start_freq,
reg_rule->end_freq, max_bw,
reg_rule->ant_gain, reg_rule->reg_power,
- flags);
+ reg_rule->psd_eirp, flags);
/* Update dfs cac timeout if the dfs domain is ETSI and the
* new rule covers weather radar band.
@@ -706,7 +737,7 @@ ath12k_reg_build_regd(struct ath12k_base *ab,
reg_info->dfs_region == ATH12K_DFS_REG_ETSI &&
(reg_rule->end_freq > ETSI_WEATHER_RADAR_BAND_LOW &&
reg_rule->start_freq < ETSI_WEATHER_RADAR_BAND_HIGH)){
- ath12k_reg_update_weather_radar_band(ab, tmp_regd,
+ ath12k_reg_update_weather_radar_band(ab, new_regd,
reg_rule, &i,
flags, max_bw);
continue;
@@ -716,36 +747,19 @@ ath12k_reg_build_regd(struct ath12k_base *ab,
ath12k_dbg(ab, ATH12K_DBG_REG, "\t%d. (%d - %d @ %d) (%d, %d) (%d ms) (FLAGS %d) (%d, %d)\n",
i + 1, reg_rule->start_freq, reg_rule->end_freq,
max_bw, reg_rule->ant_gain, reg_rule->reg_power,
- tmp_regd->reg_rules[i].dfs_cac_ms,
+ new_regd->reg_rules[i].dfs_cac_ms,
flags, reg_rule->psd_flag, reg_rule->psd_eirp);
} else {
ath12k_dbg(ab, ATH12K_DBG_REG,
"\t%d. (%d - %d @ %d) (%d, %d) (%d ms) (FLAGS %d)\n",
i + 1, reg_rule->start_freq, reg_rule->end_freq,
max_bw, reg_rule->ant_gain, reg_rule->reg_power,
- tmp_regd->reg_rules[i].dfs_cac_ms,
+ new_regd->reg_rules[i].dfs_cac_ms,
flags);
}
}
- tmp_regd->n_reg_rules = i;
-
- if (intersect) {
- default_regd = ab->default_regd[reg_info->phy_id];
-
- /* Get a new regd by intersecting the received regd with
- * our default regd.
- */
- new_regd = ath12k_regd_intersect(default_regd, tmp_regd);
- kfree(tmp_regd);
- if (!new_regd) {
- ath12k_warn(ab, "Unable to create intersected regdomain\n");
- goto ret;
- }
- } else {
- new_regd = tmp_regd;
- }
-
+ new_regd->n_reg_rules = i;
ret:
return new_regd;
}
@@ -767,9 +781,109 @@ void ath12k_regd_update_work(struct work_struct *work)
}
}
+void ath12k_reg_reset_reg_info(struct ath12k_reg_info *reg_info)
+{
+ u8 i, j;
+
+ if (!reg_info)
+ return;
+
+ kfree(reg_info->reg_rules_2g_ptr);
+ kfree(reg_info->reg_rules_5g_ptr);
+
+ if (reg_info->is_ext_reg_event) {
+ for (i = 0; i < WMI_REG_CURRENT_MAX_AP_TYPE; i++) {
+ kfree(reg_info->reg_rules_6g_ap_ptr[i]);
+
+ for (j = 0; j < WMI_REG_MAX_CLIENT_TYPE; j++)
+ kfree(reg_info->reg_rules_6g_client_ptr[i][j]);
+ }
+ }
+}
+
+enum ath12k_reg_status ath12k_reg_validate_reg_info(struct ath12k_base *ab,
+ struct ath12k_reg_info *reg_info)
+{
+ int pdev_idx = reg_info->phy_id;
+
+ if (reg_info->status_code != REG_SET_CC_STATUS_PASS) {
+ /* In case of failure to set the requested country,
+ * firmware retains the current regd. We print a failure info
+ * and return from here.
+ */
+ ath12k_warn(ab, "Failed to set the requested Country regulatory setting\n");
+ return ATH12K_REG_STATUS_DROP;
+ }
+
+ if (pdev_idx >= ab->num_radios) {
+ /* Process the event for phy0 only if single_pdev_only
+ * is true. If pdev_idx is valid but not 0, discard the
+ * event. Otherwise, it goes to fallback.
+ */
+ if (ab->hw_params->single_pdev_only &&
+ pdev_idx < ab->hw_params->num_rxdma_per_pdev)
+ return ATH12K_REG_STATUS_DROP;
+ else
+ return ATH12K_REG_STATUS_FALLBACK;
+ }
+
+ /* Avoid multiple overwrites to default regd, during core
+ * stop-start after mac registration.
+ */
+ if (ab->default_regd[pdev_idx] && !ab->new_regd[pdev_idx] &&
+ !memcmp(ab->default_regd[pdev_idx]->alpha2,
+ reg_info->alpha2, 2))
+ return ATH12K_REG_STATUS_DROP;
+
+ return ATH12K_REG_STATUS_VALID;
+}
+
+int ath12k_reg_handle_chan_list(struct ath12k_base *ab,
+ struct ath12k_reg_info *reg_info,
+ enum wmi_vdev_type vdev_type,
+ enum ieee80211_ap_reg_power power_type)
+{
+ struct ieee80211_regdomain *regd = NULL;
+ int pdev_idx = reg_info->phy_id;
+ struct ath12k *ar;
+
+ regd = ath12k_reg_build_regd(ab, reg_info, vdev_type, power_type);
+ if (!regd)
+ return -EINVAL;
+
+ spin_lock_bh(&ab->base_lock);
+ if (test_bit(ATH12K_FLAG_REGISTERED, &ab->dev_flags)) {
+ /* Once mac is registered, ar is valid and all CC events from
+ * firmware is considered to be received due to user requests
+ * currently.
+ * Free previously built regd before assigning the newly
+ * generated regd to ar. NULL pointer handling will be
+ * taken care by kfree itself.
+ */
+ ar = ab->pdevs[pdev_idx].ar;
+ kfree(ab->new_regd[pdev_idx]);
+ ab->new_regd[pdev_idx] = regd;
+ queue_work(ab->workqueue, &ar->regd_update_work);
+ } else {
+ /* Multiple events for the same *ar is not expected. But we
+ * can still clear any previously stored default_regd if we
+ * are receiving this event for the same radio by mistake.
+ * NULL pointer handling will be taken care by kfree itself.
+ */
+ kfree(ab->default_regd[pdev_idx]);
+ /* This regd would be applied during mac registration */
+ ab->default_regd[pdev_idx] = regd;
+ }
+ ab->dfs_region = reg_info->dfs_region;
+ spin_unlock_bh(&ab->base_lock);
+
+ return 0;
+}
+
void ath12k_reg_init(struct ieee80211_hw *hw)
{
hw->wiphy->regulatory_flags = REGULATORY_WIPHY_SELF_MANAGED;
+ hw->wiphy->flags |= WIPHY_FLAG_NOTIFY_REGDOM_BY_DRIVER;
hw->wiphy->reg_notifier = ath12k_reg_notifier;
}
@@ -777,8 +891,18 @@ void ath12k_reg_free(struct ath12k_base *ab)
{
int i;
+ mutex_lock(&ab->core_lock);
+ for (i = 0; i < MAX_RADIOS; i++) {
+ ath12k_reg_reset_reg_info(ab->reg_info[i]);
+ kfree(ab->reg_info[i]);
+ ab->reg_info[i] = NULL;
+ }
+
for (i = 0; i < ab->hw_params->max_radios; i++) {
kfree(ab->default_regd[i]);
kfree(ab->new_regd[i]);
+ ab->default_regd[i] = NULL;
+ ab->new_regd[i] = NULL;
}
+ mutex_unlock(&ab->core_lock);
}
diff --git a/drivers/net/wireless/ath/ath12k/reg.h b/drivers/net/wireless/ath/ath12k/reg.h
index 29c7ec3260da..8af8e9ba462e 100644
--- a/drivers/net/wireless/ath/ath12k/reg.h
+++ b/drivers/net/wireless/ath/ath12k/reg.h
@@ -1,7 +1,7 @@
/* SPDX-License-Identifier: BSD-3-Clause-Clear */
/*
* Copyright (c) 2019-2021 The Linux Foundation. All rights reserved.
- * Copyright (c) 2021-2023 Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#ifndef ATH12K_REG_H
@@ -13,6 +13,9 @@
struct ath12k_base;
struct ath12k;
+#define ATH12K_2GHZ_MAX_FREQUENCY 2495
+#define ATH12K_5GHZ_MAX_FREQUENCY 5920
+
/* DFS regdomains supported by Firmware */
enum ath12k_dfs_region {
ATH12K_DFS_REG_UNSET,
@@ -89,13 +92,29 @@ enum ath12k_reg_phy_bitmap {
ATH12K_REG_PHY_BITMAP_NO11BE = BIT(6),
};
+enum ath12k_reg_status {
+ ATH12K_REG_STATUS_VALID,
+ ATH12K_REG_STATUS_DROP,
+ ATH12K_REG_STATUS_FALLBACK,
+};
+
void ath12k_reg_init(struct ieee80211_hw *hw);
void ath12k_reg_free(struct ath12k_base *ab);
void ath12k_regd_update_work(struct work_struct *work);
struct ieee80211_regdomain *ath12k_reg_build_regd(struct ath12k_base *ab,
struct ath12k_reg_info *reg_info,
- bool intersect);
+ enum wmi_vdev_type vdev_type,
+ enum ieee80211_ap_reg_power power_type);
int ath12k_regd_update(struct ath12k *ar, bool init);
-int ath12k_reg_update_chan_list(struct ath12k *ar);
+int ath12k_reg_update_chan_list(struct ath12k *ar, bool wait);
+void ath12k_reg_reset_reg_info(struct ath12k_reg_info *reg_info);
+int ath12k_reg_handle_chan_list(struct ath12k_base *ab,
+ struct ath12k_reg_info *reg_info,
+ enum wmi_vdev_type vdev_type,
+ enum ieee80211_ap_reg_power power_type);
+enum wmi_reg_6g_ap_type
+ath12k_reg_ap_pwr_convert(enum ieee80211_ap_reg_power power_type);
+enum ath12k_reg_status ath12k_reg_validate_reg_info(struct ath12k_base *ab,
+ struct ath12k_reg_info *reg_info);
#endif
diff --git a/drivers/net/wireless/ath/ath12k/rx_desc.h b/drivers/net/wireless/ath/ath12k/rx_desc.h
index 10366bbe9999..6c600473b402 100644
--- a/drivers/net/wireless/ath/ath12k/rx_desc.h
+++ b/drivers/net/wireless/ath/ath12k/rx_desc.h
@@ -1,7 +1,7 @@
/* SPDX-License-Identifier: BSD-3-Clause-Clear */
/*
* Copyright (c) 2018-2021 The Linux Foundation. All rights reserved.
- * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#ifndef ATH12K_RX_DESC_H
#define ATH12K_RX_DESC_H
@@ -637,6 +637,8 @@ enum rx_msdu_start_pkt_type {
RX_MSDU_START_PKT_TYPE_11N,
RX_MSDU_START_PKT_TYPE_11AC,
RX_MSDU_START_PKT_TYPE_11AX,
+ RX_MSDU_START_PKT_TYPE_11BA,
+ RX_MSDU_START_PKT_TYPE_11BE,
};
enum rx_msdu_start_sgi {
@@ -1539,12 +1541,4 @@ struct hal_rx_desc {
#define MAX_MU_GROUP_SHOW 16
#define MAX_MU_GROUP_LENGTH (6 * MAX_MU_GROUP_SHOW)
-#define HAL_RX_RU_ALLOC_TYPE_MAX 6
-#define RU_26 1
-#define RU_52 2
-#define RU_106 4
-#define RU_242 9
-#define RU_484 18
-#define RU_996 37
-
#endif /* ATH12K_RX_DESC_H */
diff --git a/drivers/net/wireless/ath/ath12k/testmode.c b/drivers/net/wireless/ath/ath12k/testmode.c
new file mode 100644
index 000000000000..fb6af7ccf71f
--- /dev/null
+++ b/drivers/net/wireless/ath/ath12k/testmode.c
@@ -0,0 +1,395 @@
+// SPDX-License-Identifier: BSD-3-Clause-Clear
+/*
+ * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#include "testmode.h"
+#include <net/netlink.h>
+#include "debug.h"
+#include "wmi.h"
+#include "hw.h"
+#include "core.h"
+#include "hif.h"
+#include "../testmode_i.h"
+
+#define ATH12K_FTM_SEGHDR_CURRENT_SEQ GENMASK(3, 0)
+#define ATH12K_FTM_SEGHDR_TOTAL_SEGMENTS GENMASK(7, 4)
+
+static const struct nla_policy ath12k_tm_policy[ATH_TM_ATTR_MAX + 1] = {
+ [ATH_TM_ATTR_CMD] = { .type = NLA_U32 },
+ [ATH_TM_ATTR_DATA] = { .type = NLA_BINARY,
+ .len = ATH_TM_DATA_MAX_LEN },
+ [ATH_TM_ATTR_WMI_CMDID] = { .type = NLA_U32 },
+ [ATH_TM_ATTR_VERSION_MAJOR] = { .type = NLA_U32 },
+ [ATH_TM_ATTR_VERSION_MINOR] = { .type = NLA_U32 },
+};
+
+static struct ath12k *ath12k_tm_get_ar(struct ath12k_base *ab)
+{
+ struct ath12k_pdev *pdev;
+ struct ath12k *ar;
+ int i;
+
+ for (i = 0; i < ab->num_radios; i++) {
+ pdev = &ab->pdevs[i];
+ ar = pdev->ar;
+
+ if (ar && ar->ah->state == ATH12K_HW_STATE_TM)
+ return ar;
+ }
+
+ return NULL;
+}
+
+void ath12k_tm_wmi_event_unsegmented(struct ath12k_base *ab, u32 cmd_id,
+ struct sk_buff *skb)
+{
+ struct sk_buff *nl_skb;
+ struct ath12k *ar;
+
+ ath12k_dbg(ab, ATH12K_DBG_TESTMODE,
+ "testmode event wmi cmd_id %d skb length %d\n",
+ cmd_id, skb->len);
+
+ ath12k_dbg_dump(ab, ATH12K_DBG_TESTMODE, NULL, "", skb->data, skb->len);
+
+ ar = ath12k_tm_get_ar(ab);
+ if (!ar) {
+ ath12k_warn(ab, "testmode event not handled due to invalid pdev\n");
+ return;
+ }
+
+ spin_lock_bh(&ar->data_lock);
+
+ nl_skb = cfg80211_testmode_alloc_event_skb(ar->ah->hw->wiphy,
+ 2 * nla_total_size(sizeof(u32)) +
+ nla_total_size(skb->len),
+ GFP_ATOMIC);
+ spin_unlock_bh(&ar->data_lock);
+
+ if (!nl_skb) {
+ ath12k_warn(ab,
+ "failed to allocate skb for unsegmented testmode wmi event\n");
+ return;
+ }
+
+ if (nla_put_u32(nl_skb, ATH_TM_ATTR_CMD, ATH_TM_CMD_WMI) ||
+ nla_put_u32(nl_skb, ATH_TM_ATTR_WMI_CMDID, cmd_id) ||
+ nla_put(nl_skb, ATH_TM_ATTR_DATA, skb->len, skb->data)) {
+ ath12k_warn(ab, "failed to populate testmode unsegmented event\n");
+ kfree_skb(nl_skb);
+ return;
+ }
+
+ cfg80211_testmode_event(nl_skb, GFP_ATOMIC);
+}
+
+void ath12k_tm_process_event(struct ath12k_base *ab, u32 cmd_id,
+ const struct ath12k_wmi_ftm_event *ftm_msg,
+ u16 length)
+{
+ struct sk_buff *nl_skb;
+ struct ath12k *ar;
+ u32 data_pos, pdev_id;
+ u16 datalen;
+ u8 total_segments, current_seq;
+ u8 const *buf_pos;
+
+ ath12k_dbg(ab, ATH12K_DBG_TESTMODE,
+ "testmode event wmi cmd_id %d ftm event msg %p datalen %d\n",
+ cmd_id, ftm_msg, length);
+ ath12k_dbg_dump(ab, ATH12K_DBG_TESTMODE, NULL, "", ftm_msg, length);
+ pdev_id = DP_HW2SW_MACID(le32_to_cpu(ftm_msg->seg_hdr.pdev_id));
+
+ if (pdev_id >= ab->num_radios) {
+ ath12k_warn(ab, "testmode event not handled due to invalid pdev id\n");
+ return;
+ }
+
+ ar = ab->pdevs[pdev_id].ar;
+
+ if (!ar) {
+ ath12k_warn(ab, "testmode event not handled due to absence of pdev\n");
+ return;
+ }
+
+ current_seq = le32_get_bits(ftm_msg->seg_hdr.segmentinfo,
+ ATH12K_FTM_SEGHDR_CURRENT_SEQ);
+ total_segments = le32_get_bits(ftm_msg->seg_hdr.segmentinfo,
+ ATH12K_FTM_SEGHDR_TOTAL_SEGMENTS);
+ datalen = length - (sizeof(struct ath12k_wmi_ftm_seg_hdr_params));
+ buf_pos = ftm_msg->data;
+
+ if (current_seq == 0) {
+ ab->ftm_event_obj.expected_seq = 0;
+ ab->ftm_event_obj.data_pos = 0;
+ }
+
+ data_pos = ab->ftm_event_obj.data_pos;
+
+ if ((data_pos + datalen) > ATH_FTM_EVENT_MAX_BUF_LENGTH) {
+ ath12k_warn(ab,
+ "Invalid event length date_pos[%d] datalen[%d]\n",
+ data_pos, datalen);
+ return;
+ }
+
+ memcpy(&ab->ftm_event_obj.eventdata[data_pos], buf_pos, datalen);
+ data_pos += datalen;
+
+ if (++ab->ftm_event_obj.expected_seq != total_segments) {
+ ab->ftm_event_obj.data_pos = data_pos;
+ ath12k_dbg(ab, ATH12K_DBG_TESTMODE,
+ "partial data received current_seq[%d], total_seg[%d]\n",
+ current_seq, total_segments);
+ return;
+ }
+
+ ath12k_dbg(ab, ATH12K_DBG_TESTMODE,
+ "total data length[%d] = [%d]\n",
+ data_pos, ftm_msg->seg_hdr.len);
+
+ spin_lock_bh(&ar->data_lock);
+ nl_skb = cfg80211_testmode_alloc_event_skb(ar->ah->hw->wiphy,
+ 2 * nla_total_size(sizeof(u32)) +
+ nla_total_size(data_pos),
+ GFP_ATOMIC);
+ spin_unlock_bh(&ar->data_lock);
+
+ if (!nl_skb) {
+ ath12k_warn(ab,
+ "failed to allocate skb for testmode wmi event\n");
+ return;
+ }
+
+ if (nla_put_u32(nl_skb, ATH_TM_ATTR_CMD,
+ ATH_TM_CMD_WMI_FTM) ||
+ nla_put_u32(nl_skb, ATH_TM_ATTR_WMI_CMDID, cmd_id) ||
+ nla_put(nl_skb, ATH_TM_ATTR_DATA, data_pos,
+ &ab->ftm_event_obj.eventdata[0])) {
+ ath12k_warn(ab, "failed to populate testmode event");
+ kfree_skb(nl_skb);
+ return;
+ }
+
+ cfg80211_testmode_event(nl_skb, GFP_ATOMIC);
+}
+
+static int ath12k_tm_cmd_get_version(struct ath12k *ar, struct nlattr *tb[])
+{
+ struct sk_buff *skb;
+
+ ath12k_dbg(ar->ab, ATH12K_DBG_TESTMODE,
+ "testmode cmd get version_major %d version_minor %d\n",
+ ATH_TESTMODE_VERSION_MAJOR,
+ ATH_TESTMODE_VERSION_MINOR);
+
+ spin_lock_bh(&ar->data_lock);
+ skb = cfg80211_testmode_alloc_reply_skb(ar->ah->hw->wiphy,
+ 2 * nla_total_size(sizeof(u32)));
+ spin_unlock_bh(&ar->data_lock);
+
+ if (!skb)
+ return -ENOMEM;
+
+ if (nla_put_u32(skb, ATH_TM_ATTR_VERSION_MAJOR,
+ ATH_TESTMODE_VERSION_MAJOR) ||
+ nla_put_u32(skb, ATH_TM_ATTR_VERSION_MINOR,
+ ATH_TESTMODE_VERSION_MINOR)) {
+ kfree_skb(skb);
+ return -ENOBUFS;
+ }
+
+ return cfg80211_testmode_reply(skb);
+}
+
+static int ath12k_tm_cmd_process_ftm(struct ath12k *ar, struct nlattr *tb[])
+{
+ struct ath12k_wmi_pdev *wmi = ar->wmi;
+ struct sk_buff *skb;
+ struct ath12k_wmi_ftm_cmd *ftm_cmd;
+ int ret = 0;
+ void *buf;
+ size_t aligned_len;
+ u32 cmd_id, buf_len;
+ u16 chunk_len, total_bytes, num_segments;
+ u8 segnumber = 0, *bufpos;
+
+ ath12k_dbg(ar->ab, ATH12K_DBG_TESTMODE, "ah->state %d\n", ar->ah->state);
+ if (ar->ah->state != ATH12K_HW_STATE_TM)
+ return -ENETDOWN;
+
+ if (!tb[ATH_TM_ATTR_DATA])
+ return -EINVAL;
+
+ buf = nla_data(tb[ATH_TM_ATTR_DATA]);
+ buf_len = nla_len(tb[ATH_TM_ATTR_DATA]);
+ cmd_id = WMI_PDEV_UTF_CMDID;
+ ath12k_dbg(ar->ab, ATH12K_DBG_TESTMODE,
+ "testmode cmd wmi cmd_id %d buf %p buf_len %d\n",
+ cmd_id, buf, buf_len);
+ ath12k_dbg_dump(ar->ab, ATH12K_DBG_TESTMODE, NULL, "", buf, buf_len);
+ bufpos = buf;
+ total_bytes = buf_len;
+ num_segments = total_bytes / MAX_WMI_UTF_LEN;
+
+ if (buf_len - (num_segments * MAX_WMI_UTF_LEN))
+ num_segments++;
+
+ while (buf_len) {
+ if (buf_len > MAX_WMI_UTF_LEN)
+ chunk_len = MAX_WMI_UTF_LEN; /* MAX message */
+ else
+ chunk_len = buf_len;
+
+ skb = ath12k_wmi_alloc_skb(wmi->wmi_ab, (chunk_len +
+ sizeof(struct ath12k_wmi_ftm_cmd)));
+
+ if (!skb)
+ return -ENOMEM;
+
+ ftm_cmd = (struct ath12k_wmi_ftm_cmd *)skb->data;
+ aligned_len = chunk_len + sizeof(struct ath12k_wmi_ftm_seg_hdr_params);
+ ftm_cmd->tlv_header = ath12k_wmi_tlv_hdr(WMI_TAG_ARRAY_BYTE, aligned_len);
+ ftm_cmd->seg_hdr.len = cpu_to_le32(total_bytes);
+ ftm_cmd->seg_hdr.msgref = cpu_to_le32(ar->ftm_msgref);
+ ftm_cmd->seg_hdr.segmentinfo =
+ le32_encode_bits(num_segments,
+ ATH12K_FTM_SEGHDR_TOTAL_SEGMENTS) |
+ le32_encode_bits(segnumber,
+ ATH12K_FTM_SEGHDR_CURRENT_SEQ);
+ ftm_cmd->seg_hdr.pdev_id = cpu_to_le32(ar->pdev->pdev_id);
+ segnumber++;
+ memcpy(&ftm_cmd->data, bufpos, chunk_len);
+ ret = ath12k_wmi_cmd_send(wmi, skb, cmd_id);
+
+ if (ret) {
+ ath12k_warn(ar->ab, "ftm wmi command fail: %d\n", ret);
+ kfree_skb(skb);
+ return ret;
+ }
+
+ buf_len -= chunk_len;
+ bufpos += chunk_len;
+ }
+
+ ++ar->ftm_msgref;
+ return ret;
+}
+
+static int ath12k_tm_cmd_testmode_start(struct ath12k *ar, struct nlattr *tb[])
+{
+ if (ar->ah->state == ATH12K_HW_STATE_TM)
+ return -EALREADY;
+
+ if (ar->ah->state != ATH12K_HW_STATE_OFF)
+ return -EBUSY;
+
+ ar->ab->ftm_event_obj.eventdata = kzalloc(ATH_FTM_EVENT_MAX_BUF_LENGTH,
+ GFP_KERNEL);
+
+ if (!ar->ab->ftm_event_obj.eventdata)
+ return -ENOMEM;
+
+ ar->ah->state = ATH12K_HW_STATE_TM;
+ ar->ftm_msgref = 0;
+ return 0;
+}
+
+static int ath12k_tm_cmd_wmi(struct ath12k *ar, struct nlattr *tb[])
+{
+ struct ath12k_wmi_pdev *wmi = ar->wmi;
+ struct sk_buff *skb;
+ struct wmi_pdev_set_param_cmd *cmd;
+ int ret = 0, tag;
+ void *buf;
+ u32 cmd_id, buf_len;
+
+ if (!tb[ATH_TM_ATTR_DATA])
+ return -EINVAL;
+
+ if (!tb[ATH_TM_ATTR_WMI_CMDID])
+ return -EINVAL;
+
+ buf = nla_data(tb[ATH_TM_ATTR_DATA]);
+ buf_len = nla_len(tb[ATH_TM_ATTR_DATA]);
+
+ if (!buf_len) {
+ ath12k_warn(ar->ab, "No data present in testmode command\n");
+ return -EINVAL;
+ }
+
+ cmd_id = nla_get_u32(tb[ATH_TM_ATTR_WMI_CMDID]);
+
+ cmd = buf;
+ tag = le32_get_bits(cmd->tlv_header, WMI_TLV_TAG);
+
+ if (tag == WMI_TAG_PDEV_SET_PARAM_CMD)
+ cmd->pdev_id = cpu_to_le32(ar->pdev->pdev_id);
+
+ ath12k_dbg(ar->ab, ATH12K_DBG_TESTMODE,
+ "testmode cmd wmi cmd_id %d buf length %d\n",
+ cmd_id, buf_len);
+
+ ath12k_dbg_dump(ar->ab, ATH12K_DBG_TESTMODE, NULL, "", buf, buf_len);
+
+ skb = ath12k_wmi_alloc_skb(wmi->wmi_ab, buf_len);
+
+ if (!skb)
+ return -ENOMEM;
+
+ memcpy(skb->data, buf, buf_len);
+
+ ret = ath12k_wmi_cmd_send(wmi, skb, cmd_id);
+ if (ret) {
+ dev_kfree_skb(skb);
+ ath12k_warn(ar->ab, "failed to transmit wmi command (testmode): %d\n",
+ ret);
+ }
+
+ return ret;
+}
+
+int ath12k_tm_cmd(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ void *data, int len)
+{
+ struct ath12k_hw *ah = hw->priv;
+ struct ath12k *ar = NULL;
+ struct nlattr *tb[ATH_TM_ATTR_MAX + 1];
+ struct ath12k_base *ab;
+ struct wiphy *wiphy = hw->wiphy;
+ int ret;
+
+ lockdep_assert_held(&wiphy->mtx);
+
+ ret = nla_parse(tb, ATH_TM_ATTR_MAX, data, len, ath12k_tm_policy,
+ NULL);
+ if (ret)
+ return ret;
+
+ if (!tb[ATH_TM_ATTR_CMD])
+ return -EINVAL;
+
+ /* TODO: have to handle ar for MLO case */
+ if (ah->num_radio)
+ ar = ah->radio;
+
+ if (!ar)
+ return -EINVAL;
+
+ ab = ar->ab;
+ switch (nla_get_u32(tb[ATH_TM_ATTR_CMD])) {
+ case ATH_TM_CMD_WMI:
+ return ath12k_tm_cmd_wmi(ar, tb);
+ case ATH_TM_CMD_TESTMODE_START:
+ return ath12k_tm_cmd_testmode_start(ar, tb);
+ case ATH_TM_CMD_GET_VERSION:
+ return ath12k_tm_cmd_get_version(ar, tb);
+ case ATH_TM_CMD_WMI_FTM:
+ set_bit(ATH12K_FLAG_FTM_SEGMENTED, &ab->dev_flags);
+ return ath12k_tm_cmd_process_ftm(ar, tb);
+ default:
+ return -EOPNOTSUPP;
+ }
+}
diff --git a/drivers/net/wireless/ath/ath12k/testmode.h b/drivers/net/wireless/ath/ath12k/testmode.h
new file mode 100644
index 000000000000..ef6ab21d19b8
--- /dev/null
+++ b/drivers/net/wireless/ath/ath12k/testmode.h
@@ -0,0 +1,40 @@
+/* SPDX-License-Identifier: BSD-3-Clause-Clear */
+/*
+ * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#include "core.h"
+#include "hif.h"
+
+#ifdef CONFIG_NL80211_TESTMODE
+
+void ath12k_tm_wmi_event_unsegmented(struct ath12k_base *ab, u32 cmd_id,
+ struct sk_buff *skb);
+void ath12k_tm_process_event(struct ath12k_base *ab, u32 cmd_id,
+ const struct ath12k_wmi_ftm_event *ftm_msg,
+ u16 length);
+int ath12k_tm_cmd(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ void *data, int len);
+
+#else
+
+static inline void ath12k_tm_wmi_event_unsegmented(struct ath12k_base *ab, u32 cmd_id,
+ struct sk_buff *skb)
+{
+}
+
+static inline void ath12k_tm_process_event(struct ath12k_base *ab, u32 cmd_id,
+ const struct ath12k_wmi_ftm_event *msg,
+ u16 length)
+{
+}
+
+static inline int ath12k_tm_cmd(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ void *data, int len)
+{
+ return 0;
+}
+
+#endif
diff --git a/drivers/net/wireless/ath/ath12k/wmi.c b/drivers/net/wireless/ath/ath12k/wmi.c
index abb510d235a5..60e2444fe08c 100644
--- a/drivers/net/wireless/ath/ath12k/wmi.c
+++ b/drivers/net/wireless/ath/ath12k/wmi.c
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: BSD-3-Clause-Clear
/*
* Copyright (c) 2018-2021 The Linux Foundation. All rights reserved.
- * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#include <linux/skbuff.h>
#include <linux/ctype.h>
@@ -15,16 +15,23 @@
#include <linux/time.h>
#include <linux/of.h>
#include "core.h"
+#include "debugfs.h"
#include "debug.h"
#include "mac.h"
#include "hw.h"
#include "peer.h"
#include "p2p.h"
+#include "testmode.h"
struct ath12k_wmi_svc_ready_parse {
bool wmi_svc_bitmap_done;
};
+struct wmi_tlv_fw_stats_parse {
+ const struct wmi_stats_event *ev;
+ struct ath12k_fw_stats *stats;
+};
+
struct ath12k_wmi_dma_ring_caps_parse {
struct ath12k_wmi_dma_ring_caps_params *dma_ring_caps;
u32 n_dma_ring_caps;
@@ -171,9 +178,11 @@ static const struct ath12k_wmi_tlv_policy ath12k_wmi_tlv_policies[] = {
.min_len = sizeof(struct ath12k_wmi_p2p_noa_info) },
[WMI_TAG_P2P_NOA_EVENT] = {
.min_len = sizeof(struct wmi_p2p_noa_event) },
+ [WMI_TAG_11D_NEW_COUNTRY_EVENT] = {
+ .min_len = sizeof(struct wmi_11d_new_cc_event) },
};
-static __le32 ath12k_wmi_tlv_hdr(u32 cmd, u32 len)
+__le32 ath12k_wmi_tlv_hdr(u32 cmd, u32 len)
{
return le32_encode_bits(cmd, WMI_TLV_TAG) |
le32_encode_bits(len, WMI_TLV_LEN);
@@ -514,10 +523,10 @@ ath12k_pull_mac_phy_cap_svc_ready_ext(struct ath12k_wmi_pdev *wmi_handle,
* band to band for a single radio, need to see how this should be
* handled.
*/
- if (le32_to_cpu(mac_caps->supported_bands) & WMI_HOST_WLAN_2G_CAP) {
+ if (le32_to_cpu(mac_caps->supported_bands) & WMI_HOST_WLAN_2GHZ_CAP) {
pdev_cap->tx_chain_mask = le32_to_cpu(mac_caps->tx_chain_mask_2g);
pdev_cap->rx_chain_mask = le32_to_cpu(mac_caps->rx_chain_mask_2g);
- } else if (le32_to_cpu(mac_caps->supported_bands) & WMI_HOST_WLAN_5G_CAP) {
+ } else if (le32_to_cpu(mac_caps->supported_bands) & WMI_HOST_WLAN_5GHZ_CAP) {
pdev_cap->vht_cap = le32_to_cpu(mac_caps->vht_cap_info_5g);
pdev_cap->vht_mcs = le32_to_cpu(mac_caps->vht_supp_mcs_5g);
pdev_cap->he_mcs = le32_to_cpu(mac_caps->he_supp_mcs_5g);
@@ -540,7 +549,7 @@ ath12k_pull_mac_phy_cap_svc_ready_ext(struct ath12k_wmi_pdev *wmi_handle,
pdev_cap->rx_chain_mask_shift =
find_first_bit((unsigned long *)&pdev_cap->rx_chain_mask, 32);
- if (le32_to_cpu(mac_caps->supported_bands) & WMI_HOST_WLAN_2G_CAP) {
+ if (le32_to_cpu(mac_caps->supported_bands) & WMI_HOST_WLAN_2GHZ_CAP) {
cap_band = &pdev_cap->band[NL80211_BAND_2GHZ];
cap_band->phy_id = le32_to_cpu(mac_caps->phy_id);
cap_band->max_bw_supported = le32_to_cpu(mac_caps->max_bw_supported_2g);
@@ -560,7 +569,7 @@ ath12k_pull_mac_phy_cap_svc_ready_ext(struct ath12k_wmi_pdev *wmi_handle,
le32_to_cpu(mac_caps->he_ppet2g.ppet16_ppet8_ru3_ru0[i]);
}
- if (le32_to_cpu(mac_caps->supported_bands) & WMI_HOST_WLAN_5G_CAP) {
+ if (le32_to_cpu(mac_caps->supported_bands) & WMI_HOST_WLAN_5GHZ_CAP) {
cap_band = &pdev_cap->band[NL80211_BAND_5GHZ];
cap_band->phy_id = le32_to_cpu(mac_caps->phy_id);
cap_band->max_bw_supported =
@@ -814,6 +823,39 @@ int ath12k_wmi_mgmt_send(struct ath12k *ar, u32 vdev_id, u32 buf_id,
return ret;
}
+int ath12k_wmi_send_stats_request_cmd(struct ath12k *ar, u32 stats_id,
+ u32 vdev_id, u32 pdev_id)
+{
+ struct ath12k_wmi_pdev *wmi = ar->wmi;
+ struct wmi_request_stats_cmd *cmd;
+ struct sk_buff *skb;
+ int ret;
+
+ skb = ath12k_wmi_alloc_skb(wmi->wmi_ab, sizeof(*cmd));
+ if (!skb)
+ return -ENOMEM;
+
+ cmd = (struct wmi_request_stats_cmd *)skb->data;
+ cmd->tlv_header = ath12k_wmi_tlv_cmd_hdr(WMI_TAG_REQUEST_STATS_CMD,
+ sizeof(*cmd));
+
+ cmd->stats_id = cpu_to_le32(stats_id);
+ cmd->vdev_id = cpu_to_le32(vdev_id);
+ cmd->pdev_id = cpu_to_le32(pdev_id);
+
+ ret = ath12k_wmi_cmd_send(wmi, skb, WMI_REQUEST_STATS_CMDID);
+ if (ret) {
+ ath12k_warn(ar->ab, "failed to send WMI_REQUEST_STATS cmd\n");
+ dev_kfree_skb(skb);
+ }
+
+ ath12k_dbg(ar->ab, ATH12K_DBG_WMI,
+ "WMI request stats 0x%x vdev id %d pdev id %d\n",
+ stats_id, vdev_id, pdev_id);
+
+ return ret;
+}
+
int ath12k_wmi_vdev_create(struct ath12k *ar, u8 *macaddr,
struct ath12k_wmi_vdev_create_arg *args)
{
@@ -998,14 +1040,32 @@ int ath12k_wmi_vdev_down(struct ath12k *ar, u8 vdev_id)
static void ath12k_wmi_put_wmi_channel(struct ath12k_wmi_channel_params *chan,
struct wmi_vdev_start_req_arg *arg)
{
+ u32 center_freq1 = arg->band_center_freq1;
+
memset(chan, 0, sizeof(*chan));
chan->mhz = cpu_to_le32(arg->freq);
- chan->band_center_freq1 = cpu_to_le32(arg->band_center_freq1);
- if (arg->mode == MODE_11AC_VHT80_80)
+ chan->band_center_freq1 = cpu_to_le32(center_freq1);
+ if (arg->mode == MODE_11BE_EHT320) {
+ if (arg->freq > center_freq1)
+ chan->band_center_freq1 = cpu_to_le32(center_freq1 + 80);
+ else
+ chan->band_center_freq1 = cpu_to_le32(center_freq1 - 80);
+
+ chan->band_center_freq2 = cpu_to_le32(center_freq1);
+
+ } else if (arg->mode == MODE_11BE_EHT160) {
+ if (arg->freq > center_freq1)
+ chan->band_center_freq1 = cpu_to_le32(center_freq1 + 40);
+ else
+ chan->band_center_freq1 = cpu_to_le32(center_freq1 - 40);
+
+ chan->band_center_freq2 = cpu_to_le32(center_freq1);
+ } else if (arg->mode == MODE_11BE_EHT80_80) {
chan->band_center_freq2 = cpu_to_le32(arg->band_center_freq2);
- else
+ } else {
chan->band_center_freq2 = 0;
+ }
chan->info |= le32_encode_bits(arg->mode, WMI_CHAN_INFO_MODE);
if (arg->passive)
@@ -1888,14 +1948,19 @@ int ath12k_wmi_p2p_go_bcn_ie(struct ath12k *ar, u32 vdev_id,
return ret;
}
-int ath12k_wmi_bcn_tmpl(struct ath12k *ar, u32 vdev_id,
+int ath12k_wmi_bcn_tmpl(struct ath12k_link_vif *arvif,
struct ieee80211_mutable_offsets *offs,
struct sk_buff *bcn,
struct ath12k_wmi_bcn_tmpl_ema_arg *ema_args)
{
+ struct ath12k *ar = arvif->ar;
struct ath12k_wmi_pdev *wmi = ar->wmi;
+ struct ath12k_base *ab = ar->ab;
struct wmi_bcn_tmpl_cmd *cmd;
struct ath12k_wmi_bcn_prb_info_params *bcn_prb_info;
+ struct ath12k_vif *ahvif = arvif->ahvif;
+ struct ieee80211_bss_conf *conf;
+ u32 vdev_id = arvif->vdev_id;
struct wmi_tlv *tlv;
struct sk_buff *skb;
u32 ema_params = 0;
@@ -1903,6 +1968,14 @@ int ath12k_wmi_bcn_tmpl(struct ath12k *ar, u32 vdev_id,
int ret, len;
size_t aligned_len = roundup(bcn->len, 4);
+ conf = ath12k_mac_get_link_bss_conf(arvif);
+ if (!conf) {
+ ath12k_warn(ab,
+ "unable to access bss link conf in beacon template command for vif %pM link %u\n",
+ ahvif->vif->addr, arvif->link_id);
+ return -EINVAL;
+ }
+
len = sizeof(*cmd) + sizeof(*bcn_prb_info) + TLV_HDR_SIZE + aligned_len;
skb = ath12k_wmi_alloc_skb(wmi->wmi_ab, len);
@@ -1914,8 +1987,16 @@ int ath12k_wmi_bcn_tmpl(struct ath12k *ar, u32 vdev_id,
sizeof(*cmd));
cmd->vdev_id = cpu_to_le32(vdev_id);
cmd->tim_ie_offset = cpu_to_le32(offs->tim_offset);
- cmd->csa_switch_count_offset = cpu_to_le32(offs->cntdwn_counter_offs[0]);
- cmd->ext_csa_switch_count_offset = cpu_to_le32(offs->cntdwn_counter_offs[1]);
+
+ if (conf->csa_active) {
+ cmd->csa_switch_count_offset =
+ cpu_to_le32(offs->cntdwn_counter_offs[0]);
+ cmd->ext_csa_switch_count_offset =
+ cpu_to_le32(offs->cntdwn_counter_offs[1]);
+ cmd->csa_event_bitmap = cpu_to_le32(0xFFFFFFFF);
+ arvif->current_cntdown_counter = bcn->data[offs->cntdwn_counter_offs[0]];
+ }
+
cmd->buf_len = cpu_to_le32(bcn->len);
cmd->mbssid_ie_offset = cpu_to_le32(offs->mbssid_off);
if (ema_args) {
@@ -1945,7 +2026,7 @@ int ath12k_wmi_bcn_tmpl(struct ath12k *ar, u32 vdev_id,
ret = ath12k_wmi_cmd_send(wmi, skb, WMI_BCN_TMPL_CMDID);
if (ret) {
- ath12k_warn(ar->ab, "failed to send WMI_BCN_TMPL_CMDID\n");
+ ath12k_warn(ab, "failed to send WMI_BCN_TMPL_CMDID\n");
dev_kfree_skb(skb);
}
@@ -2106,9 +2187,10 @@ int ath12k_wmi_send_peer_assoc_cmd(struct ath12k *ar,
struct sk_buff *skb;
struct wmi_tlv *tlv;
void *ptr;
- u32 peer_legacy_rates_align;
- u32 peer_ht_rates_align;
+ u32 peer_legacy_rates_align, eml_pad_delay, eml_trans_delay;
+ u32 peer_ht_rates_align, eml_trans_timeout;
int i, ret, len;
+ u16 eml_cap;
__le32 v;
peer_legacy_rates_align = roundup(arg->peer_legacy_rates.num_rates,
@@ -2280,6 +2362,24 @@ int ath12k_wmi_send_peer_assoc_cmd(struct ath12k *ar,
ml_params->logical_link_idx = cpu_to_le32(arg->ml.logical_link_idx);
ml_params->ml_peer_id = cpu_to_le32(arg->ml.ml_peer_id);
ml_params->ieee_link_id = cpu_to_le32(arg->ml.ieee_link_id);
+
+ eml_cap = arg->ml.eml_cap;
+ if (u16_get_bits(eml_cap, IEEE80211_EML_CAP_EMLSR_SUPP)) {
+ /* Padding delay */
+ eml_pad_delay = ieee80211_emlsr_pad_delay_in_us(eml_cap);
+ ml_params->emlsr_padding_delay_us = cpu_to_le32(eml_pad_delay);
+ /* Transition delay */
+ eml_trans_delay = ieee80211_emlsr_trans_delay_in_us(eml_cap);
+ ml_params->emlsr_trans_delay_us = cpu_to_le32(eml_trans_delay);
+ /* Transition timeout */
+ eml_trans_timeout = ieee80211_eml_trans_timeout_in_us(eml_cap);
+ ml_params->emlsr_trans_timeout_us =
+ cpu_to_le32(eml_trans_timeout);
+ ath12k_dbg(ar->ab, ATH12K_DBG_WMI, "wmi peer %pM emlsr padding delay %u, trans delay %u trans timeout %u",
+ arg->peer_mac, eml_pad_delay, eml_trans_delay,
+ eml_trans_timeout);
+ }
+
ptr += sizeof(*ml_params);
skip_ml_params:
@@ -2291,7 +2391,7 @@ skip_ml_params:
for (i = 0; i < arg->peer_eht_mcs_count; i++) {
eht_mcs = ptr;
- eht_mcs->tlv_header = ath12k_wmi_tlv_cmd_hdr(WMI_TAG_HE_RATE_SET,
+ eht_mcs->tlv_header = ath12k_wmi_tlv_cmd_hdr(WMI_TAG_EHT_RATE_SET,
sizeof(*eht_mcs));
eht_mcs->rx_mcs_set = cpu_to_le32(arg->peer_eht_rx_mcs_set[i]);
@@ -2299,6 +2399,10 @@ skip_ml_params:
ptr += sizeof(*eht_mcs);
}
+ /* Update MCS15 capability */
+ if (arg->eht_disable_mcs15)
+ cmd->peer_eht_ops = cpu_to_le32(IEEE80211_EHT_OPER_MCS15_DISABLE);
+
tlv = ptr;
len = arg->ml.enabled ? arg->ml.num_partner_links * sizeof(*partner_info) : 0;
/* fill ML Partner links */
@@ -2339,7 +2443,7 @@ skip_ml_params:
send:
ath12k_dbg(ar->ab, ATH12K_DBG_WMI,
- "wmi peer assoc vdev id %d assoc id %d peer mac %pM peer_flags %x rate_caps %x peer_caps %x listen_intval %d ht_caps %x max_mpdu %d nss %d phymode %d peer_mpdu_density %d vht_caps %x he cap_info %x he ops %x he cap_info_ext %x he phy %x %x %x peer_bw_rxnss_override %x peer_flags_ext %x eht mac_cap %x %x eht phy_cap %x %x %x\n",
+ "wmi peer assoc vdev id %d assoc id %d peer mac %pM peer_flags %x rate_caps %x peer_caps %x listen_intval %d ht_caps %x max_mpdu %d nss %d phymode %d peer_mpdu_density %d vht_caps %x he cap_info %x he ops %x he cap_info_ext %x he phy %x %x %x peer_bw_rxnss_override %x peer_flags_ext %x eht mac_cap %x %x eht phy_cap %x %x %x peer_eht_ops %x\n",
cmd->vdev_id, cmd->peer_associd, arg->peer_mac,
cmd->peer_flags, cmd->peer_rate_caps, cmd->peer_caps,
cmd->peer_listen_intval, cmd->peer_ht_caps,
@@ -2352,7 +2456,7 @@ send:
cmd->peer_bw_rxnss_override, cmd->peer_flags_ext,
cmd->peer_eht_cap_mac[0], cmd->peer_eht_cap_mac[1],
cmd->peer_eht_cap_phy[0], cmd->peer_eht_cap_phy[1],
- cmd->peer_eht_cap_phy[2]);
+ cmd->peer_eht_cap_phy[2], cmd->peer_eht_ops);
ret = ath12k_wmi_cmd_send(wmi, skb, WMI_PEER_ASSOC_CMDID);
if (ret) {
@@ -2373,8 +2477,8 @@ void ath12k_wmi_start_scan_init(struct ath12k *ar,
arg->dwell_time_active = 50;
arg->dwell_time_active_2g = 0;
arg->dwell_time_passive = 150;
- arg->dwell_time_active_6g = 40;
- arg->dwell_time_passive_6g = 30;
+ arg->dwell_time_active_6g = 70;
+ arg->dwell_time_passive_6g = 70;
arg->min_rest_time = 50;
arg->max_rest_time = 500;
arg->repeat_probe_time = 0;
@@ -2531,7 +2635,10 @@ int ath12k_wmi_send_scan_start_cmd(struct ath12k *ar,
cmd->scan_id = cpu_to_le32(arg->scan_id);
cmd->scan_req_id = cpu_to_le32(arg->scan_req_id);
cmd->vdev_id = cpu_to_le32(arg->vdev_id);
- cmd->scan_priority = cpu_to_le32(arg->scan_priority);
+ if (ar->state_11d == ATH12K_11D_PREPARING)
+ arg->scan_priority = WMI_SCAN_PRIORITY_MEDIUM;
+ else
+ arg->scan_priority = WMI_SCAN_PRIORITY_LOW;
cmd->notify_scan_events = cpu_to_le32(arg->notify_scan_events);
ath12k_wmi_copy_scan_event_cntrl_flags(cmd, arg);
@@ -2794,6 +2901,8 @@ int ath12k_wmi_send_scan_chan_list_cmd(struct ath12k *ar,
WMI_CHAN_REG_INFO1_REG_CLS);
*reg2 |= le32_encode_bits(channel_arg->antennamax,
WMI_CHAN_REG_INFO2_ANT_MAX);
+ *reg2 |= le32_encode_bits(channel_arg->maxregpower,
+ WMI_CHAN_REG_INFO2_MAX_TX_PWR);
ath12k_dbg(ar->ab, ATH12K_DBG_WMI,
"WMI chan scan list chan[%d] = %u, chan_info->info %8x\n",
@@ -3251,6 +3360,110 @@ out:
return ret;
}
+int ath12k_wmi_send_set_current_country_cmd(struct ath12k *ar,
+ struct wmi_set_current_country_arg *arg)
+{
+ struct ath12k_wmi_pdev *wmi = ar->wmi;
+ struct wmi_set_current_country_cmd *cmd;
+ struct sk_buff *skb;
+ int ret;
+
+ skb = ath12k_wmi_alloc_skb(wmi->wmi_ab, sizeof(*cmd));
+ if (!skb)
+ return -ENOMEM;
+
+ cmd = (struct wmi_set_current_country_cmd *)skb->data;
+ cmd->tlv_header =
+ ath12k_wmi_tlv_cmd_hdr(WMI_TAG_SET_CURRENT_COUNTRY_CMD,
+ sizeof(*cmd));
+
+ cmd->pdev_id = cpu_to_le32(ar->pdev->pdev_id);
+ memcpy(&cmd->new_alpha2, &arg->alpha2, sizeof(arg->alpha2));
+ ret = ath12k_wmi_cmd_send(wmi, skb, WMI_SET_CURRENT_COUNTRY_CMDID);
+
+ ath12k_dbg(ar->ab, ATH12K_DBG_WMI,
+ "set current country pdev id %d alpha2 %c%c\n",
+ ar->pdev->pdev_id,
+ arg->alpha2[0],
+ arg->alpha2[1]);
+
+ if (ret) {
+ ath12k_warn(ar->ab,
+ "failed to send WMI_SET_CURRENT_COUNTRY_CMDID: %d\n", ret);
+ dev_kfree_skb(skb);
+ }
+
+ return ret;
+}
+
+int ath12k_wmi_send_11d_scan_start_cmd(struct ath12k *ar,
+ struct wmi_11d_scan_start_arg *arg)
+{
+ struct ath12k_wmi_pdev *wmi = ar->wmi;
+ struct wmi_11d_scan_start_cmd *cmd;
+ struct sk_buff *skb;
+ int ret;
+
+ skb = ath12k_wmi_alloc_skb(wmi->wmi_ab, sizeof(*cmd));
+ if (!skb)
+ return -ENOMEM;
+
+ cmd = (struct wmi_11d_scan_start_cmd *)skb->data;
+ cmd->tlv_header =
+ ath12k_wmi_tlv_cmd_hdr(WMI_TAG_11D_SCAN_START_CMD,
+ sizeof(*cmd));
+
+ cmd->vdev_id = cpu_to_le32(arg->vdev_id);
+ cmd->scan_period_msec = cpu_to_le32(arg->scan_period_msec);
+ cmd->start_interval_msec = cpu_to_le32(arg->start_interval_msec);
+ ret = ath12k_wmi_cmd_send(wmi, skb, WMI_11D_SCAN_START_CMDID);
+
+ ath12k_dbg(ar->ab, ATH12K_DBG_WMI,
+ "send 11d scan start vdev id %d period %d ms internal %d ms\n",
+ arg->vdev_id, arg->scan_period_msec,
+ arg->start_interval_msec);
+
+ if (ret) {
+ ath12k_warn(ar->ab,
+ "failed to send WMI_11D_SCAN_START_CMDID: %d\n", ret);
+ dev_kfree_skb(skb);
+ }
+
+ return ret;
+}
+
+int ath12k_wmi_send_11d_scan_stop_cmd(struct ath12k *ar, u32 vdev_id)
+{
+ struct ath12k_wmi_pdev *wmi = ar->wmi;
+ struct wmi_11d_scan_stop_cmd *cmd;
+ struct sk_buff *skb;
+ int ret;
+
+ skb = ath12k_wmi_alloc_skb(wmi->wmi_ab, sizeof(*cmd));
+ if (!skb)
+ return -ENOMEM;
+
+ cmd = (struct wmi_11d_scan_stop_cmd *)skb->data;
+ cmd->tlv_header =
+ ath12k_wmi_tlv_cmd_hdr(WMI_TAG_11D_SCAN_STOP_CMD,
+ sizeof(*cmd));
+
+ cmd->vdev_id = cpu_to_le32(vdev_id);
+ ret = ath12k_wmi_cmd_send(wmi, skb, WMI_11D_SCAN_STOP_CMDID);
+
+ ath12k_dbg(ar->ab, ATH12K_DBG_WMI,
+ "send 11d scan stop vdev id %d\n",
+ cmd->vdev_id);
+
+ if (ret) {
+ ath12k_warn(ar->ab,
+ "failed to send WMI_11D_SCAN_STOP_CMDID: %d\n", ret);
+ dev_kfree_skb(skb);
+ }
+
+ return ret;
+}
+
int
ath12k_wmi_send_twt_enable_cmd(struct ath12k *ar, u32 pdev_id)
{
@@ -3584,15 +3797,15 @@ ath12k_fill_band_to_mac_param(struct ath12k_base *soc,
arg[i].pdev_id = pdev->pdev_id;
switch (pdev->cap.supported_bands) {
- case WMI_HOST_WLAN_2G_5G_CAP:
+ case WMI_HOST_WLAN_2GHZ_5GHZ_CAP:
arg[i].start_freq = hal_reg_cap->low_2ghz_chan;
arg[i].end_freq = hal_reg_cap->high_5ghz_chan;
break;
- case WMI_HOST_WLAN_2G_CAP:
+ case WMI_HOST_WLAN_2GHZ_CAP:
arg[i].start_freq = hal_reg_cap->low_2ghz_chan;
arg[i].end_freq = hal_reg_cap->high_2ghz_chan;
break;
- case WMI_HOST_WLAN_5G_CAP:
+ case WMI_HOST_WLAN_5GHZ_CAP:
arg[i].start_freq = hal_reg_cap->low_5ghz_chan;
arg[i].end_freq = hal_reg_cap->high_5ghz_chan;
break;
@@ -3603,7 +3816,8 @@ ath12k_fill_band_to_mac_param(struct ath12k_base *soc,
}
static void
-ath12k_wmi_copy_resource_config(struct ath12k_wmi_resource_config_params *wmi_cfg,
+ath12k_wmi_copy_resource_config(struct ath12k_base *ab,
+ struct ath12k_wmi_resource_config_params *wmi_cfg,
struct ath12k_wmi_resource_config_arg *tg_cfg)
{
wmi_cfg->num_vdevs = cpu_to_le32(tg_cfg->num_vdevs);
@@ -3670,6 +3884,9 @@ ath12k_wmi_copy_resource_config(struct ath12k_wmi_resource_config_params *wmi_cf
WMI_RSRC_CFG_FLAGS2_RX_PEER_METADATA_VERSION);
wmi_cfg->host_service_flags = cpu_to_le32(tg_cfg->is_reg_cc_ext_event_supported <<
WMI_RSRC_CFG_HOST_SVC_FLAG_REG_CC_EXT_SUPPORT_BIT);
+ if (ab->hw_params->reoq_lut_support)
+ wmi_cfg->host_service_flags |=
+ cpu_to_le32(1 << WMI_RSRC_CFG_HOST_SVC_FLAG_REO_QREF_SUPPORT_BIT);
wmi_cfg->ema_max_vap_cnt = cpu_to_le32(tg_cfg->ema_max_vap_cnt);
wmi_cfg->ema_max_profile_period = cpu_to_le32(tg_cfg->ema_max_profile_period);
wmi_cfg->flags2 |= cpu_to_le32(WMI_RSRC_CFG_FLAGS2_CALC_NEXT_DTIM_COUNT_SET);
@@ -3710,7 +3927,7 @@ static int ath12k_init_cmd_send(struct ath12k_wmi_pdev *wmi,
ptr = skb->data + sizeof(*cmd);
cfg = ptr;
- ath12k_wmi_copy_resource_config(cfg, &arg->res_cfg);
+ ath12k_wmi_copy_resource_config(ab, cfg, &arg->res_cfg);
cfg->tlv_header = ath12k_wmi_tlv_cmd_hdr(WMI_TAG_RESOURCE_CONFIG,
sizeof(*cfg));
@@ -4539,6 +4756,7 @@ static int ath12k_service_ready_ext_event(struct ath12k_base *ab,
return 0;
err:
+ kfree(svc_rdy_ext.mac_phy_caps);
ath12k_wmi_free_dbring_caps(ab);
return ret;
}
@@ -4637,7 +4855,7 @@ ath12k_wmi_tlv_mac_phy_caps_ext_parse(struct ath12k_base *ab,
bands = pdev->cap.supported_bands;
}
- if (bands & WMI_HOST_WLAN_2G_CAP) {
+ if (bands & WMI_HOST_WLAN_2GHZ_CAP) {
ath12k_wmi_eht_caps_parse(pdev, NL80211_BAND_2GHZ,
caps->eht_cap_mac_info_2ghz,
caps->eht_cap_phy_info_2ghz,
@@ -4646,7 +4864,7 @@ ath12k_wmi_tlv_mac_phy_caps_ext_parse(struct ath12k_base *ab,
caps->eht_cap_info_internal);
}
- if (bands & WMI_HOST_WLAN_5G_CAP) {
+ if (bands & WMI_HOST_WLAN_5GHZ_CAP) {
ath12k_wmi_eht_caps_parse(pdev, NL80211_BAND_5GHZ,
caps->eht_cap_mac_info_5ghz,
caps->eht_cap_phy_info_5ghz,
@@ -4860,7 +5078,7 @@ static u8 ath12k_wmi_ignore_num_extra_rules(struct ath12k_wmi_reg_rule_ext_param
for (count = 0; count < num_reg_rules; count++) {
start_freq = le32_get_bits(rule[count].freq_info, REG_RULE_START_FREQ);
- if (start_freq >= ATH12K_MIN_6G_FREQ)
+ if (start_freq >= ATH12K_MIN_6GHZ_FREQ)
num_invalid_5ghz_rules++;
}
@@ -4930,9 +5148,9 @@ static int ath12k_pull_reg_chan_list_ext_update_ev(struct ath12k_base *ab,
for (i = 0; i < WMI_REG_CURRENT_MAX_AP_TYPE; i++) {
num_6g_reg_rules_ap[i] = reg_info->num_6g_reg_rules_ap[i];
- if (num_6g_reg_rules_ap[i] > MAX_6G_REG_RULES) {
+ if (num_6g_reg_rules_ap[i] > MAX_6GHZ_REG_RULES) {
ath12k_warn(ab, "Num 6G reg rules for AP mode(%d) exceeds max limit (num_6g_reg_rules_ap: %d, max_rules: %d)\n",
- i, num_6g_reg_rules_ap[i], MAX_6G_REG_RULES);
+ i, num_6g_reg_rules_ap[i], MAX_6GHZ_REG_RULES);
kfree(tb);
return -EINVAL;
}
@@ -4953,9 +5171,9 @@ static int ath12k_pull_reg_chan_list_ext_update_ev(struct ath12k_base *ab,
reg_info->num_6g_reg_rules_cl[WMI_REG_VLP_AP][i];
total_reg_rules += num_6g_reg_rules_cl[WMI_REG_VLP_AP][i];
- if (num_6g_reg_rules_cl[WMI_REG_INDOOR_AP][i] > MAX_6G_REG_RULES ||
- num_6g_reg_rules_cl[WMI_REG_STD_POWER_AP][i] > MAX_6G_REG_RULES ||
- num_6g_reg_rules_cl[WMI_REG_VLP_AP][i] > MAX_6G_REG_RULES) {
+ if (num_6g_reg_rules_cl[WMI_REG_INDOOR_AP][i] > MAX_6GHZ_REG_RULES ||
+ num_6g_reg_rules_cl[WMI_REG_STD_POWER_AP][i] > MAX_6GHZ_REG_RULES ||
+ num_6g_reg_rules_cl[WMI_REG_VLP_AP][i] > MAX_6GHZ_REG_RULES) {
ath12k_warn(ab, "Num 6g client reg rules exceeds max limit, for client(type: %d)\n",
i);
kfree(tb);
@@ -5871,30 +6089,62 @@ static void ath12k_wmi_op_ep_tx_credits(struct ath12k_base *ab)
wake_up(&ab->wmi_ab.tx_credits_wq);
}
-static void ath12k_wmi_htc_tx_complete(struct ath12k_base *ab,
- struct sk_buff *skb)
+static int ath12k_reg_11d_new_cc_event(struct ath12k_base *ab, struct sk_buff *skb)
{
- dev_kfree_skb(skb);
-}
+ const struct wmi_11d_new_cc_event *ev;
+ struct ath12k *ar;
+ struct ath12k_pdev *pdev;
+ const void **tb;
+ int ret, i;
-static bool ath12k_reg_is_world_alpha(char *alpha)
-{
- if (alpha[0] == '0' && alpha[1] == '0')
- return true;
+ tb = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC);
+ if (IS_ERR(tb)) {
+ ret = PTR_ERR(tb);
+ ath12k_warn(ab, "failed to parse tlv: %d\n", ret);
+ return ret;
+ }
- if (alpha[0] == 'n' && alpha[1] == 'a')
- return true;
+ ev = tb[WMI_TAG_11D_NEW_COUNTRY_EVENT];
+ if (!ev) {
+ kfree(tb);
+ ath12k_warn(ab, "failed to fetch 11d new cc ev");
+ return -EPROTO;
+ }
+
+ spin_lock_bh(&ab->base_lock);
+ memcpy(&ab->new_alpha2, &ev->new_alpha2, REG_ALPHA2_LEN);
+ spin_unlock_bh(&ab->base_lock);
+
+ ath12k_dbg(ab, ATH12K_DBG_WMI, "wmi 11d new cc %c%c\n",
+ ab->new_alpha2[0],
+ ab->new_alpha2[1]);
- return false;
+ kfree(tb);
+
+ for (i = 0; i < ab->num_radios; i++) {
+ pdev = &ab->pdevs[i];
+ ar = pdev->ar;
+ ar->state_11d = ATH12K_11D_IDLE;
+ ar->ah->regd_updated = false;
+ complete(&ar->completed_11d_scan);
+ }
+
+ queue_work(ab->workqueue, &ab->update_11d_work);
+
+ return 0;
+}
+
+static void ath12k_wmi_htc_tx_complete(struct ath12k_base *ab,
+ struct sk_buff *skb)
+{
+ dev_kfree_skb(skb);
}
static int ath12k_reg_chan_list_event(struct ath12k_base *ab, struct sk_buff *skb)
{
- struct ath12k_reg_info *reg_info = NULL;
- struct ieee80211_regdomain *regd = NULL;
- bool intersect = false;
- int ret = 0, pdev_idx, i, j;
- struct ath12k *ar;
+ struct ath12k_reg_info *reg_info;
+ u8 pdev_idx;
+ int ret;
reg_info = kzalloc(sizeof(*reg_info), GFP_ATOMIC);
if (!reg_info) {
@@ -5903,86 +6153,52 @@ static int ath12k_reg_chan_list_event(struct ath12k_base *ab, struct sk_buff *sk
}
ret = ath12k_pull_reg_chan_list_ext_update_ev(ab, skb, reg_info);
-
if (ret) {
ath12k_warn(ab, "failed to extract regulatory info from received event\n");
- goto fallback;
+ goto mem_free;
}
- if (reg_info->status_code != REG_SET_CC_STATUS_PASS) {
- /* In case of failure to set the requested ctry,
- * fw retains the current regd. We print a failure info
- * and return from here.
+ ret = ath12k_reg_validate_reg_info(ab, reg_info);
+ if (ret == ATH12K_REG_STATUS_FALLBACK) {
+ ath12k_warn(ab, "failed to validate reg info %d\n", ret);
+ /* firmware has successfully switches to new regd but host can not
+ * continue, so free reginfo and fallback to old regd
*/
- ath12k_warn(ab, "Failed to set the requested Country regulatory setting\n");
+ goto mem_free;
+ } else if (ret == ATH12K_REG_STATUS_DROP) {
+ /* reg info is valid but we will not store it and
+ * not going to create new regd for it
+ */
+ ret = ATH12K_REG_STATUS_VALID;
goto mem_free;
}
+ /* free old reg_info if it exist */
pdev_idx = reg_info->phy_id;
-
- if (pdev_idx >= ab->num_radios) {
- /* Process the event for phy0 only if single_pdev_only
- * is true. If pdev_idx is valid but not 0, discard the
- * event. Otherwise, it goes to fallback.
- */
- if (ab->hw_params->single_pdev_only &&
- pdev_idx < ab->hw_params->num_rxdma_per_pdev)
- goto mem_free;
- else
- goto fallback;
+ if (ab->reg_info[pdev_idx]) {
+ ath12k_reg_reset_reg_info(ab->reg_info[pdev_idx]);
+ kfree(ab->reg_info[pdev_idx]);
}
-
- /* Avoid multiple overwrites to default regd, during core
- * stop-start after mac registration.
+ /* reg_info is valid, we store it for later use
+ * even below regd build failed
*/
- if (ab->default_regd[pdev_idx] && !ab->new_regd[pdev_idx] &&
- !memcmp(ab->default_regd[pdev_idx]->alpha2,
- reg_info->alpha2, 2))
- goto mem_free;
+ ab->reg_info[pdev_idx] = reg_info;
- /* Intersect new rules with default regd if a new country setting was
- * requested, i.e a default regd was already set during initialization
- * and the regd coming from this event has a valid country info.
- */
- if (ab->default_regd[pdev_idx] &&
- !ath12k_reg_is_world_alpha((char *)
- ab->default_regd[pdev_idx]->alpha2) &&
- !ath12k_reg_is_world_alpha((char *)reg_info->alpha2))
- intersect = true;
-
- regd = ath12k_reg_build_regd(ab, reg_info, intersect);
- if (!regd) {
- ath12k_warn(ab, "failed to build regd from reg_info\n");
+ ret = ath12k_reg_handle_chan_list(ab, reg_info, WMI_VDEV_TYPE_UNSPEC,
+ IEEE80211_REG_UNSET_AP);
+ if (ret) {
+ ath12k_warn(ab, "failed to handle chan list %d\n", ret);
goto fallback;
}
- spin_lock(&ab->base_lock);
- if (test_bit(ATH12K_FLAG_REGISTERED, &ab->dev_flags)) {
- /* Once mac is registered, ar is valid and all CC events from
- * fw is considered to be received due to user requests
- * currently.
- * Free previously built regd before assigning the newly
- * generated regd to ar. NULL pointer handling will be
- * taken care by kfree itself.
- */
- ar = ab->pdevs[pdev_idx].ar;
- kfree(ab->new_regd[pdev_idx]);
- ab->new_regd[pdev_idx] = regd;
- queue_work(ab->workqueue, &ar->regd_update_work);
- } else {
- /* Multiple events for the same *ar is not expected. But we
- * can still clear any previously stored default_regd if we
- * are receiving this event for the same radio by mistake.
- * NULL pointer handling will be taken care by kfree itself.
- */
- kfree(ab->default_regd[pdev_idx]);
- /* This regd would be applied during mac registration */
- ab->default_regd[pdev_idx] = regd;
- }
- ab->dfs_region = reg_info->dfs_region;
- spin_unlock(&ab->base_lock);
+ goto out;
+
+mem_free:
+ ath12k_reg_reset_reg_info(reg_info);
+ kfree(reg_info);
- goto mem_free;
+ if (ret == ATH12K_REG_STATUS_VALID)
+ return ret;
fallback:
/* Fallback to older reg (by sending previous country setting
@@ -5994,20 +6210,8 @@ fallback:
*/
/* TODO: This is rare, but still should also be handled */
WARN_ON(1);
-mem_free:
- if (reg_info) {
- kfree(reg_info->reg_rules_2g_ptr);
- kfree(reg_info->reg_rules_5g_ptr);
- if (reg_info->is_ext_reg_event) {
- for (i = 0; i < WMI_REG_CURRENT_MAX_AP_TYPE; i++)
- kfree(reg_info->reg_rules_6g_ap_ptr[i]);
-
- for (j = 0; j < WMI_REG_CURRENT_MAX_AP_TYPE; j++)
- for (i = 0; i < WMI_REG_MAX_CLIENT_TYPE; i++)
- kfree(reg_info->reg_rules_6g_client_ptr[j][i]);
- }
- kfree(reg_info);
- }
+
+out:
return ret;
}
@@ -6163,13 +6367,14 @@ static void ath12k_vdev_start_resp_event(struct ath12k_base *ab, struct sk_buff
ar->last_wmi_vdev_start_status = 0;
status = le32_to_cpu(vdev_start_resp.status);
-
if (WARN_ON_ONCE(status)) {
ath12k_warn(ab, "vdev start resp error status %d (%s)\n",
status, ath12k_wmi_vdev_resp_print(status));
ar->last_wmi_vdev_start_status = status;
}
+ ar->max_allowed_tx_power = (s8)le32_to_cpu(vdev_start_resp.max_allowed_tx_power);
+
complete(&ar->vdev_setup_done);
rcu_read_unlock();
@@ -6255,13 +6460,13 @@ static void ath12k_mgmt_rx_event(struct ath12k_base *ab, struct sk_buff *skb)
if (rx_ev.status & WMI_RX_STATUS_ERR_MIC)
status->flag |= RX_FLAG_MMIC_ERROR;
- if (rx_ev.chan_freq >= ATH12K_MIN_6G_FREQ &&
- rx_ev.chan_freq <= ATH12K_MAX_6G_FREQ) {
+ if (rx_ev.chan_freq >= ATH12K_MIN_6GHZ_FREQ &&
+ rx_ev.chan_freq <= ATH12K_MAX_6GHZ_FREQ) {
status->band = NL80211_BAND_6GHZ;
status->freq = rx_ev.chan_freq;
} else if (rx_ev.channel >= 1 && rx_ev.channel <= 14) {
status->band = NL80211_BAND_2GHZ;
- } else if (rx_ev.channel >= 36 && rx_ev.channel <= ATH12K_MAX_5G_CHAN) {
+ } else if (rx_ev.channel >= 36 && rx_ev.channel <= ATH12K_MAX_5GHZ_CHAN) {
status->band = NL80211_BAND_5GHZ;
} else {
/* Shouldn't happen unless list of advertised channels to
@@ -6842,8 +7047,640 @@ static void ath12k_peer_assoc_conf_event(struct ath12k_base *ab, struct sk_buff
rcu_read_unlock();
}
+static void
+ath12k_wmi_fw_vdev_stats_dump(struct ath12k *ar,
+ struct ath12k_fw_stats *fw_stats,
+ char *buf, u32 *length)
+{
+ const struct ath12k_fw_stats_vdev *vdev;
+ u32 buf_len = ATH12K_FW_STATS_BUF_SIZE;
+ struct ath12k_link_vif *arvif;
+ u32 len = *length;
+ u8 *vif_macaddr;
+ int i;
+
+ len += scnprintf(buf + len, buf_len - len, "\n");
+ len += scnprintf(buf + len, buf_len - len, "%30s\n",
+ "ath12k VDEV stats");
+ len += scnprintf(buf + len, buf_len - len, "%30s\n\n",
+ "=================");
+
+ list_for_each_entry(vdev, &fw_stats->vdevs, list) {
+ arvif = ath12k_mac_get_arvif(ar, vdev->vdev_id);
+ if (!arvif)
+ continue;
+ vif_macaddr = arvif->ahvif->vif->addr;
+
+ len += scnprintf(buf + len, buf_len - len, "%30s %u\n",
+ "VDEV ID", vdev->vdev_id);
+ len += scnprintf(buf + len, buf_len - len, "%30s %pM\n",
+ "VDEV MAC address", vif_macaddr);
+ len += scnprintf(buf + len, buf_len - len, "%30s %u\n",
+ "beacon snr", vdev->beacon_snr);
+ len += scnprintf(buf + len, buf_len - len, "%30s %u\n",
+ "data snr", vdev->data_snr);
+ len += scnprintf(buf + len, buf_len - len, "%30s %u\n",
+ "num rx frames", vdev->num_rx_frames);
+ len += scnprintf(buf + len, buf_len - len, "%30s %u\n",
+ "num rts fail", vdev->num_rts_fail);
+ len += scnprintf(buf + len, buf_len - len, "%30s %u\n",
+ "num rts success", vdev->num_rts_success);
+ len += scnprintf(buf + len, buf_len - len, "%30s %u\n",
+ "num rx err", vdev->num_rx_err);
+ len += scnprintf(buf + len, buf_len - len, "%30s %u\n",
+ "num rx discard", vdev->num_rx_discard);
+ len += scnprintf(buf + len, buf_len - len, "%30s %u\n",
+ "num tx not acked", vdev->num_tx_not_acked);
+
+ for (i = 0 ; i < WLAN_MAX_AC; i++)
+ len += scnprintf(buf + len, buf_len - len,
+ "%25s [%02d] %u\n",
+ "num tx frames", i,
+ vdev->num_tx_frames[i]);
+
+ for (i = 0 ; i < WLAN_MAX_AC; i++)
+ len += scnprintf(buf + len, buf_len - len,
+ "%25s [%02d] %u\n",
+ "num tx frames retries", i,
+ vdev->num_tx_frames_retries[i]);
+
+ for (i = 0 ; i < WLAN_MAX_AC; i++)
+ len += scnprintf(buf + len, buf_len - len,
+ "%25s [%02d] %u\n",
+ "num tx frames failures", i,
+ vdev->num_tx_frames_failures[i]);
+
+ for (i = 0 ; i < MAX_TX_RATE_VALUES; i++)
+ len += scnprintf(buf + len, buf_len - len,
+ "%25s [%02d] 0x%08x\n",
+ "tx rate history", i,
+ vdev->tx_rate_history[i]);
+ for (i = 0 ; i < MAX_TX_RATE_VALUES; i++)
+ len += scnprintf(buf + len, buf_len - len,
+ "%25s [%02d] %u\n",
+ "beacon rssi history", i,
+ vdev->beacon_rssi_history[i]);
+
+ len += scnprintf(buf + len, buf_len - len, "\n");
+ *length = len;
+ }
+}
+
+static void
+ath12k_wmi_fw_bcn_stats_dump(struct ath12k *ar,
+ struct ath12k_fw_stats *fw_stats,
+ char *buf, u32 *length)
+{
+ const struct ath12k_fw_stats_bcn *bcn;
+ u32 buf_len = ATH12K_FW_STATS_BUF_SIZE;
+ struct ath12k_link_vif *arvif;
+ u32 len = *length;
+ size_t num_bcn;
+
+ num_bcn = list_count_nodes(&fw_stats->bcn);
+
+ len += scnprintf(buf + len, buf_len - len, "\n");
+ len += scnprintf(buf + len, buf_len - len, "%30s (%zu)\n",
+ "ath12k Beacon stats", num_bcn);
+ len += scnprintf(buf + len, buf_len - len, "%30s\n\n",
+ "===================");
+
+ list_for_each_entry(bcn, &fw_stats->bcn, list) {
+ arvif = ath12k_mac_get_arvif(ar, bcn->vdev_id);
+ if (!arvif)
+ continue;
+ len += scnprintf(buf + len, buf_len - len, "%30s %u\n",
+ "VDEV ID", bcn->vdev_id);
+ len += scnprintf(buf + len, buf_len - len, "%30s %pM\n",
+ "VDEV MAC address", arvif->ahvif->vif->addr);
+ len += scnprintf(buf + len, buf_len - len, "%30s\n\n",
+ "================");
+ len += scnprintf(buf + len, buf_len - len, "%30s %u\n",
+ "Num of beacon tx success", bcn->tx_bcn_succ_cnt);
+ len += scnprintf(buf + len, buf_len - len, "%30s %u\n",
+ "Num of beacon tx failures", bcn->tx_bcn_outage_cnt);
+
+ len += scnprintf(buf + len, buf_len - len, "\n");
+ *length = len;
+ }
+}
+
+static void
+ath12k_wmi_fw_pdev_base_stats_dump(const struct ath12k_fw_stats_pdev *pdev,
+ char *buf, u32 *length, u64 fw_soc_drop_cnt)
+{
+ u32 len = *length;
+ u32 buf_len = ATH12K_FW_STATS_BUF_SIZE;
+
+ len = scnprintf(buf + len, buf_len - len, "\n");
+ len += scnprintf(buf + len, buf_len - len, "%30s\n",
+ "ath12k PDEV stats");
+ len += scnprintf(buf + len, buf_len - len, "%30s\n\n",
+ "=================");
+
+ len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+ "Channel noise floor", pdev->ch_noise_floor);
+ len += scnprintf(buf + len, buf_len - len, "%30s %10u\n",
+ "Channel TX power", pdev->chan_tx_power);
+ len += scnprintf(buf + len, buf_len - len, "%30s %10u\n",
+ "TX frame count", pdev->tx_frame_count);
+ len += scnprintf(buf + len, buf_len - len, "%30s %10u\n",
+ "RX frame count", pdev->rx_frame_count);
+ len += scnprintf(buf + len, buf_len - len, "%30s %10u\n",
+ "RX clear count", pdev->rx_clear_count);
+ len += scnprintf(buf + len, buf_len - len, "%30s %10u\n",
+ "Cycle count", pdev->cycle_count);
+ len += scnprintf(buf + len, buf_len - len, "%30s %10u\n",
+ "PHY error count", pdev->phy_err_count);
+ len += scnprintf(buf + len, buf_len - len, "%30s %10llu\n",
+ "soc drop count", fw_soc_drop_cnt);
+
+ *length = len;
+}
+
+static void
+ath12k_wmi_fw_pdev_tx_stats_dump(const struct ath12k_fw_stats_pdev *pdev,
+ char *buf, u32 *length)
+{
+ u32 len = *length;
+ u32 buf_len = ATH12K_FW_STATS_BUF_SIZE;
+
+ len += scnprintf(buf + len, buf_len - len, "\n%30s\n",
+ "ath12k PDEV TX stats");
+ len += scnprintf(buf + len, buf_len - len, "%30s\n\n",
+ "====================");
+
+ len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+ "HTT cookies queued", pdev->comp_queued);
+ len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+ "HTT cookies disp.", pdev->comp_delivered);
+ len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+ "MSDU queued", pdev->msdu_enqued);
+ len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+ "MPDU queued", pdev->mpdu_enqued);
+ len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+ "MSDUs dropped", pdev->wmm_drop);
+ len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+ "Local enqued", pdev->local_enqued);
+ len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+ "Local freed", pdev->local_freed);
+ len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+ "HW queued", pdev->hw_queued);
+ len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+ "PPDUs reaped", pdev->hw_reaped);
+ len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+ "Num underruns", pdev->underrun);
+ 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_requed);
+ len += scnprintf(buf + len, buf_len - len, "%30s %10u\n",
+ "Excessive retries", pdev->tx_ko);
+ 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",
+ "Sched self triggers", pdev->self_triggers);
+ len += scnprintf(buf + len, buf_len - len, "%30s %10u\n",
+ "Dropped due to SW retries",
+ pdev->sw_retry_failure);
+ len += scnprintf(buf + len, buf_len - len, "%30s %10u\n",
+ "Illegal rate phy errors",
+ pdev->illgl_rate_phy_err);
+ len += scnprintf(buf + len, buf_len - len, "%30s %10u\n",
+ "PDEV continuous xretry", pdev->pdev_cont_xretry);
+ len += scnprintf(buf + len, buf_len - len, "%30s %10u\n",
+ "TX timeout", pdev->pdev_tx_timeout);
+ len += scnprintf(buf + len, buf_len - len, "%30s %10u\n",
+ "PDEV resets", pdev->pdev_resets);
+ len += scnprintf(buf + len, buf_len - len, "%30s %10u\n",
+ "Stateless TIDs alloc failures",
+ pdev->stateless_tid_alloc_failure);
+ len += scnprintf(buf + len, buf_len - len, "%30s %10u\n",
+ "PHY underrun", pdev->phy_underrun);
+ len += scnprintf(buf + len, buf_len - len, "%30s %10u\n",
+ "MPDU is more than txop limit", pdev->txop_ovf);
+ *length = len;
+}
+
+static void
+ath12k_wmi_fw_pdev_rx_stats_dump(const struct ath12k_fw_stats_pdev *pdev,
+ char *buf, u32 *length)
+{
+ u32 len = *length;
+ u32 buf_len = ATH12K_FW_STATS_BUF_SIZE;
+
+ len += scnprintf(buf + len, buf_len - len, "\n%30s\n",
+ "ath12k PDEV RX stats");
+ len += scnprintf(buf + len, buf_len - len, "%30s\n\n",
+ "====================");
+
+ len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+ "Mid PPDU route change",
+ pdev->mid_ppdu_route_change);
+ len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+ "Tot. number of statuses", pdev->status_rcvd);
+ len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+ "Extra frags on rings 0", pdev->r0_frags);
+ len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+ "Extra frags on rings 1", pdev->r1_frags);
+ len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+ "Extra frags on rings 2", pdev->r2_frags);
+ len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+ "Extra frags on rings 3", pdev->r3_frags);
+ len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+ "MSDUs delivered to HTT", pdev->htt_msdus);
+ len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+ "MPDUs delivered to HTT", pdev->htt_mpdus);
+ len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+ "MSDUs delivered to stack", pdev->loc_msdus);
+ len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+ "MPDUs delivered to stack", pdev->loc_mpdus);
+ len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+ "Oversized AMSUs", pdev->oversize_amsdu);
+ len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+ "PHY errors", pdev->phy_errs);
+ len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+ "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);
+ *length = len;
+}
+
+static void
+ath12k_wmi_fw_pdev_stats_dump(struct ath12k *ar,
+ struct ath12k_fw_stats *fw_stats,
+ char *buf, u32 *length)
+{
+ const struct ath12k_fw_stats_pdev *pdev;
+ u32 len = *length;
+
+ pdev = list_first_entry_or_null(&fw_stats->pdevs,
+ struct ath12k_fw_stats_pdev, list);
+ if (!pdev) {
+ ath12k_warn(ar->ab, "failed to get pdev stats\n");
+ return;
+ }
+
+ ath12k_wmi_fw_pdev_base_stats_dump(pdev, buf, &len,
+ ar->ab->fw_soc_drop_count);
+ ath12k_wmi_fw_pdev_tx_stats_dump(pdev, buf, &len);
+ ath12k_wmi_fw_pdev_rx_stats_dump(pdev, buf, &len);
+
+ *length = len;
+}
+
+void ath12k_wmi_fw_stats_dump(struct ath12k *ar,
+ struct ath12k_fw_stats *fw_stats,
+ u32 stats_id, char *buf)
+{
+ u32 len = 0;
+ u32 buf_len = ATH12K_FW_STATS_BUF_SIZE;
+
+ spin_lock_bh(&ar->data_lock);
+
+ switch (stats_id) {
+ case WMI_REQUEST_VDEV_STAT:
+ ath12k_wmi_fw_vdev_stats_dump(ar, fw_stats, buf, &len);
+ break;
+ case WMI_REQUEST_BCN_STAT:
+ ath12k_wmi_fw_bcn_stats_dump(ar, fw_stats, buf, &len);
+ break;
+ case WMI_REQUEST_PDEV_STAT:
+ ath12k_wmi_fw_pdev_stats_dump(ar, fw_stats, buf, &len);
+ break;
+ default:
+ break;
+ }
+
+ spin_unlock_bh(&ar->data_lock);
+
+ if (len >= buf_len)
+ buf[len - 1] = 0;
+ else
+ buf[len] = 0;
+
+ ath12k_fw_stats_reset(ar);
+}
+
+static void
+ath12k_wmi_pull_vdev_stats(const struct wmi_vdev_stats_params *src,
+ struct ath12k_fw_stats_vdev *dst)
+{
+ int i;
+
+ dst->vdev_id = le32_to_cpu(src->vdev_id);
+ dst->beacon_snr = le32_to_cpu(src->beacon_snr);
+ dst->data_snr = le32_to_cpu(src->data_snr);
+ dst->num_rx_frames = le32_to_cpu(src->num_rx_frames);
+ dst->num_rts_fail = le32_to_cpu(src->num_rts_fail);
+ dst->num_rts_success = le32_to_cpu(src->num_rts_success);
+ dst->num_rx_err = le32_to_cpu(src->num_rx_err);
+ dst->num_rx_discard = le32_to_cpu(src->num_rx_discard);
+ dst->num_tx_not_acked = le32_to_cpu(src->num_tx_not_acked);
+
+ for (i = 0; i < WLAN_MAX_AC; i++)
+ dst->num_tx_frames[i] =
+ le32_to_cpu(src->num_tx_frames[i]);
+
+ for (i = 0; i < WLAN_MAX_AC; i++)
+ dst->num_tx_frames_retries[i] =
+ le32_to_cpu(src->num_tx_frames_retries[i]);
+
+ for (i = 0; i < WLAN_MAX_AC; i++)
+ dst->num_tx_frames_failures[i] =
+ le32_to_cpu(src->num_tx_frames_failures[i]);
+
+ for (i = 0; i < MAX_TX_RATE_VALUES; i++)
+ dst->tx_rate_history[i] =
+ le32_to_cpu(src->tx_rate_history[i]);
+
+ for (i = 0; i < MAX_TX_RATE_VALUES; i++)
+ dst->beacon_rssi_history[i] =
+ le32_to_cpu(src->beacon_rssi_history[i]);
+}
+
+static void
+ath12k_wmi_pull_bcn_stats(const struct ath12k_wmi_bcn_stats_params *src,
+ struct ath12k_fw_stats_bcn *dst)
+{
+ dst->vdev_id = le32_to_cpu(src->vdev_id);
+ dst->tx_bcn_succ_cnt = le32_to_cpu(src->tx_bcn_succ_cnt);
+ dst->tx_bcn_outage_cnt = le32_to_cpu(src->tx_bcn_outage_cnt);
+}
+
+static void
+ath12k_wmi_pull_pdev_stats_base(const struct ath12k_wmi_pdev_base_stats_params *src,
+ struct ath12k_fw_stats_pdev *dst)
+{
+ dst->ch_noise_floor = a_sle32_to_cpu(src->chan_nf);
+ dst->tx_frame_count = __le32_to_cpu(src->tx_frame_count);
+ dst->rx_frame_count = __le32_to_cpu(src->rx_frame_count);
+ dst->rx_clear_count = __le32_to_cpu(src->rx_clear_count);
+ dst->cycle_count = __le32_to_cpu(src->cycle_count);
+ dst->phy_err_count = __le32_to_cpu(src->phy_err_count);
+ dst->chan_tx_power = __le32_to_cpu(src->chan_tx_pwr);
+}
+
+static void
+ath12k_wmi_pull_pdev_stats_tx(const struct ath12k_wmi_pdev_tx_stats_params *src,
+ struct ath12k_fw_stats_pdev *dst)
+{
+ dst->comp_queued = a_sle32_to_cpu(src->comp_queued);
+ dst->comp_delivered = a_sle32_to_cpu(src->comp_delivered);
+ dst->msdu_enqued = a_sle32_to_cpu(src->msdu_enqued);
+ dst->mpdu_enqued = a_sle32_to_cpu(src->mpdu_enqued);
+ dst->wmm_drop = a_sle32_to_cpu(src->wmm_drop);
+ dst->local_enqued = a_sle32_to_cpu(src->local_enqued);
+ dst->local_freed = a_sle32_to_cpu(src->local_freed);
+ dst->hw_queued = a_sle32_to_cpu(src->hw_queued);
+ dst->hw_reaped = a_sle32_to_cpu(src->hw_reaped);
+ dst->underrun = a_sle32_to_cpu(src->underrun);
+ dst->tx_abort = a_sle32_to_cpu(src->tx_abort);
+ dst->mpdus_requed = a_sle32_to_cpu(src->mpdus_requed);
+ dst->tx_ko = __le32_to_cpu(src->tx_ko);
+ dst->data_rc = __le32_to_cpu(src->data_rc);
+ dst->self_triggers = __le32_to_cpu(src->self_triggers);
+ dst->sw_retry_failure = __le32_to_cpu(src->sw_retry_failure);
+ dst->illgl_rate_phy_err = __le32_to_cpu(src->illgl_rate_phy_err);
+ dst->pdev_cont_xretry = __le32_to_cpu(src->pdev_cont_xretry);
+ dst->pdev_tx_timeout = __le32_to_cpu(src->pdev_tx_timeout);
+ dst->pdev_resets = __le32_to_cpu(src->pdev_resets);
+ dst->stateless_tid_alloc_failure =
+ __le32_to_cpu(src->stateless_tid_alloc_failure);
+ dst->phy_underrun = __le32_to_cpu(src->phy_underrun);
+ dst->txop_ovf = __le32_to_cpu(src->txop_ovf);
+}
+
+static void
+ath12k_wmi_pull_pdev_stats_rx(const struct ath12k_wmi_pdev_rx_stats_params *src,
+ struct ath12k_fw_stats_pdev *dst)
+{
+ dst->mid_ppdu_route_change =
+ a_sle32_to_cpu(src->mid_ppdu_route_change);
+ dst->status_rcvd = a_sle32_to_cpu(src->status_rcvd);
+ dst->r0_frags = a_sle32_to_cpu(src->r0_frags);
+ dst->r1_frags = a_sle32_to_cpu(src->r1_frags);
+ dst->r2_frags = a_sle32_to_cpu(src->r2_frags);
+ dst->r3_frags = a_sle32_to_cpu(src->r3_frags);
+ dst->htt_msdus = a_sle32_to_cpu(src->htt_msdus);
+ dst->htt_mpdus = a_sle32_to_cpu(src->htt_mpdus);
+ dst->loc_msdus = a_sle32_to_cpu(src->loc_msdus);
+ dst->loc_mpdus = a_sle32_to_cpu(src->loc_mpdus);
+ dst->oversize_amsdu = a_sle32_to_cpu(src->oversize_amsdu);
+ dst->phy_errs = a_sle32_to_cpu(src->phy_errs);
+ dst->phy_err_drop = a_sle32_to_cpu(src->phy_err_drop);
+ dst->mpdu_errs = a_sle32_to_cpu(src->mpdu_errs);
+}
+
+static int ath12k_wmi_tlv_fw_stats_data_parse(struct ath12k_base *ab,
+ struct wmi_tlv_fw_stats_parse *parse,
+ const void *ptr,
+ u16 len)
+{
+ const struct wmi_stats_event *ev = parse->ev;
+ struct ath12k_fw_stats *stats = parse->stats;
+ struct ath12k *ar;
+ struct ath12k_link_vif *arvif;
+ struct ieee80211_sta *sta;
+ struct ath12k_sta *ahsta;
+ struct ath12k_link_sta *arsta;
+ int i, ret = 0;
+ const void *data = ptr;
+
+ if (!ev) {
+ ath12k_warn(ab, "failed to fetch update stats ev");
+ return -EPROTO;
+ }
+
+ if (!stats)
+ return -EINVAL;
+
+ rcu_read_lock();
+
+ stats->pdev_id = le32_to_cpu(ev->pdev_id);
+ ar = ath12k_mac_get_ar_by_pdev_id(ab, stats->pdev_id);
+ if (!ar) {
+ ath12k_warn(ab, "invalid pdev id %d in update stats event\n",
+ le32_to_cpu(ev->pdev_id));
+ ret = -EPROTO;
+ goto exit;
+ }
+
+ for (i = 0; i < le32_to_cpu(ev->num_vdev_stats); i++) {
+ const struct wmi_vdev_stats_params *src;
+ struct ath12k_fw_stats_vdev *dst;
+
+ src = data;
+ if (len < sizeof(*src)) {
+ ret = -EPROTO;
+ goto exit;
+ }
+
+ arvif = ath12k_mac_get_arvif(ar, le32_to_cpu(src->vdev_id));
+ if (arvif) {
+ sta = ieee80211_find_sta_by_ifaddr(ath12k_ar_to_hw(ar),
+ arvif->bssid,
+ NULL);
+ if (sta) {
+ ahsta = ath12k_sta_to_ahsta(sta);
+ arsta = &ahsta->deflink;
+ arsta->rssi_beacon = le32_to_cpu(src->beacon_snr);
+ ath12k_dbg(ab, ATH12K_DBG_WMI,
+ "wmi stats vdev id %d snr %d\n",
+ src->vdev_id, src->beacon_snr);
+ } else {
+ ath12k_dbg(ab, ATH12K_DBG_WMI,
+ "not found station bssid %pM for vdev stat\n",
+ arvif->bssid);
+ }
+ }
+
+ data += sizeof(*src);
+ len -= sizeof(*src);
+ dst = kzalloc(sizeof(*dst), GFP_ATOMIC);
+ if (!dst)
+ continue;
+ ath12k_wmi_pull_vdev_stats(src, dst);
+ stats->stats_id = WMI_REQUEST_VDEV_STAT;
+ list_add_tail(&dst->list, &stats->vdevs);
+ }
+ for (i = 0; i < le32_to_cpu(ev->num_bcn_stats); i++) {
+ const struct ath12k_wmi_bcn_stats_params *src;
+ struct ath12k_fw_stats_bcn *dst;
+
+ src = data;
+ if (len < sizeof(*src)) {
+ ret = -EPROTO;
+ goto exit;
+ }
+
+ data += sizeof(*src);
+ len -= sizeof(*src);
+ dst = kzalloc(sizeof(*dst), GFP_ATOMIC);
+ if (!dst)
+ continue;
+ ath12k_wmi_pull_bcn_stats(src, dst);
+ stats->stats_id = WMI_REQUEST_BCN_STAT;
+ list_add_tail(&dst->list, &stats->bcn);
+ }
+ for (i = 0; i < le32_to_cpu(ev->num_pdev_stats); i++) {
+ const struct ath12k_wmi_pdev_stats_params *src;
+ struct ath12k_fw_stats_pdev *dst;
+
+ src = data;
+ if (len < sizeof(*src)) {
+ ret = -EPROTO;
+ goto exit;
+ }
+
+ stats->stats_id = WMI_REQUEST_PDEV_STAT;
+
+ data += sizeof(*src);
+ len -= sizeof(*src);
+
+ dst = kzalloc(sizeof(*dst), GFP_ATOMIC);
+ if (!dst)
+ continue;
+
+ ath12k_wmi_pull_pdev_stats_base(&src->base, dst);
+ ath12k_wmi_pull_pdev_stats_tx(&src->tx, dst);
+ ath12k_wmi_pull_pdev_stats_rx(&src->rx, dst);
+ list_add_tail(&dst->list, &stats->pdevs);
+ }
+
+exit:
+ rcu_read_unlock();
+ return ret;
+}
+
+static int ath12k_wmi_tlv_fw_stats_parse(struct ath12k_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 = ptr;
+ break;
+ case WMI_TAG_ARRAY_BYTE:
+ ret = ath12k_wmi_tlv_fw_stats_data_parse(ab, parse, ptr, len);
+ break;
+ default:
+ break;
+ }
+ return ret;
+}
+
+static int ath12k_wmi_pull_fw_stats(struct ath12k_base *ab, struct sk_buff *skb,
+ struct ath12k_fw_stats *stats)
+{
+ struct wmi_tlv_fw_stats_parse parse = {};
+
+ stats->stats_id = 0;
+ parse.stats = stats;
+
+ return ath12k_wmi_tlv_iter(ab, skb->data, skb->len,
+ ath12k_wmi_tlv_fw_stats_parse,
+ &parse);
+}
+
static void ath12k_update_stats_event(struct ath12k_base *ab, struct sk_buff *skb)
{
+ struct ath12k_fw_stats stats = {};
+ struct ath12k *ar;
+ int ret;
+
+ INIT_LIST_HEAD(&stats.pdevs);
+ INIT_LIST_HEAD(&stats.vdevs);
+ INIT_LIST_HEAD(&stats.bcn);
+
+ ret = ath12k_wmi_pull_fw_stats(ab, skb, &stats);
+ if (ret) {
+ ath12k_warn(ab, "failed to pull fw stats: %d\n", ret);
+ goto free;
+ }
+
+ ath12k_dbg(ab, ATH12K_DBG_WMI, "event update stats");
+
+ rcu_read_lock();
+ ar = ath12k_mac_get_ar_by_pdev_id(ab, stats.pdev_id);
+ if (!ar) {
+ rcu_read_unlock();
+ ath12k_warn(ab, "failed to get ar for pdev_id %d: %d\n",
+ stats.pdev_id, ret);
+ goto free;
+ }
+
+ spin_lock_bh(&ar->data_lock);
+
+ /* WMI_REQUEST_PDEV_STAT can be requested via .get_txpower mac ops or via
+ * debugfs fw stats. Therefore, processing it separately.
+ */
+ if (stats.stats_id == WMI_REQUEST_PDEV_STAT) {
+ list_splice_tail_init(&stats.pdevs, &ar->fw_stats.pdevs);
+ ar->fw_stats.fw_stats_done = true;
+ goto complete;
+ }
+
+ /* WMI_REQUEST_VDEV_STAT and WMI_REQUEST_BCN_STAT are currently requested only
+ * via debugfs fw stats. Hence, processing these in debugfs context.
+ */
+ ath12k_debugfs_fw_stats_process(ar, &stats);
+
+complete:
+ complete(&ar->fw_stats_complete);
+ spin_unlock_bh(&ar->data_lock);
+ rcu_read_unlock();
+
+ /* Since the stats's pdev, vdev and beacon list are spliced and reinitialised
+ * at this point, no need to free the individual list.
+ */
+ return;
+
+free:
+ ath12k_fw_stats_free(&stats);
}
/* PDEV_CTL_FAILSAFE_CHECK_EVENT is received from FW when the frequency scanned
@@ -6889,17 +7726,15 @@ ath12k_wmi_process_csa_switch_count_event(struct ath12k_base *ab,
const struct ath12k_wmi_pdev_csa_event *ev,
const u32 *vdev_ids)
{
- int i;
+ u32 current_switch_count = le32_to_cpu(ev->current_switch_count);
+ u32 num_vdevs = le32_to_cpu(ev->num_vdevs);
struct ieee80211_bss_conf *conf;
struct ath12k_link_vif *arvif;
struct ath12k_vif *ahvif;
-
- /* Finish CSA once the switch count becomes NULL */
- if (ev->current_switch_count)
- return;
+ int i;
rcu_read_lock();
- for (i = 0; i < le32_to_cpu(ev->num_vdevs); i++) {
+ for (i = 0; i < num_vdevs; i++) {
arvif = ath12k_mac_get_arvif_by_vdev_id(ab, vdev_ids[i]);
if (!arvif) {
@@ -6922,8 +7757,26 @@ ath12k_wmi_process_csa_switch_count_event(struct ath12k_base *ab,
continue;
}
- if (arvif->is_up && conf->csa_active)
- ieee80211_csa_finish(ahvif->vif, 0);
+ if (!arvif->is_up || !conf->csa_active)
+ continue;
+
+ /* Finish CSA when counter reaches zero */
+ if (!current_switch_count) {
+ ieee80211_csa_finish(ahvif->vif, arvif->link_id);
+ arvif->current_cntdown_counter = 0;
+ } else if (current_switch_count > 1) {
+ /* If the count in event is not what we expect, don't update the
+ * mac80211 count. Since during beacon Tx failure, count in the
+ * firmware will not decrement and this event will come with the
+ * previous count value again
+ */
+ if (current_switch_count != arvif->current_cntdown_counter)
+ continue;
+
+ arvif->current_cntdown_counter =
+ ieee80211_beacon_update_cntdwn(ahvif->vif,
+ arvif->link_id);
+ }
}
rcu_read_unlock();
}
@@ -7026,6 +7879,35 @@ exit:
kfree(tb);
}
+static void ath12k_tm_wmi_event_segmented(struct ath12k_base *ab, u32 cmd_id,
+ struct sk_buff *skb)
+{
+ const struct ath12k_wmi_ftm_event *ev;
+ const void **tb;
+ int ret;
+ u16 length;
+
+ tb = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC);
+
+ if (IS_ERR(tb)) {
+ ret = PTR_ERR(tb);
+ ath12k_warn(ab, "failed to parse ftm event tlv: %d\n", ret);
+ return;
+ }
+
+ ev = tb[WMI_TAG_ARRAY_BYTE];
+ if (!ev) {
+ ath12k_warn(ab, "failed to fetch ftm msg\n");
+ kfree(tb);
+ return;
+ }
+
+ length = skb->len - TLV_HDR_SIZE;
+ ath12k_tm_process_event(ab, cmd_id, ev, length);
+ kfree(tb);
+ tb = NULL;
+}
+
static void
ath12k_wmi_pdev_temperature_event(struct ath12k_base *ab,
struct sk_buff *skb)
@@ -7446,6 +8328,389 @@ static void ath12k_wmi_event_teardown_complete(struct ath12k_base *ab,
kfree(tb);
}
+#ifdef CONFIG_ATH12K_DEBUGFS
+static int ath12k_wmi_tpc_stats_copy_buffer(struct ath12k_base *ab,
+ const void *ptr, u16 tag, u16 len,
+ struct wmi_tpc_stats_arg *tpc_stats)
+{
+ u32 len1, len2, len3, len4;
+ s16 *dst_ptr;
+ s8 *dst_ptr_ctl;
+
+ len1 = le32_to_cpu(tpc_stats->max_reg_allowed_power.tpc_reg_pwr.reg_array_len);
+ len2 = le32_to_cpu(tpc_stats->rates_array1.tpc_rates_array.rate_array_len);
+ len3 = le32_to_cpu(tpc_stats->rates_array2.tpc_rates_array.rate_array_len);
+ len4 = le32_to_cpu(tpc_stats->ctl_array.tpc_ctl_pwr.ctl_array_len);
+
+ switch (tpc_stats->event_count) {
+ case ATH12K_TPC_STATS_CONFIG_REG_PWR_EVENT:
+ if (len1 > len)
+ return -ENOBUFS;
+
+ if (tpc_stats->tlvs_rcvd & WMI_TPC_REG_PWR_ALLOWED) {
+ dst_ptr = tpc_stats->max_reg_allowed_power.reg_pwr_array;
+ memcpy(dst_ptr, ptr, len1);
+ }
+ break;
+ case ATH12K_TPC_STATS_RATES_EVENT1:
+ if (len2 > len)
+ return -ENOBUFS;
+
+ if (tpc_stats->tlvs_rcvd & WMI_TPC_RATES_ARRAY1) {
+ dst_ptr = tpc_stats->rates_array1.rate_array;
+ memcpy(dst_ptr, ptr, len2);
+ }
+ break;
+ case ATH12K_TPC_STATS_RATES_EVENT2:
+ if (len3 > len)
+ return -ENOBUFS;
+
+ if (tpc_stats->tlvs_rcvd & WMI_TPC_RATES_ARRAY2) {
+ dst_ptr = tpc_stats->rates_array2.rate_array;
+ memcpy(dst_ptr, ptr, len3);
+ }
+ break;
+ case ATH12K_TPC_STATS_CTL_TABLE_EVENT:
+ if (len4 > len)
+ return -ENOBUFS;
+
+ if (tpc_stats->tlvs_rcvd & WMI_TPC_CTL_PWR_ARRAY) {
+ dst_ptr_ctl = tpc_stats->ctl_array.ctl_pwr_table;
+ memcpy(dst_ptr_ctl, ptr, len4);
+ }
+ break;
+ }
+ return 0;
+}
+
+static int ath12k_tpc_get_reg_pwr(struct ath12k_base *ab,
+ struct wmi_tpc_stats_arg *tpc_stats,
+ struct wmi_max_reg_power_fixed_params *ev)
+{
+ struct wmi_max_reg_power_allowed_arg *reg_pwr;
+ u32 total_size;
+
+ ath12k_dbg(ab, ATH12K_DBG_WMI,
+ "Received reg power array type %d length %d for tpc stats\n",
+ ev->reg_power_type, ev->reg_array_len);
+
+ switch (le32_to_cpu(ev->reg_power_type)) {
+ case TPC_STATS_REG_PWR_ALLOWED_TYPE:
+ reg_pwr = &tpc_stats->max_reg_allowed_power;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* Each entry is 2 byte hence multiplying the indices with 2 */
+ total_size = le32_to_cpu(ev->d1) * le32_to_cpu(ev->d2) *
+ le32_to_cpu(ev->d3) * le32_to_cpu(ev->d4) * 2;
+ if (le32_to_cpu(ev->reg_array_len) != total_size) {
+ ath12k_warn(ab,
+ "Total size and reg_array_len doesn't match for tpc stats\n");
+ return -EINVAL;
+ }
+
+ memcpy(&reg_pwr->tpc_reg_pwr, ev, sizeof(struct wmi_max_reg_power_fixed_params));
+
+ reg_pwr->reg_pwr_array = kzalloc(le32_to_cpu(reg_pwr->tpc_reg_pwr.reg_array_len),
+ GFP_ATOMIC);
+ if (!reg_pwr->reg_pwr_array)
+ return -ENOMEM;
+
+ tpc_stats->tlvs_rcvd |= WMI_TPC_REG_PWR_ALLOWED;
+
+ return 0;
+}
+
+static int ath12k_tpc_get_rate_array(struct ath12k_base *ab,
+ struct wmi_tpc_stats_arg *tpc_stats,
+ struct wmi_tpc_rates_array_fixed_params *ev)
+{
+ struct wmi_tpc_rates_array_arg *rates_array;
+ u32 flag = 0, rate_array_len;
+
+ ath12k_dbg(ab, ATH12K_DBG_WMI,
+ "Received rates array type %d length %d for tpc stats\n",
+ ev->rate_array_type, ev->rate_array_len);
+
+ switch (le32_to_cpu(ev->rate_array_type)) {
+ case ATH12K_TPC_STATS_RATES_ARRAY1:
+ rates_array = &tpc_stats->rates_array1;
+ flag = WMI_TPC_RATES_ARRAY1;
+ break;
+ case ATH12K_TPC_STATS_RATES_ARRAY2:
+ rates_array = &tpc_stats->rates_array2;
+ flag = WMI_TPC_RATES_ARRAY2;
+ break;
+ default:
+ ath12k_warn(ab,
+ "Received invalid type of rates array for tpc stats\n");
+ return -EINVAL;
+ }
+ memcpy(&rates_array->tpc_rates_array, ev,
+ sizeof(struct wmi_tpc_rates_array_fixed_params));
+ rate_array_len = le32_to_cpu(rates_array->tpc_rates_array.rate_array_len);
+ rates_array->rate_array = kzalloc(rate_array_len, GFP_ATOMIC);
+ if (!rates_array->rate_array)
+ return -ENOMEM;
+
+ tpc_stats->tlvs_rcvd |= flag;
+ return 0;
+}
+
+static int ath12k_tpc_get_ctl_pwr_tbl(struct ath12k_base *ab,
+ struct wmi_tpc_stats_arg *tpc_stats,
+ struct wmi_tpc_ctl_pwr_fixed_params *ev)
+{
+ struct wmi_tpc_ctl_pwr_table_arg *ctl_array;
+ u32 total_size, ctl_array_len, flag = 0;
+
+ ath12k_dbg(ab, ATH12K_DBG_WMI,
+ "Received ctl array type %d length %d for tpc stats\n",
+ ev->ctl_array_type, ev->ctl_array_len);
+
+ switch (le32_to_cpu(ev->ctl_array_type)) {
+ case ATH12K_TPC_STATS_CTL_ARRAY:
+ ctl_array = &tpc_stats->ctl_array;
+ flag = WMI_TPC_CTL_PWR_ARRAY;
+ break;
+ default:
+ ath12k_warn(ab,
+ "Received invalid type of ctl pwr table for tpc stats\n");
+ return -EINVAL;
+ }
+
+ total_size = le32_to_cpu(ev->d1) * le32_to_cpu(ev->d2) *
+ le32_to_cpu(ev->d3) * le32_to_cpu(ev->d4);
+ if (le32_to_cpu(ev->ctl_array_len) != total_size) {
+ ath12k_warn(ab,
+ "Total size and ctl_array_len doesn't match for tpc stats\n");
+ return -EINVAL;
+ }
+
+ memcpy(&ctl_array->tpc_ctl_pwr, ev, sizeof(struct wmi_tpc_ctl_pwr_fixed_params));
+ ctl_array_len = le32_to_cpu(ctl_array->tpc_ctl_pwr.ctl_array_len);
+ ctl_array->ctl_pwr_table = kzalloc(ctl_array_len, GFP_ATOMIC);
+ if (!ctl_array->ctl_pwr_table)
+ return -ENOMEM;
+
+ tpc_stats->tlvs_rcvd |= flag;
+ return 0;
+}
+
+static int ath12k_wmi_tpc_stats_subtlv_parser(struct ath12k_base *ab,
+ u16 tag, u16 len,
+ const void *ptr, void *data)
+{
+ struct wmi_tpc_rates_array_fixed_params *tpc_rates_array;
+ struct wmi_max_reg_power_fixed_params *tpc_reg_pwr;
+ struct wmi_tpc_ctl_pwr_fixed_params *tpc_ctl_pwr;
+ struct wmi_tpc_stats_arg *tpc_stats = data;
+ struct wmi_tpc_config_params *tpc_config;
+ int ret = 0;
+
+ if (!tpc_stats) {
+ ath12k_warn(ab, "tpc stats memory unavailable\n");
+ return -EINVAL;
+ }
+
+ switch (tag) {
+ case WMI_TAG_TPC_STATS_CONFIG_EVENT:
+ tpc_config = (struct wmi_tpc_config_params *)ptr;
+ memcpy(&tpc_stats->tpc_config, tpc_config,
+ sizeof(struct wmi_tpc_config_params));
+ break;
+ case WMI_TAG_TPC_STATS_REG_PWR_ALLOWED:
+ tpc_reg_pwr = (struct wmi_max_reg_power_fixed_params *)ptr;
+ ret = ath12k_tpc_get_reg_pwr(ab, tpc_stats, tpc_reg_pwr);
+ break;
+ case WMI_TAG_TPC_STATS_RATES_ARRAY:
+ tpc_rates_array = (struct wmi_tpc_rates_array_fixed_params *)ptr;
+ ret = ath12k_tpc_get_rate_array(ab, tpc_stats, tpc_rates_array);
+ break;
+ case WMI_TAG_TPC_STATS_CTL_PWR_TABLE_EVENT:
+ tpc_ctl_pwr = (struct wmi_tpc_ctl_pwr_fixed_params *)ptr;
+ ret = ath12k_tpc_get_ctl_pwr_tbl(ab, tpc_stats, tpc_ctl_pwr);
+ break;
+ default:
+ ath12k_warn(ab,
+ "Received invalid tag for tpc stats in subtlvs\n");
+ return -EINVAL;
+ }
+ return ret;
+}
+
+static int ath12k_wmi_tpc_stats_event_parser(struct ath12k_base *ab,
+ u16 tag, u16 len,
+ const void *ptr, void *data)
+{
+ struct wmi_tpc_stats_arg *tpc_stats = (struct wmi_tpc_stats_arg *)data;
+ int ret;
+
+ switch (tag) {
+ case WMI_TAG_HALPHY_CTRL_PATH_EVENT_FIXED_PARAM:
+ ret = 0;
+ /* Fixed param is already processed*/
+ break;
+ case WMI_TAG_ARRAY_STRUCT:
+ /* len 0 is expected for array of struct when there
+ * is no content of that type to pack inside that tlv
+ */
+ if (len == 0)
+ return 0;
+ ret = ath12k_wmi_tlv_iter(ab, ptr, len,
+ ath12k_wmi_tpc_stats_subtlv_parser,
+ tpc_stats);
+ break;
+ case WMI_TAG_ARRAY_INT16:
+ if (len == 0)
+ return 0;
+ ret = ath12k_wmi_tpc_stats_copy_buffer(ab, ptr,
+ WMI_TAG_ARRAY_INT16,
+ len, tpc_stats);
+ break;
+ case WMI_TAG_ARRAY_BYTE:
+ if (len == 0)
+ return 0;
+ ret = ath12k_wmi_tpc_stats_copy_buffer(ab, ptr,
+ WMI_TAG_ARRAY_BYTE,
+ len, tpc_stats);
+ break;
+ default:
+ ath12k_warn(ab, "Received invalid tag for tpc stats\n");
+ ret = -EINVAL;
+ break;
+ }
+ return ret;
+}
+
+void ath12k_wmi_free_tpc_stats_mem(struct ath12k *ar)
+{
+ struct wmi_tpc_stats_arg *tpc_stats = ar->debug.tpc_stats;
+
+ lockdep_assert_held(&ar->data_lock);
+ ath12k_dbg(ar->ab, ATH12K_DBG_WMI, "tpc stats mem free\n");
+ if (tpc_stats) {
+ kfree(tpc_stats->max_reg_allowed_power.reg_pwr_array);
+ kfree(tpc_stats->rates_array1.rate_array);
+ kfree(tpc_stats->rates_array2.rate_array);
+ kfree(tpc_stats->ctl_array.ctl_pwr_table);
+ kfree(tpc_stats);
+ ar->debug.tpc_stats = NULL;
+ }
+}
+
+static void ath12k_wmi_process_tpc_stats(struct ath12k_base *ab,
+ struct sk_buff *skb)
+{
+ struct ath12k_wmi_pdev_tpc_stats_event_fixed_params *fixed_param;
+ struct wmi_tpc_stats_arg *tpc_stats;
+ const struct wmi_tlv *tlv;
+ void *ptr = skb->data;
+ struct ath12k *ar;
+ u16 tlv_tag;
+ u32 event_count;
+ int ret;
+
+ if (!skb->data) {
+ ath12k_warn(ab, "No data present in tpc stats event\n");
+ return;
+ }
+
+ if (skb->len < (sizeof(*fixed_param) + TLV_HDR_SIZE)) {
+ ath12k_warn(ab, "TPC stats event size invalid\n");
+ return;
+ }
+
+ tlv = (struct wmi_tlv *)ptr;
+ tlv_tag = le32_get_bits(tlv->header, WMI_TLV_TAG);
+ ptr += sizeof(*tlv);
+
+ if (tlv_tag != WMI_TAG_HALPHY_CTRL_PATH_EVENT_FIXED_PARAM) {
+ ath12k_warn(ab, "TPC stats without fixed param tlv at start\n");
+ return;
+ }
+
+ fixed_param = (struct ath12k_wmi_pdev_tpc_stats_event_fixed_params *)ptr;
+ rcu_read_lock();
+ ar = ath12k_mac_get_ar_by_pdev_id(ab, le32_to_cpu(fixed_param->pdev_id) + 1);
+ if (!ar) {
+ ath12k_warn(ab, "Failed to get ar for tpc stats\n");
+ rcu_read_unlock();
+ return;
+ }
+ spin_lock_bh(&ar->data_lock);
+ if (!ar->debug.tpc_request) {
+ /* Event is received either without request or the
+ * timeout, if memory is already allocated free it
+ */
+ if (ar->debug.tpc_stats) {
+ ath12k_warn(ab, "Freeing memory for tpc_stats\n");
+ ath12k_wmi_free_tpc_stats_mem(ar);
+ }
+ goto unlock;
+ }
+
+ event_count = le32_to_cpu(fixed_param->event_count);
+ if (event_count == 0) {
+ if (ar->debug.tpc_stats) {
+ ath12k_warn(ab,
+ "Invalid tpc memory present\n");
+ goto unlock;
+ }
+ ar->debug.tpc_stats =
+ kzalloc(sizeof(struct wmi_tpc_stats_arg),
+ GFP_ATOMIC);
+ if (!ar->debug.tpc_stats) {
+ ath12k_warn(ab,
+ "Failed to allocate memory for tpc stats\n");
+ goto unlock;
+ }
+ }
+
+ tpc_stats = ar->debug.tpc_stats;
+ if (!tpc_stats) {
+ ath12k_warn(ab, "tpc stats memory unavailable\n");
+ goto unlock;
+ }
+
+ if (!(event_count == 0)) {
+ if (event_count != tpc_stats->event_count + 1) {
+ ath12k_warn(ab,
+ "Invalid tpc event received\n");
+ goto unlock;
+ }
+ }
+ tpc_stats->pdev_id = le32_to_cpu(fixed_param->pdev_id);
+ tpc_stats->end_of_event = le32_to_cpu(fixed_param->end_of_event);
+ tpc_stats->event_count = le32_to_cpu(fixed_param->event_count);
+ ath12k_dbg(ab, ATH12K_DBG_WMI,
+ "tpc stats event_count %d\n",
+ tpc_stats->event_count);
+ ret = ath12k_wmi_tlv_iter(ab, skb->data, skb->len,
+ ath12k_wmi_tpc_stats_event_parser,
+ tpc_stats);
+ if (ret) {
+ ath12k_wmi_free_tpc_stats_mem(ar);
+ ath12k_warn(ab, "failed to parse tpc_stats tlv: %d\n", ret);
+ goto unlock;
+ }
+
+ if (tpc_stats->end_of_event)
+ complete(&ar->debug.tpc_complete);
+
+unlock:
+ spin_unlock_bh(&ar->data_lock);
+ rcu_read_unlock();
+}
+#else
+static void ath12k_wmi_process_tpc_stats(struct ath12k_base *ab,
+ struct sk_buff *skb)
+{
+}
+#endif
+
static void ath12k_wmi_op_rx(struct ath12k_base *ab, struct sk_buff *skb)
{
struct wmi_cmd_hdr *cmd_hdr;
@@ -7571,6 +8836,12 @@ static void ath12k_wmi_op_rx(struct ath12k_base *ab, struct sk_buff *skb)
case WMI_MLO_TEARDOWN_COMPLETE_EVENTID:
ath12k_wmi_event_teardown_complete(ab, skb);
break;
+ case WMI_HALPHY_STATS_CTRL_PATH_EVENTID:
+ ath12k_wmi_process_tpc_stats(ab, skb);
+ break;
+ case WMI_11D_NEW_COUNTRY_EVENTID:
+ ath12k_reg_11d_new_cc_event(ab, skb);
+ break;
/* add Unsupported events (rare) here */
case WMI_TBTTOFFSET_EXT_UPDATE_EVENTID:
case WMI_PEER_OPER_MODE_CHANGE_EVENTID:
@@ -7584,7 +8855,12 @@ static void ath12k_wmi_op_rx(struct ath12k_base *ab, struct sk_buff *skb)
case WMI_OBSS_COLOR_COLLISION_DETECTION_EVENTID:
/* debug might flood hence silently ignore (no-op) */
break;
- /* TODO: Add remaining events */
+ case WMI_PDEV_UTF_EVENTID:
+ if (test_bit(ATH12K_FLAG_FTM_SEGMENTED, &ab->dev_flags))
+ ath12k_tm_wmi_event_segmented(ab, id, skb);
+ else
+ ath12k_tm_wmi_event_unsegmented(ab, id, skb);
+ break;
default:
ath12k_dbg(ab, ATH12K_DBG_WMI, "Unknown eventid: 0x%x\n", id);
break;
@@ -7721,6 +8997,74 @@ int ath12k_wmi_simulate_radar(struct ath12k *ar)
return ath12k_wmi_send_unit_test_cmd(ar, wmi_ut, dfs_args);
}
+int ath12k_wmi_send_tpc_stats_request(struct ath12k *ar,
+ enum wmi_halphy_ctrl_path_stats_id tpc_stats_type)
+{
+ struct wmi_request_halphy_ctrl_path_stats_cmd_fixed_params *cmd;
+ struct ath12k_wmi_pdev *wmi = ar->wmi;
+ struct sk_buff *skb;
+ struct wmi_tlv *tlv;
+ __le32 *pdev_id;
+ u32 buf_len;
+ void *ptr;
+ int ret;
+
+ buf_len = sizeof(*cmd) + TLV_HDR_SIZE + sizeof(u32) + TLV_HDR_SIZE + TLV_HDR_SIZE;
+
+ skb = ath12k_wmi_alloc_skb(wmi->wmi_ab, buf_len);
+ if (!skb)
+ return -ENOMEM;
+ cmd = (struct wmi_request_halphy_ctrl_path_stats_cmd_fixed_params *)skb->data;
+ cmd->tlv_header = ath12k_wmi_tlv_cmd_hdr(WMI_TAG_HALPHY_CTRL_PATH_CMD_FIXED_PARAM,
+ sizeof(*cmd));
+
+ cmd->stats_id_mask = cpu_to_le32(WMI_REQ_CTRL_PATH_PDEV_TX_STAT);
+ cmd->action = cpu_to_le32(WMI_REQUEST_CTRL_PATH_STAT_GET);
+ cmd->subid = cpu_to_le32(tpc_stats_type);
+
+ ptr = skb->data + sizeof(*cmd);
+
+ /* The below TLV arrays optionally follow this fixed param TLV structure
+ * 1. ARRAY_UINT32 pdev_ids[]
+ * If this array is present and non-zero length, stats should only
+ * be provided from the pdevs identified in the array.
+ * 2. ARRAY_UNIT32 vdev_ids[]
+ * If this array is present and non-zero length, stats should only
+ * be provided from the vdevs identified in the array.
+ * 3. ath12k_wmi_mac_addr_params peer_macaddr[];
+ * If this array is present and non-zero length, stats should only
+ * be provided from the peers with the MAC addresses specified
+ * in the array
+ */
+ tlv = ptr;
+ tlv->header = ath12k_wmi_tlv_hdr(WMI_TAG_ARRAY_UINT32, sizeof(u32));
+ ptr += TLV_HDR_SIZE;
+
+ pdev_id = ptr;
+ *pdev_id = cpu_to_le32(ath12k_mac_get_target_pdev_id(ar));
+ ptr += sizeof(*pdev_id);
+
+ tlv = ptr;
+ tlv->header = ath12k_wmi_tlv_hdr(WMI_TAG_ARRAY_UINT32, 0);
+ ptr += TLV_HDR_SIZE;
+
+ tlv = ptr;
+ tlv->header = ath12k_wmi_tlv_hdr(WMI_TAG_ARRAY_FIXED_STRUCT, 0);
+ ptr += TLV_HDR_SIZE;
+
+ ret = ath12k_wmi_cmd_send(wmi, skb, WMI_REQUEST_HALPHY_CTRL_PATH_STATS_CMDID);
+ if (ret) {
+ ath12k_warn(ar->ab,
+ "failed to submit WMI_REQUEST_STATS_CTRL_PATH_CMDID\n");
+ dev_kfree_skb(skb);
+ return ret;
+ }
+ ath12k_dbg(ar->ab, ATH12K_DBG_WMI, "WMI get TPC STATS sent on pdev %d\n",
+ ar->pdev->pdev_id);
+
+ return ret;
+}
+
int ath12k_wmi_connect(struct ath12k_base *ab)
{
u32 i;
@@ -8501,3 +9845,69 @@ int ath12k_wmi_mlo_teardown(struct ath12k *ar)
return 0;
}
+
+bool ath12k_wmi_supports_6ghz_cc_ext(struct ath12k *ar)
+{
+ return test_bit(WMI_TLV_SERVICE_REG_CC_EXT_EVENT_SUPPORT,
+ ar->ab->wmi_ab.svc_map) && ar->supports_6ghz;
+}
+
+int ath12k_wmi_send_vdev_set_tpc_power(struct ath12k *ar,
+ u32 vdev_id,
+ struct ath12k_reg_tpc_power_info *param)
+{
+ struct wmi_vdev_set_tpc_power_cmd *cmd;
+ struct ath12k_wmi_pdev *wmi = ar->wmi;
+ struct wmi_vdev_ch_power_params *ch;
+ int i, ret, len, array_len;
+ struct sk_buff *skb;
+ struct wmi_tlv *tlv;
+ u8 *ptr;
+
+ array_len = sizeof(*ch) * param->num_pwr_levels;
+ len = sizeof(*cmd) + TLV_HDR_SIZE + array_len;
+
+ skb = ath12k_wmi_alloc_skb(wmi->wmi_ab, len);
+ if (!skb)
+ return -ENOMEM;
+
+ ptr = skb->data;
+
+ cmd = (struct wmi_vdev_set_tpc_power_cmd *)ptr;
+ cmd->tlv_header = ath12k_wmi_tlv_cmd_hdr(WMI_TAG_VDEV_SET_TPC_POWER_CMD,
+ sizeof(*cmd));
+ cmd->vdev_id = cpu_to_le32(vdev_id);
+ cmd->psd_power = cpu_to_le32(param->is_psd_power);
+ cmd->eirp_power = cpu_to_le32(param->eirp_power);
+ cmd->power_type_6ghz = cpu_to_le32(param->ap_power_type);
+
+ ath12k_dbg(ar->ab, ATH12K_DBG_WMI,
+ "tpc vdev id %d is psd power %d eirp power %d 6 ghz power type %d\n",
+ vdev_id, param->is_psd_power, param->eirp_power, param->ap_power_type);
+
+ ptr += sizeof(*cmd);
+ tlv = (struct wmi_tlv *)ptr;
+ tlv->header = ath12k_wmi_tlv_hdr(WMI_TAG_ARRAY_STRUCT, array_len);
+
+ ptr += TLV_HDR_SIZE;
+ ch = (struct wmi_vdev_ch_power_params *)ptr;
+
+ for (i = 0; i < param->num_pwr_levels; i++, ch++) {
+ ch->tlv_header = ath12k_wmi_tlv_cmd_hdr(WMI_TAG_VDEV_CH_POWER_INFO,
+ sizeof(*ch));
+ ch->chan_cfreq = cpu_to_le32(param->chan_power_info[i].chan_cfreq);
+ ch->tx_power = cpu_to_le32(param->chan_power_info[i].tx_power);
+
+ ath12k_dbg(ar->ab, ATH12K_DBG_WMI, "tpc chan freq %d TX power %d\n",
+ ch->chan_cfreq, ch->tx_power);
+ }
+
+ ret = ath12k_wmi_cmd_send(wmi, skb, WMI_VDEV_SET_TPC_POWER_CMDID);
+ if (ret) {
+ ath12k_warn(ar->ab, "failed to send WMI_VDEV_SET_TPC_POWER_CMDID\n");
+ dev_kfree_skb(skb);
+ return ret;
+ }
+
+ return 0;
+}
diff --git a/drivers/net/wireless/ath/ath12k/wmi.h b/drivers/net/wireless/ath/ath12k/wmi.h
index 45fe699ce8a5..ac18f75e0449 100644
--- a/drivers/net/wireless/ath/ath12k/wmi.h
+++ b/drivers/net/wireless/ath/ath12k/wmi.h
@@ -1,7 +1,7 @@
/* SPDX-License-Identifier: BSD-3-Clause-Clear */
/*
* Copyright (c) 2018-2021 The Linux Foundation. All rights reserved.
- * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#ifndef ATH12K_WMI_H
@@ -25,6 +25,8 @@
struct ath12k_base;
struct ath12k;
struct ath12k_link_vif;
+struct ath12k_fw_stats;
+struct ath12k_reg_tpc_power_info;
/* There is no signed version of __le32, so for a temporary solution come
* up with our own version. The idea is from fs/ntfs/endian.h.
@@ -215,9 +217,9 @@ enum wmi_host_hw_mode_priority {
};
enum WMI_HOST_WLAN_BAND {
- WMI_HOST_WLAN_2G_CAP = 1,
- WMI_HOST_WLAN_5G_CAP = 2,
- WMI_HOST_WLAN_2G_5G_CAP = 3,
+ WMI_HOST_WLAN_2GHZ_CAP = 1,
+ WMI_HOST_WLAN_5GHZ_CAP = 2,
+ WMI_HOST_WLAN_2GHZ_5GHZ_CAP = 3,
};
enum wmi_cmd_group {
@@ -385,6 +387,22 @@ enum wmi_tlv_cmd_id {
WMI_VDEV_SET_CUSTOM_AGGR_SIZE_CMDID,
WMI_VDEV_ENCRYPT_DECRYPT_DATA_REQ_CMDID,
WMI_VDEV_ADD_MAC_ADDR_TO_RX_FILTER_CMDID,
+ WMI_VDEV_SET_ARP_STAT_CMDID,
+ WMI_VDEV_GET_ARP_STAT_CMDID,
+ WMI_VDEV_GET_TX_POWER_CMDID,
+ WMI_VDEV_LIMIT_OFFCHAN_CMDID,
+ WMI_VDEV_SET_CUSTOM_SW_RETRY_TH_CMDID,
+ WMI_VDEV_CHAINMASK_CONFIG_CMDID,
+ WMI_VDEV_GET_BCN_RECEPTION_STATS_CMDID,
+ WMI_VDEV_GET_MWS_COEX_INFO_CMDID,
+ WMI_VDEV_DELETE_ALL_PEER_CMDID,
+ WMI_VDEV_BSS_MAX_IDLE_TIME_CMDID,
+ WMI_VDEV_AUDIO_SYNC_TRIGGER_CMDID,
+ WMI_VDEV_AUDIO_SYNC_QTIMER_CMDID,
+ WMI_VDEV_SET_PCL_CMDID,
+ WMI_VDEV_GET_BIG_DATA_CMDID,
+ WMI_VDEV_GET_BIG_DATA_P2_CMDID,
+ WMI_VDEV_SET_TPC_POWER_CMDID,
WMI_PEER_CREATE_CMDID = WMI_TLV_CMD(WMI_GRP_PEER),
WMI_PEER_DELETE_CMDID,
WMI_PEER_FLUSH_TIDS_CMDID,
@@ -516,6 +534,9 @@ enum wmi_tlv_cmd_id {
WMI_REQUEST_RCPI_CMDID,
WMI_REQUEST_PEER_STATS_INFO_CMDID,
WMI_REQUEST_RADIO_CHAN_STATS_CMDID,
+ WMI_REQUEST_WLM_STATS_CMDID,
+ WMI_REQUEST_CTRL_PATH_STATS_CMDID,
+ WMI_REQUEST_HALPHY_CTRL_PATH_STATS_CMDID = WMI_REQUEST_CTRL_PATH_STATS_CMDID + 3,
WMI_SET_ARP_NS_OFFLOAD_CMDID = WMI_TLV_CMD(WMI_GRP_ARP_NS_OFL),
WMI_ADD_PROACTIVE_ARP_RSP_PATTERN_CMDID,
WMI_DEL_PROACTIVE_ARP_RSP_PATTERN_CMDID,
@@ -785,6 +806,9 @@ enum wmi_tlv_event_id {
WMI_UPDATE_RCPI_EVENTID,
WMI_PEER_STATS_INFO_EVENTID,
WMI_RADIO_CHAN_STATS_EVENTID,
+ WMI_WLM_STATS_EVENTID,
+ WMI_CTRL_PATH_STATS_EVENTID,
+ WMI_HALPHY_STATS_CTRL_PATH_EVENTID,
WMI_NLO_MATCH_EVENTID = WMI_TLV_CMD(WMI_GRP_NLO_OFL),
WMI_NLO_SCAN_COMPLETE_EVENTID,
WMI_APFIND_EVENTID,
@@ -1191,6 +1215,7 @@ enum wmi_tlv_tag {
WMI_TAG_ARRAY_BYTE,
WMI_TAG_ARRAY_STRUCT,
WMI_TAG_ARRAY_FIXED_STRUCT,
+ WMI_TAG_ARRAY_INT16,
WMI_TAG_LAST_ARRAY_ENUM = 31,
WMI_TAG_SERVICE_READY_EVENT,
WMI_TAG_HAL_REG_CAPABILITIES,
@@ -1941,6 +1966,14 @@ enum wmi_tlv_tag {
WMI_TAG_MAC_PHY_CAPABILITIES_EXT = 0x36F,
WMI_TAG_REGULATORY_RULE_EXT_STRUCT = 0x3A9,
WMI_TAG_REG_CHAN_LIST_CC_EXT_EVENT,
+ WMI_TAG_TPC_STATS_GET_CMD = 0x38B,
+ WMI_TAG_TPC_STATS_EVENT_FIXED_PARAM,
+ WMI_TAG_TPC_STATS_CONFIG_EVENT,
+ WMI_TAG_TPC_STATS_REG_PWR_ALLOWED,
+ WMI_TAG_TPC_STATS_RATES_ARRAY,
+ WMI_TAG_TPC_STATS_CTL_PWR_TABLE_EVENT,
+ WMI_TAG_VDEV_SET_TPC_POWER_CMD = 0x3B5,
+ WMI_TAG_VDEV_CH_POWER_INFO,
WMI_TAG_EHT_RATE_SET = 0x3C4,
WMI_TAG_DCS_AWGN_INT_TYPE = 0x3C5,
WMI_TAG_MLO_TX_SEND_PARAMS,
@@ -1958,6 +1991,8 @@ enum wmi_tlv_tag {
WMI_TAG_PDEV_SET_BIOS_SAR_TABLE_CMD = 0x3D8,
WMI_TAG_PDEV_SET_BIOS_GEO_TABLE_CMD = 0x3D9,
WMI_TAG_PDEV_SET_BIOS_INTERFACE_CMD = 0x3FB,
+ WMI_TAG_HALPHY_CTRL_PATH_CMD_FIXED_PARAM = 0x442,
+ WMI_TAG_HALPHY_CTRL_PATH_EVENT_FIXED_PARAM,
WMI_TAG_MAX
};
@@ -2185,6 +2220,8 @@ enum wmi_tlv_service {
WMI_MAX_EXT_SERVICE = 256,
+ WMI_TLV_SERVICE_EXT_TPC_REG_SUPPORT = 280,
+
WMI_TLV_SERVICE_REG_CC_EXT_EVENT_SUPPORT = 281,
WMI_TLV_SERVICE_11BE = 289,
@@ -2445,6 +2482,7 @@ struct wmi_init_cmd {
} __packed;
#define WMI_RSRC_CFG_HOST_SVC_FLAG_REG_CC_EXT_SUPPORT_BIT 4
+#define WMI_RSRC_CFG_HOST_SVC_FLAG_REO_QREF_SUPPORT_BIT 12
#define WMI_RSRC_CFG_FLAGS2_RX_PEER_METADATA_VERSION GENMASK(5, 4)
#define WMI_RSRC_CFG_FLAG1_BSS_CHANNEL_INFO_64 BIT(5)
#define WMI_RSRC_CFG_FLAGS2_CALC_NEXT_DTIM_COUNT_SET BIT(9)
@@ -2674,8 +2712,8 @@ enum wmi_channel_width {
* 2 - index for 160 MHz, first 3 bytes valid
* 3 - index for 320 MHz, first 3 bytes valid
*/
-#define WMI_MAX_EHT_SUPP_MCS_2G_SIZE 2
-#define WMI_MAX_EHT_SUPP_MCS_5G_SIZE 4
+#define WMI_MAX_EHT_SUPP_MCS_2GHZ_SIZE 2
+#define WMI_MAX_EHT_SUPP_MCS_5GHZ_SIZE 4
#define WMI_EHTCAP_TXRX_MCS_NSS_IDX_80 0
#define WMI_EHTCAP_TXRX_MCS_NSS_IDX_160 1
@@ -2714,8 +2752,8 @@ struct ath12k_wmi_caps_ext_params {
struct ath12k_wmi_ppe_threshold_params eht_ppet_2ghz;
struct ath12k_wmi_ppe_threshold_params eht_ppet_5ghz;
__le32 eht_cap_info_internal;
- __le32 eht_supp_mcs_ext_2ghz[WMI_MAX_EHT_SUPP_MCS_2G_SIZE];
- __le32 eht_supp_mcs_ext_5ghz[WMI_MAX_EHT_SUPP_MCS_5G_SIZE];
+ __le32 eht_supp_mcs_ext_2ghz[WMI_MAX_EHT_SUPP_MCS_2GHZ_SIZE];
+ __le32 eht_supp_mcs_ext_5ghz[WMI_MAX_EHT_SUPP_MCS_5GHZ_SIZE];
__le32 eml_capability;
__le32 mld_capability;
} __packed;
@@ -3624,6 +3662,26 @@ struct ath12k_wmi_p2p_noa_info {
struct ath12k_wmi_p2p_noa_descriptor descriptors[WMI_P2P_MAX_NOA_DESCRIPTORS];
} __packed;
+#define MAX_WMI_UTF_LEN 252
+
+struct ath12k_wmi_ftm_seg_hdr_params {
+ __le32 len;
+ __le32 msgref;
+ __le32 segmentinfo;
+ __le32 pdev_id;
+} __packed;
+
+struct ath12k_wmi_ftm_cmd {
+ __le32 tlv_header;
+ struct ath12k_wmi_ftm_seg_hdr_params seg_hdr;
+ u8 data[];
+} __packed;
+
+struct ath12k_wmi_ftm_event {
+ struct ath12k_wmi_ftm_seg_hdr_params seg_hdr;
+ u8 data[];
+} __packed;
+
#define WMI_BEACON_TX_BUFFER_SIZE 512
#define WMI_EMA_BEACON_CNT GENMASK(7, 0)
@@ -3718,6 +3776,7 @@ struct peer_assoc_mlo_params {
u32 ieee_link_id;
u8 num_partner_links;
struct wmi_ml_partner_info partner_info[ATH12K_WMI_MLO_MAX_LINKS];
+ u16 eml_cap;
};
struct wmi_rate_set_arg {
@@ -3796,6 +3855,7 @@ struct ath12k_wmi_peer_assoc_arg {
u32 punct_bitmap;
bool is_assoc;
struct peer_assoc_mlo_params ml;
+ bool eht_disable_mcs15;
};
#define ATH12K_WMI_FLAG_MLO_ENABLED BIT(0)
@@ -3990,6 +4050,28 @@ struct wmi_init_country_cmd {
} cc_info;
} __packed;
+struct wmi_11d_scan_start_arg {
+ u32 vdev_id;
+ u32 scan_period_msec;
+ u32 start_interval_msec;
+};
+
+struct wmi_11d_scan_start_cmd {
+ __le32 tlv_header;
+ __le32 vdev_id;
+ __le32 scan_period_msec;
+ __le32 start_interval_msec;
+} __packed;
+
+struct wmi_11d_scan_stop_cmd {
+ __le32 tlv_header;
+ __le32 vdev_id;
+} __packed;
+
+struct wmi_11d_new_cc_event {
+ __le32 new_alpha2;
+} __packed;
+
struct wmi_delba_send_cmd {
__le32 tlv_header;
__le32 vdev_id;
@@ -4072,7 +4154,17 @@ struct ath12k_wmi_eht_rate_set_params {
#define MAX_REG_RULES 10
#define REG_ALPHA2_LEN 2
-#define MAX_6G_REG_RULES 5
+#define MAX_6GHZ_REG_RULES 5
+
+struct wmi_set_current_country_arg {
+ u8 alpha2[REG_ALPHA2_LEN];
+};
+
+struct wmi_set_current_country_cmd {
+ __le32 tlv_header;
+ __le32 pdev_id;
+ __le32 new_alpha2;
+} __packed;
enum wmi_start_event_param {
WMI_VDEV_START_RESP_EVENT = 0,
@@ -4093,6 +4185,7 @@ struct wmi_vdev_start_resp_event {
};
__le32 cfgd_tx_streams;
__le32 cfgd_rx_streams;
+ __le32 max_allowed_tx_power;
} __packed;
/* VDEV start response status codes */
@@ -4438,6 +4531,7 @@ struct ath12k_wmi_target_cap_arg {
};
enum wmi_vdev_type {
+ WMI_VDEV_TYPE_UNSPEC = 0,
WMI_VDEV_TYPE_AP = 1,
WMI_VDEV_TYPE_STA = 2,
WMI_VDEV_TYPE_IBSS = 3,
@@ -4604,6 +4698,7 @@ enum wmi_rate_preamble {
WMI_RATE_PREAMBLE_HT,
WMI_RATE_PREAMBLE_VHT,
WMI_RATE_PREAMBLE_HE,
+ WMI_RATE_PREAMBLE_EHT,
};
/**
@@ -5628,6 +5723,280 @@ enum wmi_sta_keepalive_method {
#define WMI_STA_KEEPALIVE_INTERVAL_DEFAULT 30
#define WMI_STA_KEEPALIVE_INTERVAL_DISABLE 0
+struct wmi_stats_event {
+ __le32 stats_id;
+ __le32 num_pdev_stats;
+ __le32 num_vdev_stats;
+ __le32 num_peer_stats;
+ __le32 num_bcnflt_stats;
+ __le32 num_chan_stats;
+ __le32 num_mib_stats;
+ __le32 pdev_id;
+ __le32 num_bcn_stats;
+ __le32 num_peer_extd_stats;
+ __le32 num_peer_extd2_stats;
+} __packed;
+
+enum wmi_stats_id {
+ WMI_REQUEST_PDEV_STAT = BIT(2),
+ WMI_REQUEST_VDEV_STAT = BIT(3),
+ WMI_REQUEST_BCN_STAT = BIT(11),
+};
+
+struct wmi_request_stats_cmd {
+ __le32 tlv_header;
+ __le32 stats_id;
+ __le32 vdev_id;
+ struct ath12k_wmi_mac_addr_params peer_macaddr;
+ __le32 pdev_id;
+} __packed;
+
+#define WLAN_MAX_AC 4
+#define MAX_TX_RATE_VALUES 10
+
+struct wmi_vdev_stats_params {
+ __le32 vdev_id;
+ __le32 beacon_snr;
+ __le32 data_snr;
+ __le32 num_tx_frames[WLAN_MAX_AC];
+ __le32 num_rx_frames;
+ __le32 num_tx_frames_retries[WLAN_MAX_AC];
+ __le32 num_tx_frames_failures[WLAN_MAX_AC];
+ __le32 num_rts_fail;
+ __le32 num_rts_success;
+ __le32 num_rx_err;
+ __le32 num_rx_discard;
+ __le32 num_tx_not_acked;
+ __le32 tx_rate_history[MAX_TX_RATE_VALUES];
+ __le32 beacon_rssi_history[MAX_TX_RATE_VALUES];
+} __packed;
+
+struct ath12k_wmi_bcn_stats_params {
+ __le32 vdev_id;
+ __le32 tx_bcn_succ_cnt;
+ __le32 tx_bcn_outage_cnt;
+} __packed;
+
+struct ath12k_wmi_pdev_base_stats_params {
+ a_sle32 chan_nf;
+ __le32 tx_frame_count; /* Cycles spent transmitting frames */
+ __le32 rx_frame_count; /* Cycles spent receiving frames */
+ __le32 rx_clear_count; /* Total channel busy time, evidently */
+ __le32 cycle_count; /* Total on-channel time */
+ __le32 phy_err_count;
+ __le32 chan_tx_pwr;
+} __packed;
+
+struct ath12k_wmi_pdev_tx_stats_params {
+ a_sle32 comp_queued;
+ a_sle32 comp_delivered;
+ a_sle32 msdu_enqued;
+ a_sle32 mpdu_enqued;
+ a_sle32 wmm_drop;
+ a_sle32 local_enqued;
+ a_sle32 local_freed;
+ a_sle32 hw_queued;
+ a_sle32 hw_reaped;
+ a_sle32 underrun;
+ a_sle32 tx_abort;
+ a_sle32 mpdus_requed;
+ __le32 tx_ko;
+ __le32 data_rc;
+ __le32 self_triggers;
+ __le32 sw_retry_failure;
+ __le32 illgl_rate_phy_err;
+ __le32 pdev_cont_xretry;
+ __le32 pdev_tx_timeout;
+ __le32 pdev_resets;
+ __le32 stateless_tid_alloc_failure;
+ __le32 phy_underrun;
+ __le32 txop_ovf;
+} __packed;
+
+struct ath12k_wmi_pdev_rx_stats_params {
+ a_sle32 mid_ppdu_route_change;
+ a_sle32 status_rcvd;
+ a_sle32 r0_frags;
+ a_sle32 r1_frags;
+ a_sle32 r2_frags;
+ a_sle32 r3_frags;
+ a_sle32 htt_msdus;
+ a_sle32 htt_mpdus;
+ a_sle32 loc_msdus;
+ a_sle32 loc_mpdus;
+ a_sle32 oversize_amsdu;
+ a_sle32 phy_errs;
+ a_sle32 phy_err_drop;
+ a_sle32 mpdu_errs;
+} __packed;
+
+struct ath12k_wmi_pdev_stats_params {
+ struct ath12k_wmi_pdev_base_stats_params base;
+ struct ath12k_wmi_pdev_tx_stats_params tx;
+ struct ath12k_wmi_pdev_rx_stats_params rx;
+} __packed;
+
+struct ath12k_fw_stats_req_params {
+ u32 stats_id;
+ u32 vdev_id;
+ u32 pdev_id;
+};
+
+#define WMI_REQ_CTRL_PATH_PDEV_TX_STAT 1
+#define WMI_REQUEST_CTRL_PATH_STAT_GET 1
+
+#define WMI_TPC_CONFIG BIT(1)
+#define WMI_TPC_REG_PWR_ALLOWED BIT(2)
+#define WMI_TPC_RATES_ARRAY1 BIT(3)
+#define WMI_TPC_RATES_ARRAY2 BIT(4)
+#define WMI_TPC_RATES_DL_OFDMA_ARRAY BIT(5)
+#define WMI_TPC_CTL_PWR_ARRAY BIT(6)
+#define WMI_TPC_CONFIG_PARAM 0x1
+#define ATH12K_TPC_RATE_ARRAY_MU GENMASK(15, 8)
+#define ATH12K_TPC_RATE_ARRAY_SU GENMASK(7, 0)
+#define TPC_STATS_REG_PWR_ALLOWED_TYPE 0
+
+enum wmi_halphy_ctrl_path_stats_id {
+ WMI_HALPHY_PDEV_TX_SU_STATS = 0,
+ WMI_HALPHY_PDEV_TX_SUTXBF_STATS,
+ WMI_HALPHY_PDEV_TX_MU_STATS,
+ WMI_HALPHY_PDEV_TX_MUTXBF_STATS,
+ WMI_HALPHY_PDEV_TX_STATS_MAX,
+};
+
+enum ath12k_wmi_tpc_stats_rates_array {
+ ATH12K_TPC_STATS_RATES_ARRAY1,
+ ATH12K_TPC_STATS_RATES_ARRAY2,
+};
+
+enum ath12k_wmi_tpc_stats_ctl_array {
+ ATH12K_TPC_STATS_CTL_ARRAY,
+ ATH12K_TPC_STATS_CTL_160ARRAY,
+};
+
+enum ath12k_wmi_tpc_stats_events {
+ ATH12K_TPC_STATS_CONFIG_REG_PWR_EVENT,
+ ATH12K_TPC_STATS_RATES_EVENT1,
+ ATH12K_TPC_STATS_RATES_EVENT2,
+ ATH12K_TPC_STATS_CTL_TABLE_EVENT
+};
+
+struct wmi_request_halphy_ctrl_path_stats_cmd_fixed_params {
+ __le32 tlv_header;
+ __le32 stats_id_mask;
+ __le32 request_id;
+ __le32 action;
+ __le32 subid;
+} __packed;
+
+struct ath12k_wmi_pdev_tpc_stats_event_fixed_params {
+ __le32 pdev_id;
+ __le32 end_of_event;
+ __le32 event_count;
+} __packed;
+
+struct wmi_tpc_config_params {
+ __le32 reg_domain;
+ __le32 chan_freq;
+ __le32 phy_mode;
+ __le32 twice_antenna_reduction;
+ __le32 twice_max_reg_power;
+ __le32 twice_antenna_gain;
+ __le32 power_limit;
+ __le32 rate_max;
+ __le32 num_tx_chain;
+ __le32 ctl;
+ __le32 flags;
+ __le32 caps;
+} __packed;
+
+struct wmi_max_reg_power_fixed_params {
+ __le32 reg_power_type;
+ __le32 reg_array_len;
+ __le32 d1;
+ __le32 d2;
+ __le32 d3;
+ __le32 d4;
+} __packed;
+
+struct wmi_max_reg_power_allowed_arg {
+ struct wmi_max_reg_power_fixed_params tpc_reg_pwr;
+ s16 *reg_pwr_array;
+};
+
+struct wmi_tpc_rates_array_fixed_params {
+ __le32 rate_array_type;
+ __le32 rate_array_len;
+} __packed;
+
+struct wmi_tpc_rates_array_arg {
+ struct wmi_tpc_rates_array_fixed_params tpc_rates_array;
+ s16 *rate_array;
+};
+
+struct wmi_tpc_ctl_pwr_fixed_params {
+ __le32 ctl_array_type;
+ __le32 ctl_array_len;
+ __le32 end_of_ctl_pwr;
+ __le32 ctl_pwr_count;
+ __le32 d1;
+ __le32 d2;
+ __le32 d3;
+ __le32 d4;
+} __packed;
+
+struct wmi_tpc_ctl_pwr_table_arg {
+ struct wmi_tpc_ctl_pwr_fixed_params tpc_ctl_pwr;
+ s8 *ctl_pwr_table;
+};
+
+struct wmi_tpc_stats_arg {
+ u32 pdev_id;
+ u32 event_count;
+ u32 end_of_event;
+ u32 tlvs_rcvd;
+ struct wmi_max_reg_power_allowed_arg max_reg_allowed_power;
+ struct wmi_tpc_rates_array_arg rates_array1;
+ struct wmi_tpc_rates_array_arg rates_array2;
+ struct wmi_tpc_config_params tpc_config;
+ struct wmi_tpc_ctl_pwr_table_arg ctl_array;
+};
+
+struct wmi_vdev_ch_power_params {
+ __le32 tlv_header;
+
+ /* Channel center frequency (MHz) */
+ __le32 chan_cfreq;
+
+ /* Unit: dBm, either PSD/EIRP power for this frequency or
+ * incremental for non-PSD BW
+ */
+ __le32 tx_power;
+} __packed;
+
+struct wmi_vdev_set_tpc_power_cmd {
+ __le32 tlv_header;
+ __le32 vdev_id;
+
+ /* Value: 0 or 1, is PSD power or not */
+ __le32 psd_power;
+
+ /* Maximum EIRP power (dBm units), valid only if power is PSD */
+ __le32 eirp_power;
+
+ /* Type: WMI_6GHZ_REG_TYPE, used for halphy CTL lookup */
+ __le32 power_type_6ghz;
+
+ /* This fixed_param TLV is followed by the below TLVs:
+ * num_pwr_levels of wmi_vdev_ch_power_info
+ * For PSD power, it is the PSD/EIRP power of the frequency (20 MHz chunks).
+ * For non-PSD power, the power values are for 20, 40, and till
+ * BSS BW power levels.
+ * The num_pwr_levels will be checked by sw how many elements present
+ * in the variable-length array.
+ */
+} __packed;
+
void ath12k_wmi_init_qcn9274(struct ath12k_base *ab,
struct ath12k_wmi_resource_config_arg *config);
void ath12k_wmi_init_wcn7850(struct ath12k_base *ab,
@@ -5639,7 +6008,7 @@ int ath12k_wmi_mgmt_send(struct ath12k *ar, u32 vdev_id, u32 buf_id,
struct sk_buff *frame);
int ath12k_wmi_p2p_go_bcn_ie(struct ath12k *ar, u32 vdev_id,
const u8 *p2p_ie);
-int ath12k_wmi_bcn_tmpl(struct ath12k *ar, u32 vdev_id,
+int ath12k_wmi_bcn_tmpl(struct ath12k_link_vif *arvif,
struct ieee80211_mutable_offsets *offs,
struct sk_buff *bcn,
struct ath12k_wmi_bcn_tmpl_ema_arg *ema_args);
@@ -5714,11 +6083,17 @@ int ath12k_wmi_send_bcn_offload_control_cmd(struct ath12k *ar,
u32 vdev_id, u32 bcn_ctrl_op);
int ath12k_wmi_send_init_country_cmd(struct ath12k *ar,
struct ath12k_wmi_init_country_arg *arg);
+int
+ath12k_wmi_send_set_current_country_cmd(struct ath12k *ar,
+ struct wmi_set_current_country_arg *arg);
int ath12k_wmi_peer_rx_reorder_queue_setup(struct ath12k *ar,
int vdev_id, const u8 *addr,
dma_addr_t paddr, u8 tid,
u8 ba_window_size_valid,
u32 ba_window_size);
+int ath12k_wmi_send_11d_scan_start_cmd(struct ath12k *ar,
+ struct wmi_11d_scan_start_arg *arg);
+int ath12k_wmi_send_11d_scan_stop_cmd(struct ath12k *ar, u32 vdev_id);
int
ath12k_wmi_rx_reord_queue_remove(struct ath12k *ar,
struct ath12k_wmi_rx_reorder_queue_remove_arg *arg);
@@ -5753,6 +6128,13 @@ int ath12k_wmi_set_bios_cmd(struct ath12k_base *ab, u32 param_id,
const u8 *buf, size_t buf_len);
int ath12k_wmi_set_bios_sar_cmd(struct ath12k_base *ab, const u8 *psar_table);
int ath12k_wmi_set_bios_geo_cmd(struct ath12k_base *ab, const u8 *pgeo_table);
+int ath12k_wmi_send_stats_request_cmd(struct ath12k *ar, u32 stats_id,
+ u32 vdev_id, u32 pdev_id);
+__le32 ath12k_wmi_tlv_hdr(u32 cmd, u32 len);
+
+int ath12k_wmi_send_tpc_stats_request(struct ath12k *ar,
+ enum wmi_halphy_ctrl_path_stats_id tpc_stats_type);
+void ath12k_wmi_free_tpc_stats_mem(struct ath12k *ar);
static inline u32
ath12k_wmi_caps_ext_get_pdev_id(const struct ath12k_wmi_caps_ext_params *param)
@@ -5806,5 +6188,12 @@ int ath12k_wmi_sta_keepalive(struct ath12k *ar,
int ath12k_wmi_mlo_setup(struct ath12k *ar, struct wmi_mlo_setup_arg *mlo_params);
int ath12k_wmi_mlo_ready(struct ath12k *ar);
int ath12k_wmi_mlo_teardown(struct ath12k *ar);
+void ath12k_wmi_fw_stats_dump(struct ath12k *ar,
+ struct ath12k_fw_stats *fw_stats, u32 stats_id,
+ char *buf);
+bool ath12k_wmi_supports_6ghz_cc_ext(struct ath12k *ar);
+int ath12k_wmi_send_vdev_set_tpc_power(struct ath12k *ar,
+ u32 vdev_id,
+ struct ath12k_reg_tpc_power_info *param);
#endif
diff --git a/drivers/net/wireless/ath/ath12k/wow.c b/drivers/net/wireless/ath/ath12k/wow.c
index 9e1c0bfd212f..dce9bd0bcaef 100644
--- a/drivers/net/wireless/ath/ath12k/wow.c
+++ b/drivers/net/wireless/ath/ath12k/wow.c
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: BSD-3-Clause-Clear
/*
* Copyright (c) 2020 The Linux Foundation. All rights reserved.
- * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) 2022-2025 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#include <linux/delay.h>
@@ -990,6 +990,7 @@ exit:
case ATH12K_HW_STATE_RESTARTING:
case ATH12K_HW_STATE_RESTARTED:
case ATH12K_HW_STATE_WEDGED:
+ case ATH12K_HW_STATE_TM:
ath12k_warn(ar->ab, "encountered unexpected device state %d on resume, cannot recover\n",
ah->state);
ret = -EIO;
diff --git a/drivers/net/wireless/ath/ath6kl/cfg80211.c b/drivers/net/wireless/ath/ath6kl/cfg80211.c
index 72ce321f2a77..8c2e8081112e 100644
--- a/drivers/net/wireless/ath/ath6kl/cfg80211.c
+++ b/drivers/net/wireless/ath/ath6kl/cfg80211.c
@@ -149,7 +149,7 @@ static bool __ath6kl_cfg80211_sscan_stop(struct ath6kl_vif *vif)
if (!test_and_clear_bit(SCHED_SCANNING, &vif->flags))
return false;
- del_timer_sync(&vif->sched_scan_timer);
+ timer_delete_sync(&vif->sched_scan_timer);
if (ar->state == ATH6KL_STATE_RECOVERY)
return true;
@@ -1200,7 +1200,7 @@ static int ath6kl_cfg80211_add_key(struct wiphy *wiphy, struct net_device *ndev,
if (((vif->auth_mode == WPA_PSK_AUTH) ||
(vif->auth_mode == WPA2_PSK_AUTH)) &&
(key_usage & GROUP_USAGE))
- del_timer(&vif->disconnect_timer);
+ timer_delete(&vif->disconnect_timer);
ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
"%s: index %d, key_len %d, key_type 0x%x, key_usage 0x%x, seq_len %d\n",
@@ -3612,7 +3612,7 @@ void ath6kl_cfg80211_vif_stop(struct ath6kl_vif *vif, bool wmi_ready)
discon_issued = test_bit(CONNECTED, &vif->flags) ||
test_bit(CONNECT_PEND, &vif->flags);
ath6kl_disconnect(vif);
- del_timer(&vif->disconnect_timer);
+ timer_delete(&vif->disconnect_timer);
if (discon_issued)
ath6kl_disconnect_event(vif, DISCONNECT_CMD,
diff --git a/drivers/net/wireless/ath/ath6kl/init.c b/drivers/net/wireless/ath/ath6kl/init.c
index 15f455adb860..9b100ee2ebc3 100644
--- a/drivers/net/wireless/ath/ath6kl/init.c
+++ b/drivers/net/wireless/ath/ath6kl/init.c
@@ -1915,7 +1915,7 @@ void ath6kl_stop_txrx(struct ath6kl *ar)
clear_bit(WMI_READY, &ar->flag);
if (ar->fw_recovery.enable)
- del_timer_sync(&ar->fw_recovery.hb_timer);
+ timer_delete_sync(&ar->fw_recovery.hb_timer);
/*
* After wmi_shudown all WMI events will be dropped. We
diff --git a/drivers/net/wireless/ath/ath6kl/main.c b/drivers/net/wireless/ath/ath6kl/main.c
index 8f9fe23e9755..867089a3c096 100644
--- a/drivers/net/wireless/ath/ath6kl/main.c
+++ b/drivers/net/wireless/ath/ath6kl/main.c
@@ -1027,7 +1027,7 @@ void ath6kl_disconnect_event(struct ath6kl_vif *vif, u8 reason, u8 *bssid,
aggr_reset_state(vif->aggr_cntxt->aggr_conn);
- del_timer(&vif->disconnect_timer);
+ timer_delete(&vif->disconnect_timer);
ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "disconnect reason is %d\n", reason);
diff --git a/drivers/net/wireless/ath/ath6kl/recovery.c b/drivers/net/wireless/ath/ath6kl/recovery.c
index c09e40c9010f..fd2dceb8b63d 100644
--- a/drivers/net/wireless/ath/ath6kl/recovery.c
+++ b/drivers/net/wireless/ath/ath6kl/recovery.c
@@ -25,7 +25,7 @@ static void ath6kl_recovery_work(struct work_struct *work)
ar->state = ATH6KL_STATE_RECOVERY;
- del_timer_sync(&ar->fw_recovery.hb_timer);
+ timer_delete_sync(&ar->fw_recovery.hb_timer);
ath6kl_init_hw_restart(ar);
@@ -119,7 +119,7 @@ void ath6kl_recovery_cleanup(struct ath6kl *ar)
set_bit(RECOVERY_CLEANUP, &ar->flag);
- del_timer_sync(&ar->fw_recovery.hb_timer);
+ timer_delete_sync(&ar->fw_recovery.hb_timer);
cancel_work_sync(&ar->fw_recovery.recovery_work);
}
diff --git a/drivers/net/wireless/ath/ath6kl/txrx.c b/drivers/net/wireless/ath/ath6kl/txrx.c
index 80e66acc5cf6..3a6f0b647e17 100644
--- a/drivers/net/wireless/ath/ath6kl/txrx.c
+++ b/drivers/net/wireless/ath/ath6kl/txrx.c
@@ -1827,7 +1827,7 @@ void aggr_reset_state(struct aggr_info_conn *aggr_conn)
return;
if (aggr_conn->timer_scheduled) {
- del_timer(&aggr_conn->timer);
+ timer_delete(&aggr_conn->timer);
aggr_conn->timer_scheduled = false;
}
diff --git a/drivers/net/wireless/ath/ath9k/ahb.c b/drivers/net/wireless/ath/ath9k/ahb.c
index d4805e02b927..49b7ab26c477 100644
--- a/drivers/net/wireless/ath/ath9k/ahb.c
+++ b/drivers/net/wireless/ath/ath9k/ahb.c
@@ -74,7 +74,6 @@ static int ath_ahb_probe(struct platform_device *pdev)
void __iomem *mem;
struct ath_softc *sc;
struct ieee80211_hw *hw;
- struct resource *res;
const struct platform_device_id *id = platform_get_device_id(pdev);
int irq;
int ret = 0;
@@ -86,16 +85,10 @@ static int ath_ahb_probe(struct platform_device *pdev)
return -EINVAL;
}
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (res == NULL) {
- dev_err(&pdev->dev, "no memory resource found\n");
- return -ENXIO;
- }
-
- mem = devm_ioremap(&pdev->dev, res->start, resource_size(res));
- if (mem == NULL) {
+ mem = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(mem)) {
dev_err(&pdev->dev, "ioremap failed\n");
- return -ENOMEM;
+ return PTR_ERR(mem);
}
irq = platform_get_irq(pdev, 0);
diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h
index a728cc0387df..6e38aa7351e3 100644
--- a/drivers/net/wireless/ath/ath9k/ath9k.h
+++ b/drivers/net/wireless/ath/ath9k/ath9k.h
@@ -274,7 +274,6 @@ struct ath_node {
struct ath_tx_control {
struct ath_txq *txq;
- struct ath_node *an;
struct ieee80211_sta *sta;
u8 paprd;
};
@@ -1018,7 +1017,7 @@ struct ath_softc {
u8 gtt_cnt;
u32 intrstatus;
- u32 rx_active_check_time;
+ unsigned long rx_active_check_time;
u32 rx_active_count;
u16 ps_flags; /* PS_* */
bool ps_enabled;
diff --git a/drivers/net/wireless/ath/ath9k/channel.c b/drivers/net/wireless/ath/ath9k/channel.c
index bae24e3d3168..799be0be24f4 100644
--- a/drivers/net/wireless/ath/ath9k/channel.c
+++ b/drivers/net/wireless/ath/ath9k/channel.c
@@ -1556,7 +1556,7 @@ void ath9k_p2p_ps_timer(void *priv)
struct ath_node *an;
u32 tsf;
- del_timer_sync(&sc->sched.timer);
+ timer_delete_sync(&sc->sched.timer);
ath9k_hw_gen_timer_stop(sc->sc_ah, sc->p2p_ps_timer);
ath_chanctx_event(sc, NULL, ATH_CHANCTX_EVENT_TSF_TIMER);
diff --git a/drivers/net/wireless/ath/ath9k/common-spectral.c b/drivers/net/wireless/ath/ath9k/common-spectral.c
index 628eeec4b82f..300d178830ad 100644
--- a/drivers/net/wireless/ath/ath9k/common-spectral.c
+++ b/drivers/net/wireless/ath/ath9k/common-spectral.c
@@ -628,12 +628,12 @@ int ath_cmn_process_fft(struct ath_spec_scan_priv *spec_priv, struct ieee80211_h
else
RX_STAT_INC(sc, rx_spectral_sample_err);
- memset(sample_buf, 0, SPECTRAL_SAMPLE_MAX_LEN);
-
/* Mix the received bins to the /dev/random
* pool
*/
add_device_randomness(sample_buf, num_bins);
+
+ memset(sample_buf, 0, SPECTRAL_SAMPLE_MAX_LEN);
}
/* Process a normal frame */
diff --git a/drivers/net/wireless/ath/ath9k/gpio.c b/drivers/net/wireless/ath/ath9k/gpio.c
index b457e52dd365..5a26f1d05f04 100644
--- a/drivers/net/wireless/ath/ath9k/gpio.c
+++ b/drivers/net/wireless/ath/ath9k/gpio.c
@@ -305,7 +305,7 @@ void ath9k_btcoex_timer_resume(struct ath_softc *sc)
ath_dbg(ath9k_hw_common(ah), BTCOEX, "Starting btcoex timers\n");
/* make sure duty cycle timer is also stopped when resuming */
- del_timer_sync(&btcoex->no_stomp_timer);
+ timer_delete_sync(&btcoex->no_stomp_timer);
btcoex->bt_priority_cnt = 0;
btcoex->bt_priority_time = jiffies;
@@ -329,15 +329,15 @@ void ath9k_btcoex_timer_pause(struct ath_softc *sc)
ath_dbg(ath9k_hw_common(ah), BTCOEX, "Stopping btcoex timers\n");
- del_timer_sync(&btcoex->period_timer);
- del_timer_sync(&btcoex->no_stomp_timer);
+ timer_delete_sync(&btcoex->period_timer);
+ timer_delete_sync(&btcoex->no_stomp_timer);
}
void ath9k_btcoex_stop_gen_timer(struct ath_softc *sc)
{
struct ath_btcoex *btcoex = &sc->btcoex;
- del_timer_sync(&btcoex->no_stomp_timer);
+ timer_delete_sync(&btcoex->no_stomp_timer);
}
u16 ath9k_btcoex_aggr_limit(struct ath_softc *sc, u32 max_4ms_framelen)
diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_beacon.c b/drivers/net/wireless/ath/ath9k/htc_drv_beacon.c
index 547634f82183..81fa7cbad892 100644
--- a/drivers/net/wireless/ath/ath9k/htc_drv_beacon.c
+++ b/drivers/net/wireless/ath/ath9k/htc_drv_beacon.c
@@ -290,6 +290,9 @@ void ath9k_htc_swba(struct ath9k_htc_priv *priv,
struct ath_common *common = ath9k_hw_common(priv->ah);
int slot;
+ if (!priv->cur_beacon_conf.enable_beacon)
+ return;
+
if (swba->beacon_pending != 0) {
priv->beacon.bmisscnt++;
if (priv->beacon.bmisscnt > BSTUCK_THRESHOLD) {
diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_main.c b/drivers/net/wireless/ath/ath9k/htc_drv_main.c
index 57094bd45d98..19600018e562 100644
--- a/drivers/net/wireless/ath/ath9k/htc_drv_main.c
+++ b/drivers/net/wireless/ath/ath9k/htc_drv_main.c
@@ -198,7 +198,7 @@ void ath9k_htc_reset(struct ath9k_htc_priv *priv)
ath9k_htc_stop_ani(priv);
ieee80211_stop_queues(priv->hw);
- del_timer_sync(&priv->tx.cleanup_timer);
+ timer_delete_sync(&priv->tx.cleanup_timer);
ath9k_htc_tx_drain(priv);
WMI_CMD(WMI_DISABLE_INTR_CMDID);
@@ -260,7 +260,7 @@ static int ath9k_htc_set_channel(struct ath9k_htc_priv *priv,
ath9k_htc_ps_wakeup(priv);
ath9k_htc_stop_ani(priv);
- del_timer_sync(&priv->tx.cleanup_timer);
+ timer_delete_sync(&priv->tx.cleanup_timer);
ath9k_htc_tx_drain(priv);
WMI_CMD(WMI_DISABLE_INTR_CMDID);
@@ -997,7 +997,7 @@ static void ath9k_htc_stop(struct ieee80211_hw *hw, bool suspend)
tasklet_kill(&priv->rx_tasklet);
- del_timer_sync(&priv->tx.cleanup_timer);
+ timer_delete_sync(&priv->tx.cleanup_timer);
ath9k_htc_tx_drain(priv);
ath9k_wmi_event_drain(priv);
diff --git a/drivers/net/wireless/ath/ath9k/init.c b/drivers/net/wireless/ath/ath9k/init.c
index f9e77c4624d9..ee951493e993 100644
--- a/drivers/net/wireless/ath/ath9k/init.c
+++ b/drivers/net/wireless/ath/ath9k/init.c
@@ -647,7 +647,9 @@ static int ath9k_of_init(struct ath_softc *sc)
ah->ah_flags |= AH_NO_EEP_SWAP;
}
- of_get_mac_address(np, common->macaddr);
+ ret = of_get_mac_address(np, common->macaddr);
+ if (ret == -EPROBE_DEFER)
+ return ret;
return 0;
}
@@ -1097,7 +1099,7 @@ static void ath9k_deinit_softc(struct ath_softc *sc)
if (ATH_TXQ_SETUP(sc, i))
ath_tx_cleanupq(sc, &sc->tx.txq[i]);
- del_timer_sync(&sc->sleep_timer);
+ timer_delete_sync(&sc->sleep_timer);
ath9k_hw_deinit(sc->sc_ah);
if (sc->dfs_detector != NULL)
sc->dfs_detector->exit(sc->dfs_detector);
diff --git a/drivers/net/wireless/ath/ath9k/link.c b/drivers/net/wireless/ath/ath9k/link.c
index d078a59d7d3c..7f890997bb53 100644
--- a/drivers/net/wireless/ath/ath9k/link.c
+++ b/drivers/net/wireless/ath/ath9k/link.c
@@ -472,7 +472,7 @@ void ath_stop_ani(struct ath_softc *sc)
struct ath_common *common = ath9k_hw_common(sc->sc_ah);
ath_dbg(common, ANI, "Stopping ANI\n");
- del_timer_sync(&common->ani.timer);
+ timer_delete_sync(&common->ani.timer);
}
void ath_check_ani(struct ath_softc *sc)
diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c
index a70c94564814..92fc5e3d756e 100644
--- a/drivers/net/wireless/ath/ath9k/main.c
+++ b/drivers/net/wireless/ath/ath9k/main.c
@@ -123,7 +123,7 @@ void ath9k_ps_wakeup(struct ath_softc *sc)
if (++sc->ps_usecount != 1)
goto unlock;
- del_timer_sync(&sc->sleep_timer);
+ timer_delete_sync(&sc->sleep_timer);
power_mode = sc->sc_ah->power_mode;
ath9k_hw_setpower(sc->sc_ah, ATH9K_PM_AWAKE);
@@ -2418,7 +2418,7 @@ static void ath9k_cancel_pending_offchannel(struct ath_softc *sc)
ath_dbg(common, CHAN_CTX,
"%s: Aborting RoC\n", __func__);
- del_timer_sync(&sc->offchannel.timer);
+ timer_delete_sync(&sc->offchannel.timer);
if (sc->offchannel.state >= ATH_OFFCHANNEL_ROC_START)
ath_roc_complete(sc, ATH_ROC_COMPLETE_ABORT);
}
@@ -2427,7 +2427,7 @@ static void ath9k_cancel_pending_offchannel(struct ath_softc *sc)
ath_dbg(common, CHAN_CTX,
"%s: Aborting HW scan\n", __func__);
- del_timer_sync(&sc->offchannel.timer);
+ timer_delete_sync(&sc->offchannel.timer);
ath_scan_complete(sc, true);
}
}
@@ -2476,7 +2476,7 @@ static void ath9k_cancel_hw_scan(struct ieee80211_hw *hw,
ath_dbg(common, CHAN_CTX, "Cancel HW scan on vif: %pM\n", vif->addr);
mutex_lock(&sc->mutex);
- del_timer_sync(&sc->offchannel.timer);
+ timer_delete_sync(&sc->offchannel.timer);
ath_scan_complete(sc, true);
mutex_unlock(&sc->mutex);
}
@@ -2526,7 +2526,7 @@ static int ath9k_cancel_remain_on_channel(struct ieee80211_hw *hw,
mutex_lock(&sc->mutex);
ath_dbg(common, CHAN_CTX, "Cancel RoC\n");
- del_timer_sync(&sc->offchannel.timer);
+ timer_delete_sync(&sc->offchannel.timer);
if (sc->offchannel.roc_vif) {
if (sc->offchannel.state >= ATH_OFFCHANNEL_ROC_START)
diff --git a/drivers/net/wireless/ath/ath9k/pci.c b/drivers/net/wireless/ath/ath9k/pci.c
index 1ff53520f0a3..27d4034c814e 100644
--- a/drivers/net/wireless/ath/ath9k/pci.c
+++ b/drivers/net/wireless/ath/ath9k/pci.c
@@ -1029,7 +1029,7 @@ static int ath_pci_suspend(struct device *device)
*/
ath9k_stop_btcoex(sc);
ath9k_hw_disable(sc->sc_ah);
- del_timer_sync(&sc->sleep_timer);
+ timer_delete_sync(&sc->sleep_timer);
ath9k_hw_setpower(sc->sc_ah, ATH9K_PM_FULL_SLEEP);
return 0;
diff --git a/drivers/net/wireless/ath/ath9k/xmit.c b/drivers/net/wireless/ath/ath9k/xmit.c
index db07ce6dbc08..0ac9212e42f7 100644
--- a/drivers/net/wireless/ath/ath9k/xmit.c
+++ b/drivers/net/wireless/ath/ath9k/xmit.c
@@ -2291,19 +2291,10 @@ static int ath_tx_prepare(struct ieee80211_hw *hw, struct sk_buff *skb,
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
struct ieee80211_sta *sta = txctl->sta;
struct ieee80211_vif *vif = info->control.vif;
- struct ath_vif *avp;
struct ath_softc *sc = hw->priv;
int frmlen = skb->len + FCS_LEN;
int padpos, padsize;
- /* NOTE: sta can be NULL according to net/mac80211.h */
- if (sta)
- txctl->an = (struct ath_node *)sta->drv_priv;
- else if (vif && ieee80211_is_data(hdr->frame_control)) {
- avp = (void *)vif->drv_priv;
- txctl->an = &avp->mcast_node;
- }
-
if (info->control.hw_key)
frmlen += info->control.hw_key->icv_len;
diff --git a/drivers/net/wireless/ath/carl9170/fw.c b/drivers/net/wireless/ath/carl9170/fw.c
index 4c1aecd1163c..419f5530f885 100644
--- a/drivers/net/wireless/ath/carl9170/fw.c
+++ b/drivers/net/wireless/ath/carl9170/fw.c
@@ -15,7 +15,7 @@
#include "fwcmd.h"
#include "version.h"
-static const u8 otus_magic[4] = { OTUS_MAGIC };
+static const u8 otus_magic[4] __nonstring = { OTUS_MAGIC };
static const void *carl9170_fw_find_desc(struct ar9170 *ar, const u8 descid[4],
const unsigned int len, const u8 compatible_revision)
diff --git a/drivers/net/wireless/ath/carl9170/tx.c b/drivers/net/wireless/ath/carl9170/tx.c
index 0226c31a6cae..b7717f9e1e9b 100644
--- a/drivers/net/wireless/ath/carl9170/tx.c
+++ b/drivers/net/wireless/ath/carl9170/tx.c
@@ -366,8 +366,7 @@ static void carl9170_tx_shift_bm(struct ar9170 *ar,
if (WARN_ON_ONCE(off >= CARL9170_BAW_BITS))
return;
- if (!bitmap_empty(tid_info->bitmap, off))
- off = find_first_bit(tid_info->bitmap, off);
+ off = min(off, find_first_bit(tid_info->bitmap, off));
tid_info->bsn += off;
tid_info->bsn &= 0x0fff;
diff --git a/drivers/net/wireless/ath/ath11k/testmode_i.h b/drivers/net/wireless/ath/testmode_i.h
index 91b83873d660..980ef2f3f05f 100644
--- a/drivers/net/wireless/ath/ath11k/testmode_i.h
+++ b/drivers/net/wireless/ath/testmode_i.h
@@ -1,59 +1,59 @@
/* SPDX-License-Identifier: BSD-3-Clause-Clear */
/*
* Copyright (c) 2018-2019 The Linux Foundation. All rights reserved.
- * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) 2023-2025 Qualcomm Innovation Center, Inc. All rights reserved.
*/
-/* "API" level of the ath11k testmode interface. Bump it after every
+/* "API" level of the ath testmode interface. Bump it after every
* incompatible interface change.
*/
-#define ATH11K_TESTMODE_VERSION_MAJOR 1
+#define ATH_TESTMODE_VERSION_MAJOR 1
/* Bump this after every _compatible_ interface change, for example
* addition of a new command or an attribute.
*/
-#define ATH11K_TESTMODE_VERSION_MINOR 1
+#define ATH_TESTMODE_VERSION_MINOR 1
-#define ATH11K_TM_DATA_MAX_LEN 5000
-#define ATH11K_FTM_EVENT_MAX_BUF_LENGTH 2048
+#define ATH_TM_DATA_MAX_LEN 5000
+#define ATH_FTM_EVENT_MAX_BUF_LENGTH 2048
-enum ath11k_tm_attr {
- __ATH11K_TM_ATTR_INVALID = 0,
- ATH11K_TM_ATTR_CMD = 1,
- ATH11K_TM_ATTR_DATA = 2,
- ATH11K_TM_ATTR_WMI_CMDID = 3,
- ATH11K_TM_ATTR_VERSION_MAJOR = 4,
- ATH11K_TM_ATTR_VERSION_MINOR = 5,
- ATH11K_TM_ATTR_WMI_OP_VERSION = 6,
+enum ath_tm_attr {
+ __ATH_TM_ATTR_INVALID = 0,
+ ATH_TM_ATTR_CMD = 1,
+ ATH_TM_ATTR_DATA = 2,
+ ATH_TM_ATTR_WMI_CMDID = 3,
+ ATH_TM_ATTR_VERSION_MAJOR = 4,
+ ATH_TM_ATTR_VERSION_MINOR = 5,
+ ATH_TM_ATTR_WMI_OP_VERSION = 6,
/* keep last */
- __ATH11K_TM_ATTR_AFTER_LAST,
- ATH11K_TM_ATTR_MAX = __ATH11K_TM_ATTR_AFTER_LAST - 1,
+ __ATH_TM_ATTR_AFTER_LAST,
+ ATH_TM_ATTR_MAX = __ATH_TM_ATTR_AFTER_LAST - 1,
};
-/* All ath11k testmode interface commands specified in
- * ATH11K_TM_ATTR_CMD
+/* All ath testmode interface commands specified in
+ * ATH_TM_ATTR_CMD
*/
-enum ath11k_tm_cmd {
- /* Returns the supported ath11k testmode interface version in
- * ATH11K_TM_ATTR_VERSION. Always guaranteed to work. User space
+enum ath_tm_cmd {
+ /* Returns the supported ath testmode interface version in
+ * ATH_TM_ATTR_VERSION. Always guaranteed to work. User space
* uses this to verify it's using the correct version of the
* testmode interface
*/
- ATH11K_TM_CMD_GET_VERSION = 0,
+ ATH_TM_CMD_GET_VERSION = 0,
/* The command used to transmit a WMI command to the firmware and
* the event to receive WMI events from the firmware. Without
* struct wmi_cmd_hdr header, only the WMI payload. Command id is
- * provided with ATH11K_TM_ATTR_WMI_CMDID and payload in
- * ATH11K_TM_ATTR_DATA.
+ * provided with ATH_TM_ATTR_WMI_CMDID and payload in
+ * ATH_TM_ATTR_DATA.
*/
- ATH11K_TM_CMD_WMI = 1,
+ ATH_TM_CMD_WMI = 1,
/* Boots the UTF firmware, the netdev interface must be down at the
* time.
*/
- ATH11K_TM_CMD_TESTMODE_START = 2,
+ ATH_TM_CMD_TESTMODE_START = 2,
/* The command used to transmit a FTM WMI command to the firmware
* and the event to receive WMI events from the firmware. The data
@@ -62,5 +62,5 @@ enum ath11k_tm_cmd {
* The data payload size could be large and the driver needs to
* send segmented data to firmware.
*/
- ATH11K_TM_CMD_WMI_FTM = 3,
+ ATH_TM_CMD_WMI_FTM = 3,
};
diff --git a/drivers/net/wireless/ath/wcn36xx/dxe.c b/drivers/net/wireless/ath/wcn36xx/dxe.c
index d405a4c34059..cc2a033e87f5 100644
--- a/drivers/net/wireless/ath/wcn36xx/dxe.c
+++ b/drivers/net/wireless/ath/wcn36xx/dxe.c
@@ -350,7 +350,7 @@ void wcn36xx_dxe_tx_ack_ind(struct wcn36xx *wcn, u32 status)
spin_lock_irqsave(&wcn->dxe_lock, flags);
skb = wcn->tx_ack_skb;
wcn->tx_ack_skb = NULL;
- del_timer(&wcn->tx_ack_timer);
+ timer_delete(&wcn->tx_ack_timer);
spin_unlock_irqrestore(&wcn->dxe_lock, flags);
if (!skb) {
@@ -1055,7 +1055,7 @@ void wcn36xx_dxe_deinit(struct wcn36xx *wcn)
free_irq(wcn->tx_irq, wcn);
free_irq(wcn->rx_irq, wcn);
- del_timer(&wcn->tx_ack_timer);
+ timer_delete(&wcn->tx_ack_timer);
if (wcn->tx_ack_skb) {
ieee80211_tx_status_irqsafe(wcn->hw, wcn->tx_ack_skb);
diff --git a/drivers/net/wireless/ath/wcn36xx/testmode.c b/drivers/net/wireless/ath/wcn36xx/testmode.c
index e5142c052985..d7a2a483cbc4 100644
--- a/drivers/net/wireless/ath/wcn36xx/testmode.c
+++ b/drivers/net/wireless/ath/wcn36xx/testmode.c
@@ -56,7 +56,7 @@ static int wcn36xx_tm_cmd_ptt(struct wcn36xx *wcn, struct ieee80211_vif *vif,
msg = buf;
wcn36xx_dbg(WCN36XX_DBG_TESTMODE,
- "testmode cmd wmi msg_id 0x%04X msg_len %d buf %pK buf_len %d\n",
+ "testmode cmd wmi msg_id 0x%04X msg_len %d buf %p buf_len %d\n",
msg->msg_id, msg->msg_body_length,
buf, buf_len);
diff --git a/drivers/net/wireless/ath/wil6210/cfg80211.c b/drivers/net/wireless/ath/wil6210/cfg80211.c
index a1a0a9223e74..5473c01cbe66 100644
--- a/drivers/net/wireless/ath/wil6210/cfg80211.c
+++ b/drivers/net/wireless/ath/wil6210/cfg80211.c
@@ -1017,7 +1017,7 @@ static int wil_cfg80211_scan(struct wiphy *wiphy,
out_restore:
if (rc) {
- del_timer_sync(&vif->scan_timer);
+ timer_delete_sync(&vif->scan_timer);
if (vif->mid == 0)
wil->radio_wdev = wil->main_ndev->ieee80211_ptr;
vif->scan_request = NULL;
diff --git a/drivers/net/wireless/ath/wil6210/main.c b/drivers/net/wireless/ath/wil6210/main.c
index 94e61dbe94f8..44c24c6c8360 100644
--- a/drivers/net/wireless/ath/wil6210/main.c
+++ b/drivers/net/wireless/ath/wil6210/main.c
@@ -798,7 +798,7 @@ void wil6210_disconnect(struct wil6210_vif *vif, const u8 *bssid,
wil_dbg_misc(wil, "disconnecting\n");
- del_timer_sync(&vif->connect_timer);
+ timer_delete_sync(&vif->connect_timer);
_wil6210_disconnect(vif, bssid, reason_code);
}
@@ -818,7 +818,7 @@ void wil6210_disconnect_complete(struct wil6210_vif *vif, const u8 *bssid,
wil_dbg_misc(wil, "got disconnect\n");
- del_timer_sync(&vif->connect_timer);
+ timer_delete_sync(&vif->connect_timer);
_wil6210_disconnect_complete(vif, bssid, reason_code);
}
@@ -1465,7 +1465,7 @@ void wil_abort_scan(struct wil6210_vif *vif, bool sync)
return;
wil_dbg_misc(wil, "Abort scan_request 0x%p\n", vif->scan_request);
- del_timer_sync(&vif->scan_timer);
+ timer_delete_sync(&vif->scan_timer);
mutex_unlock(&wil->vif_mutex);
rc = wmi_abort_scan(vif);
if (!rc && sync)
diff --git a/drivers/net/wireless/ath/wil6210/netdev.c b/drivers/net/wireless/ath/wil6210/netdev.c
index d5d364683c0e..59884e8e3765 100644
--- a/drivers/net/wireless/ath/wil6210/netdev.c
+++ b/drivers/net/wireless/ath/wil6210/netdev.c
@@ -200,8 +200,8 @@ static void wil_dev_setup(struct net_device *dev)
static void wil_vif_deinit(struct wil6210_vif *vif)
{
- del_timer_sync(&vif->scan_timer);
- del_timer_sync(&vif->p2p.discovery_timer);
+ timer_delete_sync(&vif->scan_timer);
+ timer_delete_sync(&vif->p2p.discovery_timer);
cancel_work_sync(&vif->disconnect_worker);
cancel_work_sync(&vif->p2p.discovery_expired_work);
cancel_work_sync(&vif->p2p.delayed_listen_work);
@@ -533,7 +533,7 @@ void wil_vif_remove(struct wil6210_priv *wil, u8 mid)
mutex_unlock(&wil->vif_mutex);
flush_work(&wil->wmi_event_worker);
- del_timer_sync(&vif->connect_timer);
+ timer_delete_sync(&vif->connect_timer);
cancel_work_sync(&vif->disconnect_worker);
wil_probe_client_flush(vif);
cancel_work_sync(&vif->probe_client_worker);
diff --git a/drivers/net/wireless/ath/wil6210/p2p.c b/drivers/net/wireless/ath/wil6210/p2p.c
index f26bf046d889..f20caf1a3905 100644
--- a/drivers/net/wireless/ath/wil6210/p2p.c
+++ b/drivers/net/wireless/ath/wil6210/p2p.c
@@ -184,7 +184,7 @@ u8 wil_p2p_stop_discovery(struct wil6210_vif *vif)
/* discovery not really started, only pending */
p2p->pending_listen_wdev = NULL;
} else {
- del_timer_sync(&p2p->discovery_timer);
+ timer_delete_sync(&p2p->discovery_timer);
wmi_stop_discovery(vif);
}
p2p->discovery_started = 0;
diff --git a/drivers/net/wireless/ath/wil6210/txrx.h b/drivers/net/wireless/ath/wil6210/txrx.h
index 689f68d89a44..fff8b7f8abc5 100644
--- a/drivers/net/wireless/ath/wil6210/txrx.h
+++ b/drivers/net/wireless/ath/wil6210/txrx.h
@@ -7,6 +7,7 @@
#ifndef WIL6210_TXRX_H
#define WIL6210_TXRX_H
+#include <net/sock.h>
#include "wil6210.h"
#include "txrx_edma.h"
@@ -616,8 +617,7 @@ static inline bool wil_need_txstat(struct sk_buff *skb)
{
const u8 *da = wil_skb_get_da(skb);
- return is_unicast_ether_addr(da) && skb->sk &&
- (skb_shinfo(skb)->tx_flags & SKBTX_WIFI_STATUS);
+ return is_unicast_ether_addr(da) && sk_requests_wifi_status(skb->sk);
}
static inline void wil_consume_skb(struct sk_buff *skb, bool acked)
diff --git a/drivers/net/wireless/ath/wil6210/wmi.c b/drivers/net/wireless/ath/wil6210/wmi.c
index 8ff69dc72fb9..74edd007cd8d 100644
--- a/drivers/net/wireless/ath/wil6210/wmi.c
+++ b/drivers/net/wireless/ath/wil6210/wmi.c
@@ -933,7 +933,7 @@ static void wmi_evt_scan_complete(struct wil6210_vif *vif, int id,
wil_dbg_wmi(wil, "SCAN_COMPLETE(0x%08x)\n", status);
wil_dbg_misc(wil, "Complete scan_request 0x%p aborted %d\n",
vif->scan_request, info.aborted);
- del_timer_sync(&vif->scan_timer);
+ timer_delete_sync(&vif->scan_timer);
cfg80211_scan_done(vif->scan_request, &info);
if (vif->mid == 0)
wil->radio_wdev = wil->main_ndev->ieee80211_ptr;
@@ -1023,7 +1023,7 @@ static void wmi_evt_connect(struct wil6210_vif *vif, int id, void *d, int len)
mutex_unlock(&wil->mutex);
return;
}
- del_timer_sync(&vif->connect_timer);
+ timer_delete_sync(&vif->connect_timer);
} else if ((wdev->iftype == NL80211_IFTYPE_AP) ||
(wdev->iftype == NL80211_IFTYPE_P2P_GO)) {
if (wil->sta[evt->cid].status != wil_sta_unused) {
@@ -1814,7 +1814,7 @@ wmi_evt_reassoc_status(struct wil6210_vif *vif, int id, void *d, int len)
wil->sta[cid].stats.ft_roams++;
ether_addr_copy(wil->sta[cid].addr, vif->bss->bssid);
mutex_unlock(&wil->mutex);
- del_timer_sync(&vif->connect_timer);
+ timer_delete_sync(&vif->connect_timer);
cfg80211_ref_bss(wiphy, vif->bss);
freq = ieee80211_channel_to_frequency(ch, NL80211_BAND_60GHZ);
diff --git a/drivers/net/wireless/atmel/at76c50x-usb.c b/drivers/net/wireless/atmel/at76c50x-usb.c
index 504e05ea30f2..6842c2b02b39 100644
--- a/drivers/net/wireless/atmel/at76c50x-usb.c
+++ b/drivers/net/wireless/atmel/at76c50x-usb.c
@@ -2417,7 +2417,7 @@ static void at76_delete_device(struct at76_priv *priv)
kfree(priv->bulk_out_buffer);
- del_timer_sync(&ledtrig_tx_timer);
+ timer_delete_sync(&ledtrig_tx_timer);
kfree_skb(priv->rx_skb);
@@ -2552,7 +2552,7 @@ static void at76_disconnect(struct usb_interface *interface)
wiphy_info(priv->hw->wiphy, "disconnecting\n");
at76_delete_device(priv);
- usb_put_dev(priv->udev);
+ usb_put_dev(interface_to_usbdev(interface));
dev_info(&interface->dev, "disconnected\n");
}
diff --git a/drivers/net/wireless/broadcom/b43/main.c b/drivers/net/wireless/broadcom/b43/main.c
index 25b4ef9d3c9a..7529afd24aed 100644
--- a/drivers/net/wireless/broadcom/b43/main.c
+++ b/drivers/net/wireless/broadcom/b43/main.c
@@ -2166,7 +2166,7 @@ static void b43_print_fw_helptext(struct b43_wl *wl, bool error)
{
const char text[] =
"You must go to " \
- "https://wireless.wiki.kernel.org/en/users/Drivers/b43#devicefirmware " \
+ "https://wireless.docs.kernel.org/en/latest/en/users/drivers/b43/developers.html#list-of-firmware " \
"and download the correct firmware for this driver version. " \
"Please carefully read all instructions on this website.\n";
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c
index 60eb95fc19a5..6bc107476a2a 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c
@@ -1172,6 +1172,7 @@ static int brcmf_ops_sdio_suspend(struct device *dev)
struct brcmf_bus *bus_if;
struct brcmf_sdio_dev *sdiodev;
mmc_pm_flag_t sdio_flags;
+ bool cap_power_off;
int ret = 0;
func = container_of(dev, struct sdio_func, dev);
@@ -1179,19 +1180,23 @@ static int brcmf_ops_sdio_suspend(struct device *dev)
if (func->num != 1)
return 0;
+ cap_power_off = !!(func->card->host->caps & MMC_CAP_POWER_OFF_CARD);
bus_if = dev_get_drvdata(dev);
sdiodev = bus_if->bus_priv.sdio;
- if (sdiodev->wowl_enabled) {
+ if (sdiodev->wowl_enabled || !cap_power_off) {
brcmf_sdiod_freezer_on(sdiodev);
brcmf_sdio_wd_timer(sdiodev->bus, 0);
sdio_flags = MMC_PM_KEEP_POWER;
- if (sdiodev->settings->bus.sdio.oob_irq_supported)
- enable_irq_wake(sdiodev->settings->bus.sdio.oob_irq_nr);
- else
- sdio_flags |= MMC_PM_WAKE_SDIO_IRQ;
+
+ if (sdiodev->wowl_enabled) {
+ if (sdiodev->settings->bus.sdio.oob_irq_supported)
+ enable_irq_wake(sdiodev->settings->bus.sdio.oob_irq_nr);
+ else
+ sdio_flags |= MMC_PM_WAKE_SDIO_IRQ;
+ }
if (sdio_set_host_pm_flags(sdiodev->func1, sdio_flags))
brcmf_err("Failed to set pm_flags %x\n", sdio_flags);
@@ -1213,18 +1218,19 @@ static int brcmf_ops_sdio_resume(struct device *dev)
struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio;
struct sdio_func *func = container_of(dev, struct sdio_func, dev);
int ret = 0;
+ bool cap_power_off = !!(func->card->host->caps & MMC_CAP_POWER_OFF_CARD);
brcmf_dbg(SDIO, "Enter: F%d\n", func->num);
if (func->num != 2)
return 0;
- if (!sdiodev->wowl_enabled) {
+ if (!sdiodev->wowl_enabled && cap_power_off) {
/* bus was powered off and device removed, probe again */
ret = brcmf_sdiod_probe(sdiodev);
if (ret)
brcmf_err("Failed to probe device on resume\n");
} else {
- if (sdiodev->settings->bus.sdio.oob_irq_supported)
+ if (sdiodev->wowl_enabled && sdiodev->settings->bus.sdio.oob_irq_supported)
disable_irq_wake(sdiodev->settings->bus.sdio.oob_irq_nr);
brcmf_sdiod_freezer_off(sdiodev);
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/btcoex.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/btcoex.c
index 1e8495f50c16..e0de34a3e43a 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/btcoex.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/btcoex.c
@@ -289,7 +289,7 @@ static void brcmf_btcoex_handler(struct work_struct *work)
btci = container_of(work, struct brcmf_btcoex_info, work);
if (btci->timer_on) {
btci->timer_on = false;
- del_timer_sync(&btci->timer);
+ timer_delete_sync(&btci->timer);
}
switch (btci->bt_state) {
@@ -428,7 +428,7 @@ static void brcmf_btcoex_dhcp_end(struct brcmf_btcoex_info *btci)
if (btci->timer_on) {
brcmf_dbg(INFO, "disable BT DHCP Timer\n");
btci->timer_on = false;
- del_timer_sync(&btci->timer);
+ timer_delete_sync(&btci->timer);
/* schedule worker if transition to IDLE is needed */
if (btci->bt_state != BRCMF_BT_DHCP_IDLE) {
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
index 4b70845e1a26..dc2383faddd1 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
@@ -74,7 +74,6 @@
#define VNDR_IE_HDR_SIZE 12
#define VNDR_IE_PARSE_LIMIT 5
-#define DOT11_MGMT_HDR_LEN 24 /* d11 management header len */
#define DOT11_BCN_PRB_FIXED_LEN 12 /* beacon/probe fixed length */
#define BRCMF_SCAN_JOIN_ACTIVE_DWELL_TIME_MS 320
@@ -1945,17 +1944,22 @@ static s32 brcmf_set_wpa_version(struct net_device *ndev,
struct brcmf_cfg80211_profile *profile = ndev_to_prof(ndev);
struct brcmf_pub *drvr = ifp->drvr;
struct brcmf_cfg80211_security *sec;
- s32 val = 0;
- s32 err = 0;
+ s32 val;
+ s32 err;
- if (sme->crypto.wpa_versions & NL80211_WPA_VERSION_1)
+ if (sme->crypto.wpa_versions & NL80211_WPA_VERSION_1) {
val = WPA_AUTH_PSK | WPA_AUTH_UNSPECIFIED;
- else if (sme->crypto.wpa_versions & NL80211_WPA_VERSION_2)
- val = WPA2_AUTH_PSK | WPA2_AUTH_UNSPECIFIED;
- else if (sme->crypto.wpa_versions & NL80211_WPA_VERSION_3)
+ } else if (sme->crypto.wpa_versions & NL80211_WPA_VERSION_2) {
+ if (drvr->bus_if->fwvid == BRCMF_FWVENDOR_CYW &&
+ sme->crypto.akm_suites[0] == WLAN_AKM_SUITE_SAE)
+ val = WPA3_AUTH_SAE_PSK;
+ else
+ val = WPA2_AUTH_PSK | WPA2_AUTH_UNSPECIFIED;
+ } else if (sme->crypto.wpa_versions & NL80211_WPA_VERSION_3) {
val = WPA3_AUTH_SAE_PSK;
- else
+ } else {
val = WPA_AUTH_DISABLED;
+ }
brcmf_dbg(CONN, "setting wpa_auth to 0x%0x\n", val);
err = brcmf_fil_bsscfg_int_set(ifp, "wpa_auth", val);
if (err) {
@@ -2163,28 +2167,25 @@ brcmf_set_key_mgmt(struct net_device *ndev, struct cfg80211_connect_params *sme)
switch (sme->crypto.akm_suites[0]) {
case WLAN_AKM_SUITE_SAE:
val = WPA3_AUTH_SAE_PSK;
- if (sme->crypto.sae_pwd) {
- brcmf_dbg(INFO, "using SAE offload\n");
- profile->use_fwsup = BRCMF_PROFILE_FWSUP_SAE;
- }
break;
case WLAN_AKM_SUITE_FT_OVER_SAE:
val = WPA3_AUTH_SAE_PSK | WPA2_AUTH_FT;
profile->is_ft = true;
- if (sme->crypto.sae_pwd) {
- brcmf_dbg(INFO, "using SAE offload\n");
- profile->use_fwsup = BRCMF_PROFILE_FWSUP_SAE;
- }
break;
default:
bphy_err(drvr, "invalid akm suite (%d)\n",
sme->crypto.akm_suites[0]);
return -EINVAL;
}
+ if (sme->crypto.sae_pwd) {
+ profile->use_fwsup = BRCMF_PROFILE_FWSUP_SAE;
+ }
}
if (profile->use_fwsup == BRCMF_PROFILE_FWSUP_1X)
brcmf_dbg(INFO, "using 1X offload\n");
+ if (profile->use_fwsup == BRCMF_PROFILE_FWSUP_SAE)
+ brcmf_dbg(INFO, "using SAE offload\n");
if (!brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MFP))
goto skip_mfp_config;
@@ -2221,7 +2222,7 @@ brcmf_set_key_mgmt(struct net_device *ndev, struct cfg80211_connect_params *sme)
brcmf_fil_bsscfg_int_set(netdev_priv(ndev), "mfp", mfp);
skip_mfp_config:
- brcmf_dbg(CONN, "setting wpa_auth to %d\n", val);
+ brcmf_dbg(CONN, "setting wpa_auth to 0x%0x\n", val);
err = brcmf_fil_bsscfg_int_set(netdev_priv(ndev), "wpa_auth", val);
if (err) {
bphy_err(drvr, "could not set wpa_auth (%d)\n", err);
@@ -5509,7 +5510,7 @@ brcmf_cfg80211_update_mgmt_frame_registrations(struct wiphy *wiphy,
}
-static int
+int
brcmf_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
struct cfg80211_mgmt_tx_params *params, u64 *cookie)
{
@@ -5616,6 +5617,7 @@ brcmf_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
exit:
return err;
}
+BRCMF_EXPORT_SYMBOL_GPL(brcmf_cfg80211_mgmt_tx);
static int brcmf_cfg80211_set_cqm_rssi_range_config(struct wiphy *wiphy,
struct net_device *ndev,
@@ -6009,6 +6011,7 @@ struct brcmf_cfg80211_vif *brcmf_alloc_vif(struct brcmf_cfg80211_info *cfg,
vif->wdev.wiphy = cfg->wiphy;
vif->wdev.iftype = type;
+ init_completion(&vif->mgmt_tx);
brcmf_init_prof(&vif->profile);
@@ -6760,6 +6763,8 @@ static void brcmf_register_event_handlers(struct brcmf_cfg80211_info *cfg)
brcmf_fweh_register(cfg->pub, BRCMF_E_PSK_SUP,
brcmf_notify_connect_status);
brcmf_fweh_register(cfg->pub, BRCMF_E_RSSI, brcmf_notify_rssi);
+
+ brcmf_fwvid_register_event_handlers(cfg->pub);
}
static void brcmf_deinit_priv_mem(struct brcmf_cfg80211_info *cfg)
@@ -7346,6 +7351,7 @@ brcmf_txrx_stypes[NUM_NL80211_IFTYPES] = {
[NL80211_IFTYPE_STATION] = {
.tx = 0xffff,
.rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
+ BIT(IEEE80211_STYPE_AUTH >> 4) |
BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
},
[NL80211_IFTYPE_P2P_CLIENT] = {
@@ -7653,6 +7659,8 @@ static int brcmf_setup_wiphy(struct wiphy *wiphy, struct brcmf_if *ifp)
wiphy_ext_feature_set(wiphy,
NL80211_EXT_FEATURE_SAE_OFFLOAD_AP);
}
+ if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_SAE_EXT))
+ wiphy->features |= NL80211_FEATURE_SAE;
wiphy->mgmt_stypes = brcmf_txrx_stypes;
wiphy->max_remain_on_channel_duration = 5000;
if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_PNO)) {
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.h
index 2abae8894614..b83485ec7b87 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.h
@@ -13,6 +13,8 @@
#include "fwil_types.h"
#include "p2p.h"
+#define DOT11_MGMT_HDR_LEN 24 /* d11 management header len */
+
#define BRCMF_SCAN_IE_LEN_MAX 2048
#define WL_NUM_SCAN_MAX 10
@@ -142,6 +144,21 @@ enum brcmf_profile_fwauth {
};
/**
+ * enum brcmf_mgmt_tx_status - mgmt frame tx status
+ *
+ * @BRCMF_MGMT_TX_ACK: mgmt frame acked
+ * @BRCMF_MGMT_TX_NOACK: mgmt frame not acked
+ * @BRCMF_MGMT_TX_OFF_CHAN_COMPLETED: off-channel complete
+ * @BRCMF_MGMT_TX_SEND_FRAME: mgmt frame tx is in progres
+ */
+enum brcmf_mgmt_tx_status {
+ BRCMF_MGMT_TX_ACK,
+ BRCMF_MGMT_TX_NOACK,
+ BRCMF_MGMT_TX_OFF_CHAN_COMPLETED,
+ BRCMF_MGMT_TX_SEND_FRAME
+};
+
+/**
* struct brcmf_cfg80211_profile - profile information.
*
* @bssid: bssid of joined/joining ibss.
@@ -211,6 +228,9 @@ struct vif_saved_ie {
* @profile: profile information.
* @sme_state: SME state using enum brcmf_vif_status bits.
* @list: linked list.
+ * @mgmt_tx: completion for management frame transmit.
+ * @mgmt_tx_status: status of last management frame sent to firmware.
+ * @mgmt_tx_id:
* @mgmt_rx_reg: registered rx mgmt frame types.
* @mbss: Multiple BSS type, set if not first AP (not relevant for P2P).
* @cqm_rssi_low: Lower RSSI limit for CQM monitoring
@@ -224,6 +244,9 @@ struct brcmf_cfg80211_vif {
unsigned long sme_state;
struct vif_saved_ie saved_ie;
struct list_head list;
+ struct completion mgmt_tx;
+ unsigned long mgmt_tx_status;
+ u32 mgmt_tx_id;
u16 mgmt_rx_reg;
bool mbss;
int is_11d;
@@ -468,5 +491,7 @@ void brcmf_abort_scanning(struct brcmf_cfg80211_info *cfg);
void brcmf_cfg80211_free_netdev(struct net_device *ndev);
int brcmf_set_wsec(struct brcmf_if *ifp, const u8 *key, u16 key_len, u16 flags);
+int brcmf_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
+ struct cfg80211_mgmt_tx_params *params, u64 *cookie);
#endif /* BRCMFMAC_CFG80211_H */
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c
index cfcf01eb0daa..75f101622db1 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c
@@ -491,6 +491,7 @@ void __brcmf_dbg(u32 level, const char *func, const char *fmt, ...)
trace_brcmf_dbg(level, func, &vaf);
va_end(args);
}
+BRCMF_EXPORT_SYMBOL_GPL(__brcmf_dbg);
#endif
static void brcmf_mp_attach(void)
@@ -561,8 +562,10 @@ struct brcmf_mp_device *brcmf_get_module_param(struct device *dev,
if (!found) {
/* No platform data for this device, try OF and DMI data */
brcmf_dmi_probe(settings, chip, chiprev);
- if (brcmf_of_probe(dev, bus_type, settings) == -EPROBE_DEFER)
+ if (brcmf_of_probe(dev, bus_type, settings) == -EPROBE_DEFER) {
+ kfree(settings);
return ERR_PTR(-EPROBE_DEFER);
+ }
brcmf_acpi_probe(dev, bus_type, settings);
}
return settings;
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c
index 3d63010ae079..04f41c09deca 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c
@@ -1363,6 +1363,8 @@ int brcmf_attach(struct device *dev)
brcmf_fweh_register(drvr, BRCMF_E_PSM_WATCHDOG,
brcmf_psm_watchdog_notify);
+ brcmf_fwvid_get_cfg80211_ops(drvr);
+
ret = brcmf_bus_started(drvr, drvr->ops);
if (ret != 0) {
bphy_err(drvr, "dongle is not responding: err=%d\n", ret);
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cyw/core.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cyw/core.c
index 9a4837881486..c9537fb597ce 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cyw/core.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cyw/core.c
@@ -8,11 +8,21 @@
#include <bus.h>
#include <fwvid.h>
#include <fwil.h>
+#include <fweh.h>
#include "vops.h"
+#include "fwil_types.h"
+/* event definitions */
+#define BRCMF_CYW_E_EXT_AUTH_REQ 187
+#define BRCMF_CYW_E_EXT_AUTH_FRAME_RX 188
+#define BRCMF_CYW_E_MGMT_FRAME_TXS 189
+#define BRCMF_CYW_E_MGMT_FRAME_TXS_OC 190
#define BRCMF_CYW_E_LAST 197
+#define MGMT_AUTH_FRAME_DWELL_TIME 4000
+#define MGMT_AUTH_FRAME_WAIT_TIME (MGMT_AUTH_FRAME_DWELL_TIME + 100)
+
static int brcmf_cyw_set_sae_pwd(struct brcmf_if *ifp,
struct cfg80211_crypto_settings *crypto)
{
@@ -39,6 +49,19 @@ static int brcmf_cyw_set_sae_pwd(struct brcmf_if *ifp,
return err;
}
+static const struct brcmf_fweh_event_map brcmf_cyw_event_map = {
+ .items = {
+ { BRCMF_E_EXT_AUTH_REQ, BRCMF_CYW_E_EXT_AUTH_REQ },
+ { BRCMF_E_EXT_AUTH_FRAME_RX, BRCMF_CYW_E_EXT_AUTH_FRAME_RX },
+ { BRCMF_E_MGMT_FRAME_TXSTATUS, BRCMF_CYW_E_MGMT_FRAME_TXS },
+ {
+ BRCMF_E_MGMT_FRAME_OFFCHAN_DONE,
+ BRCMF_CYW_E_MGMT_FRAME_TXS_OC
+ },
+ },
+ .n_items = 4
+};
+
static int brcmf_cyw_alloc_fweh_info(struct brcmf_pub *drvr)
{
struct brcmf_fweh_info *fweh;
@@ -49,11 +72,296 @@ static int brcmf_cyw_alloc_fweh_info(struct brcmf_pub *drvr)
return -ENOMEM;
fweh->num_event_codes = BRCMF_CYW_E_LAST;
+ fweh->event_map = &brcmf_cyw_event_map;
drvr->fweh = fweh;
return 0;
}
+static int brcmf_cyw_activate_events(struct brcmf_if *ifp)
+{
+ struct brcmf_fweh_info *fweh = ifp->drvr->fweh;
+ struct brcmf_eventmsgs_ext *eventmask_msg;
+ u32 msglen;
+ int err;
+
+ msglen = sizeof(*eventmask_msg) + fweh->event_mask_len;
+ eventmask_msg = kzalloc(msglen, GFP_KERNEL);
+ if (!eventmask_msg)
+ return -ENOMEM;
+ eventmask_msg->ver = EVENTMSGS_VER;
+ eventmask_msg->command = CYW_EVENTMSGS_SET_MASK;
+ eventmask_msg->len = fweh->event_mask_len;
+ memcpy(eventmask_msg->mask, fweh->event_mask, fweh->event_mask_len);
+
+ err = brcmf_fil_iovar_data_set(ifp, "event_msgs_ext", eventmask_msg,
+ msglen);
+ kfree(eventmask_msg);
+ return err;
+}
+
+static
+int brcmf_cyw_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
+ struct cfg80211_mgmt_tx_params *params, u64 *cookie)
+{
+ struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
+ struct ieee80211_channel *chan = params->chan;
+ struct brcmf_pub *drvr = cfg->pub;
+ const u8 *buf = params->buf;
+ size_t len = params->len;
+ const struct ieee80211_mgmt *mgmt;
+ struct brcmf_cfg80211_vif *vif;
+ s32 err = 0;
+ bool ack = false;
+ s32 chan_nr;
+ u32 freq;
+ struct brcmf_mf_params_le *mf_params;
+ u32 mf_params_len;
+ s32 ready;
+
+ brcmf_dbg(TRACE, "Enter\n");
+
+ mgmt = (const struct ieee80211_mgmt *)buf;
+
+ if (!ieee80211_is_auth(mgmt->frame_control))
+ return brcmf_cfg80211_mgmt_tx(wiphy, wdev, params, cookie);
+
+ *cookie = 0;
+ vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev);
+
+ reinit_completion(&vif->mgmt_tx);
+ clear_bit(BRCMF_MGMT_TX_ACK, &vif->mgmt_tx_status);
+ clear_bit(BRCMF_MGMT_TX_NOACK, &vif->mgmt_tx_status);
+ clear_bit(BRCMF_MGMT_TX_OFF_CHAN_COMPLETED,
+ &vif->mgmt_tx_status);
+ mf_params_len = offsetof(struct brcmf_mf_params_le, data) +
+ (len - DOT11_MGMT_HDR_LEN);
+ mf_params = kzalloc(mf_params_len, GFP_KERNEL);
+ if (!mf_params)
+ return -ENOMEM;
+
+ mf_params->dwell_time = cpu_to_le32(MGMT_AUTH_FRAME_DWELL_TIME);
+ mf_params->len = cpu_to_le16(len - DOT11_MGMT_HDR_LEN);
+ mf_params->frame_control = mgmt->frame_control;
+
+ if (chan)
+ freq = chan->center_freq;
+ else
+ brcmf_fil_cmd_int_get(vif->ifp, BRCMF_C_GET_CHANNEL,
+ &freq);
+ chan_nr = ieee80211_frequency_to_channel(freq);
+ mf_params->channel = cpu_to_le16(chan_nr);
+ memcpy(&mf_params->da[0], &mgmt->da[0], ETH_ALEN);
+ memcpy(&mf_params->bssid[0], &mgmt->bssid[0], ETH_ALEN);
+ mf_params->packet_id = cpu_to_le32(*cookie);
+ memcpy(mf_params->data, &buf[DOT11_MGMT_HDR_LEN],
+ le16_to_cpu(mf_params->len));
+
+ brcmf_dbg(TRACE, "Auth frame, cookie=%d, fc=%04x, len=%d, channel=%d\n",
+ le32_to_cpu(mf_params->packet_id),
+ le16_to_cpu(mf_params->frame_control),
+ le16_to_cpu(mf_params->len), chan_nr);
+
+ vif->mgmt_tx_id = le32_to_cpu(mf_params->packet_id);
+ set_bit(BRCMF_MGMT_TX_SEND_FRAME, &vif->mgmt_tx_status);
+
+ err = brcmf_fil_bsscfg_data_set(vif->ifp, "mgmt_frame",
+ mf_params, mf_params_len);
+ if (err) {
+ bphy_err(drvr, "Failed to send Auth frame: err=%d\n",
+ err);
+ goto tx_status;
+ }
+
+ ready = wait_for_completion_timeout(&vif->mgmt_tx,
+ MGMT_AUTH_FRAME_WAIT_TIME);
+ if (test_bit(BRCMF_MGMT_TX_ACK, &vif->mgmt_tx_status)) {
+ brcmf_dbg(TRACE, "TX Auth frame operation is success\n");
+ ack = true;
+ } else {
+ bphy_err(drvr, "TX Auth frame operation is %s: status=%ld)\n",
+ ready ? "failed" : "timedout", vif->mgmt_tx_status);
+ }
+
+tx_status:
+ cfg80211_mgmt_tx_status(wdev, *cookie, buf, len, ack,
+ GFP_KERNEL);
+ kfree(mf_params);
+ return err;
+}
+
+static int
+brcmf_cyw_external_auth(struct wiphy *wiphy, struct net_device *dev,
+ struct cfg80211_external_auth_params *params)
+{
+ struct brcmf_if *ifp;
+ struct brcmf_pub *drvr;
+ struct brcmf_auth_req_status_le auth_status;
+ int ret = 0;
+
+ brcmf_dbg(TRACE, "Enter\n");
+
+ ifp = netdev_priv(dev);
+ drvr = ifp->drvr;
+ if (params->status == WLAN_STATUS_SUCCESS) {
+ auth_status.flags = cpu_to_le16(BRCMF_EXTAUTH_SUCCESS);
+ } else {
+ bphy_err(drvr, "External authentication failed: status=%d\n",
+ params->status);
+ auth_status.flags = cpu_to_le16(BRCMF_EXTAUTH_FAIL);
+ }
+
+ memcpy(auth_status.peer_mac, params->bssid, ETH_ALEN);
+ params->ssid.ssid_len = min_t(u8, params->ssid.ssid_len,
+ IEEE80211_MAX_SSID_LEN);
+ auth_status.ssid_len = cpu_to_le32(params->ssid.ssid_len);
+ memcpy(auth_status.ssid, params->ssid.ssid, params->ssid.ssid_len);
+
+ ret = brcmf_fil_iovar_data_set(ifp, "auth_status", &auth_status,
+ sizeof(auth_status));
+ if (ret < 0)
+ bphy_err(drvr, "auth_status iovar failed: ret=%d\n", ret);
+
+ return ret;
+}
+
+static void brcmf_cyw_get_cfg80211_ops(struct brcmf_pub *drvr)
+{
+ drvr->ops->mgmt_tx = brcmf_cyw_mgmt_tx;
+ drvr->ops->external_auth = brcmf_cyw_external_auth;
+}
+
+static s32
+brcmf_cyw_notify_ext_auth_req(struct brcmf_if *ifp,
+ const struct brcmf_event_msg *e, void *data)
+{
+ struct brcmf_pub *drvr = ifp->drvr;
+ struct cfg80211_external_auth_params params;
+ struct brcmf_auth_req_status_le *auth_req =
+ (struct brcmf_auth_req_status_le *)data;
+ s32 err = 0;
+
+ brcmf_dbg(INFO, "Enter: event %s (%d) received\n",
+ brcmf_fweh_event_name(e->event_code), e->event_code);
+
+ if (e->datalen < sizeof(*auth_req)) {
+ bphy_err(drvr, "Event %s (%d) data too small. Ignore\n",
+ brcmf_fweh_event_name(e->event_code), e->event_code);
+ return -EINVAL;
+ }
+
+ memset(&params, 0, sizeof(params));
+ params.action = NL80211_EXTERNAL_AUTH_START;
+ params.key_mgmt_suite = WLAN_AKM_SUITE_SAE;
+ params.status = WLAN_STATUS_SUCCESS;
+ params.ssid.ssid_len = min_t(u32, 32, le32_to_cpu(auth_req->ssid_len));
+ memcpy(params.ssid.ssid, auth_req->ssid, params.ssid.ssid_len);
+ memcpy(params.bssid, auth_req->peer_mac, ETH_ALEN);
+
+ err = cfg80211_external_auth_request(ifp->ndev, &params, GFP_KERNEL);
+ if (err)
+ bphy_err(drvr, "Ext Auth request to supplicant failed (%d)\n",
+ err);
+
+ return err;
+}
+
+static s32
+brcmf_notify_auth_frame_rx(struct brcmf_if *ifp,
+ const struct brcmf_event_msg *e, void *data)
+{
+ struct brcmf_pub *drvr = ifp->drvr;
+ struct brcmf_cfg80211_info *cfg = drvr->config;
+ struct wireless_dev *wdev;
+ u32 mgmt_frame_len = e->datalen - sizeof(struct brcmf_rx_mgmt_data);
+ struct brcmf_rx_mgmt_data *rxframe = (struct brcmf_rx_mgmt_data *)data;
+ u8 *frame = (u8 *)(rxframe + 1);
+ struct brcmu_chan ch;
+ struct ieee80211_mgmt *mgmt_frame;
+ s32 freq;
+
+ brcmf_dbg(INFO, "Enter: event %s (%d) received\n",
+ brcmf_fweh_event_name(e->event_code), e->event_code);
+
+ if (e->datalen < sizeof(*rxframe)) {
+ bphy_err(drvr, "Event %s (%d) data too small. Ignore\n",
+ brcmf_fweh_event_name(e->event_code), e->event_code);
+ return -EINVAL;
+ }
+
+ wdev = &ifp->vif->wdev;
+ WARN_ON(!wdev);
+
+ ch.chspec = be16_to_cpu(rxframe->chanspec);
+ cfg->d11inf.decchspec(&ch);
+
+ mgmt_frame = kzalloc(mgmt_frame_len, GFP_KERNEL);
+ if (!mgmt_frame)
+ return -ENOMEM;
+
+ mgmt_frame->frame_control = cpu_to_le16(IEEE80211_STYPE_AUTH);
+ memcpy(mgmt_frame->da, ifp->mac_addr, ETH_ALEN);
+ memcpy(mgmt_frame->sa, e->addr, ETH_ALEN);
+ brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_BSSID, mgmt_frame->bssid,
+ ETH_ALEN);
+ frame += offsetof(struct ieee80211_mgmt, u);
+ memcpy(&mgmt_frame->u, frame,
+ mgmt_frame_len - offsetof(struct ieee80211_mgmt, u));
+
+ freq = ieee80211_channel_to_frequency(ch.control_ch_num,
+ ch.band == BRCMU_CHAN_BAND_2G ?
+ NL80211_BAND_2GHZ :
+ NL80211_BAND_5GHZ);
+
+ cfg80211_rx_mgmt(wdev, freq, 0, (u8 *)mgmt_frame, mgmt_frame_len,
+ NL80211_RXMGMT_FLAG_EXTERNAL_AUTH);
+ kfree(mgmt_frame);
+ return 0;
+}
+
+static s32
+brcmf_notify_mgmt_tx_status(struct brcmf_if *ifp,
+ const struct brcmf_event_msg *e, void *data)
+{
+ struct brcmf_cfg80211_vif *vif = ifp->vif;
+ u32 *packet_id = (u32 *)data;
+
+ brcmf_dbg(INFO, "Enter: event %s (%d), status=%d\n",
+ brcmf_fweh_event_name(e->event_code), e->event_code,
+ e->status);
+
+ if (!test_bit(BRCMF_MGMT_TX_SEND_FRAME, &vif->mgmt_tx_status) ||
+ (*packet_id != vif->mgmt_tx_id))
+ return 0;
+
+ if (e->event_code == BRCMF_E_MGMT_FRAME_TXSTATUS) {
+ if (e->status == BRCMF_E_STATUS_SUCCESS)
+ set_bit(BRCMF_MGMT_TX_ACK, &vif->mgmt_tx_status);
+ else
+ set_bit(BRCMF_MGMT_TX_NOACK, &vif->mgmt_tx_status);
+ } else {
+ set_bit(BRCMF_MGMT_TX_OFF_CHAN_COMPLETED, &vif->mgmt_tx_status);
+ }
+
+ complete(&vif->mgmt_tx);
+ return 0;
+}
+
+static void brcmf_cyw_register_event_handlers(struct brcmf_pub *drvr)
+{
+ brcmf_fweh_register(drvr, BRCMF_E_EXT_AUTH_REQ,
+ brcmf_cyw_notify_ext_auth_req);
+ brcmf_fweh_register(drvr, BRCMF_E_EXT_AUTH_FRAME_RX,
+ brcmf_notify_auth_frame_rx);
+ brcmf_fweh_register(drvr, BRCMF_E_MGMT_FRAME_TXSTATUS,
+ brcmf_notify_mgmt_tx_status);
+ brcmf_fweh_register(drvr, BRCMF_E_MGMT_FRAME_OFFCHAN_DONE,
+ brcmf_notify_mgmt_tx_status);
+}
+
const struct brcmf_fwvid_ops brcmf_cyw_ops = {
.set_sae_password = brcmf_cyw_set_sae_pwd,
.alloc_fweh_info = brcmf_cyw_alloc_fweh_info,
+ .activate_events = brcmf_cyw_activate_events,
+ .get_cfg80211_ops = brcmf_cyw_get_cfg80211_ops,
+ .register_event_handlers = brcmf_cyw_register_event_handlers,
};
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cyw/fwil_types.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cyw/fwil_types.h
new file mode 100644
index 000000000000..08c69142495a
--- /dev/null
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cyw/fwil_types.h
@@ -0,0 +1,87 @@
+// SPDX-License-Identifier: ISC
+/*
+ * Copyright (c) 2012 Broadcom Corporation
+ */
+
+#ifndef CYW_FWIL_TYPES_H_
+#define CYW_FWIL_TYPES_H_
+
+#include <fwil_types.h>
+
+enum brcmf_event_msgs_ext_command {
+ CYW_EVENTMSGS_NONE = 0,
+ CYW_EVENTMSGS_SET_BIT = 1,
+ CYW_EVENTMSGS_RESET_BIT = 2,
+ CYW_EVENTMSGS_SET_MASK = 3,
+};
+
+#define EVENTMSGS_VER 1
+#define EVENTMSGS_EXT_STRUCT_SIZE offsetof(struct eventmsgs_ext, mask[0])
+
+/**
+ * struct brcmf_eventmsgs_ext - structure used with "eventmsgs_ext" iovar.
+ *
+ * @ver: version.
+ * @command: requested operation (see &enum event_msgs_ext_command).
+ * @len: length of the @mask array.
+ * @maxgetsize: indicates maximum mask size that may be returned by firmware
+ * upon iovar GET.
+ * @mask: array where each bit represents firmware event.
+ */
+struct brcmf_eventmsgs_ext {
+ u8 ver;
+ u8 command;
+ u8 len;
+ u8 maxgetsize;
+ u8 mask[] __counted_by(len);
+};
+
+#define BRCMF_EXTAUTH_START 1
+#define BRCMF_EXTAUTH_ABORT 2
+#define BRCMF_EXTAUTH_FAIL 3
+#define BRCMF_EXTAUTH_SUCCESS 4
+
+/**
+ * struct brcmf_auth_req_status_le - external auth request and status update
+ *
+ * @flags: flags for external auth status
+ * @peer_mac: peer MAC address
+ * @ssid_len: length of ssid
+ * @ssid: ssid characters
+ * @pmkid: PMKSA identifier
+ */
+struct brcmf_auth_req_status_le {
+ __le16 flags;
+ u8 peer_mac[ETH_ALEN];
+ __le32 ssid_len;
+ u8 ssid[IEEE80211_MAX_SSID_LEN];
+ u8 pmkid[WLAN_PMKID_LEN];
+};
+
+/**
+ * struct brcmf_mf_params_le - management frame parameters for mgmt_frame iovar
+ *
+ * @version: version of the iovar
+ * @dwell_time: dwell duration in ms
+ * @len: length of frame data
+ * @frame_control: frame control
+ * @channel: channel
+ * @da: peer MAC address
+ * @bssid: BSS network identifier
+ * @packet_id: packet identifier
+ * @data: frame data
+ */
+struct brcmf_mf_params_le {
+ __le32 version;
+ __le32 dwell_time;
+ __le16 len;
+ __le16 frame_control;
+ __le16 channel;
+ u8 da[ETH_ALEN];
+ u8 bssid[ETH_ALEN];
+ __le32 packet_id;
+ u8 data[] __counted_by(len);
+};
+
+#endif /* CYW_FWIL_TYPES_H_ */
+
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.c
index 0d9ae197fa1e..488364ef8ff2 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.c
@@ -42,8 +42,9 @@ static const struct brcmf_feat_fwcap brcmf_fwcap_map[] = {
{ BRCMF_FEAT_MONITOR_FLAG, "rtap" },
{ BRCMF_FEAT_MONITOR_FMT_RADIOTAP, "rtap" },
{ BRCMF_FEAT_DOT11H, "802.11h" },
- { BRCMF_FEAT_SAE, "sae" },
+ { BRCMF_FEAT_SAE, "sae " },
{ BRCMF_FEAT_FWAUTH, "idauth" },
+ { BRCMF_FEAT_SAE_EXT, "sae_ext" },
};
#ifdef DEBUG
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.h
index 7f4f0b3e4a7b..31f8695ca417 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.h
@@ -31,6 +31,7 @@
* FWAUTH: Firmware authenticator
* DUMP_OBSS: Firmware has capable to dump obss info to support ACS
* SCAN_V2: Version 2 scan params
+ * SAE_EXT: SAE authentication handled by user-space supplicant
*/
#define BRCMF_FEAT_LIST \
BRCMF_FEAT_DEF(MBSS) \
@@ -57,7 +58,8 @@
BRCMF_FEAT_DEF(DUMP_OBSS) \
BRCMF_FEAT_DEF(SCAN_V2) \
BRCMF_FEAT_DEF(PMKID_V2) \
- BRCMF_FEAT_DEF(PMKID_V3)
+ BRCMF_FEAT_DEF(PMKID_V3) \
+ BRCMF_FEAT_DEF(SAE_EXT)
/*
* Quirks:
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fweh.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fweh.c
index f0b6a7607f16..c2d98ee6652f 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fweh.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fweh.c
@@ -75,6 +75,7 @@ const char *brcmf_fweh_event_name(enum brcmf_fweh_event_code code)
return "nodebug";
}
#endif
+BRCMF_EXPORT_SYMBOL_GPL(brcmf_fweh_event_name);
/**
* brcmf_fweh_queue_event() - create and queue event.
@@ -405,6 +406,7 @@ int brcmf_fweh_register(struct brcmf_pub *drvr, enum brcmf_fweh_event_code code,
brcmf_fweh_event_name(code));
return 0;
}
+BRCMF_EXPORT_SYMBOL_GPL(brcmf_fweh_register);
/**
* brcmf_fweh_unregister() - remove handler for given code.
@@ -448,11 +450,14 @@ int brcmf_fweh_activate_events(struct brcmf_if *ifp)
brcmf_dbg(EVENT, "enable event IF\n");
setbit(fweh->event_mask, BRCMF_E_IF);
+ /* allow per-vendor method to activate firmware events */
+ if (!brcmf_fwvid_activate_events(ifp))
+ return 0;
+
err = brcmf_fil_iovar_data_set(ifp, "event_msgs", fweh->event_mask,
fweh->event_mask_len);
if (err)
bphy_err(fweh->drvr, "Set event_msgs error (%d)\n", err);
-
return err;
}
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fweh.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fweh.h
index eed439b84010..e327dd58d29c 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fweh.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fweh.h
@@ -94,7 +94,11 @@ struct brcmf_cfg80211_info;
BRCMF_ENUM_DEF(FIFO_CREDIT_MAP, 74) \
BRCMF_ENUM_DEF(ACTION_FRAME_RX, 75) \
BRCMF_ENUM_DEF(TDLS_PEER_EVENT, 92) \
- BRCMF_ENUM_DEF(BCMC_CREDIT_SUPPORT, 127)
+ BRCMF_ENUM_DEF(BCMC_CREDIT_SUPPORT, 127) \
+ BRCMF_ABSTRACT_ENUM_DEF(EXT_AUTH_REQ, 0) \
+ BRCMF_ABSTRACT_ENUM_DEF(EXT_AUTH_FRAME_RX, 1) \
+ BRCMF_ABSTRACT_ENUM_DEF(MGMT_FRAME_TXSTATUS, 2) \
+ BRCMF_ABSTRACT_ENUM_DEF(MGMT_FRAME_OFFCHAN_DONE, 3)
#define BRCMF_ENUM_DEF(id, val) \
BRCMF_E_##id = (val),
@@ -337,7 +341,7 @@ struct brcmf_fweh_info {
struct list_head event_q;
uint event_mask_len;
u8 *event_mask;
- struct brcmf_fweh_event_map *event_map;
+ const struct brcmf_fweh_event_map *event_map;
uint num_event_codes;
brcmf_fweh_handler_t evt_handler[] __counted_by(num_event_codes);
};
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwvid.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwvid.h
index e6ac9fc341bc..f3e011d090f2 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwvid.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwvid.h
@@ -15,6 +15,9 @@ struct brcmf_fwvid_ops {
void (*feat_attach)(struct brcmf_if *ifp);
int (*set_sae_password)(struct brcmf_if *ifp, struct cfg80211_crypto_settings *crypto);
int (*alloc_fweh_info)(struct brcmf_pub *drvr);
+ int (*activate_events)(struct brcmf_if *ifp);
+ void (*get_cfg80211_ops)(struct brcmf_pub *drvr);
+ void (*register_event_handlers)(struct brcmf_pub *drvr);
};
/* exported functions */
@@ -56,4 +59,30 @@ static inline int brcmf_fwvid_alloc_fweh_info(struct brcmf_pub *drvr)
return drvr->vops->alloc_fweh_info(drvr);
}
+static inline int brcmf_fwvid_activate_events(struct brcmf_if *ifp)
+{
+ const struct brcmf_fwvid_ops *vops = ifp->drvr->vops;
+
+ if (!vops || !vops->activate_events)
+ return -EOPNOTSUPP;
+
+ return vops->activate_events(ifp);
+}
+
+static inline void brcmf_fwvid_get_cfg80211_ops(struct brcmf_pub *drvr)
+{
+ if (!drvr->vops || !drvr->vops->get_cfg80211_ops)
+ return;
+
+ drvr->vops->get_cfg80211_ops(drvr);
+}
+
+static inline void brcmf_fwvid_register_event_handlers(struct brcmf_pub *drvr)
+{
+ if (!drvr->vops || !drvr->vops->register_event_handlers)
+ return;
+
+ drvr->vops->register_event_handlers(drvr);
+}
+
#endif /* FWVID_H_ */
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c
index d2caa80e9412..9f1854b3d1a5 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c
@@ -2304,7 +2304,7 @@ brcmf_pcie_fwcon_timer(struct brcmf_pciedev_info *devinfo, bool active)
{
if (!active) {
if (devinfo->console_active) {
- del_timer_sync(&devinfo->timer);
+ timer_delete_sync(&devinfo->timer);
devinfo->console_active = false;
}
return;
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c
index b1727f35217b..93727b9a5f0d 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c
@@ -4611,7 +4611,7 @@ void brcmf_sdio_wd_timer(struct brcmf_sdio *bus, bool active)
{
/* Totally stop the timer */
if (!active && bus->wd_active) {
- del_timer_sync(&bus->timer);
+ timer_delete_sync(&bus->timer);
bus->wd_active = false;
return;
}
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c
index 2821c27f317e..b056336d5da6 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c
@@ -741,15 +741,19 @@ static int brcmf_usb_dl_cmd(struct brcmf_usbdev_info *devinfo, u8 cmd,
void *buffer, int buflen)
{
int ret;
- char *tmpbuf;
+ char *tmpbuf = NULL;
u16 size;
- if ((!devinfo) || (devinfo->ctl_urb == NULL))
- return -EINVAL;
+ if (!devinfo || !devinfo->ctl_urb) {
+ ret = -EINVAL;
+ goto err;
+ }
tmpbuf = kmalloc(buflen, GFP_ATOMIC);
- if (!tmpbuf)
- return -ENOMEM;
+ if (!tmpbuf) {
+ ret = -ENOMEM;
+ goto err;
+ }
size = buflen;
devinfo->ctl_urb->transfer_buffer_length = size;
@@ -770,18 +774,23 @@ static int brcmf_usb_dl_cmd(struct brcmf_usbdev_info *devinfo, u8 cmd,
ret = usb_submit_urb(devinfo->ctl_urb, GFP_ATOMIC);
if (ret < 0) {
brcmf_err("usb_submit_urb failed %d\n", ret);
- goto finalize;
+ goto err;
}
if (!brcmf_usb_ioctl_resp_wait(devinfo)) {
usb_kill_urb(devinfo->ctl_urb);
ret = -ETIMEDOUT;
+ goto err;
} else {
memcpy(buffer, tmpbuf, buflen);
}
-finalize:
kfree(tmpbuf);
+ return 0;
+
+err:
+ kfree(tmpbuf);
+ brcmf_err("dl cmd %u failed: err=%d\n", cmd, ret);
return ret;
}
@@ -896,14 +905,16 @@ brcmf_usb_dl_writeimage(struct brcmf_usbdev_info *devinfo, u8 *fw, int fwlen)
}
/* 1) Prepare USB boot loader for runtime image */
- brcmf_usb_dl_cmd(devinfo, DL_START, &state, sizeof(state));
+ err = brcmf_usb_dl_cmd(devinfo, DL_START, &state, sizeof(state));
+ if (err)
+ goto fail;
rdlstate = le32_to_cpu(state.state);
rdlbytes = le32_to_cpu(state.bytes);
/* 2) Check we are in the Waiting state */
if (rdlstate != DL_WAITING) {
- brcmf_err("Failed to DL_START\n");
+ brcmf_err("Invalid DL state: %u\n", rdlstate);
err = -EINVAL;
goto fail;
}
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmsmac/aiutils.c b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/aiutils.c
index 50d817485cf9..0cb64fc56783 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmsmac/aiutils.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/aiutils.c
@@ -198,7 +198,7 @@
#define CST4313_SPROM_OTP_SEL_SHIFT 0
/* 4313 Chip specific ChipControl register bits */
- /* 12 mA drive strengh for later 4313 */
+ /* 12 mA drive strength for later 4313 */
#define CCTRL_4313_12MA_LED_DRIVE 0x00000007
/* Manufacturer Ids */
@@ -453,7 +453,7 @@ ai_buscore_setup(struct si_info *sii, struct bcma_device *cc)
/* get chipcommon chipstatus */
sii->chipst = bcma_read32(cc, CHIPCREGOFFS(chipstatus));
- /* get chipcommon capabilites */
+ /* get chipcommon capabilities */
sii->pub.cccaps = bcma_read32(cc, CHIPCREGOFFS(capabilities));
/* get pmu rev and caps */
@@ -657,7 +657,7 @@ u16 ai_clkctl_fast_pwrup_delay(struct si_pub *sih)
}
/*
- * clock control policy function throught chipcommon
+ * clock control policy function through chipcommon
*
* set dynamic clk control mode (forceslow, forcefast, dynamic)
* returns true if we are forcing fast clock
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmsmac/aiutils.h b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/aiutils.h
index 90b6e3982d2c..e791dd07ca78 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmsmac/aiutils.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/aiutils.h
@@ -76,7 +76,7 @@
* conventions for the use the flash space:
*/
-/* Minumum amount of flash we support */
+/* Minimum amount of flash we support */
#define FLASH_MIN 0x00020000 /* Minimum flash size */
#define CC_SROM_OTP 0x800 /* SROM/OTP address space */
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmsmac/ampdu.c b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/ampdu.c
index a767cbb79185..e1d707a7c964 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmsmac/ampdu.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/ampdu.c
@@ -479,7 +479,7 @@ void brcms_c_ampdu_reset_session(struct brcms_ampdu_session *session,
/*
* Preps the given packet for AMPDU based on the session data. If the
- * frame cannot be accomodated in the current session, -ENOSPC is
+ * frame cannot be accommodated in the current session, -ENOSPC is
* returned.
*/
int brcms_c_ampdu_add_frame(struct brcms_ampdu_session *session,
@@ -529,7 +529,7 @@ int brcms_c_ampdu_add_frame(struct brcms_ampdu_session *session,
}
/*
- * Now that we're sure this frame can be accomodated, update the
+ * Now that we're sure this frame can be accommodated, update the
* session information.
*/
session->ampdu_len += len;
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmsmac/channel.c b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/channel.c
index d1b9a18d0374..3878c4124e25 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmsmac/channel.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/channel.c
@@ -445,7 +445,7 @@ brcms_c_channel_reg_limits(struct brcms_cm_info *wlc_cm, u16 chanspec,
/*
* OFDM 40 MHz SISO has the same power as the corresponding
- * MCS0-7 rate unless overriden by the locale specific code.
+ * MCS0-7 rate unless overridden by the locale specific code.
* We set this value to 0 as a flag (presumably 0 dBm isn't
* a possibility) and then copy the MCS0-7 value to the 40 MHz
* value if it wasn't explicitly set.
@@ -479,7 +479,7 @@ brcms_c_channel_reg_limits(struct brcms_cm_info *wlc_cm, u16 chanspec,
/*
* 20 MHz has the same power as the corresponding OFDM rate
- * unless overriden by the locale specific code.
+ * unless overridden by the locale specific code.
*/
txpwr->mcs_20_siso[i] = txpwr->ofdm[i];
txpwr->mcs_40_siso[i] = 0;
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmsmac/dma.c b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/dma.c
index 80c35027787a..c739bf7463b3 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmsmac/dma.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/dma.c
@@ -1348,7 +1348,7 @@ static void prep_ampdu_frame(struct dma_info *di, struct sk_buff *p)
ret = brcms_c_ampdu_add_frame(session, p);
if (ret == -ENOSPC) {
/*
- * AMPDU cannot accomodate this frame. Close out the in-
+ * AMPDU cannot accommodate this frame. Close out the in-
* progress AMPDU session and start a new one.
*/
ampdu_finalize(di);
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmsmac/mac80211_if.c b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/mac80211_if.c
index 5b1d35601bbd..1c3d29dca424 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmsmac/mac80211_if.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/mac80211_if.c
@@ -48,9 +48,9 @@
FIF_BCN_PRBRESP_PROMISC | \
FIF_PSPOLL)
-#define CHAN2GHZ(channel, freqency, chflags) { \
+#define CHAN2GHZ(channel, frequency, chflags) { \
.band = NL80211_BAND_2GHZ, \
- .center_freq = (freqency), \
+ .center_freq = (frequency), \
.hw_value = (channel), \
.flags = chflags, \
.max_antenna_gain = 0, \
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmsmac/main.c b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/main.c
index 2738d4d6c60a..c1a9c1e442ee 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmsmac/main.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/main.c
@@ -921,7 +921,7 @@ brcms_c_dotxstatus(struct brcms_c_info *wlc, struct tx_status *txs)
* The "fallback limit" is the number of tx attempts a given
* MPDU is sent at the "primary" rate. Tx attempts beyond that
* limit are sent at the "secondary" rate.
- * A 'short frame' does not exceed RTS treshold.
+ * A 'short frame' does not exceed RTS threshold.
*/
u16 sfbl, /* Short Frame Rate Fallback Limit */
lfbl, /* Long Frame Rate Fallback Limit */
@@ -6259,7 +6259,7 @@ brcms_c_d11hdrs_mac80211(struct brcms_c_info *wlc, struct ieee80211_hw *hw,
}
/*
- * Currently only support same setting for primay and
+ * Currently only support same setting for primary and
* fallback rates. Unify flags for each rate into a
* single value for the frame
*/
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmsmac/main.h b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/main.h
index 9f76b880814e..b7ca0d9891c4 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmsmac/main.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/main.h
@@ -603,7 +603,7 @@ enum brcms_bss_type {
* cur_etheraddr: h/w address
* flags: BSSCFG flags; see below
*
- * current_bss: BSS parms in ASSOCIATED state
+ * current_bss: BSS parameters in ASSOCIATED state
*
*
* ID: 'unique' ID of this bsscfg, assigned at bsscfg allocation
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmsmac/pmu.c b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/pmu.c
index 71b80381f3ad..c9a29e626daa 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmsmac/pmu.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/pmu.c
@@ -31,7 +31,7 @@
#define EXT_ILP_HZ 32768
/*
- * Duration for ILP clock frequency measurment in milliseconds
+ * Duration for ILP clock frequency measurement in milliseconds
*
* remark: 1000 must be an integer multiple of this duration
*/
diff --git a/drivers/net/wireless/intel/ipw2x00/ipw2200.c b/drivers/net/wireless/intel/ipw2x00/ipw2200.c
index be1d971b3d32..24a5624ef207 100644
--- a/drivers/net/wireless/intel/ipw2x00/ipw2200.c
+++ b/drivers/net/wireless/intel/ipw2x00/ipw2200.c
@@ -3295,7 +3295,7 @@ static int ipw_init_nic(struct ipw_priv *priv)
rc = ipw_poll_bit(priv, IPW_GP_CNTRL_RW,
IPW_GP_CNTRL_BIT_CLOCK_READY, 250);
if (rc < 0)
- IPW_DEBUG_INFO("FAILED wait for clock stablization\n");
+ IPW_DEBUG_INFO("FAILED wait for clock stabilization\n");
/* assert SW reset */
ipw_set_bit(priv, IPW_RESET_REG, IPW_RESET_REG_SW_RESET);
diff --git a/drivers/net/wireless/intel/ipw2x00/libipw.h b/drivers/net/wireless/intel/ipw2x00/libipw.h
index 3c20353e5a41..e031e8692ca6 100644
--- a/drivers/net/wireless/intel/ipw2x00/libipw.h
+++ b/drivers/net/wireless/intel/ipw2x00/libipw.h
@@ -1011,8 +1011,6 @@ netdev_tx_t libipw_xmit(struct sk_buff *skb, struct net_device *dev);
void libipw_txb_free(struct libipw_txb *);
/* libipw_rx.c */
-void libipw_rx_any(struct libipw_device *ieee, struct sk_buff *skb,
- struct libipw_rx_stats *stats);
int libipw_rx(struct libipw_device *ieee, struct sk_buff *skb,
struct libipw_rx_stats *rx_stats);
/* make sure to set stats->len */
diff --git a/drivers/net/wireless/intel/ipw2x00/libipw_crypto.c b/drivers/net/wireless/intel/ipw2x00/libipw_crypto.c
index 32639e0e8430..dfcc12aa8620 100644
--- a/drivers/net/wireless/intel/ipw2x00/libipw_crypto.c
+++ b/drivers/net/wireless/intel/ipw2x00/libipw_crypto.c
@@ -59,7 +59,7 @@ void libipw_crypt_info_free(struct libipw_crypt_info *info)
int i;
libipw_crypt_quiescing(info);
- del_timer_sync(&info->crypt_deinit_timer);
+ timer_delete_sync(&info->crypt_deinit_timer);
libipw_crypt_deinit_entries(info, 1);
for (i = 0; i < NUM_WEP_KEYS; i++) {
diff --git a/drivers/net/wireless/intel/ipw2x00/libipw_rx.c b/drivers/net/wireless/intel/ipw2x00/libipw_rx.c
index dc4e91f58bb4..b7bc94f7abd8 100644
--- a/drivers/net/wireless/intel/ipw2x00/libipw_rx.c
+++ b/drivers/net/wireless/intel/ipw2x00/libipw_rx.c
@@ -823,96 +823,6 @@ int libipw_rx(struct libipw_device *ieee, struct sk_buff *skb,
return 0;
}
-/* Filter out unrelated packets, call libipw_rx[_mgt]
- * This function takes over the skb, it should not be used again after calling
- * this function. */
-void libipw_rx_any(struct libipw_device *ieee,
- struct sk_buff *skb, struct libipw_rx_stats *stats)
-{
- struct libipw_hdr_4addr *hdr;
- int is_packet_for_us;
- u16 fc;
-
- if (ieee->iw_mode == IW_MODE_MONITOR) {
- if (!libipw_rx(ieee, skb, stats))
- dev_kfree_skb_irq(skb);
- return;
- }
-
- if (skb->len < sizeof(struct ieee80211_hdr))
- goto drop_free;
-
- hdr = (struct libipw_hdr_4addr *)skb->data;
- fc = le16_to_cpu(hdr->frame_ctl);
-
- if ((fc & IEEE80211_FCTL_VERS) != 0)
- goto drop_free;
-
- switch (fc & IEEE80211_FCTL_FTYPE) {
- case IEEE80211_FTYPE_MGMT:
- if (skb->len < sizeof(struct libipw_hdr_3addr))
- goto drop_free;
- libipw_rx_mgt(ieee, hdr, stats);
- dev_kfree_skb_irq(skb);
- return;
- case IEEE80211_FTYPE_DATA:
- break;
- case IEEE80211_FTYPE_CTL:
- return;
- default:
- return;
- }
-
- is_packet_for_us = 0;
- switch (ieee->iw_mode) {
- case IW_MODE_ADHOC:
- /* our BSS and not from/to DS */
- if (ether_addr_equal(hdr->addr3, ieee->bssid) &&
- ((fc & (IEEE80211_FCTL_TODS + IEEE80211_FCTL_FROMDS)) == 0)) {
- /* promisc: get all */
- if (ieee->dev->flags & IFF_PROMISC)
- is_packet_for_us = 1;
- /* to us */
- else if (ether_addr_equal(hdr->addr1, ieee->dev->dev_addr))
- is_packet_for_us = 1;
- /* mcast */
- else if (is_multicast_ether_addr(hdr->addr1))
- is_packet_for_us = 1;
- }
- break;
- case IW_MODE_INFRA:
- /* our BSS (== from our AP) and from DS */
- if (ether_addr_equal(hdr->addr2, ieee->bssid) &&
- ((fc & (IEEE80211_FCTL_TODS + IEEE80211_FCTL_FROMDS)) == IEEE80211_FCTL_FROMDS)) {
- /* promisc: get all */
- if (ieee->dev->flags & IFF_PROMISC)
- is_packet_for_us = 1;
- /* to us */
- else if (ether_addr_equal(hdr->addr1, ieee->dev->dev_addr))
- is_packet_for_us = 1;
- /* mcast */
- else if (is_multicast_ether_addr(hdr->addr1)) {
- /* not our own packet bcasted from AP */
- if (!ether_addr_equal(hdr->addr3, ieee->dev->dev_addr))
- is_packet_for_us = 1;
- }
- }
- break;
- default:
- /* ? */
- break;
- }
-
- if (is_packet_for_us)
- if (!libipw_rx(ieee, skb, stats))
- dev_kfree_skb_irq(skb);
- return;
-
-drop_free:
- dev_kfree_skb_irq(skb);
- ieee->dev->stats.rx_dropped++;
-}
-
#define MGMT_FRAME_FIXED_PART_LENGTH 0x24
static u8 qos_oui[QOS_OUI_LEN] = { 0x00, 0x50, 0xF2 };
@@ -1729,6 +1639,5 @@ void libipw_rx_mgt(struct libipw_device *ieee,
}
}
-EXPORT_SYMBOL_GPL(libipw_rx_any);
EXPORT_SYMBOL(libipw_rx_mgt);
EXPORT_SYMBOL(libipw_rx);
diff --git a/drivers/net/wireless/intel/iwlegacy/3945-mac.c b/drivers/net/wireless/intel/iwlegacy/3945-mac.c
index 4013443698a2..104748fcdc33 100644
--- a/drivers/net/wireless/intel/iwlegacy/3945-mac.c
+++ b/drivers/net/wireless/intel/iwlegacy/3945-mac.c
@@ -2188,7 +2188,7 @@ __il3945_down(struct il_priv *il)
/* Stop TX queues watchdog. We need to have S_EXIT_PENDING bit set
* to prevent rearm timer */
- del_timer_sync(&il->watchdog);
+ timer_delete_sync(&il->watchdog);
/* Station information will now be cleared in device */
il_clear_ucode_stations(il);
diff --git a/drivers/net/wireless/intel/iwlegacy/3945-rs.c b/drivers/net/wireless/intel/iwlegacy/3945-rs.c
index 0eaad980c85c..df1b8ec86651 100644
--- a/drivers/net/wireless/intel/iwlegacy/3945-rs.c
+++ b/drivers/net/wireless/intel/iwlegacy/3945-rs.c
@@ -413,7 +413,7 @@ il3945_rs_free_sta(void *il_priv, struct ieee80211_sta *sta, void *il_sta)
* to use il_priv to print out debugging) since it may not be fully
* initialized at this point.
*/
- del_timer_sync(&rs_sta->rate_scale_flush);
+ timer_delete_sync(&rs_sta->rate_scale_flush);
}
/*
diff --git a/drivers/net/wireless/intel/iwlegacy/4965-mac.c b/drivers/net/wireless/intel/iwlegacy/4965-mac.c
index 05c4af41bdb9..dc8c408902e6 100644
--- a/drivers/net/wireless/intel/iwlegacy/4965-mac.c
+++ b/drivers/net/wireless/intel/iwlegacy/4965-mac.c
@@ -5350,7 +5350,7 @@ __il4965_down(struct il_priv *il)
/* Stop TX queues watchdog. We need to have S_EXIT_PENDING bit set
* to prevent rearm timer */
- del_timer_sync(&il->watchdog);
+ timer_delete_sync(&il->watchdog);
il_clear_ucode_stations(il);
@@ -6243,7 +6243,7 @@ il4965_cancel_deferred_work(struct il_priv *il)
il_cancel_scan_deferred_work(il);
- del_timer_sync(&il->stats_periodic);
+ timer_delete_sync(&il->stats_periodic);
}
static void
diff --git a/drivers/net/wireless/intel/iwlegacy/4965-rs.c b/drivers/net/wireless/intel/iwlegacy/4965-rs.c
index 718efb1aa1b0..0e5130d1fccd 100644
--- a/drivers/net/wireless/intel/iwlegacy/4965-rs.c
+++ b/drivers/net/wireless/intel/iwlegacy/4965-rs.c
@@ -132,15 +132,8 @@ static void il4965_rs_fill_link_cmd(struct il_priv *il,
static void il4965_rs_stay_in_table(struct il_lq_sta *lq_sta,
bool force_search);
-#ifdef CONFIG_MAC80211_DEBUGFS
static void il4965_rs_dbgfs_set_mcs(struct il_lq_sta *lq_sta,
u32 *rate_n_flags, int idx);
-#else
-static void
-il4965_rs_dbgfs_set_mcs(struct il_lq_sta *lq_sta, u32 * rate_n_flags, int idx)
-{
-}
-#endif
/*
* The following tables contain the expected throughput metrics for all rates
@@ -2495,8 +2488,6 @@ il4965_rs_free_sta(void *il_r, struct ieee80211_sta *sta, void *il_sta)
D_RATE("leave\n");
}
-#ifdef CONFIG_MAC80211_DEBUGFS
-
static void
il4965_rs_dbgfs_set_mcs(struct il_lq_sta *lq_sta, u32 * rate_n_flags, int idx)
{
@@ -2504,6 +2495,9 @@ il4965_rs_dbgfs_set_mcs(struct il_lq_sta *lq_sta, u32 * rate_n_flags, int idx)
u8 valid_tx_ant;
u8 ant_sel_tx;
+ if (!IS_ENABLED(CONFIG_MAC80211_DEBUGFS))
+ return;
+
il = lq_sta->drv;
valid_tx_ant = il->hw_params.valid_tx_ant;
if (lq_sta->dbg_fixed_rate) {
@@ -2758,7 +2752,6 @@ il4965_rs_add_debugfs(void *il, void *il_sta, struct dentry *dir)
debugfs_create_u8("tx_agg_tid_enable", 0600, dir,
&lq_sta->tx_agg_tid_en);
}
-#endif
/*
* Initialization of rate scaling information is done by driver after
@@ -2781,9 +2774,8 @@ static const struct rate_control_ops rs_4965_ops = {
.free = il4965_rs_free,
.alloc_sta = il4965_rs_alloc_sta,
.free_sta = il4965_rs_free_sta,
-#ifdef CONFIG_MAC80211_DEBUGFS
- .add_sta_debugfs = il4965_rs_add_debugfs,
-#endif
+ .add_sta_debugfs = PTR_IF(IS_ENABLED(CONFIG_MAC80211_DEBUGFS),
+ il4965_rs_add_debugfs),
};
int
diff --git a/drivers/net/wireless/intel/iwlegacy/common.c b/drivers/net/wireless/intel/iwlegacy/common.c
index af4f42534ea0..09fb4b758704 100644
--- a/drivers/net/wireless/intel/iwlegacy/common.c
+++ b/drivers/net/wireless/intel/iwlegacy/common.c
@@ -4842,7 +4842,7 @@ il_setup_watchdog(struct il_priv *il)
mod_timer(&il->watchdog,
jiffies + msecs_to_jiffies(IL_WD_TICK(timeout)));
else
- del_timer(&il->watchdog);
+ timer_delete(&il->watchdog);
}
EXPORT_SYMBOL(il_setup_watchdog);
diff --git a/drivers/net/wireless/intel/iwlegacy/common.h b/drivers/net/wireless/intel/iwlegacy/common.h
index 92285412ab10..52610f5e57a3 100644
--- a/drivers/net/wireless/intel/iwlegacy/common.h
+++ b/drivers/net/wireless/intel/iwlegacy/common.h
@@ -2815,9 +2815,7 @@ struct il_lq_sta {
struct il_scale_tbl_info lq_info[LQ_SIZE]; /* "active", "search" */
struct il_traffic_load load[TID_MAX_LOAD_COUNT];
u8 tx_agg_tid_en;
-#ifdef CONFIG_MAC80211_DEBUGFS
u32 dbg_fixed_rate;
-#endif
struct il_priv *drv;
/* used to be in sta_info */
diff --git a/drivers/net/wireless/intel/iwlwifi/Kconfig b/drivers/net/wireless/intel/iwlwifi/Kconfig
index 4b04865fc2c9..82f577da1a8b 100644
--- a/drivers/net/wireless/intel/iwlwifi/Kconfig
+++ b/drivers/net/wireless/intel/iwlwifi/Kconfig
@@ -81,14 +81,25 @@ config IWLMVM
of the devices that use this firmware is available here:
https://wireless.wiki.kernel.org/en/users/drivers/iwlwifi#firmware
+config IWLMLD
+ tristate "Intel Wireless WiFi MLD Firmware support"
+ select WANT_DEV_COREDUMP
+ depends on MAC80211
+ depends on PTP_1588_CLOCK_OPTIONAL
+ help
+ This is the driver that supports firmwares of MLD capable devices.
+ The list of the devices that use this firmware is available here:
+ https://wireless.wiki.kernel.org/en/users/drivers/iwlwifi#firmware
+
# don't call it _MODULE -- will confuse Kconfig/fixdep/...
config IWLWIFI_OPMODE_MODULAR
bool
default y if IWLDVM=m
default y if IWLMVM=m
+ default y if IWLMLD=m
-comment "WARNING: iwlwifi is useless without IWLDVM or IWLMVM"
- depends on IWLDVM=n && IWLMVM=n
+comment "WARNING: iwlwifi is useless without IWLDVM or IWLMVM or IWLMLD"
+ depends on IWLDVM=n && IWLMVM=n && IWLMLD=n
menu "Debugging Options"
diff --git a/drivers/net/wireless/intel/iwlwifi/Makefile b/drivers/net/wireless/intel/iwlwifi/Makefile
index 19c4ce6f2465..3f476e333726 100644
--- a/drivers/net/wireless/intel/iwlwifi/Makefile
+++ b/drivers/net/wireless/intel/iwlwifi/Makefile
@@ -8,11 +8,23 @@ iwlwifi-objs += iwl-nvm-utils.o
iwlwifi-objs += iwl-utils.o
iwlwifi-objs += iwl-phy-db.o iwl-nvm-parse.o
iwlwifi-objs += pcie/drv.o pcie/rx.o pcie/tx.o pcie/trans.o
-iwlwifi-objs += pcie/ctxt-info.o pcie/ctxt-info-gen3.o
+iwlwifi-objs += pcie/ctxt-info.o pcie/ctxt-info-v2.o
iwlwifi-objs += pcie/trans-gen2.o pcie/tx-gen2.o
-iwlwifi-$(CONFIG_IWLDVM) += cfg/1000.o cfg/2000.o cfg/5000.o cfg/6000.o
-iwlwifi-$(CONFIG_IWLMVM) += cfg/7000.o cfg/8000.o cfg/9000.o cfg/22000.o
-iwlwifi-$(CONFIG_IWLMVM) += cfg/ax210.o cfg/bz.o cfg/sc.o cfg/dr.o
+
+CFLAGS_pcie/drv.o += -Wno-override-init
+
+# combined MAC/RF configurations
+iwlwifi-$(CONFIG_IWLDVM) += cfg/1000.o cfg/2000.o
+iwlwifi-$(CONFIG_IWLDVM) += cfg/5000.o cfg/6000.o
+iwlwifi-$(CONFIG_IWLMVM) += cfg/7000.o cfg/8000.o
+# MAC configurations
+iwlwifi-$(CONFIG_IWLMVM) += cfg/9000.o cfg/22000.o
+iwlwifi-$(CONFIG_IWLMVM) += cfg/ax210.o
+iwlwifi-$(CONFIG_IWLMLD) += cfg/bz.o cfg/sc.o cfg/dr.o
+# RF configurations
+iwlwifi-$(CONFIG_IWLMVM) += cfg/rf-jf.o cfg/rf-hr.o cfg/rf-gf.o
+iwlwifi-$(CONFIG_IWLMLD) += cfg/rf-fm.o cfg/rf-wh.o cfg/rf-pe.o
+
iwlwifi-objs += iwl-dbg-tlv.o
iwlwifi-objs += iwl-trans.o
@@ -20,6 +32,7 @@ iwlwifi-objs += fw/img.o fw/notif-wait.o fw/rs.o
iwlwifi-objs += fw/dbg.o fw/pnvm.o fw/dump.o
iwlwifi-objs += fw/regulatory.o
iwlwifi-$(CONFIG_IWLMVM) += fw/paging.o fw/smem.o fw/init.o
+iwlwifi-$(CONFIG_IWLMLD) += fw/smem.o fw/init.o
iwlwifi-$(CONFIG_ACPI) += fw/acpi.o
iwlwifi-$(CONFIG_EFI) += fw/uefi.o
iwlwifi-$(CONFIG_IWLWIFI_DEBUGFS) += fw/debugfs.o
@@ -33,6 +46,7 @@ ccflags-y += -I$(src)
obj-$(CONFIG_IWLDVM) += dvm/
obj-$(CONFIG_IWLMVM) += mvm/
obj-$(CONFIG_IWLMEI) += mei/
+obj-$(CONFIG_IWLMLD) += mld/
obj-$(CONFIG_IWLWIFI_KUNIT_TESTS) += tests/
diff --git a/drivers/net/wireless/intel/iwlwifi/cfg/1000.c b/drivers/net/wireless/intel/iwlwifi/cfg/1000.c
index f172ffd2a841..c029e595e7c2 100644
--- a/drivers/net/wireless/intel/iwlwifi/cfg/1000.c
+++ b/drivers/net/wireless/intel/iwlwifi/cfg/1000.c
@@ -2,7 +2,7 @@
/******************************************************************************
*
* Copyright(c) 2008 - 2014 Intel Corporation. All rights reserved.
- * Copyright(c) 2018 - 2020, 2023 Intel Corporation
+ * Copyright(c) 2018 - 2020, 2023, 2025 Intel Corporation
*****************************************************************************/
#include <linux/module.h>
@@ -29,7 +29,7 @@
#define IWL100_MODULE_FIRMWARE(api) IWL100_FW_PRE "-" __stringify(api) ".ucode"
-static const struct iwl_base_params iwl1000_base_params = {
+static const struct iwl_family_base_params iwl1000_base = {
.num_of_queues = IWLAGN_NUM_QUEUES,
.max_tfd_queue_size = 256,
.eeprom_size = OTP_LOW_IMAGE_SIZE_2K,
@@ -42,12 +42,6 @@ static const struct iwl_base_params iwl1000_base_params = {
.scd_chain_ext_wa = true,
};
-static const struct iwl_ht_params iwl1000_ht_params = {
- .ht_greenfield_support = true,
- .use_rts_for_aggregation = true, /* use rts/cts protection */
- .ht40_bands = BIT(NL80211_BAND_2GHZ),
-};
-
static const struct iwl_eeprom_params iwl1000_eeprom_params = {
.regulatory_bands = {
EEPROM_REG_BAND_1_CHANNELS,
@@ -60,54 +54,67 @@ static const struct iwl_eeprom_params iwl1000_eeprom_params = {
}
};
+const struct iwl_mac_cfg iwl1000_mac_cfg = {
+ .device_family = IWL_DEVICE_FAMILY_1000,
+ .base = &iwl1000_base,
+};
+
#define IWL_DEVICE_1000 \
.fw_name_pre = IWL1000_FW_PRE, \
.ucode_api_max = IWL1000_UCODE_API_MAX, \
.ucode_api_min = IWL1000_UCODE_API_MIN, \
- .trans.device_family = IWL_DEVICE_FAMILY_1000, \
.max_inst_size = IWLAGN_RTC_INST_SIZE, \
.max_data_size = IWLAGN_RTC_DATA_SIZE, \
.nvm_ver = EEPROM_1000_EEPROM_VERSION, \
.nvm_calib_ver = EEPROM_1000_TX_POWER_VERSION, \
- .trans.base_params = &iwl1000_base_params, \
.eeprom_params = &iwl1000_eeprom_params, \
.led_mode = IWL_LED_BLINK
-const struct iwl_cfg iwl1000_bgn_cfg = {
- .name = "Intel(R) Centrino(R) Wireless-N 1000 BGN",
+const struct iwl_rf_cfg iwl1000_bgn_cfg = {
IWL_DEVICE_1000,
- .ht_params = &iwl1000_ht_params,
+ .ht_params = {
+ .ht_greenfield_support = true,
+ .use_rts_for_aggregation = true, /* use rts/cts protection */
+ .ht40_bands = BIT(NL80211_BAND_2GHZ),
+ },
};
-const struct iwl_cfg iwl1000_bg_cfg = {
- .name = "Intel(R) Centrino(R) Wireless-N 1000 BG",
+const char iwl1000_bgn_name[] = "Intel(R) Centrino(R) Wireless-N 1000 BGN";
+
+const struct iwl_rf_cfg iwl1000_bg_cfg = {
IWL_DEVICE_1000,
};
+const char iwl1000_bg_name[] = "Intel(R) Centrino(R) Wireless-N 1000 BG";
+
#define IWL_DEVICE_100 \
.fw_name_pre = IWL100_FW_PRE, \
.ucode_api_max = IWL100_UCODE_API_MAX, \
.ucode_api_min = IWL100_UCODE_API_MIN, \
- .trans.device_family = IWL_DEVICE_FAMILY_100, \
.max_inst_size = IWLAGN_RTC_INST_SIZE, \
.max_data_size = IWLAGN_RTC_DATA_SIZE, \
.nvm_ver = EEPROM_1000_EEPROM_VERSION, \
.nvm_calib_ver = EEPROM_1000_TX_POWER_VERSION, \
- .trans.base_params = &iwl1000_base_params, \
.eeprom_params = &iwl1000_eeprom_params, \
.led_mode = IWL_LED_RF_STATE, \
.rx_with_siso_diversity = true
-const struct iwl_cfg iwl100_bgn_cfg = {
- .name = "Intel(R) Centrino(R) Wireless-N 100 BGN",
+const struct iwl_rf_cfg iwl100_bgn_cfg = {
IWL_DEVICE_100,
- .ht_params = &iwl1000_ht_params,
+ .ht_params = {
+ .ht_greenfield_support = true,
+ .use_rts_for_aggregation = true, /* use rts/cts protection */
+ .ht40_bands = BIT(NL80211_BAND_2GHZ),
+ },
};
-const struct iwl_cfg iwl100_bg_cfg = {
- .name = "Intel(R) Centrino(R) Wireless-N 100 BG",
+const char iwl100_bgn_name[] = "Intel(R) Centrino(R) Wireless-N 100 BGN";
+
+const struct iwl_rf_cfg iwl100_bg_cfg = {
IWL_DEVICE_100,
};
+const char iwl100_bg_name[] = "Intel(R) Centrino(R) Wireless-N 100 BG";
+
MODULE_FIRMWARE(IWL1000_MODULE_FIRMWARE(IWL1000_UCODE_API_MAX));
MODULE_FIRMWARE(IWL100_MODULE_FIRMWARE(IWL100_UCODE_API_MAX));
diff --git a/drivers/net/wireless/intel/iwlwifi/cfg/2000.c b/drivers/net/wireless/intel/iwlwifi/cfg/2000.c
index 6f3f26da0ad5..47554a5f406e 100644
--- a/drivers/net/wireless/intel/iwlwifi/cfg/2000.c
+++ b/drivers/net/wireless/intel/iwlwifi/cfg/2000.c
@@ -2,7 +2,7 @@
/******************************************************************************
*
* Copyright(c) 2008 - 2014 Intel Corporation. All rights reserved.
- * Copyright(c) 2018 - 2020, 2023 Intel Corporation
+ * Copyright(c) 2018 - 2020, 2023, 2025 Intel Corporation
*****************************************************************************/
#include <linux/module.h>
@@ -40,7 +40,7 @@
#define IWL135_FW_PRE "iwlwifi-135"
#define IWL135_MODULE_FIRMWARE(api) IWL135_FW_PRE "-" __stringify(api) ".ucode"
-static const struct iwl_base_params iwl2000_base_params = {
+static const struct iwl_family_base_params iwl2000_base = {
.eeprom_size = OTP_LOW_IMAGE_SIZE_2K,
.num_of_queues = IWLAGN_NUM_QUEUES,
.max_tfd_queue_size = 256,
@@ -54,7 +54,7 @@ static const struct iwl_base_params iwl2000_base_params = {
};
-static const struct iwl_base_params iwl2030_base_params = {
+static const struct iwl_family_base_params iwl2030_base = {
.eeprom_size = OTP_LOW_IMAGE_SIZE_2K,
.num_of_queues = IWLAGN_NUM_QUEUES,
.max_tfd_queue_size = 256,
@@ -67,12 +67,6 @@ static const struct iwl_base_params iwl2030_base_params = {
.scd_chain_ext_wa = true,
};
-static const struct iwl_ht_params iwl2000_ht_params = {
- .ht_greenfield_support = true,
- .use_rts_for_aggregation = true, /* use rts/cts protection */
- .ht40_bands = BIT(NL80211_BAND_2GHZ),
-};
-
static const struct iwl_eeprom_params iwl20x0_eeprom_params = {
.regulatory_bands = {
EEPROM_REG_BAND_1_CHANNELS,
@@ -86,97 +80,119 @@ static const struct iwl_eeprom_params iwl20x0_eeprom_params = {
.enhanced_txpower = true,
};
+const struct iwl_mac_cfg iwl2000_mac_cfg = {
+ .device_family = IWL_DEVICE_FAMILY_2000,
+ .base = &iwl2000_base,
+};
+
#define IWL_DEVICE_2000 \
.fw_name_pre = IWL2000_FW_PRE, \
.ucode_api_max = IWL2000_UCODE_API_MAX, \
.ucode_api_min = IWL2000_UCODE_API_MIN, \
- .trans.device_family = IWL_DEVICE_FAMILY_2000, \
.max_inst_size = IWL60_RTC_INST_SIZE, \
.max_data_size = IWL60_RTC_DATA_SIZE, \
.nvm_ver = EEPROM_2000_EEPROM_VERSION, \
.nvm_calib_ver = EEPROM_2000_TX_POWER_VERSION, \
- .trans.base_params = &iwl2000_base_params, \
.eeprom_params = &iwl20x0_eeprom_params, \
.led_mode = IWL_LED_RF_STATE
-const struct iwl_cfg iwl2000_2bgn_cfg = {
- .name = "Intel(R) Centrino(R) Wireless-N 2200 BGN",
+const struct iwl_rf_cfg iwl2000_2bgn_cfg = {
IWL_DEVICE_2000,
- .ht_params = &iwl2000_ht_params,
+ .ht_params = {
+ .ht_greenfield_support = true,
+ .use_rts_for_aggregation = true, /* use rts/cts protection */
+ .ht40_bands = BIT(NL80211_BAND_2GHZ),
+ },
};
-const struct iwl_cfg iwl2000_2bgn_d_cfg = {
- .name = "Intel(R) Centrino(R) Wireless-N 2200D BGN",
- IWL_DEVICE_2000,
- .ht_params = &iwl2000_ht_params,
+const char iwl2000_2bgn_name[] = "Intel(R) Centrino(R) Wireless-N 2200 BGN";
+const char iwl2000_2bgn_d_name[] = "Intel(R) Centrino(R) Wireless-N 2200D BGN";
+
+const struct iwl_mac_cfg iwl2030_mac_cfg = {
+ .device_family = IWL_DEVICE_FAMILY_2030,
+ .base = &iwl2030_base,
};
#define IWL_DEVICE_2030 \
.fw_name_pre = IWL2030_FW_PRE, \
.ucode_api_max = IWL2030_UCODE_API_MAX, \
.ucode_api_min = IWL2030_UCODE_API_MIN, \
- .trans.device_family = IWL_DEVICE_FAMILY_2030, \
.max_inst_size = IWL60_RTC_INST_SIZE, \
.max_data_size = IWL60_RTC_DATA_SIZE, \
.nvm_ver = EEPROM_2000_EEPROM_VERSION, \
.nvm_calib_ver = EEPROM_2000_TX_POWER_VERSION, \
- .trans.base_params = &iwl2030_base_params, \
.eeprom_params = &iwl20x0_eeprom_params, \
.led_mode = IWL_LED_RF_STATE
-const struct iwl_cfg iwl2030_2bgn_cfg = {
- .name = "Intel(R) Centrino(R) Wireless-N 2230 BGN",
+const struct iwl_rf_cfg iwl2030_2bgn_cfg = {
IWL_DEVICE_2030,
- .ht_params = &iwl2000_ht_params,
+ .ht_params = {
+ .ht_greenfield_support = true,
+ .use_rts_for_aggregation = true, /* use rts/cts protection */
+ .ht40_bands = BIT(NL80211_BAND_2GHZ),
+ },
+};
+
+const char iwl2030_2bgn_name[] = "Intel(R) Centrino(R) Wireless-N 2230 BGN";
+
+const struct iwl_mac_cfg iwl105_mac_cfg = {
+ .device_family = IWL_DEVICE_FAMILY_105,
+ .base = &iwl2000_base,
};
#define IWL_DEVICE_105 \
.fw_name_pre = IWL105_FW_PRE, \
.ucode_api_max = IWL105_UCODE_API_MAX, \
.ucode_api_min = IWL105_UCODE_API_MIN, \
- .trans.device_family = IWL_DEVICE_FAMILY_105, \
.max_inst_size = IWL60_RTC_INST_SIZE, \
.max_data_size = IWL60_RTC_DATA_SIZE, \
.nvm_ver = EEPROM_2000_EEPROM_VERSION, \
.nvm_calib_ver = EEPROM_2000_TX_POWER_VERSION, \
- .trans.base_params = &iwl2000_base_params, \
.eeprom_params = &iwl20x0_eeprom_params, \
.led_mode = IWL_LED_RF_STATE, \
.rx_with_siso_diversity = true
-const struct iwl_cfg iwl105_bgn_cfg = {
- .name = "Intel(R) Centrino(R) Wireless-N 105 BGN",
+const struct iwl_rf_cfg iwl105_bgn_cfg = {
IWL_DEVICE_105,
- .ht_params = &iwl2000_ht_params,
+ .ht_params = {
+ .ht_greenfield_support = true,
+ .use_rts_for_aggregation = true, /* use rts/cts protection */
+ .ht40_bands = BIT(NL80211_BAND_2GHZ),
+ },
};
-const struct iwl_cfg iwl105_bgn_d_cfg = {
- .name = "Intel(R) Centrino(R) Wireless-N 105D BGN",
- IWL_DEVICE_105,
- .ht_params = &iwl2000_ht_params,
+const char iwl105_bgn_name[] = "Intel(R) Centrino(R) Wireless-N 105 BGN";
+const char iwl105_bgn_d_name[] = "Intel(R) Centrino(R) Wireless-N 105D BGN";
+
+const struct iwl_mac_cfg iwl135_mac_cfg = {
+ .device_family = IWL_DEVICE_FAMILY_135,
+ .base = &iwl2030_base,
};
#define IWL_DEVICE_135 \
.fw_name_pre = IWL135_FW_PRE, \
.ucode_api_max = IWL135_UCODE_API_MAX, \
.ucode_api_min = IWL135_UCODE_API_MIN, \
- .trans.device_family = IWL_DEVICE_FAMILY_135, \
.max_inst_size = IWL60_RTC_INST_SIZE, \
.max_data_size = IWL60_RTC_DATA_SIZE, \
.nvm_ver = EEPROM_2000_EEPROM_VERSION, \
.nvm_calib_ver = EEPROM_2000_TX_POWER_VERSION, \
- .trans.base_params = &iwl2030_base_params, \
.eeprom_params = &iwl20x0_eeprom_params, \
.led_mode = IWL_LED_RF_STATE, \
.rx_with_siso_diversity = true
-const struct iwl_cfg iwl135_bgn_cfg = {
- .name = "Intel(R) Centrino(R) Wireless-N 135 BGN",
+const struct iwl_rf_cfg iwl135_bgn_cfg = {
IWL_DEVICE_135,
- .ht_params = &iwl2000_ht_params,
+ .ht_params = {
+ .ht_greenfield_support = true,
+ .use_rts_for_aggregation = true, /* use rts/cts protection */
+ .ht40_bands = BIT(NL80211_BAND_2GHZ),
+ },
};
+const char iwl135_bgn_name[] = "Intel(R) Centrino(R) Wireless-N 135 BGN";
+
MODULE_FIRMWARE(IWL2000_MODULE_FIRMWARE(IWL2000_UCODE_API_MAX));
MODULE_FIRMWARE(IWL2030_MODULE_FIRMWARE(IWL2030_UCODE_API_MAX));
MODULE_FIRMWARE(IWL105_MODULE_FIRMWARE(IWL105_UCODE_API_MAX));
diff --git a/drivers/net/wireless/intel/iwlwifi/cfg/22000.c b/drivers/net/wireless/intel/iwlwifi/cfg/22000.c
index 2e2fcb3807ef..52e0beebf9ce 100644
--- a/drivers/net/wireless/intel/iwlwifi/cfg/22000.c
+++ b/drivers/net/wireless/intel/iwlwifi/cfg/22000.c
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
/*
* Copyright (C) 2015-2017 Intel Deutschland GmbH
- * Copyright (C) 2018-2024 Intel Corporation
+ * Copyright (C) 2018-2025 Intel Corporation
*/
#include <linux/module.h>
#include <linux/stringify.h>
@@ -15,14 +15,7 @@
/* Lowest firmware API version supported */
#define IWL_22000_UCODE_API_MIN 77
-/* NVM versions */
-#define IWL_22000_NVM_VERSION 0x0a1d
-
/* Memory offsets and lengths */
-#define IWL_22000_DCCM_OFFSET 0x800000 /* LMAC1 */
-#define IWL_22000_DCCM_LEN 0x10000 /* LMAC1 */
-#define IWL_22000_DCCM2_OFFSET 0x880000
-#define IWL_22000_DCCM2_LEN 0x8000
#define IWL_22000_SMEM_OFFSET 0x400000
#define IWL_22000_SMEM_LEN 0xD0000
@@ -44,11 +37,12 @@
IWL_QU_C_HR_B_FW_PRE "-" __stringify(api) ".ucode"
#define IWL_QU_B_JF_B_MODULE_FIRMWARE(api) \
IWL_QU_B_JF_B_FW_PRE "-" __stringify(api) ".ucode"
+#define IWL_QU_C_JF_B_MODULE_FIRMWARE(api) \
+ IWL_QU_C_JF_B_FW_PRE "-" __stringify(api) ".ucode"
#define IWL_CC_A_MODULE_FIRMWARE(api) \
IWL_CC_A_FW_PRE "-" __stringify(api) ".ucode"
-static const struct iwl_base_params iwl_22000_base_params = {
- .eeprom_size = OTP_LOW_IMAGE_SIZE_32K,
+static const struct iwl_family_base_params iwl_22000_base = {
.num_of_queues = 512,
.max_tfd_queue_size = 256,
.shadow_ram_support = true,
@@ -57,156 +51,78 @@ static const struct iwl_base_params iwl_22000_base_params = {
.max_event_log_size = 512,
.shadow_reg_enable = true,
.pcie_l1_allowed = true,
-};
-
-const struct iwl_ht_params iwl_22000_ht_params = {
- .stbc = true,
- .ldpc = true,
- .ht40_bands = BIT(NL80211_BAND_2GHZ) | BIT(NL80211_BAND_5GHZ) |
- BIT(NL80211_BAND_6GHZ),
-};
-
-#define IWL_DEVICE_22000_COMMON \
- .ucode_api_min = IWL_22000_UCODE_API_MIN, \
- .led_mode = IWL_LED_RF_STATE, \
- .nvm_hw_section_num = 10, \
- .non_shared_ant = ANT_B, \
- .dccm_offset = IWL_22000_DCCM_OFFSET, \
- .dccm_len = IWL_22000_DCCM_LEN, \
- .dccm2_offset = IWL_22000_DCCM2_OFFSET, \
- .dccm2_len = IWL_22000_DCCM2_LEN, \
- .smem_offset = IWL_22000_SMEM_OFFSET, \
- .smem_len = IWL_22000_SMEM_LEN, \
- .features = IWL_TX_CSUM_NETIF_FLAGS | NETIF_F_RXCSUM, \
- .apmg_not_supported = true, \
- .trans.mq_rx_supported = true, \
- .vht_mu_mimo_supported = true, \
- .mac_addr_from_csr = 0x380, \
- .ht_params = &iwl_22000_ht_params, \
- .nvm_ver = IWL_22000_NVM_VERSION, \
- .trans.rf_id = true, \
- .trans.gen2 = true, \
- .nvm_type = IWL_NVM_EXT, \
- .dbgc_supported = true, \
- .min_umac_error_event_table = 0x400000, \
- .d3_debug_data_base_addr = 0x401000, \
- .d3_debug_data_length = 60 * 1024, \
- .mon_smem_regs = { \
- .write_ptr = { \
- .addr = LDBG_M2S_BUF_WPTR, \
- .mask = LDBG_M2S_BUF_WPTR_VAL_MSK, \
- }, \
- .cycle_cnt = { \
- .addr = LDBG_M2S_BUF_WRAP_CNT, \
- .mask = LDBG_M2S_BUF_WRAP_CNT_VAL_MSK, \
- }, \
- }
-
-#define IWL_DEVICE_22500 \
- IWL_DEVICE_22000_COMMON, \
- .ucode_api_max = IWL_22000_UCODE_API_MAX, \
- .trans.device_family = IWL_DEVICE_FAMILY_22000, \
- .trans.base_params = &iwl_22000_base_params, \
- .gp2_reg_addr = 0xa02c68, \
- .mon_dram_regs = { \
- .write_ptr = { \
- .addr = MON_BUFF_WRPTR_VER2, \
- .mask = 0xffffffff, \
- }, \
- .cycle_cnt = { \
- .addr = MON_BUFF_CYCLE_CNT_VER2, \
- .mask = 0xffffffff, \
- }, \
- }
-
-const struct iwl_cfg_trans_params iwl_qu_trans_cfg = {
+ .smem_offset = IWL_22000_SMEM_OFFSET,
+ .smem_len = IWL_22000_SMEM_LEN,
+ .features = IWL_TX_CSUM_NETIF_FLAGS | NETIF_F_RXCSUM,
+ .apmg_not_supported = true,
+ .mac_addr_from_csr = 0x380,
+ .min_umac_error_event_table = 0x400000,
+ .d3_debug_data_base_addr = 0x401000,
+ .d3_debug_data_length = 60 * 1024,
+ .mon_smem_regs = {
+ .write_ptr = {
+ .addr = LDBG_M2S_BUF_WPTR,
+ .mask = LDBG_M2S_BUF_WPTR_VAL_MSK,
+ },
+ .cycle_cnt = {
+ .addr = LDBG_M2S_BUF_WRAP_CNT,
+ .mask = LDBG_M2S_BUF_WRAP_CNT_VAL_MSK,
+ },
+ },
+ .gp2_reg_addr = 0xa02c68,
+ .mon_dram_regs = {
+ .write_ptr = {
+ .addr = MON_BUFF_WRPTR_VER2,
+ .mask = 0xffffffff,
+ },
+ .cycle_cnt = {
+ .addr = MON_BUFF_CYCLE_CNT_VER2,
+ .mask = 0xffffffff,
+ },
+ },
+ .ucode_api_min = IWL_22000_UCODE_API_MIN,
+ .ucode_api_max = IWL_22000_UCODE_API_MAX,
+};
+
+const struct iwl_mac_cfg iwl_qu_mac_cfg = {
.mq_rx_supported = true,
- .rf_id = true,
.gen2 = true,
.device_family = IWL_DEVICE_FAMILY_22000,
- .base_params = &iwl_22000_base_params,
+ .base = &iwl_22000_base,
.integrated = true,
.xtal_latency = 500,
.ltr_delay = IWL_CFG_TRANS_LTR_DELAY_200US,
};
-const struct iwl_cfg_trans_params iwl_qu_medium_latency_trans_cfg = {
+const struct iwl_mac_cfg iwl_qu_medium_latency_mac_cfg = {
.mq_rx_supported = true,
- .rf_id = true,
.gen2 = true,
.device_family = IWL_DEVICE_FAMILY_22000,
- .base_params = &iwl_22000_base_params,
+ .base = &iwl_22000_base,
.integrated = true,
.xtal_latency = 1820,
.ltr_delay = IWL_CFG_TRANS_LTR_DELAY_1820US,
};
-const struct iwl_cfg_trans_params iwl_qu_long_latency_trans_cfg = {
+const struct iwl_mac_cfg iwl_qu_long_latency_mac_cfg = {
.mq_rx_supported = true,
- .rf_id = true,
.gen2 = true,
.device_family = IWL_DEVICE_FAMILY_22000,
- .base_params = &iwl_22000_base_params,
+ .base = &iwl_22000_base,
.integrated = true,
.xtal_latency = 12000,
.low_latency_xtal = true,
.ltr_delay = IWL_CFG_TRANS_LTR_DELAY_2500US,
};
-/*
- * If the device doesn't support HE, no need to have that many buffers.
- * 22000 devices can split multiple frames into a single RB, so fewer are
- * needed; AX210 cannot (but use smaller RBs by default) - these sizes
- * were picked according to 8 MSDUs inside 256 A-MSDUs in an A-MPDU, with
- * additional overhead to account for processing time.
- */
-#define IWL_NUM_RBDS_NON_HE 512
-#define IWL_NUM_RBDS_22000_HE 2048
-
-/*
- * All JF radio modules are part of the 9000 series, but the MAC part
- * looks more like 22000. That's why this device is here, but called
- * 9560 nevertheless.
- */
-const struct iwl_cfg iwl9560_qu_b0_jf_b0_cfg = {
- .fw_name_pre = IWL_QU_B_JF_B_FW_PRE,
- IWL_DEVICE_22500,
- .num_rbds = IWL_NUM_RBDS_NON_HE,
-};
-
-const struct iwl_cfg iwl9560_qu_c0_jf_b0_cfg = {
- .fw_name_pre = IWL_QU_C_JF_B_FW_PRE,
- IWL_DEVICE_22500,
- .num_rbds = IWL_NUM_RBDS_NON_HE,
-};
-
-const struct iwl_cfg iwl9560_quz_a0_jf_b0_cfg = {
- .fw_name_pre = IWL_QUZ_A_JF_B_FW_PRE,
- IWL_DEVICE_22500,
- /*
- * This device doesn't support receiving BlockAck with a large bitmap
- * so we need to restrict the size of transmitted aggregation to the
- * HT size; mac80211 would otherwise pick the HE max (256) by default.
- */
- .max_tx_agg_size = IEEE80211_MAX_AMPDU_BUF_HT,
- .num_rbds = IWL_NUM_RBDS_NON_HE,
-};
-
-const struct iwl_cfg_trans_params iwl_ax200_trans_cfg = {
+const struct iwl_mac_cfg iwl_ax200_mac_cfg = {
.device_family = IWL_DEVICE_FAMILY_22000,
- .base_params = &iwl_22000_base_params,
+ .base = &iwl_22000_base,
.mq_rx_supported = true,
- .rf_id = true,
.gen2 = true,
.bisr_workaround = 1,
};
-const char iwl_ax101_name[] = "Intel(R) Wi-Fi 6 AX101";
-const char iwl_ax200_name[] = "Intel(R) Wi-Fi 6 AX200 160MHz";
-const char iwl_ax201_name[] = "Intel(R) Wi-Fi 6 AX201 160MHz";
-const char iwl_ax203_name[] = "Intel(R) Wi-Fi 6 AX203";
-const char iwl_ax204_name[] = "Intel(R) Wi-Fi 6 AX204 160MHz";
-
const char iwl_ax200_killer_1650w_name[] =
"Killer(R) Wi-Fi 6 AX1650w 160MHz Wireless Network Adapter (200D2W)";
const char iwl_ax200_killer_1650x_name[] =
@@ -216,213 +132,10 @@ const char iwl_ax201_killer_1650s_name[] =
const char iwl_ax201_killer_1650i_name[] =
"Killer(R) Wi-Fi 6 AX1650i 160MHz Wireless Network Adapter (201NGW)";
-const struct iwl_cfg iwl_qu_b0_hr1_b0 = {
- .fw_name_pre = IWL_QU_B_HR_B_FW_PRE,
- IWL_DEVICE_22500,
- /*
- * This device doesn't support receiving BlockAck with a large bitmap
- * so we need to restrict the size of transmitted aggregation to the
- * HT size; mac80211 would otherwise pick the HE max (256) by default.
- */
- .max_tx_agg_size = IEEE80211_MAX_AMPDU_BUF_HT,
- .tx_with_siso_diversity = true,
- .num_rbds = IWL_NUM_RBDS_22000_HE,
-};
-
-const struct iwl_cfg iwl_qu_b0_hr_b0 = {
- .fw_name_pre = IWL_QU_B_HR_B_FW_PRE,
- IWL_DEVICE_22500,
- /*
- * This device doesn't support receiving BlockAck with a large bitmap
- * so we need to restrict the size of transmitted aggregation to the
- * HT size; mac80211 would otherwise pick the HE max (256) by default.
- */
- .max_tx_agg_size = IEEE80211_MAX_AMPDU_BUF_HT,
- .num_rbds = IWL_NUM_RBDS_22000_HE,
-};
-
-const struct iwl_cfg iwl_ax201_cfg_qu_hr = {
- .name = "Intel(R) Wi-Fi 6 AX201 160MHz",
- .fw_name_pre = IWL_QU_B_HR_B_FW_PRE,
- IWL_DEVICE_22500,
- /*
- * This device doesn't support receiving BlockAck with a large bitmap
- * so we need to restrict the size of transmitted aggregation to the
- * HT size; mac80211 would otherwise pick the HE max (256) by default.
- */
- .max_tx_agg_size = IEEE80211_MAX_AMPDU_BUF_HT,
- .num_rbds = IWL_NUM_RBDS_22000_HE,
-};
-
-const struct iwl_cfg iwl_qu_c0_hr1_b0 = {
- .fw_name_pre = IWL_QU_C_HR_B_FW_PRE,
- IWL_DEVICE_22500,
- /*
- * This device doesn't support receiving BlockAck with a large bitmap
- * so we need to restrict the size of transmitted aggregation to the
- * HT size; mac80211 would otherwise pick the HE max (256) by default.
- */
- .max_tx_agg_size = IEEE80211_MAX_AMPDU_BUF_HT,
- .tx_with_siso_diversity = true,
- .num_rbds = IWL_NUM_RBDS_22000_HE,
-};
-
-const struct iwl_cfg iwl_qu_c0_hr_b0 = {
- .fw_name_pre = IWL_QU_C_HR_B_FW_PRE,
- IWL_DEVICE_22500,
- /*
- * This device doesn't support receiving BlockAck with a large bitmap
- * so we need to restrict the size of transmitted aggregation to the
- * HT size; mac80211 would otherwise pick the HE max (256) by default.
- */
- .max_tx_agg_size = IEEE80211_MAX_AMPDU_BUF_HT,
- .num_rbds = IWL_NUM_RBDS_22000_HE,
-};
-
-const struct iwl_cfg iwl_ax201_cfg_qu_c0_hr_b0 = {
- .name = "Intel(R) Wi-Fi 6 AX201 160MHz",
- .fw_name_pre = IWL_QU_C_HR_B_FW_PRE,
- IWL_DEVICE_22500,
- /*
- * This device doesn't support receiving BlockAck with a large bitmap
- * so we need to restrict the size of transmitted aggregation to the
- * HT size; mac80211 would otherwise pick the HE max (256) by default.
- */
- .max_tx_agg_size = IEEE80211_MAX_AMPDU_BUF_HT,
- .num_rbds = IWL_NUM_RBDS_22000_HE,
-};
-
-const struct iwl_cfg iwl_quz_a0_hr1_b0 = {
- .fw_name_pre = IWL_QUZ_A_HR_B_FW_PRE,
- IWL_DEVICE_22500,
- /*
- * This device doesn't support receiving BlockAck with a large bitmap
- * so we need to restrict the size of transmitted aggregation to the
- * HT size; mac80211 would otherwise pick the HE max (256) by default.
- */
- .max_tx_agg_size = IEEE80211_MAX_AMPDU_BUF_HT,
- .tx_with_siso_diversity = true,
- .num_rbds = IWL_NUM_RBDS_22000_HE,
-};
-
-const struct iwl_cfg iwl_ax201_cfg_quz_hr = {
- .name = "Intel(R) Wi-Fi 6 AX201 160MHz",
- .fw_name_pre = IWL_QUZ_A_HR_B_FW_PRE,
- IWL_DEVICE_22500,
- /*
- * This device doesn't support receiving BlockAck with a large bitmap
- * so we need to restrict the size of transmitted aggregation to the
- * HT size; mac80211 would otherwise pick the HE max (256) by default.
- */
- .max_tx_agg_size = IEEE80211_MAX_AMPDU_BUF_HT,
- .num_rbds = IWL_NUM_RBDS_22000_HE,
-};
-
-const struct iwl_cfg iwl_ax1650s_cfg_quz_hr = {
- .name = "Killer(R) Wi-Fi 6 AX1650s 160MHz Wireless Network Adapter (201D2W)",
- .fw_name_pre = IWL_QUZ_A_HR_B_FW_PRE,
- IWL_DEVICE_22500,
- /*
- * This device doesn't support receiving BlockAck with a large bitmap
- * so we need to restrict the size of transmitted aggregation to the
- * HT size; mac80211 would otherwise pick the HE max (256) by default.
- */
- .max_tx_agg_size = IEEE80211_MAX_AMPDU_BUF_HT,
- .num_rbds = IWL_NUM_RBDS_22000_HE,
-};
-
-const struct iwl_cfg iwl_ax1650i_cfg_quz_hr = {
- .name = "Killer(R) Wi-Fi 6 AX1650i 160MHz Wireless Network Adapter (201NGW)",
- .fw_name_pre = IWL_QUZ_A_HR_B_FW_PRE,
- IWL_DEVICE_22500,
- /*
- * This device doesn't support receiving BlockAck with a large bitmap
- * so we need to restrict the size of transmitted aggregation to the
- * HT size; mac80211 would otherwise pick the HE max (256) by default.
- */
- .max_tx_agg_size = IEEE80211_MAX_AMPDU_BUF_HT,
- .num_rbds = IWL_NUM_RBDS_22000_HE,
-};
-
-const struct iwl_cfg iwl_ax200_cfg_cc = {
- .fw_name_pre = IWL_CC_A_FW_PRE,
- IWL_DEVICE_22500,
- /*
- * This device doesn't support receiving BlockAck with a large bitmap
- * so we need to restrict the size of transmitted aggregation to the
- * HT size; mac80211 would otherwise pick the HE max (256) by default.
- */
- .max_tx_agg_size = IEEE80211_MAX_AMPDU_BUF_HT,
- .num_rbds = IWL_NUM_RBDS_22000_HE,
-};
-
-const struct iwl_cfg killer1650s_2ax_cfg_qu_b0_hr_b0 = {
- .name = "Killer(R) Wi-Fi 6 AX1650s 160MHz Wireless Network Adapter (201NGW)",
- .fw_name_pre = IWL_QU_B_HR_B_FW_PRE,
- IWL_DEVICE_22500,
- /*
- * This device doesn't support receiving BlockAck with a large bitmap
- * so we need to restrict the size of transmitted aggregation to the
- * HT size; mac80211 would otherwise pick the HE max (256) by default.
- */
- .max_tx_agg_size = IEEE80211_MAX_AMPDU_BUF_HT,
- .num_rbds = IWL_NUM_RBDS_22000_HE,
-};
-
-const struct iwl_cfg killer1650i_2ax_cfg_qu_b0_hr_b0 = {
- .name = "Killer(R) Wi-Fi 6 AX1650i 160MHz Wireless Network Adapter (201D2W)",
- .fw_name_pre = IWL_QU_B_HR_B_FW_PRE,
- IWL_DEVICE_22500,
- /*
- * This device doesn't support receiving BlockAck with a large bitmap
- * so we need to restrict the size of transmitted aggregation to the
- * HT size; mac80211 would otherwise pick the HE max (256) by default.
- */
- .max_tx_agg_size = IEEE80211_MAX_AMPDU_BUF_HT,
- .num_rbds = IWL_NUM_RBDS_22000_HE,
-};
-
-const struct iwl_cfg killer1650s_2ax_cfg_qu_c0_hr_b0 = {
- .name = "Killer(R) Wi-Fi 6 AX1650s 160MHz Wireless Network Adapter (201NGW)",
- .fw_name_pre = IWL_QU_C_HR_B_FW_PRE,
- IWL_DEVICE_22500,
- /*
- * This device doesn't support receiving BlockAck with a large bitmap
- * so we need to restrict the size of transmitted aggregation to the
- * HT size; mac80211 would otherwise pick the HE max (256) by default.
- */
- .max_tx_agg_size = IEEE80211_MAX_AMPDU_BUF_HT,
- .num_rbds = IWL_NUM_RBDS_22000_HE,
-};
-
-const struct iwl_cfg killer1650i_2ax_cfg_qu_c0_hr_b0 = {
- .name = "Killer(R) Wi-Fi 6 AX1650i 160MHz Wireless Network Adapter (201D2W)",
- .fw_name_pre = IWL_QU_C_HR_B_FW_PRE,
- IWL_DEVICE_22500,
- /*
- * This device doesn't support receiving BlockAck with a large bitmap
- * so we need to restrict the size of transmitted aggregation to the
- * HT size; mac80211 would otherwise pick the HE max (256) by default.
- */
- .max_tx_agg_size = IEEE80211_MAX_AMPDU_BUF_HT,
- .num_rbds = IWL_NUM_RBDS_22000_HE,
-};
-
-const struct iwl_cfg iwl_cfg_quz_a0_hr_b0 = {
- .fw_name_pre = IWL_QUZ_A_HR_B_FW_PRE,
- IWL_DEVICE_22500,
- /*
- * This device doesn't support receiving BlockAck with a large bitmap
- * so we need to restrict the size of transmitted aggregation to the
- * HT size; mac80211 would otherwise pick the HE max (256) by default.
- */
- .max_tx_agg_size = IEEE80211_MAX_AMPDU_BUF_HT,
- .num_rbds = IWL_NUM_RBDS_22000_HE,
-};
-
MODULE_FIRMWARE(IWL_QU_B_HR_B_MODULE_FIRMWARE(IWL_22000_UCODE_API_MAX));
MODULE_FIRMWARE(IWL_QU_C_HR_B_MODULE_FIRMWARE(IWL_22000_UCODE_API_MAX));
MODULE_FIRMWARE(IWL_QU_B_JF_B_MODULE_FIRMWARE(IWL_22000_UCODE_API_MAX));
+MODULE_FIRMWARE(IWL_QU_C_JF_B_MODULE_FIRMWARE(IWL_22000_UCODE_API_MAX));
MODULE_FIRMWARE(IWL_QUZ_A_HR_B_MODULE_FIRMWARE(IWL_22000_UCODE_API_MAX));
MODULE_FIRMWARE(IWL_QUZ_A_JF_B_MODULE_FIRMWARE(IWL_22000_UCODE_API_MAX));
MODULE_FIRMWARE(IWL_CC_A_MODULE_FIRMWARE(IWL_22000_UCODE_API_MAX));
diff --git a/drivers/net/wireless/intel/iwlwifi/cfg/5000.c b/drivers/net/wireless/intel/iwlwifi/cfg/5000.c
index de7ede59a994..aaae3b1f5433 100644
--- a/drivers/net/wireless/intel/iwlwifi/cfg/5000.c
+++ b/drivers/net/wireless/intel/iwlwifi/cfg/5000.c
@@ -2,7 +2,7 @@
/******************************************************************************
*
* Copyright(c) 2007 - 2014 Intel Corporation. All rights reserved.
- * Copyright(c) 2018 - 2020, 2023 Intel Corporation
+ * Copyright(c) 2018 - 2020, 2023, 2025 Intel Corporation
*****************************************************************************/
#include <linux/module.h>
@@ -30,7 +30,7 @@
#define IWL5150_FW_PRE "iwlwifi-5150"
#define IWL5150_MODULE_FIRMWARE(api) IWL5150_FW_PRE "-" __stringify(api) ".ucode"
-static const struct iwl_base_params iwl5000_base_params = {
+static const struct iwl_family_base_params iwl5000_base = {
.eeprom_size = IWLAGN_EEPROM_IMG_SIZE,
.num_of_queues = IWLAGN_NUM_QUEUES,
.max_tfd_queue_size = 256,
@@ -41,11 +41,6 @@ static const struct iwl_base_params iwl5000_base_params = {
.scd_chain_ext_wa = true,
};
-static const struct iwl_ht_params iwl5000_ht_params = {
- .ht_greenfield_support = true,
- .ht40_bands = BIT(NL80211_BAND_2GHZ) | BIT(NL80211_BAND_5GHZ),
-};
-
static const struct iwl_eeprom_params iwl5000_eeprom_params = {
.regulatory_bands = {
EEPROM_REG_BAND_1_CHANNELS,
@@ -58,93 +53,107 @@ static const struct iwl_eeprom_params iwl5000_eeprom_params = {
},
};
+const struct iwl_mac_cfg iwl5000_mac_cfg = {
+ .device_family = IWL_DEVICE_FAMILY_5000,
+ .base = &iwl5000_base,
+};
+
#define IWL_DEVICE_5000 \
.fw_name_pre = IWL5000_FW_PRE, \
.ucode_api_max = IWL5000_UCODE_API_MAX, \
.ucode_api_min = IWL5000_UCODE_API_MIN, \
- .trans.device_family = IWL_DEVICE_FAMILY_5000, \
.max_inst_size = IWLAGN_RTC_INST_SIZE, \
.max_data_size = IWLAGN_RTC_DATA_SIZE, \
.nvm_ver = EEPROM_5000_EEPROM_VERSION, \
.nvm_calib_ver = EEPROM_5000_TX_POWER_VERSION, \
- .trans.base_params = &iwl5000_base_params, \
.eeprom_params = &iwl5000_eeprom_params, \
.led_mode = IWL_LED_BLINK
-const struct iwl_cfg iwl5300_agn_cfg = {
- .name = "Intel(R) Ultimate N WiFi Link 5300 AGN",
+const struct iwl_rf_cfg iwl5300_agn_cfg = {
IWL_DEVICE_5000,
/* at least EEPROM 0x11A has wrong info */
.valid_tx_ant = ANT_ABC, /* .cfg overwrite */
.valid_rx_ant = ANT_ABC, /* .cfg overwrite */
- .ht_params = &iwl5000_ht_params,
+ .ht_params = {
+ .ht_greenfield_support = true,
+ .ht40_bands = BIT(NL80211_BAND_2GHZ) | BIT(NL80211_BAND_5GHZ),
+ },
};
-const struct iwl_cfg iwl5100_bgn_cfg = {
- .name = "Intel(R) WiFi Link 5100 BGN",
- IWL_DEVICE_5000,
- .valid_tx_ant = ANT_B, /* .cfg overwrite */
- .valid_rx_ant = ANT_AB, /* .cfg overwrite */
- .ht_params = &iwl5000_ht_params,
-};
+const char iwl5300_agn_name[] = "Intel(R) Ultimate N WiFi Link 5300 AGN";
-const struct iwl_cfg iwl5100_abg_cfg = {
- .name = "Intel(R) WiFi Link 5100 ABG",
+const struct iwl_rf_cfg iwl5100_n_cfg = {
IWL_DEVICE_5000,
.valid_tx_ant = ANT_B, /* .cfg overwrite */
.valid_rx_ant = ANT_AB, /* .cfg overwrite */
+ .ht_params = {
+ .ht_greenfield_support = true,
+ .ht40_bands = BIT(NL80211_BAND_2GHZ) | BIT(NL80211_BAND_5GHZ),
+ },
};
-const struct iwl_cfg iwl5100_agn_cfg = {
- .name = "Intel(R) WiFi Link 5100 AGN",
+const char iwl5100_bgn_name[] = "Intel(R) WiFi Link 5100 BGN";
+
+const struct iwl_rf_cfg iwl5100_abg_cfg = {
IWL_DEVICE_5000,
.valid_tx_ant = ANT_B, /* .cfg overwrite */
.valid_rx_ant = ANT_AB, /* .cfg overwrite */
- .ht_params = &iwl5000_ht_params,
};
-const struct iwl_cfg iwl5350_agn_cfg = {
- .name = "Intel(R) WiMAX/WiFi Link 5350 AGN",
+const char iwl5100_abg_name[] = "Intel(R) WiFi Link 5100 ABG";
+const char iwl5100_agn_name[] = "Intel(R) WiFi Link 5100 AGN";
+
+const struct iwl_rf_cfg iwl5350_agn_cfg = {
.fw_name_pre = IWL5000_FW_PRE,
.ucode_api_max = IWL5000_UCODE_API_MAX,
.ucode_api_min = IWL5000_UCODE_API_MIN,
- .trans.device_family = IWL_DEVICE_FAMILY_5000,
.max_inst_size = IWLAGN_RTC_INST_SIZE,
.max_data_size = IWLAGN_RTC_DATA_SIZE,
.nvm_ver = EEPROM_5050_EEPROM_VERSION,
.nvm_calib_ver = EEPROM_5050_TX_POWER_VERSION,
- .trans.base_params = &iwl5000_base_params,
.eeprom_params = &iwl5000_eeprom_params,
- .ht_params = &iwl5000_ht_params,
+ .ht_params = {
+ .ht_greenfield_support = true,
+ .ht40_bands = BIT(NL80211_BAND_2GHZ) | BIT(NL80211_BAND_5GHZ),
+ },
.led_mode = IWL_LED_BLINK,
.internal_wimax_coex = true,
};
+const char iwl5350_agn_name[] = "Intel(R) WiMAX/WiFi Link 5350 AGN";
+
+const struct iwl_mac_cfg iwl5150_mac_cfg = {
+ .device_family = IWL_DEVICE_FAMILY_5150,
+ .base = &iwl5000_base,
+};
+
#define IWL_DEVICE_5150 \
.fw_name_pre = IWL5150_FW_PRE, \
.ucode_api_max = IWL5150_UCODE_API_MAX, \
.ucode_api_min = IWL5150_UCODE_API_MIN, \
- .trans.device_family = IWL_DEVICE_FAMILY_5150, \
.max_inst_size = IWLAGN_RTC_INST_SIZE, \
.max_data_size = IWLAGN_RTC_DATA_SIZE, \
.nvm_ver = EEPROM_5050_EEPROM_VERSION, \
.nvm_calib_ver = EEPROM_5050_TX_POWER_VERSION, \
- .trans.base_params = &iwl5000_base_params, \
.eeprom_params = &iwl5000_eeprom_params, \
.led_mode = IWL_LED_BLINK, \
.internal_wimax_coex = true
-const struct iwl_cfg iwl5150_agn_cfg = {
- .name = "Intel(R) WiMAX/WiFi Link 5150 AGN",
+const struct iwl_rf_cfg iwl5150_agn_cfg = {
IWL_DEVICE_5150,
- .ht_params = &iwl5000_ht_params,
-
+ .ht_params = {
+ .ht_greenfield_support = true,
+ .ht40_bands = BIT(NL80211_BAND_2GHZ) | BIT(NL80211_BAND_5GHZ),
+ },
};
-const struct iwl_cfg iwl5150_abg_cfg = {
- .name = "Intel(R) WiMAX/WiFi Link 5150 ABG",
+const char iwl5150_agn_name[] = "Intel(R) WiMAX/WiFi Link 5150 AGN";
+
+const struct iwl_rf_cfg iwl5150_abg_cfg = {
IWL_DEVICE_5150,
};
+const char iwl5150_abg_name[] = "Intel(R) WiMAX/WiFi Link 5150 ABG";
+
MODULE_FIRMWARE(IWL5000_MODULE_FIRMWARE(IWL5000_UCODE_API_MAX));
MODULE_FIRMWARE(IWL5150_MODULE_FIRMWARE(IWL5150_UCODE_API_MAX));
diff --git a/drivers/net/wireless/intel/iwlwifi/cfg/6000.c b/drivers/net/wireless/intel/iwlwifi/cfg/6000.c
index f013cf420569..ab13394896e0 100644
--- a/drivers/net/wireless/intel/iwlwifi/cfg/6000.c
+++ b/drivers/net/wireless/intel/iwlwifi/cfg/6000.c
@@ -2,7 +2,7 @@
/******************************************************************************
*
* Copyright(c) 2008 - 2014 Intel Corporation. All rights reserved.
- * Copyright(c) 2018 - 2020, 2023 Intel Corporation
+ * Copyright(c) 2018 - 2020, 2023, 2025 Intel Corporation
*****************************************************************************/
#include <linux/module.h>
@@ -49,7 +49,7 @@
#define IWL6030_FW_PRE "iwlwifi-6000g2b"
#define IWL6030_MODULE_FIRMWARE(api) IWL6030_FW_PRE "-" __stringify(api) ".ucode"
-static const struct iwl_base_params iwl6000_base_params = {
+static const struct iwl_family_base_params iwl6000_base = {
.eeprom_size = OTP_LOW_IMAGE_SIZE_2K,
.num_of_queues = IWLAGN_NUM_QUEUES,
.max_tfd_queue_size = 256,
@@ -62,7 +62,7 @@ static const struct iwl_base_params iwl6000_base_params = {
.scd_chain_ext_wa = true,
};
-static const struct iwl_base_params iwl6050_base_params = {
+static const struct iwl_family_base_params iwl6050_base = {
.eeprom_size = OTP_LOW_IMAGE_SIZE_2K,
.num_of_queues = IWLAGN_NUM_QUEUES,
.max_tfd_queue_size = 256,
@@ -75,7 +75,7 @@ static const struct iwl_base_params iwl6050_base_params = {
.scd_chain_ext_wa = true,
};
-static const struct iwl_base_params iwl6000_g2_base_params = {
+static const struct iwl_family_base_params iwl6000_g2_base = {
.eeprom_size = OTP_LOW_IMAGE_SIZE_2K,
.num_of_queues = IWLAGN_NUM_QUEUES,
.max_tfd_queue_size = 256,
@@ -88,12 +88,6 @@ static const struct iwl_base_params iwl6000_g2_base_params = {
.scd_chain_ext_wa = true,
};
-static const struct iwl_ht_params iwl6000_ht_params = {
- .ht_greenfield_support = true,
- .use_rts_for_aggregation = true, /* use rts/cts protection */
- .ht40_bands = BIT(NL80211_BAND_2GHZ) | BIT(NL80211_BAND_5GHZ),
-};
-
static const struct iwl_eeprom_params iwl6000_eeprom_params = {
.regulatory_bands = {
EEPROM_REG_BAND_1_CHANNELS,
@@ -107,141 +101,126 @@ static const struct iwl_eeprom_params iwl6000_eeprom_params = {
.enhanced_txpower = true,
};
+const struct iwl_mac_cfg iwl6005_mac_cfg = {
+ .device_family = IWL_DEVICE_FAMILY_6005,
+ .base = &iwl6000_g2_base,
+};
+
#define IWL_DEVICE_6005 \
.fw_name_pre = IWL6005_FW_PRE, \
.ucode_api_max = IWL6000G2_UCODE_API_MAX, \
.ucode_api_min = IWL6000G2_UCODE_API_MIN, \
- .trans.device_family = IWL_DEVICE_FAMILY_6005, \
.max_inst_size = IWL60_RTC_INST_SIZE, \
.max_data_size = IWL60_RTC_DATA_SIZE, \
.nvm_ver = EEPROM_6005_EEPROM_VERSION, \
.nvm_calib_ver = EEPROM_6005_TX_POWER_VERSION, \
- .trans.base_params = &iwl6000_g2_base_params, \
.eeprom_params = &iwl6000_eeprom_params, \
.led_mode = IWL_LED_RF_STATE
-const struct iwl_cfg iwl6005_2agn_cfg = {
- .name = "Intel(R) Centrino(R) Advanced-N 6205 AGN",
- IWL_DEVICE_6005,
- .ht_params = &iwl6000_ht_params,
-};
-
-const struct iwl_cfg iwl6005_2abg_cfg = {
- .name = "Intel(R) Centrino(R) Advanced-N 6205 ABG",
- IWL_DEVICE_6005,
-};
-
-const struct iwl_cfg iwl6005_2bg_cfg = {
- .name = "Intel(R) Centrino(R) Advanced-N 6205 BG",
+const struct iwl_rf_cfg iwl6005_n_cfg = {
IWL_DEVICE_6005,
+ .ht_params = {
+ .ht_greenfield_support = true,
+ .use_rts_for_aggregation = true, /* use rts/cts protection */
+ .ht40_bands = BIT(NL80211_BAND_2GHZ) | BIT(NL80211_BAND_5GHZ),
+ },
};
-const struct iwl_cfg iwl6005_2agn_sff_cfg = {
- .name = "Intel(R) Centrino(R) Advanced-N 6205S AGN",
- IWL_DEVICE_6005,
- .ht_params = &iwl6000_ht_params,
-};
+const char iwl6005_2agn_name[] = "Intel(R) Centrino(R) Advanced-N 6205 AGN";
+const char iwl6005_2agn_sff_name[] = "Intel(R) Centrino(R) Advanced-N 6205S AGN";
+const char iwl6005_2agn_d_name[] = "Intel(R) Centrino(R) Advanced-N 6205D AGN";
+const char iwl6005_2agn_mow1_name[] = "Intel(R) Centrino(R) Advanced-N 6206 AGN";
+const char iwl6005_2agn_mow2_name[] = "Intel(R) Centrino(R) Advanced-N 6207 AGN";
-const struct iwl_cfg iwl6005_2agn_d_cfg = {
- .name = "Intel(R) Centrino(R) Advanced-N 6205D AGN",
+const struct iwl_rf_cfg iwl6005_non_n_cfg = {
IWL_DEVICE_6005,
- .ht_params = &iwl6000_ht_params,
};
-const struct iwl_cfg iwl6005_2agn_mow1_cfg = {
- .name = "Intel(R) Centrino(R) Advanced-N 6206 AGN",
- IWL_DEVICE_6005,
- .ht_params = &iwl6000_ht_params,
-};
+const char iwl6005_2abg_name[] = "Intel(R) Centrino(R) Advanced-N 6205 ABG";
+const char iwl6005_2bg_name[] = "Intel(R) Centrino(R) Advanced-N 6205 BG";
-const struct iwl_cfg iwl6005_2agn_mow2_cfg = {
- .name = "Intel(R) Centrino(R) Advanced-N 6207 AGN",
- IWL_DEVICE_6005,
- .ht_params = &iwl6000_ht_params,
+const struct iwl_mac_cfg iwl6030_mac_cfg = {
+ .device_family = IWL_DEVICE_FAMILY_6030,
+ .base = &iwl6000_g2_base,
};
#define IWL_DEVICE_6030 \
.fw_name_pre = IWL6030_FW_PRE, \
.ucode_api_max = IWL6000G2_UCODE_API_MAX, \
.ucode_api_min = IWL6000G2_UCODE_API_MIN, \
- .trans.device_family = IWL_DEVICE_FAMILY_6030, \
.max_inst_size = IWL60_RTC_INST_SIZE, \
.max_data_size = IWL60_RTC_DATA_SIZE, \
.nvm_ver = EEPROM_6030_EEPROM_VERSION, \
.nvm_calib_ver = EEPROM_6030_TX_POWER_VERSION, \
- .trans.base_params = &iwl6000_g2_base_params, \
.eeprom_params = &iwl6000_eeprom_params, \
.led_mode = IWL_LED_RF_STATE
-const struct iwl_cfg iwl6030_2agn_cfg = {
- .name = "Intel(R) Centrino(R) Advanced-N 6230 AGN",
+const struct iwl_rf_cfg iwl6030_n_cfg = {
IWL_DEVICE_6030,
- .ht_params = &iwl6000_ht_params,
+ .ht_params = {
+ .ht_greenfield_support = true,
+ .use_rts_for_aggregation = true, /* use rts/cts protection */
+ .ht40_bands = BIT(NL80211_BAND_2GHZ) | BIT(NL80211_BAND_5GHZ),
+ },
};
-const struct iwl_cfg iwl6030_2abg_cfg = {
- .name = "Intel(R) Centrino(R) Advanced-N 6230 ABG",
- IWL_DEVICE_6030,
-};
+const char iwl6030_2agn_name[] = "Intel(R) Centrino(R) Advanced-N 6230 AGN";
+const char iwl6030_2bgn_name[] = "Intel(R) Centrino(R) Advanced-N 6230 BGN";
+const char iwl1030_bgn_name[] = "Intel(R) Centrino(R) Wireless-N 1030 BGN";
+const char iwl1030_bg_name[] = "Intel(R) Centrino(R) Wireless-N 1030 BG";
-const struct iwl_cfg iwl6030_2bgn_cfg = {
- .name = "Intel(R) Centrino(R) Advanced-N 6230 BGN",
+const struct iwl_rf_cfg iwl6030_non_n_cfg = {
IWL_DEVICE_6030,
- .ht_params = &iwl6000_ht_params,
};
-const struct iwl_cfg iwl6030_2bg_cfg = {
- .name = "Intel(R) Centrino(R) Advanced-N 6230 BG",
- IWL_DEVICE_6030,
-};
+const char iwl6030_2abg_name[] = "Intel(R) Centrino(R) Advanced-N 6230 ABG";
+const char iwl6030_2bg_name[] = "Intel(R) Centrino(R) Advanced-N 6230 BG";
#define IWL_DEVICE_6035 \
.fw_name_pre = IWL6030_FW_PRE, \
.ucode_api_max = IWL6035_UCODE_API_MAX, \
.ucode_api_min = IWL6035_UCODE_API_MIN, \
- .trans.device_family = IWL_DEVICE_FAMILY_6030, \
.max_inst_size = IWL60_RTC_INST_SIZE, \
.max_data_size = IWL60_RTC_DATA_SIZE, \
.nvm_ver = EEPROM_6030_EEPROM_VERSION, \
.nvm_calib_ver = EEPROM_6030_TX_POWER_VERSION, \
- .trans.base_params = &iwl6000_g2_base_params, \
.eeprom_params = &iwl6000_eeprom_params, \
.led_mode = IWL_LED_RF_STATE
-const struct iwl_cfg iwl6035_2agn_cfg = {
- .name = "Intel(R) Centrino(R) Advanced-N 6235 AGN",
+const struct iwl_rf_cfg iwl6035_2agn_cfg = {
IWL_DEVICE_6035,
- .ht_params = &iwl6000_ht_params,
+ .ht_params = {
+ .ht_greenfield_support = true,
+ .use_rts_for_aggregation = true, /* use rts/cts protection */
+ .ht40_bands = BIT(NL80211_BAND_2GHZ) | BIT(NL80211_BAND_5GHZ),
+ },
};
-const struct iwl_cfg iwl6035_2agn_sff_cfg = {
- .name = "Intel(R) Centrino(R) Ultimate-N 6235 AGN",
- IWL_DEVICE_6035,
- .ht_params = &iwl6000_ht_params,
-};
+const char iwl6035_2agn_name[] = "Intel(R) Centrino(R) Advanced-N 6235 AGN";
+const char iwl6035_2agn_sff_name[] = "Intel(R) Centrino(R) Ultimate-N 6235 AGN";
-const struct iwl_cfg iwl1030_bgn_cfg = {
- .name = "Intel(R) Centrino(R) Wireless-N 1030 BGN",
+const struct iwl_rf_cfg iwl130_bgn_cfg = {
IWL_DEVICE_6030,
- .ht_params = &iwl6000_ht_params,
+ .ht_params = {
+ .ht_greenfield_support = true,
+ .use_rts_for_aggregation = true, /* use rts/cts protection */
+ .ht40_bands = BIT(NL80211_BAND_2GHZ) | BIT(NL80211_BAND_5GHZ),
+ },
+ .rx_with_siso_diversity = true,
};
-const struct iwl_cfg iwl1030_bg_cfg = {
- .name = "Intel(R) Centrino(R) Wireless-N 1030 BG",
- IWL_DEVICE_6030,
-};
+const char iwl130_bgn_name[] = "Intel(R) Centrino(R) Wireless-N 130 BGN";
-const struct iwl_cfg iwl130_bgn_cfg = {
- .name = "Intel(R) Centrino(R) Wireless-N 130 BGN",
+const struct iwl_rf_cfg iwl130_bg_cfg = {
IWL_DEVICE_6030,
- .ht_params = &iwl6000_ht_params,
.rx_with_siso_diversity = true,
};
-const struct iwl_cfg iwl130_bg_cfg = {
- .name = "Intel(R) Centrino(R) Wireless-N 130 BG",
- IWL_DEVICE_6030,
- .rx_with_siso_diversity = true,
+const char iwl130_bg_name[] = "Intel(R) Centrino(R) Wireless-N 130 BG";
+
+const struct iwl_mac_cfg iwl6000i_mac_cfg = {
+ .device_family = IWL_DEVICE_FAMILY_6000i,
+ .base = &iwl6000_base,
};
/*
@@ -251,101 +230,127 @@ const struct iwl_cfg iwl130_bg_cfg = {
.fw_name_pre = IWL6000_FW_PRE, \
.ucode_api_max = IWL6000_UCODE_API_MAX, \
.ucode_api_min = IWL6000_UCODE_API_MIN, \
- .trans.device_family = IWL_DEVICE_FAMILY_6000i, \
.max_inst_size = IWL60_RTC_INST_SIZE, \
.max_data_size = IWL60_RTC_DATA_SIZE, \
.valid_tx_ant = ANT_BC, /* .cfg overwrite */ \
.valid_rx_ant = ANT_BC, /* .cfg overwrite */ \
.nvm_ver = EEPROM_6000_EEPROM_VERSION, \
.nvm_calib_ver = EEPROM_6000_TX_POWER_VERSION, \
- .trans.base_params = &iwl6000_base_params, \
.eeprom_params = &iwl6000_eeprom_params, \
.led_mode = IWL_LED_BLINK
-const struct iwl_cfg iwl6000i_2agn_cfg = {
- .name = "Intel(R) Centrino(R) Advanced-N 6200 AGN",
+const struct iwl_rf_cfg iwl6000i_2agn_cfg = {
IWL_DEVICE_6000i,
- .ht_params = &iwl6000_ht_params,
+ .ht_params = {
+ .ht_greenfield_support = true,
+ .use_rts_for_aggregation = true, /* use rts/cts protection */
+ .ht40_bands = BIT(NL80211_BAND_2GHZ) | BIT(NL80211_BAND_5GHZ),
+ },
};
-const struct iwl_cfg iwl6000i_2abg_cfg = {
- .name = "Intel(R) Centrino(R) Advanced-N 6200 ABG",
+const char iwl6000i_2agn_name[] = "Intel(R) Centrino(R) Advanced-N 6200 AGN";
+
+const struct iwl_rf_cfg iwl6000i_non_n_cfg = {
IWL_DEVICE_6000i,
};
-const struct iwl_cfg iwl6000i_2bg_cfg = {
- .name = "Intel(R) Centrino(R) Advanced-N 6200 BG",
- IWL_DEVICE_6000i,
+const char iwl6000i_2abg_name[] = "Intel(R) Centrino(R) Advanced-N 6200 ABG";
+const char iwl6000i_2bg_name[] = "Intel(R) Centrino(R) Advanced-N 6200 BG";
+
+const struct iwl_mac_cfg iwl6050_mac_cfg = {
+ .device_family = IWL_DEVICE_FAMILY_6050,
+ .base = &iwl6050_base,
};
#define IWL_DEVICE_6050 \
.fw_name_pre = IWL6050_FW_PRE, \
.ucode_api_max = IWL6050_UCODE_API_MAX, \
.ucode_api_min = IWL6050_UCODE_API_MIN, \
- .trans.device_family = IWL_DEVICE_FAMILY_6050, \
.max_inst_size = IWL60_RTC_INST_SIZE, \
.max_data_size = IWL60_RTC_DATA_SIZE, \
.valid_tx_ant = ANT_AB, /* .cfg overwrite */ \
.valid_rx_ant = ANT_AB, /* .cfg overwrite */ \
.nvm_ver = EEPROM_6050_EEPROM_VERSION, \
.nvm_calib_ver = EEPROM_6050_TX_POWER_VERSION, \
- .trans.base_params = &iwl6050_base_params, \
.eeprom_params = &iwl6000_eeprom_params, \
.led_mode = IWL_LED_BLINK, \
.internal_wimax_coex = true
-const struct iwl_cfg iwl6050_2agn_cfg = {
- .name = "Intel(R) Centrino(R) Advanced-N + WiMAX 6250 AGN",
+const struct iwl_rf_cfg iwl6050_2agn_cfg = {
IWL_DEVICE_6050,
- .ht_params = &iwl6000_ht_params,
+ .ht_params = {
+ .ht_greenfield_support = true,
+ .use_rts_for_aggregation = true, /* use rts/cts protection */
+ .ht40_bands = BIT(NL80211_BAND_2GHZ) | BIT(NL80211_BAND_5GHZ),
+ },
};
-const struct iwl_cfg iwl6050_2abg_cfg = {
- .name = "Intel(R) Centrino(R) Advanced-N + WiMAX 6250 ABG",
+const char iwl6050_2agn_name[] = "Intel(R) Centrino(R) Advanced-N + WiMAX 6250 AGN";
+
+const struct iwl_rf_cfg iwl6050_2abg_cfg = {
IWL_DEVICE_6050,
};
+const char iwl6050_2abg_name[] = "Intel(R) Centrino(R) Advanced-N + WiMAX 6250 ABG";
+
+const struct iwl_mac_cfg iwl6150_mac_cfg = {
+ .device_family = IWL_DEVICE_FAMILY_6150,
+ .base = &iwl6050_base,
+};
+
#define IWL_DEVICE_6150 \
.fw_name_pre = IWL6050_FW_PRE, \
.ucode_api_max = IWL6050_UCODE_API_MAX, \
.ucode_api_min = IWL6050_UCODE_API_MIN, \
- .trans.device_family = IWL_DEVICE_FAMILY_6150, \
.max_inst_size = IWL60_RTC_INST_SIZE, \
.max_data_size = IWL60_RTC_DATA_SIZE, \
.nvm_ver = EEPROM_6150_EEPROM_VERSION, \
.nvm_calib_ver = EEPROM_6150_TX_POWER_VERSION, \
- .trans.base_params = &iwl6050_base_params, \
.eeprom_params = &iwl6000_eeprom_params, \
.led_mode = IWL_LED_BLINK, \
.internal_wimax_coex = true
-const struct iwl_cfg iwl6150_bgn_cfg = {
- .name = "Intel(R) Centrino(R) Wireless-N + WiMAX 6150 BGN",
+const struct iwl_rf_cfg iwl6150_bgn_cfg = {
IWL_DEVICE_6150,
- .ht_params = &iwl6000_ht_params,
+ .ht_params = {
+ .ht_greenfield_support = true,
+ .use_rts_for_aggregation = true, /* use rts/cts protection */
+ .ht40_bands = BIT(NL80211_BAND_2GHZ) | BIT(NL80211_BAND_5GHZ),
+ },
};
-const struct iwl_cfg iwl6150_bg_cfg = {
- .name = "Intel(R) Centrino(R) Wireless-N + WiMAX 6150 BG",
+const char iwl6150_bgn_name[] = "Intel(R) Centrino(R) Wireless-N + WiMAX 6150 BGN";
+
+const struct iwl_rf_cfg iwl6150_bg_cfg = {
IWL_DEVICE_6150,
};
-const struct iwl_cfg iwl6000_3agn_cfg = {
- .name = "Intel(R) Centrino(R) Ultimate-N 6300 AGN",
+const char iwl6150_bg_name[] = "Intel(R) Centrino(R) Wireless-N + WiMAX 6150 BG";
+
+const struct iwl_mac_cfg iwl6000_mac_cfg = {
+ .device_family = IWL_DEVICE_FAMILY_6000,
+ .base = &iwl6000_base,
+};
+
+const struct iwl_rf_cfg iwl6000_3agn_cfg = {
.fw_name_pre = IWL6000_FW_PRE,
.ucode_api_max = IWL6000_UCODE_API_MAX,
.ucode_api_min = IWL6000_UCODE_API_MIN,
- .trans.device_family = IWL_DEVICE_FAMILY_6000,
.max_inst_size = IWL60_RTC_INST_SIZE,
.max_data_size = IWL60_RTC_DATA_SIZE,
.nvm_ver = EEPROM_6000_EEPROM_VERSION,
.nvm_calib_ver = EEPROM_6000_TX_POWER_VERSION,
- .trans.base_params = &iwl6000_base_params,
.eeprom_params = &iwl6000_eeprom_params,
- .ht_params = &iwl6000_ht_params,
+ .ht_params = {
+ .ht_greenfield_support = true,
+ .use_rts_for_aggregation = true, /* use rts/cts protection */
+ .ht40_bands = BIT(NL80211_BAND_2GHZ) | BIT(NL80211_BAND_5GHZ),
+ },
.led_mode = IWL_LED_BLINK,
};
+const char iwl6000_3agn_name[] = "Intel(R) Centrino(R) Ultimate-N 6300 AGN";
+
MODULE_FIRMWARE(IWL6000_MODULE_FIRMWARE(IWL6000_UCODE_API_MAX));
MODULE_FIRMWARE(IWL6050_MODULE_FIRMWARE(IWL6050_UCODE_API_MAX));
MODULE_FIRMWARE(IWL6005_MODULE_FIRMWARE(IWL6000G2_UCODE_API_MAX));
diff --git a/drivers/net/wireless/intel/iwlwifi/cfg/7000.c b/drivers/net/wireless/intel/iwlwifi/cfg/7000.c
index 4e2afdedf4c6..f987ad3192c1 100644
--- a/drivers/net/wireless/intel/iwlwifi/cfg/7000.c
+++ b/drivers/net/wireless/intel/iwlwifi/cfg/7000.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
/*
- * Copyright (C) 2012-2014, 2018-2020, 2023 Intel Corporation
+ * Copyright (C) 2012-2014, 2018-2020, 2023, 2025 Intel Corporation
* Copyright (C) 2013-2014 Intel Mobile Communications GmbH
* Copyright (C) 2015 Intel Deutschland GmbH
*/
@@ -49,7 +49,7 @@
#define IWL7265D_FW_PRE "iwlwifi-7265D"
#define IWL7265D_MODULE_FIRMWARE(api) IWL7265D_FW_PRE "-" __stringify(api) ".ucode"
-static const struct iwl_base_params iwl7000_base_params = {
+static const struct iwl_family_base_params iwl7000_base = {
.eeprom_size = OTP_LOW_IMAGE_SIZE_16K,
.num_of_queues = 31,
.max_tfd_queue_size = 256,
@@ -60,6 +60,7 @@ static const struct iwl_base_params iwl7000_base_params = {
.shadow_reg_enable = true,
.pcie_l1_allowed = true,
.apmg_wake_up_wa = true,
+ .nvm_hw_section_num = 0,
};
static const struct iwl_tt_params iwl7000_high_temp_tt_params = {
@@ -84,16 +85,13 @@ static const struct iwl_tt_params iwl7000_high_temp_tt_params = {
.support_tx_backoff = true,
};
-static const struct iwl_ht_params iwl7000_ht_params = {
- .stbc = true,
- .ht40_bands = BIT(NL80211_BAND_2GHZ) | BIT(NL80211_BAND_5GHZ),
+const struct iwl_mac_cfg iwl7000_mac_cfg = {
+ .device_family = IWL_DEVICE_FAMILY_7000,
+ .base = &iwl7000_base,
};
#define IWL_DEVICE_7000_COMMON \
- .trans.device_family = IWL_DEVICE_FAMILY_7000, \
- .trans.base_params = &iwl7000_base_params, \
.led_mode = IWL_LED_RF_STATE, \
- .nvm_hw_section_num = 0, \
.non_shared_ant = ANT_A, \
.dccm_offset = IWL7000_DCCM_OFFSET
@@ -117,77 +115,52 @@ static const struct iwl_ht_params iwl7000_ht_params = {
.ucode_api_max = IWL7265D_UCODE_API_MAX, \
.ucode_api_min = IWL7265D_UCODE_API_MIN
-const struct iwl_cfg iwl7260_2ac_cfg = {
- .name = "Intel(R) Dual Band Wireless AC 7260",
+const char iwl7260_2ac_name[] = "Intel(R) Dual Band Wireless AC 7260";
+const char iwl7260_2n_name[] = "Intel(R) Dual Band Wireless N 7260";
+const char iwl7260_n_name[] = "Intel(R) Wireless N 7260";
+const char iwl3160_2ac_name[] = "Intel(R) Dual Band Wireless AC 3160";
+const char iwl3160_2n_name[] = "Intel(R) Dual Band Wireless N 3160";
+const char iwl3160_n_name[] = "Intel(R) Wireless N 3160";
+const char iwl3165_2ac_name[] = "Intel(R) Dual Band Wireless-AC 3165";
+const char iwl3168_2ac_name[] = "Intel(R) Dual Band Wireless-AC 3168";
+const char iwl7265_2ac_name[] = "Intel(R) Dual Band Wireless-AC 7265";
+const char iwl7265_2n_name[] = "Intel(R) Dual Band Wireless-N 7265";
+const char iwl7265_n_name[] = "Intel(R) Wireless-N 7265";
+
+const struct iwl_rf_cfg iwl7260_cfg = {
.fw_name_pre = IWL7260_FW_PRE,
IWL_DEVICE_7000,
- .ht_params = &iwl7000_ht_params,
+ .ht_params = {
+ .stbc = true,
+ .ht40_bands = BIT(NL80211_BAND_2GHZ) | BIT(NL80211_BAND_5GHZ),
+ },
.nvm_ver = IWL7260_NVM_VERSION,
.host_interrupt_operation_mode = true,
.lp_xtal_workaround = true,
.dccm_len = IWL7260_DCCM_LEN,
};
-const struct iwl_cfg iwl7260_2ac_cfg_high_temp = {
- .name = "Intel(R) Dual Band Wireless AC 7260",
+const struct iwl_rf_cfg iwl7260_high_temp_cfg = {
.fw_name_pre = IWL7260_FW_PRE,
IWL_DEVICE_7000,
- .ht_params = &iwl7000_ht_params,
+ .ht_params = {
+ .stbc = true,
+ .ht40_bands = BIT(NL80211_BAND_2GHZ) | BIT(NL80211_BAND_5GHZ),
+ },
.nvm_ver = IWL7260_NVM_VERSION,
- .high_temp = true,
.host_interrupt_operation_mode = true,
.lp_xtal_workaround = true,
.dccm_len = IWL7260_DCCM_LEN,
.thermal_params = &iwl7000_high_temp_tt_params,
};
-const struct iwl_cfg iwl7260_2n_cfg = {
- .name = "Intel(R) Dual Band Wireless N 7260",
- .fw_name_pre = IWL7260_FW_PRE,
- IWL_DEVICE_7000,
- .ht_params = &iwl7000_ht_params,
- .nvm_ver = IWL7260_NVM_VERSION,
- .host_interrupt_operation_mode = true,
- .lp_xtal_workaround = true,
- .dccm_len = IWL7260_DCCM_LEN,
-};
-
-const struct iwl_cfg iwl7260_n_cfg = {
- .name = "Intel(R) Wireless N 7260",
- .fw_name_pre = IWL7260_FW_PRE,
- IWL_DEVICE_7000,
- .ht_params = &iwl7000_ht_params,
- .nvm_ver = IWL7260_NVM_VERSION,
- .host_interrupt_operation_mode = true,
- .lp_xtal_workaround = true,
- .dccm_len = IWL7260_DCCM_LEN,
-};
-
-const struct iwl_cfg iwl3160_2ac_cfg = {
- .name = "Intel(R) Dual Band Wireless AC 3160",
+const struct iwl_rf_cfg iwl3160_cfg = {
.fw_name_pre = IWL3160_FW_PRE,
IWL_DEVICE_7000,
- .ht_params = &iwl7000_ht_params,
- .nvm_ver = IWL3160_NVM_VERSION,
- .host_interrupt_operation_mode = true,
- .dccm_len = IWL3160_DCCM_LEN,
-};
-
-const struct iwl_cfg iwl3160_2n_cfg = {
- .name = "Intel(R) Dual Band Wireless N 3160",
- .fw_name_pre = IWL3160_FW_PRE,
- IWL_DEVICE_7000,
- .ht_params = &iwl7000_ht_params,
- .nvm_ver = IWL3160_NVM_VERSION,
- .host_interrupt_operation_mode = true,
- .dccm_len = IWL3160_DCCM_LEN,
-};
-
-const struct iwl_cfg iwl3160_n_cfg = {
- .name = "Intel(R) Wireless N 3160",
- .fw_name_pre = IWL3160_FW_PRE,
- IWL_DEVICE_7000,
- .ht_params = &iwl7000_ht_params,
+ .ht_params = {
+ .stbc = true,
+ .ht40_bands = BIT(NL80211_BAND_2GHZ) | BIT(NL80211_BAND_5GHZ),
+ },
.nvm_ver = IWL3160_NVM_VERSION,
.host_interrupt_operation_mode = true,
.dccm_len = IWL3160_DCCM_LEN,
@@ -204,88 +177,52 @@ static const struct iwl_pwr_tx_backoff iwl7265_pwr_tx_backoffs[] = {
{0},
};
-static const struct iwl_ht_params iwl7265_ht_params = {
- .stbc = true,
- .ldpc = true,
- .ht40_bands = BIT(NL80211_BAND_2GHZ) | BIT(NL80211_BAND_5GHZ),
-};
-
-const struct iwl_cfg iwl3165_2ac_cfg = {
- .name = "Intel(R) Dual Band Wireless AC 3165",
+const struct iwl_rf_cfg iwl3165_2ac_cfg = {
.fw_name_pre = IWL7265D_FW_PRE,
IWL_DEVICE_7005D,
- .ht_params = &iwl7000_ht_params,
+ .ht_params = {
+ .stbc = true,
+ .ht40_bands = BIT(NL80211_BAND_2GHZ) | BIT(NL80211_BAND_5GHZ),
+ },
.nvm_ver = IWL3165_NVM_VERSION,
.pwr_tx_backoffs = iwl7265_pwr_tx_backoffs,
.dccm_len = IWL7265_DCCM_LEN,
};
-const struct iwl_cfg iwl3168_2ac_cfg = {
- .name = "Intel(R) Dual Band Wireless AC 3168",
+const struct iwl_rf_cfg iwl3168_2ac_cfg = {
.fw_name_pre = IWL3168_FW_PRE,
IWL_DEVICE_3008,
- .ht_params = &iwl7000_ht_params,
+ .ht_params = {
+ .stbc = true,
+ .ht40_bands = BIT(NL80211_BAND_2GHZ) | BIT(NL80211_BAND_5GHZ),
+ },
.nvm_ver = IWL3168_NVM_VERSION,
.pwr_tx_backoffs = iwl7265_pwr_tx_backoffs,
.dccm_len = IWL7265_DCCM_LEN,
.nvm_type = IWL_NVM_SDP,
};
-const struct iwl_cfg iwl7265_2ac_cfg = {
- .name = "Intel(R) Dual Band Wireless AC 7265",
- .fw_name_pre = IWL7265_FW_PRE,
- IWL_DEVICE_7005,
- .ht_params = &iwl7265_ht_params,
- .nvm_ver = IWL7265_NVM_VERSION,
- .pwr_tx_backoffs = iwl7265_pwr_tx_backoffs,
- .dccm_len = IWL7265_DCCM_LEN,
-};
-
-const struct iwl_cfg iwl7265_2n_cfg = {
- .name = "Intel(R) Dual Band Wireless N 7265",
- .fw_name_pre = IWL7265_FW_PRE,
- IWL_DEVICE_7005,
- .ht_params = &iwl7265_ht_params,
- .nvm_ver = IWL7265_NVM_VERSION,
- .pwr_tx_backoffs = iwl7265_pwr_tx_backoffs,
- .dccm_len = IWL7265_DCCM_LEN,
-};
-
-const struct iwl_cfg iwl7265_n_cfg = {
- .name = "Intel(R) Wireless N 7265",
+const struct iwl_rf_cfg iwl7265_cfg = {
.fw_name_pre = IWL7265_FW_PRE,
IWL_DEVICE_7005,
- .ht_params = &iwl7265_ht_params,
+ .ht_params = {
+ .stbc = true,
+ .ldpc = true,
+ .ht40_bands = BIT(NL80211_BAND_2GHZ) | BIT(NL80211_BAND_5GHZ),
+ },
.nvm_ver = IWL7265_NVM_VERSION,
.pwr_tx_backoffs = iwl7265_pwr_tx_backoffs,
.dccm_len = IWL7265_DCCM_LEN,
};
-const struct iwl_cfg iwl7265d_2ac_cfg = {
- .name = "Intel(R) Dual Band Wireless AC 7265",
- .fw_name_pre = IWL7265D_FW_PRE,
- IWL_DEVICE_7005D,
- .ht_params = &iwl7265_ht_params,
- .nvm_ver = IWL7265D_NVM_VERSION,
- .pwr_tx_backoffs = iwl7265_pwr_tx_backoffs,
- .dccm_len = IWL7265_DCCM_LEN,
-};
-
-const struct iwl_cfg iwl7265d_2n_cfg = {
- .name = "Intel(R) Dual Band Wireless N 7265",
+const struct iwl_rf_cfg iwl7265d_cfg = {
.fw_name_pre = IWL7265D_FW_PRE,
IWL_DEVICE_7005D,
- .ht_params = &iwl7265_ht_params,
- .nvm_ver = IWL7265D_NVM_VERSION,
- .pwr_tx_backoffs = iwl7265_pwr_tx_backoffs,
- .dccm_len = IWL7265_DCCM_LEN,
-};
-
-const struct iwl_cfg iwl7265d_n_cfg = {
- .name = "Intel(R) Wireless N 7265",
- .fw_name_pre = IWL7265D_FW_PRE,
- IWL_DEVICE_7005D,
- .ht_params = &iwl7265_ht_params,
+ .ht_params = {
+ .stbc = true,
+ .ldpc = true,
+ .ht40_bands = BIT(NL80211_BAND_2GHZ) | BIT(NL80211_BAND_5GHZ),
+ },
.nvm_ver = IWL7265D_NVM_VERSION,
.pwr_tx_backoffs = iwl7265_pwr_tx_backoffs,
.dccm_len = IWL7265_DCCM_LEN,
diff --git a/drivers/net/wireless/intel/iwlwifi/cfg/8000.c b/drivers/net/wireless/intel/iwlwifi/cfg/8000.c
index d09cf8d7dc01..b56574006ee0 100644
--- a/drivers/net/wireless/intel/iwlwifi/cfg/8000.c
+++ b/drivers/net/wireless/intel/iwlwifi/cfg/8000.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
/*
- * Copyright (C) 2014, 2018-2020, 2023 Intel Corporation
+ * Copyright (C) 2014, 2018-2020, 2023, 2025 Intel Corporation
* Copyright (C) 2014-2015 Intel Mobile Communications GmbH
* Copyright (C) 2016 Intel Deutschland GmbH
*/
@@ -35,9 +35,7 @@
#define IWL8265_MODULE_FIRMWARE(api) \
IWL8265_FW_PRE "-" __stringify(api) ".ucode"
-#define DEFAULT_NVM_FILE_FAMILY_8000C "nvmData-8000C"
-
-static const struct iwl_base_params iwl8000_base_params = {
+static const struct iwl_family_base_params iwl8000_base = {
.eeprom_size = OTP_LOW_IMAGE_SIZE_32K,
.num_of_queues = 31,
.max_tfd_queue_size = 256,
@@ -47,12 +45,12 @@ static const struct iwl_base_params iwl8000_base_params = {
.max_event_log_size = 512,
.shadow_reg_enable = true,
.pcie_l1_allowed = true,
-};
-
-static const struct iwl_ht_params iwl8000_ht_params = {
- .stbc = true,
- .ldpc = true,
- .ht40_bands = BIT(NL80211_BAND_2GHZ) | BIT(NL80211_BAND_5GHZ),
+ .nvm_hw_section_num = 10,
+ .features = NETIF_F_RXCSUM,
+ .smem_offset = IWL8260_SMEM_OFFSET,
+ .smem_len = IWL8260_SMEM_LEN,
+ .apmg_not_supported = true,
+ .min_umac_error_event_table = 0x800000,
};
static const struct iwl_tt_params iwl8000_tt_params = {
@@ -76,30 +74,20 @@ static const struct iwl_tt_params iwl8000_tt_params = {
.support_tx_backoff = true,
};
+const struct iwl_mac_cfg iwl8000_mac_cfg = {
+ .device_family = IWL_DEVICE_FAMILY_8000,
+ .base = &iwl8000_base,
+};
+
#define IWL_DEVICE_8000_COMMON \
- .trans.device_family = IWL_DEVICE_FAMILY_8000, \
- .trans.base_params = &iwl8000_base_params, \
.led_mode = IWL_LED_RF_STATE, \
- .nvm_hw_section_num = 10, \
- .features = NETIF_F_RXCSUM, \
.non_shared_ant = ANT_A, \
.dccm_offset = IWL8260_DCCM_OFFSET, \
.dccm_len = IWL8260_DCCM_LEN, \
.dccm2_offset = IWL8260_DCCM2_OFFSET, \
.dccm2_len = IWL8260_DCCM2_LEN, \
- .smem_offset = IWL8260_SMEM_OFFSET, \
- .smem_len = IWL8260_SMEM_LEN, \
- .default_nvm_file_C_step = DEFAULT_NVM_FILE_FAMILY_8000C, \
.thermal_params = &iwl8000_tt_params, \
- .apmg_not_supported = true, \
- .nvm_type = IWL_NVM_EXT, \
- .dbgc_supported = true, \
- .min_umac_error_event_table = 0x800000
-
-#define IWL_DEVICE_8000 \
- IWL_DEVICE_8000_COMMON, \
- .ucode_api_max = IWL8000_UCODE_API_MAX, \
- .ucode_api_min = IWL8000_UCODE_API_MIN \
+ .nvm_type = IWL_NVM_EXT
#define IWL_DEVICE_8260 \
IWL_DEVICE_8000_COMMON, \
@@ -111,47 +99,39 @@ static const struct iwl_tt_params iwl8000_tt_params = {
.ucode_api_max = IWL8265_UCODE_API_MAX, \
.ucode_api_min = IWL8265_UCODE_API_MIN \
-const struct iwl_cfg iwl8260_2n_cfg = {
- .name = "Intel(R) Dual Band Wireless N 8260",
- .fw_name_pre = IWL8000_FW_PRE,
- IWL_DEVICE_8260,
- .ht_params = &iwl8000_ht_params,
- .nvm_ver = IWL8000_NVM_VERSION,
-};
+const char iwl8260_2n_name[] = "Intel(R) Dual Band Wireless-N 8260";
+const char iwl8260_2ac_name[] = "Intel(R) Dual Band Wireless-AC 8260";
+const char iwl8265_2ac_name[] = "Intel(R) Dual Band Wireless-AC 8265";
+const char iwl8275_2ac_name[] = "Intel(R) Dual Band Wireless-AC 8275";
+const char iwl4165_2ac_name[] = "Intel(R) Dual Band Wireless-AC 4165";
-const struct iwl_cfg iwl8260_2ac_cfg = {
- .name = "Intel(R) Dual Band Wireless AC 8260",
+const char iwl_killer_1435i_name[] =
+ "Killer(R) Wireless-AC 1435i Wireless Network Adapter (8265D2W)";
+const char iwl_killer_1434_kix_name[] =
+ "Killer(R) Wireless-AC 1435-KIX Wireless Network Adapter (8265NGW)";
+
+const struct iwl_rf_cfg iwl8260_cfg = {
.fw_name_pre = IWL8000_FW_PRE,
IWL_DEVICE_8260,
- .ht_params = &iwl8000_ht_params,
+ .ht_params = {
+ .stbc = true,
+ .ldpc = true,
+ .ht40_bands = BIT(NL80211_BAND_2GHZ) | BIT(NL80211_BAND_5GHZ),
+ },
.nvm_ver = IWL8000_NVM_VERSION,
};
-const struct iwl_cfg iwl8265_2ac_cfg = {
- .name = "Intel(R) Dual Band Wireless AC 8265",
+const struct iwl_rf_cfg iwl8265_cfg = {
.fw_name_pre = IWL8265_FW_PRE,
IWL_DEVICE_8265,
- .ht_params = &iwl8000_ht_params,
- .nvm_ver = IWL8000_NVM_VERSION,
- .vht_mu_mimo_supported = true,
-};
-
-const struct iwl_cfg iwl8275_2ac_cfg = {
- .name = "Intel(R) Dual Band Wireless AC 8275",
- .fw_name_pre = IWL8265_FW_PRE,
- IWL_DEVICE_8265,
- .ht_params = &iwl8000_ht_params,
+ .ht_params = {
+ .stbc = true,
+ .ldpc = true,
+ .ht40_bands = BIT(NL80211_BAND_2GHZ) | BIT(NL80211_BAND_5GHZ),
+ },
.nvm_ver = IWL8000_NVM_VERSION,
.vht_mu_mimo_supported = true,
};
-const struct iwl_cfg iwl4165_2ac_cfg = {
- .name = "Intel(R) Dual Band Wireless AC 4165",
- .fw_name_pre = IWL8000_FW_PRE,
- IWL_DEVICE_8000,
- .ht_params = &iwl8000_ht_params,
- .nvm_ver = IWL8000_NVM_VERSION,
-};
-
MODULE_FIRMWARE(IWL8000_MODULE_FIRMWARE(IWL8000_UCODE_API_MAX));
MODULE_FIRMWARE(IWL8265_MODULE_FIRMWARE(IWL8265_UCODE_API_MAX));
diff --git a/drivers/net/wireless/intel/iwlwifi/cfg/9000.c b/drivers/net/wireless/intel/iwlwifi/cfg/9000.c
index 0130d9a9b78b..ac1fa291cf2f 100644
--- a/drivers/net/wireless/intel/iwlwifi/cfg/9000.c
+++ b/drivers/net/wireless/intel/iwlwifi/cfg/9000.c
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
/*
* Copyright (C) 2015-2017 Intel Deutschland GmbH
- * Copyright (C) 2018-2021, 2023 Intel Corporation
+ * Copyright (C) 2018-2021, 2023, 2025 Intel Corporation
*/
#include <linux/module.h>
#include <linux/stringify.h>
@@ -15,14 +15,7 @@
/* Lowest firmware API version supported */
#define IWL9000_UCODE_API_MIN 30
-/* NVM versions */
-#define IWL9000_NVM_VERSION 0x0a1d
-
/* Memory offsets and lengths */
-#define IWL9000_DCCM_OFFSET 0x800000
-#define IWL9000_DCCM_LEN 0x18000
-#define IWL9000_DCCM2_OFFSET 0x880000
-#define IWL9000_DCCM2_LEN 0x8000
#define IWL9000_SMEM_OFFSET 0x400000
#define IWL9000_SMEM_LEN 0x68000
@@ -33,7 +26,7 @@
#define IWL9260_MODULE_FIRMWARE(api) \
IWL9260_FW_PRE "-" __stringify(api) ".ucode"
-static const struct iwl_base_params iwl9000_base_params = {
+static const struct iwl_family_base_params iwl9000_base = {
.eeprom_size = OTP_LOW_IMAGE_SIZE_32K,
.num_of_queues = 31,
.max_tfd_queue_size = 256,
@@ -43,150 +36,69 @@ static const struct iwl_base_params iwl9000_base_params = {
.max_event_log_size = 512,
.shadow_reg_enable = true,
.pcie_l1_allowed = true,
-};
-
-static const struct iwl_ht_params iwl9000_ht_params = {
- .stbc = true,
- .ldpc = true,
- .ht40_bands = BIT(NL80211_BAND_2GHZ) | BIT(NL80211_BAND_5GHZ),
-};
-
-static const struct iwl_tt_params iwl9000_tt_params = {
- .ct_kill_entry = 115,
- .ct_kill_exit = 93,
- .ct_kill_duration = 5,
- .dynamic_smps_entry = 111,
- .dynamic_smps_exit = 107,
- .tx_protection_entry = 112,
- .tx_protection_exit = 105,
- .tx_backoff = {
- {.temperature = 110, .backoff = 200},
- {.temperature = 111, .backoff = 600},
- {.temperature = 112, .backoff = 1200},
- {.temperature = 113, .backoff = 2000},
- {.temperature = 114, .backoff = 4000},
+ .smem_offset = IWL9000_SMEM_OFFSET,
+ .smem_len = IWL9000_SMEM_LEN,
+ .features = IWL_TX_CSUM_NETIF_FLAGS | NETIF_F_RXCSUM,
+ .apmg_not_supported = true,
+ .mac_addr_from_csr = 0x380,
+ .min_umac_error_event_table = 0x800000,
+ .d3_debug_data_base_addr = 0x401000,
+ .d3_debug_data_length = 92 * 1024,
+ .nvm_hw_section_num = 10,
+ .mon_smem_regs = {
+ .write_ptr = {
+ .addr = LDBG_M2S_BUF_WPTR,
+ .mask = LDBG_M2S_BUF_WPTR_VAL_MSK,
+ },
+ .cycle_cnt = {
+ .addr = LDBG_M2S_BUF_WRAP_CNT,
+ .mask = LDBG_M2S_BUF_WRAP_CNT_VAL_MSK,
+ },
+ },
+ .mon_dram_regs = {
+ .write_ptr = {
+ .addr = MON_BUFF_WRPTR_VER2,
+ .mask = 0xffffffff,
+ },
+ .cycle_cnt = {
+ .addr = MON_BUFF_CYCLE_CNT_VER2,
+ .mask = 0xffffffff,
+ },
},
- .support_ct_kill = true,
- .support_dynamic_smps = true,
- .support_tx_protection = true,
- .support_tx_backoff = true,
+ .ucode_api_max = IWL9000_UCODE_API_MAX,
+ .ucode_api_min = IWL9000_UCODE_API_MIN,
};
-#define IWL_DEVICE_9000 \
- .ucode_api_max = IWL9000_UCODE_API_MAX, \
- .ucode_api_min = IWL9000_UCODE_API_MIN, \
- .led_mode = IWL_LED_RF_STATE, \
- .nvm_hw_section_num = 10, \
- .non_shared_ant = ANT_B, \
- .dccm_offset = IWL9000_DCCM_OFFSET, \
- .dccm_len = IWL9000_DCCM_LEN, \
- .dccm2_offset = IWL9000_DCCM2_OFFSET, \
- .dccm2_len = IWL9000_DCCM2_LEN, \
- .smem_offset = IWL9000_SMEM_OFFSET, \
- .smem_len = IWL9000_SMEM_LEN, \
- .features = IWL_TX_CSUM_NETIF_FLAGS | NETIF_F_RXCSUM, \
- .thermal_params = &iwl9000_tt_params, \
- .apmg_not_supported = true, \
- .num_rbds = 512, \
- .vht_mu_mimo_supported = true, \
- .mac_addr_from_csr = 0x380, \
- .nvm_type = IWL_NVM_EXT, \
- .dbgc_supported = true, \
- .min_umac_error_event_table = 0x800000, \
- .d3_debug_data_base_addr = 0x401000, \
- .d3_debug_data_length = 92 * 1024, \
- .ht_params = &iwl9000_ht_params, \
- .nvm_ver = IWL9000_NVM_VERSION, \
- .mon_smem_regs = { \
- .write_ptr = { \
- .addr = LDBG_M2S_BUF_WPTR, \
- .mask = LDBG_M2S_BUF_WPTR_VAL_MSK, \
- }, \
- .cycle_cnt = { \
- .addr = LDBG_M2S_BUF_WRAP_CNT, \
- .mask = LDBG_M2S_BUF_WRAP_CNT_VAL_MSK, \
- }, \
- }, \
- .mon_dram_regs = { \
- .write_ptr = { \
- .addr = MON_BUFF_WRPTR_VER2, \
- .mask = 0xffffffff, \
- }, \
- .cycle_cnt = { \
- .addr = MON_BUFF_CYCLE_CNT_VER2, \
- .mask = 0xffffffff, \
- }, \
- }
-
-const struct iwl_cfg_trans_params iwl9000_trans_cfg = {
+const struct iwl_mac_cfg iwl9000_mac_cfg = {
.device_family = IWL_DEVICE_FAMILY_9000,
- .base_params = &iwl9000_base_params,
+ .base = &iwl9000_base,
.mq_rx_supported = true,
- .rf_id = true,
};
-const struct iwl_cfg_trans_params iwl9560_trans_cfg = {
+const struct iwl_mac_cfg iwl9560_mac_cfg = {
.device_family = IWL_DEVICE_FAMILY_9000,
- .base_params = &iwl9000_base_params,
+ .base = &iwl9000_base,
.mq_rx_supported = true,
- .rf_id = true,
.integrated = true,
.xtal_latency = 650,
};
-const struct iwl_cfg_trans_params iwl9560_long_latency_trans_cfg = {
+const struct iwl_mac_cfg iwl9560_long_latency_mac_cfg = {
.device_family = IWL_DEVICE_FAMILY_9000,
- .base_params = &iwl9000_base_params,
+ .base = &iwl9000_base,
.mq_rx_supported = true,
- .rf_id = true,
.integrated = true,
.xtal_latency = 2820,
};
-const struct iwl_cfg_trans_params iwl9560_shared_clk_trans_cfg = {
+const struct iwl_mac_cfg iwl9560_shared_clk_mac_cfg = {
.device_family = IWL_DEVICE_FAMILY_9000,
- .base_params = &iwl9000_base_params,
+ .base = &iwl9000_base,
.mq_rx_supported = true,
- .rf_id = true,
.integrated = true,
.xtal_latency = 670,
.extra_phy_cfg_flags = FW_PHY_CFG_SHARED_CLK
};
-const char iwl9162_name[] = "Intel(R) Wireless-AC 9162";
-const char iwl9260_name[] = "Intel(R) Wireless-AC 9260";
-const char iwl9260_1_name[] = "Intel(R) Wireless-AC 9260-1";
-const char iwl9270_name[] = "Intel(R) Wireless-AC 9270";
-const char iwl9461_name[] = "Intel(R) Wireless-AC 9461";
-const char iwl9462_name[] = "Intel(R) Wireless-AC 9462";
-const char iwl9560_name[] = "Intel(R) Wireless-AC 9560";
-const char iwl9162_160_name[] = "Intel(R) Wireless-AC 9162 160MHz";
-const char iwl9260_160_name[] = "Intel(R) Wireless-AC 9260 160MHz";
-const char iwl9270_160_name[] = "Intel(R) Wireless-AC 9270 160MHz";
-const char iwl9461_160_name[] = "Intel(R) Wireless-AC 9461 160MHz";
-const char iwl9462_160_name[] = "Intel(R) Wireless-AC 9462 160MHz";
-const char iwl9560_160_name[] = "Intel(R) Wireless-AC 9560 160MHz";
-
-const char iwl9260_killer_1550_name[] =
- "Killer (R) Wireless-AC 1550 Wireless Network Adapter (9260NGW) 160MHz";
-const char iwl9560_killer_1550i_name[] =
- "Killer (R) Wireless-AC 1550i Wireless Network Adapter (9560NGW)";
-const char iwl9560_killer_1550i_160_name[] =
- "Killer(R) Wireless-AC 1550i Wireless Network Adapter (9560NGW) 160MHz";
-const char iwl9560_killer_1550s_name[] =
- "Killer (R) Wireless-AC 1550s Wireless Network Adapter (9560NGW)";
-const char iwl9560_killer_1550s_160_name[] =
- "Killer(R) Wireless-AC 1550s Wireless Network Adapter (9560D2W) 160MHz";
-
-const struct iwl_cfg iwl9260_2ac_cfg = {
- .fw_name_pre = IWL9260_FW_PRE,
- IWL_DEVICE_9000,
-};
-
-const struct iwl_cfg iwl9560_2ac_cfg_soc = {
- .fw_name_pre = IWL9000_FW_PRE,
- IWL_DEVICE_9000,
-};
-
MODULE_FIRMWARE(IWL9000_MODULE_FIRMWARE(IWL9000_UCODE_API_MAX));
MODULE_FIRMWARE(IWL9260_MODULE_FIRMWARE(IWL9000_UCODE_API_MAX));
diff --git a/drivers/net/wireless/intel/iwlwifi/cfg/ax210.c b/drivers/net/wireless/intel/iwlwifi/cfg/ax210.c
index dcba1a5d793b..3bf9fdbe01c6 100644
--- a/drivers/net/wireless/intel/iwlwifi/cfg/ax210.c
+++ b/drivers/net/wireless/intel/iwlwifi/cfg/ax210.c
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
/*
* Copyright (C) 2015-2017 Intel Deutschland GmbH
- * Copyright (C) 2018-2024 Intel Corporation
+ * Copyright (C) 2018-2025 Intel Corporation
*/
#include <linux/module.h>
#include <linux/stringify.h>
@@ -15,14 +15,7 @@
/* Lowest firmware API version supported */
#define IWL_AX210_UCODE_API_MIN 77
-/* NVM versions */
-#define IWL_AX210_NVM_VERSION 0x0a1d
-
/* Memory offsets and lengths */
-#define IWL_AX210_DCCM_OFFSET 0x800000 /* LMAC1 */
-#define IWL_AX210_DCCM_LEN 0x10000 /* LMAC1 */
-#define IWL_AX210_DCCM2_OFFSET 0x880000
-#define IWL_AX210_DCCM2_LEN 0x8000
#define IWL_AX210_SMEM_OFFSET 0x400000
#define IWL_AX210_SMEM_LEN 0xD0000
@@ -47,8 +40,7 @@
#define IWL_MA_B_HR_B_FW_MODULE_FIRMWARE(api) \
IWL_MA_B_HR_B_FW_PRE "-" __stringify(api) ".ucode"
-static const struct iwl_base_params iwl_ax210_base_params = {
- .eeprom_size = OTP_LOW_IMAGE_SIZE_32K,
+static const struct iwl_family_base_params iwl_ax210_base = {
.num_of_queues = 512,
.max_tfd_queue_size = 65536,
.shadow_ram_support = true,
@@ -57,74 +49,60 @@ static const struct iwl_base_params iwl_ax210_base_params = {
.max_event_log_size = 512,
.shadow_reg_enable = true,
.pcie_l1_allowed = true,
+ .smem_offset = IWL_AX210_SMEM_OFFSET,
+ .smem_len = IWL_AX210_SMEM_LEN,
+ .features = IWL_TX_CSUM_NETIF_FLAGS | NETIF_F_RXCSUM,
+ .apmg_not_supported = true,
+ .mac_addr_from_csr = 0x380,
+ .min_umac_error_event_table = 0x400000,
+ .d3_debug_data_base_addr = 0x401000,
+ .d3_debug_data_length = 60 * 1024,
+ .mon_smem_regs = {
+ .write_ptr = {
+ .addr = LDBG_M2S_BUF_WPTR,
+ .mask = LDBG_M2S_BUF_WPTR_VAL_MSK,
+ },
+ .cycle_cnt = {
+ .addr = LDBG_M2S_BUF_WRAP_CNT,
+ .mask = LDBG_M2S_BUF_WRAP_CNT_VAL_MSK,
+ },
+ },
+ .min_txq_size = 128,
+ .gp2_reg_addr = 0xd02c68,
+ .min_ba_txq_size = IWL_DEFAULT_QUEUE_SIZE_HE,
+ .mon_dram_regs = {
+ .write_ptr = {
+ .addr = DBGC_CUR_DBGBUF_STATUS,
+ .mask = DBGC_CUR_DBGBUF_STATUS_OFFSET_MSK,
+ },
+ .cycle_cnt = {
+ .addr = DBGC_DBGBUF_WRAP_AROUND,
+ .mask = 0xffffffff,
+ },
+ .cur_frag = {
+ .addr = DBGC_CUR_DBGBUF_STATUS,
+ .mask = DBGC_CUR_DBGBUF_STATUS_IDX_MSK,
+ },
+ },
+ .ucode_api_min = IWL_AX210_UCODE_API_MIN,
+ .ucode_api_max = IWL_AX210_UCODE_API_MAX,
};
-#define IWL_DEVICE_AX210_COMMON \
- .ucode_api_min = IWL_AX210_UCODE_API_MIN, \
- .led_mode = IWL_LED_RF_STATE, \
- .nvm_hw_section_num = 10, \
- .non_shared_ant = ANT_B, \
- .dccm_offset = IWL_AX210_DCCM_OFFSET, \
- .dccm_len = IWL_AX210_DCCM_LEN, \
- .dccm2_offset = IWL_AX210_DCCM2_OFFSET, \
- .dccm2_len = IWL_AX210_DCCM2_LEN, \
- .smem_offset = IWL_AX210_SMEM_OFFSET, \
- .smem_len = IWL_AX210_SMEM_LEN, \
- .features = IWL_TX_CSUM_NETIF_FLAGS | NETIF_F_RXCSUM, \
- .apmg_not_supported = true, \
- .trans.mq_rx_supported = true, \
- .vht_mu_mimo_supported = true, \
- .mac_addr_from_csr = 0x380, \
- .ht_params = &iwl_22000_ht_params, \
- .nvm_ver = IWL_AX210_NVM_VERSION, \
- .trans.rf_id = true, \
- .trans.gen2 = true, \
- .nvm_type = IWL_NVM_EXT, \
- .dbgc_supported = true, \
- .min_umac_error_event_table = 0x400000, \
- .d3_debug_data_base_addr = 0x401000, \
- .d3_debug_data_length = 60 * 1024, \
- .mon_smem_regs = { \
- .write_ptr = { \
- .addr = LDBG_M2S_BUF_WPTR, \
- .mask = LDBG_M2S_BUF_WPTR_VAL_MSK, \
- }, \
- .cycle_cnt = { \
- .addr = LDBG_M2S_BUF_WRAP_CNT, \
- .mask = LDBG_M2S_BUF_WRAP_CNT_VAL_MSK, \
- }, \
- }
-
-#define IWL_DEVICE_AX210 \
- IWL_DEVICE_AX210_COMMON, \
- .ucode_api_max = IWL_AX210_UCODE_API_MAX, \
- .trans.umac_prph_offset = 0x300000, \
- .trans.device_family = IWL_DEVICE_FAMILY_AX210, \
- .trans.base_params = &iwl_ax210_base_params, \
- .min_txq_size = 128, \
- .gp2_reg_addr = 0xd02c68, \
- .min_ba_txq_size = IWL_DEFAULT_QUEUE_SIZE_HE, \
- .mon_dram_regs = { \
- .write_ptr = { \
- .addr = DBGC_CUR_DBGBUF_STATUS, \
- .mask = DBGC_CUR_DBGBUF_STATUS_OFFSET_MSK, \
- }, \
- .cycle_cnt = { \
- .addr = DBGC_DBGBUF_WRAP_AROUND, \
- .mask = 0xffffffff, \
- }, \
- .cur_frag = { \
- .addr = DBGC_CUR_DBGBUF_STATUS, \
- .mask = DBGC_CUR_DBGBUF_STATUS_IDX_MSK, \
- }, \
- }
+const struct iwl_mac_cfg iwl_ty_mac_cfg = {
+ .mq_rx_supported = true,
+ .gen2 = true,
+ .device_family = IWL_DEVICE_FAMILY_AX210,
+ .base = &iwl_ax210_base,
+ .umac_prph_offset = 0x300000,
+ /* TODO: the following values need to be checked */
+ .xtal_latency = 500,
+};
-const struct iwl_cfg_trans_params iwl_so_trans_cfg = {
+const struct iwl_mac_cfg iwl_so_mac_cfg = {
.mq_rx_supported = true,
- .rf_id = true,
.gen2 = true,
.device_family = IWL_DEVICE_FAMILY_AX210,
- .base_params = &iwl_ax210_base_params,
+ .base = &iwl_ax210_base,
.umac_prph_offset = 0x300000,
.integrated = true,
/* TODO: the following values need to be checked */
@@ -132,12 +110,11 @@ const struct iwl_cfg_trans_params iwl_so_trans_cfg = {
.ltr_delay = IWL_CFG_TRANS_LTR_DELAY_200US,
};
-const struct iwl_cfg_trans_params iwl_so_long_latency_trans_cfg = {
+const struct iwl_mac_cfg iwl_so_long_latency_mac_cfg = {
.mq_rx_supported = true,
- .rf_id = true,
.gen2 = true,
.device_family = IWL_DEVICE_FAMILY_AX210,
- .base_params = &iwl_ax210_base_params,
+ .base = &iwl_ax210_base,
.umac_prph_offset = 0x300000,
.integrated = true,
.low_latency_xtal = true,
@@ -145,12 +122,11 @@ const struct iwl_cfg_trans_params iwl_so_long_latency_trans_cfg = {
.ltr_delay = IWL_CFG_TRANS_LTR_DELAY_2500US,
};
-const struct iwl_cfg_trans_params iwl_so_long_latency_imr_trans_cfg = {
+const struct iwl_mac_cfg iwl_so_long_latency_imr_mac_cfg = {
.mq_rx_supported = true,
- .rf_id = true,
.gen2 = true,
.device_family = IWL_DEVICE_FAMILY_AX210,
- .base_params = &iwl_ax210_base_params,
+ .base = &iwl_ax210_base,
.umac_prph_offset = 0x300000,
.integrated = true,
.low_latency_xtal = true,
@@ -159,108 +135,15 @@ const struct iwl_cfg_trans_params iwl_so_long_latency_imr_trans_cfg = {
.imr_enabled = true,
};
-/*
- * If the device doesn't support HE, no need to have that many buffers.
- * AX210 devices can split multiple frames into a single RB, so fewer are
- * needed; AX210 cannot (but use smaller RBs by default) - these sizes
- * were picked according to 8 MSDUs inside 256 A-MSDUs in an A-MPDU, with
- * additional overhead to account for processing time.
- */
-#define IWL_NUM_RBDS_NON_HE 512
-#define IWL_NUM_RBDS_AX210_HE 4096
-
-const struct iwl_cfg_trans_params iwl_ma_trans_cfg = {
+const struct iwl_mac_cfg iwl_ma_mac_cfg = {
.device_family = IWL_DEVICE_FAMILY_AX210,
- .base_params = &iwl_ax210_base_params,
+ .base = &iwl_ax210_base,
.mq_rx_supported = true,
- .rf_id = true,
.gen2 = true,
.integrated = true,
.umac_prph_offset = 0x300000
};
-const char iwl_ax211_name[] = "Intel(R) Wi-Fi 6E AX211 160MHz";
-const char iwl_ax221_name[] = "Intel(R) Wi-Fi 6E AX221 160MHz";
-const char iwl_ax231_name[] = "Intel(R) Wi-Fi 6E AX231 160MHz";
-const char iwl_ax411_name[] = "Intel(R) Wi-Fi 6E AX411 160MHz";
-
-const char iwl_ax210_killer_1675w_name[] =
- "Killer(R) Wi-Fi 6E AX1675w 160MHz Wireless Network Adapter (210D2W)";
-const char iwl_ax210_killer_1675x_name[] =
- "Killer(R) Wi-Fi 6E AX1675x 160MHz Wireless Network Adapter (210NGW)";
-const char iwl_ax211_killer_1675s_name[] =
- "Killer(R) Wi-Fi 6E AX1675s 160MHz Wireless Network Adapter (211NGW)";
-const char iwl_ax211_killer_1675i_name[] =
- "Killer(R) Wi-Fi 6E AX1675i 160MHz Wireless Network Adapter (211NGW)";
-const char iwl_ax411_killer_1690s_name[] =
- "Killer(R) Wi-Fi 6E AX1690s 160MHz Wireless Network Adapter (411D2W)";
-const char iwl_ax411_killer_1690i_name[] =
- "Killer(R) Wi-Fi 6E AX1690i 160MHz Wireless Network Adapter (411NGW)";
-
-const struct iwl_cfg iwlax210_2ax_cfg_so_jf_b0 = {
- .name = "Intel(R) Wireless-AC 9560 160MHz",
- .fw_name_pre = IWL_SO_A_JF_B_FW_PRE,
- IWL_DEVICE_AX210,
- .num_rbds = IWL_NUM_RBDS_NON_HE,
-};
-
-const struct iwl_cfg iwlax211_2ax_cfg_so_gf_a0 = {
- .name = iwl_ax211_name,
- .fw_name_pre = IWL_SO_A_GF_A_FW_PRE,
- .uhb_supported = true,
- IWL_DEVICE_AX210,
- .num_rbds = IWL_NUM_RBDS_AX210_HE,
-};
-
-const struct iwl_cfg iwlax211_2ax_cfg_so_gf_a0_long = {
- .name = iwl_ax211_name,
- .fw_name_pre = IWL_SO_A_GF_A_FW_PRE,
- .uhb_supported = true,
- IWL_DEVICE_AX210,
- .num_rbds = IWL_NUM_RBDS_AX210_HE,
- .trans.xtal_latency = 12000,
- .trans.low_latency_xtal = true,
-};
-
-const struct iwl_cfg iwlax210_2ax_cfg_ty_gf_a0 = {
- .name = "Intel(R) Wi-Fi 6 AX210 160MHz",
- .fw_name_pre = IWL_TY_A_GF_A_FW_PRE,
- .uhb_supported = true,
- IWL_DEVICE_AX210,
- .num_rbds = IWL_NUM_RBDS_AX210_HE,
-};
-
-const struct iwl_cfg iwlax411_2ax_cfg_so_gf4_a0 = {
- .name = iwl_ax411_name,
- .fw_name_pre = IWL_SO_A_GF4_A_FW_PRE,
- .uhb_supported = true,
- IWL_DEVICE_AX210,
- .num_rbds = IWL_NUM_RBDS_AX210_HE,
-};
-
-const struct iwl_cfg iwlax411_2ax_cfg_so_gf4_a0_long = {
- .name = iwl_ax411_name,
- .fw_name_pre = IWL_SO_A_GF4_A_FW_PRE,
- .uhb_supported = true,
- IWL_DEVICE_AX210,
- .num_rbds = IWL_NUM_RBDS_AX210_HE,
- .trans.xtal_latency = 12000,
- .trans.low_latency_xtal = true,
-};
-
-const struct iwl_cfg iwl_cfg_ma = {
- .fw_name_mac = "ma",
- .uhb_supported = true,
- IWL_DEVICE_AX210,
- .num_rbds = IWL_NUM_RBDS_AX210_HE,
-};
-
-const struct iwl_cfg iwl_cfg_so_a0_hr_a0 = {
- .fw_name_pre = IWL_SO_A_HR_B_FW_PRE,
- IWL_DEVICE_AX210,
- .num_rbds = IWL_NUM_RBDS_AX210_HE,
-};
-
MODULE_FIRMWARE(IWL_SO_A_JF_B_MODULE_FIRMWARE(IWL_AX210_UCODE_API_MAX));
MODULE_FIRMWARE(IWL_SO_A_HR_B_MODULE_FIRMWARE(IWL_AX210_UCODE_API_MAX));
IWL_FW_AND_PNVM(IWL_SO_A_GF_A_FW_PRE, IWL_AX210_UCODE_API_MAX);
diff --git a/drivers/net/wireless/intel/iwlwifi/cfg/bz.c b/drivers/net/wireless/intel/iwlwifi/cfg/bz.c
index efa3e0e35f79..05e45fff8b36 100644
--- a/drivers/net/wireless/intel/iwlwifi/cfg/bz.c
+++ b/drivers/net/wireless/intel/iwlwifi/cfg/bz.c
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
/*
* Copyright (C) 2015-2017 Intel Deutschland GmbH
- * Copyright (C) 2018-2024 Intel Corporation
+ * Copyright (C) 2018-2025 Intel Corporation
*/
#include <linux/module.h>
#include <linux/stringify.h>
@@ -10,19 +10,12 @@
#include "fw/api/txq.h"
/* Highest firmware API version supported */
-#define IWL_BZ_UCODE_API_MAX 96
+#define IWL_BZ_UCODE_API_MAX 99
/* Lowest firmware API version supported */
-#define IWL_BZ_UCODE_API_MIN 92
-
-/* NVM versions */
-#define IWL_BZ_NVM_VERSION 0x0a1d
+#define IWL_BZ_UCODE_API_MIN 93
/* Memory offsets and lengths */
-#define IWL_BZ_DCCM_OFFSET 0x800000 /* LMAC1 */
-#define IWL_BZ_DCCM_LEN 0x10000 /* LMAC1 */
-#define IWL_BZ_DCCM2_OFFSET 0x880000
-#define IWL_BZ_DCCM2_LEN 0x8000
#define IWL_BZ_SMEM_OFFSET 0x400000
#define IWL_BZ_SMEM_LEN 0xD0000
@@ -38,8 +31,7 @@
#define IWL_BZ_A_HR_B_MODULE_FIRMWARE(api) \
IWL_BZ_A_HR_B_FW_PRE "-" __stringify(api) ".ucode"
-static const struct iwl_base_params iwl_bz_base_params = {
- .eeprom_size = OTP_LOW_IMAGE_SIZE_32K,
+static const struct iwl_family_base_params iwl_bz_base = {
.num_of_queues = 512,
.max_tfd_queue_size = 65536,
.shadow_ram_support = true,
@@ -48,84 +40,55 @@ static const struct iwl_base_params iwl_bz_base_params = {
.max_event_log_size = 512,
.shadow_reg_enable = true,
.pcie_l1_allowed = true,
+ .smem_offset = IWL_BZ_SMEM_OFFSET,
+ .smem_len = IWL_BZ_SMEM_LEN,
+ .apmg_not_supported = true,
+ .mac_addr_from_csr = 0x30,
+ .min_umac_error_event_table = 0xD0000,
+ .d3_debug_data_base_addr = 0x401000,
+ .d3_debug_data_length = 60 * 1024,
+ .mon_smem_regs = {
+ .write_ptr = {
+ .addr = LDBG_M2S_BUF_WPTR,
+ .mask = LDBG_M2S_BUF_WPTR_VAL_MSK,
+ },
+ .cycle_cnt = {
+ .addr = LDBG_M2S_BUF_WRAP_CNT,
+ .mask = LDBG_M2S_BUF_WRAP_CNT_VAL_MSK,
+ },
+ },
+ .min_txq_size = 128,
+ .gp2_reg_addr = 0xd02c68,
+ .min_ba_txq_size = IWL_DEFAULT_QUEUE_SIZE_EHT,
+ .mon_dram_regs = {
+ .write_ptr = {
+ .addr = DBGC_CUR_DBGBUF_STATUS,
+ .mask = DBGC_CUR_DBGBUF_STATUS_OFFSET_MSK,
+ },
+ .cycle_cnt = {
+ .addr = DBGC_DBGBUF_WRAP_AROUND,
+ .mask = 0xffffffff,
+ },
+ .cur_frag = {
+ .addr = DBGC_CUR_DBGBUF_STATUS,
+ .mask = DBGC_CUR_DBGBUF_STATUS_IDX_MSK,
+ },
+ },
+ .mon_dbgi_regs = {
+ .write_ptr = {
+ .addr = DBGI_SRAM_FIFO_POINTERS,
+ .mask = DBGI_SRAM_FIFO_POINTERS_WR_PTR_MSK,
+ },
+ },
+ .features = IWL_TX_CSUM_NETIF_FLAGS | NETIF_F_RXCSUM,
+ .ucode_api_max = IWL_BZ_UCODE_API_MAX,
+ .ucode_api_min = IWL_BZ_UCODE_API_MIN,
};
-#define IWL_DEVICE_BZ_COMMON \
- .ucode_api_max = IWL_BZ_UCODE_API_MAX, \
- .ucode_api_min = IWL_BZ_UCODE_API_MIN, \
- .led_mode = IWL_LED_RF_STATE, \
- .nvm_hw_section_num = 10, \
- .non_shared_ant = ANT_B, \
- .dccm_offset = IWL_BZ_DCCM_OFFSET, \
- .dccm_len = IWL_BZ_DCCM_LEN, \
- .dccm2_offset = IWL_BZ_DCCM2_OFFSET, \
- .dccm2_len = IWL_BZ_DCCM2_LEN, \
- .smem_offset = IWL_BZ_SMEM_OFFSET, \
- .smem_len = IWL_BZ_SMEM_LEN, \
- .apmg_not_supported = true, \
- .trans.mq_rx_supported = true, \
- .vht_mu_mimo_supported = true, \
- .mac_addr_from_csr = 0x30, \
- .nvm_ver = IWL_BZ_NVM_VERSION, \
- .trans.rf_id = true, \
- .trans.gen2 = true, \
- .nvm_type = IWL_NVM_EXT, \
- .dbgc_supported = true, \
- .min_umac_error_event_table = 0xD0000, \
- .d3_debug_data_base_addr = 0x401000, \
- .d3_debug_data_length = 60 * 1024, \
- .mon_smem_regs = { \
- .write_ptr = { \
- .addr = LDBG_M2S_BUF_WPTR, \
- .mask = LDBG_M2S_BUF_WPTR_VAL_MSK, \
- }, \
- .cycle_cnt = { \
- .addr = LDBG_M2S_BUF_WRAP_CNT, \
- .mask = LDBG_M2S_BUF_WRAP_CNT_VAL_MSK, \
- }, \
- }, \
- .trans.umac_prph_offset = 0x300000, \
- .trans.device_family = IWL_DEVICE_FAMILY_BZ, \
- .trans.base_params = &iwl_bz_base_params, \
- .min_txq_size = 128, \
- .gp2_reg_addr = 0xd02c68, \
- .min_ba_txq_size = IWL_DEFAULT_QUEUE_SIZE_EHT, \
- .mon_dram_regs = { \
- .write_ptr = { \
- .addr = DBGC_CUR_DBGBUF_STATUS, \
- .mask = DBGC_CUR_DBGBUF_STATUS_OFFSET_MSK, \
- }, \
- .cycle_cnt = { \
- .addr = DBGC_DBGBUF_WRAP_AROUND, \
- .mask = 0xffffffff, \
- }, \
- .cur_frag = { \
- .addr = DBGC_CUR_DBGBUF_STATUS, \
- .mask = DBGC_CUR_DBGBUF_STATUS_IDX_MSK, \
- }, \
- }, \
- .mon_dbgi_regs = { \
- .write_ptr = { \
- .addr = DBGI_SRAM_FIFO_POINTERS, \
- .mask = DBGI_SRAM_FIFO_POINTERS_WR_PTR_MSK, \
- }, \
- }
-
-#define IWL_DEVICE_BZ \
- IWL_DEVICE_BZ_COMMON, \
- .ht_params = &iwl_22000_ht_params
-
-/*
- * This size was picked according to 8 MSDUs inside 512 A-MSDUs in an
- * A-MPDU, with additional overhead to account for processing time.
- */
-#define IWL_NUM_RBDS_BZ_EHT (512 * 16)
-
-const struct iwl_cfg_trans_params iwl_bz_trans_cfg = {
+const struct iwl_mac_cfg iwl_bz_mac_cfg = {
.device_family = IWL_DEVICE_FAMILY_BZ,
- .base_params = &iwl_bz_base_params,
+ .base = &iwl_bz_base,
.mq_rx_supported = true,
- .rf_id = true,
.gen2 = true,
.integrated = true,
.umac_prph_offset = 0x300000,
@@ -134,39 +97,16 @@ const struct iwl_cfg_trans_params iwl_bz_trans_cfg = {
.ltr_delay = IWL_CFG_TRANS_LTR_DELAY_2500US,
};
-const struct iwl_cfg_trans_params iwl_gl_trans_cfg = {
+const struct iwl_mac_cfg iwl_gl_mac_cfg = {
.device_family = IWL_DEVICE_FAMILY_BZ,
- .base_params = &iwl_bz_base_params,
+ .base = &iwl_bz_base,
.mq_rx_supported = true,
- .rf_id = true,
.gen2 = true,
.umac_prph_offset = 0x300000,
.xtal_latency = 12000,
.low_latency_xtal = true,
};
-const char iwl_bz_name[] = "Intel(R) TBD Bz device";
-const char iwl_fm_name[] = "Intel(R) Wi-Fi 7 BE201 320MHz";
-const char iwl_wh_name[] = "Intel(R) Wi-Fi 7 BE211 320MHz";
-const char iwl_gl_name[] = "Intel(R) Wi-Fi 7 BE200 320MHz";
-const char iwl_mtp_name[] = "Intel(R) Wi-Fi 7 BE202 160MHz";
-
-const struct iwl_cfg iwl_cfg_bz = {
- .fw_name_mac = "bz",
- .uhb_supported = true,
- IWL_DEVICE_BZ,
- .features = IWL_TX_CSUM_NETIF_FLAGS | NETIF_F_RXCSUM,
- .num_rbds = IWL_NUM_RBDS_BZ_EHT,
-};
-
-const struct iwl_cfg iwl_cfg_gl = {
- .fw_name_mac = "gl",
- .uhb_supported = true,
- IWL_DEVICE_BZ,
- .features = IWL_TX_CSUM_NETIF_FLAGS | NETIF_F_RXCSUM,
- .num_rbds = IWL_NUM_RBDS_BZ_EHT,
-};
-
MODULE_FIRMWARE(IWL_BZ_A_HR_B_MODULE_FIRMWARE(IWL_BZ_UCODE_API_MAX));
IWL_FW_AND_PNVM(IWL_BZ_A_GF_A_FW_PRE, IWL_BZ_UCODE_API_MAX);
IWL_FW_AND_PNVM(IWL_BZ_A_GF4_A_FW_PRE, IWL_BZ_UCODE_API_MAX);
diff --git a/drivers/net/wireless/intel/iwlwifi/cfg/dr.c b/drivers/net/wireless/intel/iwlwifi/cfg/dr.c
index ab7c0f8d54f4..45e55cef42ea 100644
--- a/drivers/net/wireless/intel/iwlwifi/cfg/dr.c
+++ b/drivers/net/wireless/intel/iwlwifi/cfg/dr.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
/*
- * Copyright (C) 2024 Intel Corporation
+ * Copyright (C) 2024-2025 Intel Corporation
*/
#include <linux/module.h>
#include <linux/stringify.h>
@@ -9,35 +9,21 @@
#include "fw/api/txq.h"
/* Highest firmware API version supported */
-#define IWL_DR_UCODE_API_MAX 96
+#define IWL_DR_UCODE_API_MAX 99
/* Lowest firmware API version supported */
-#define IWL_DR_UCODE_API_MIN 96
-
-/* NVM versions */
-#define IWL_DR_NVM_VERSION 0x0a1d
+#define IWL_DR_UCODE_API_MIN 97
/* Memory offsets and lengths */
-#define IWL_DR_DCCM_OFFSET 0x800000 /* LMAC1 */
-#define IWL_DR_DCCM_LEN 0x10000 /* LMAC1 */
-#define IWL_DR_DCCM2_OFFSET 0x880000
-#define IWL_DR_DCCM2_LEN 0x8000
#define IWL_DR_SMEM_OFFSET 0x400000
#define IWL_DR_SMEM_LEN 0xD0000
#define IWL_DR_A_PE_A_FW_PRE "iwlwifi-dr-a0-pe-a0"
-#define IWL_BR_A_PET_A_FW_PRE "iwlwifi-br-a0-petc-a0"
-#define IWL_BR_A_PE_A_FW_PRE "iwlwifi-br-a0-pe-a0"
#define IWL_DR_A_PE_A_FW_MODULE_FIRMWARE(api) \
IWL_DR_A_PE_A_FW_PRE "-" __stringify(api) ".ucode"
-#define IWL_BR_A_PET_A_FW_MODULE_FIRMWARE(api) \
- IWL_BR_A_PET_A_FW_PRE "-" __stringify(api) ".ucode"
-#define IWL_BR_A_PE_A_FW_MODULE_FIRMWARE(api) \
- IWL_BR_A_PE_A_FW_PRE "-" __stringify(api) ".ucode"
-static const struct iwl_base_params iwl_dr_base_params = {
- .eeprom_size = OTP_LOW_IMAGE_SIZE_32K,
+static const struct iwl_family_base_params iwl_dr_base = {
.num_of_queues = 512,
.max_tfd_queue_size = 65536,
.shadow_ram_support = true,
@@ -46,87 +32,55 @@ static const struct iwl_base_params iwl_dr_base_params = {
.max_event_log_size = 512,
.shadow_reg_enable = true,
.pcie_l1_allowed = true,
+ .smem_offset = IWL_DR_SMEM_OFFSET,
+ .smem_len = IWL_DR_SMEM_LEN,
+ .apmg_not_supported = true,
+ .mac_addr_from_csr = 0x30,
+ .min_umac_error_event_table = 0xD0000,
+ .d3_debug_data_base_addr = 0x401000,
+ .d3_debug_data_length = 60 * 1024,
+ .mon_smem_regs = {
+ .write_ptr = {
+ .addr = LDBG_M2S_BUF_WPTR,
+ .mask = LDBG_M2S_BUF_WPTR_VAL_MSK,
+ },
+ .cycle_cnt = {
+ .addr = LDBG_M2S_BUF_WRAP_CNT,
+ .mask = LDBG_M2S_BUF_WRAP_CNT_VAL_MSK,
+ },
+ },
+ .min_txq_size = 128,
+ .gp2_reg_addr = 0xd02c68,
+ .min_ba_txq_size = IWL_DEFAULT_QUEUE_SIZE_EHT,
+ .mon_dram_regs = {
+ .write_ptr = {
+ .addr = DBGC_CUR_DBGBUF_STATUS,
+ .mask = DBGC_CUR_DBGBUF_STATUS_OFFSET_MSK,
+ },
+ .cycle_cnt = {
+ .addr = DBGC_DBGBUF_WRAP_AROUND,
+ .mask = 0xffffffff,
+ },
+ .cur_frag = {
+ .addr = DBGC_CUR_DBGBUF_STATUS,
+ .mask = DBGC_CUR_DBGBUF_STATUS_IDX_MSK,
+ },
+ },
+ .mon_dbgi_regs = {
+ .write_ptr = {
+ .addr = DBGI_SRAM_FIFO_POINTERS,
+ .mask = DBGI_SRAM_FIFO_POINTERS_WR_PTR_MSK,
+ },
+ },
+ .features = IWL_TX_CSUM_NETIF_FLAGS | NETIF_F_RXCSUM,
+ .ucode_api_max = IWL_DR_UCODE_API_MAX,
+ .ucode_api_min = IWL_DR_UCODE_API_MIN,
};
-#define IWL_DEVICE_DR_COMMON \
- .ucode_api_max = IWL_DR_UCODE_API_MAX, \
- .ucode_api_min = IWL_DR_UCODE_API_MIN, \
- .led_mode = IWL_LED_RF_STATE, \
- .nvm_hw_section_num = 10, \
- .non_shared_ant = ANT_B, \
- .dccm_offset = IWL_DR_DCCM_OFFSET, \
- .dccm_len = IWL_DR_DCCM_LEN, \
- .dccm2_offset = IWL_DR_DCCM2_OFFSET, \
- .dccm2_len = IWL_DR_DCCM2_LEN, \
- .smem_offset = IWL_DR_SMEM_OFFSET, \
- .smem_len = IWL_DR_SMEM_LEN, \
- .apmg_not_supported = true, \
- .trans.mq_rx_supported = true, \
- .vht_mu_mimo_supported = true, \
- .mac_addr_from_csr = 0x30, \
- .nvm_ver = IWL_DR_NVM_VERSION, \
- .trans.rf_id = true, \
- .trans.gen2 = true, \
- .nvm_type = IWL_NVM_EXT, \
- .dbgc_supported = true, \
- .min_umac_error_event_table = 0xD0000, \
- .d3_debug_data_base_addr = 0x401000, \
- .d3_debug_data_length = 60 * 1024, \
- .mon_smem_regs = { \
- .write_ptr = { \
- .addr = LDBG_M2S_BUF_WPTR, \
- .mask = LDBG_M2S_BUF_WPTR_VAL_MSK, \
- }, \
- .cycle_cnt = { \
- .addr = LDBG_M2S_BUF_WRAP_CNT, \
- .mask = LDBG_M2S_BUF_WRAP_CNT_VAL_MSK, \
- }, \
- }, \
- .trans.umac_prph_offset = 0x300000, \
- .trans.device_family = IWL_DEVICE_FAMILY_DR, \
- .trans.base_params = &iwl_dr_base_params, \
- .min_txq_size = 128, \
- .gp2_reg_addr = 0xd02c68, \
- .min_ba_txq_size = IWL_DEFAULT_QUEUE_SIZE_EHT, \
- .mon_dram_regs = { \
- .write_ptr = { \
- .addr = DBGC_CUR_DBGBUF_STATUS, \
- .mask = DBGC_CUR_DBGBUF_STATUS_OFFSET_MSK, \
- }, \
- .cycle_cnt = { \
- .addr = DBGC_DBGBUF_WRAP_AROUND, \
- .mask = 0xffffffff, \
- }, \
- .cur_frag = { \
- .addr = DBGC_CUR_DBGBUF_STATUS, \
- .mask = DBGC_CUR_DBGBUF_STATUS_IDX_MSK, \
- }, \
- }, \
- .mon_dbgi_regs = { \
- .write_ptr = { \
- .addr = DBGI_SRAM_FIFO_POINTERS, \
- .mask = DBGI_SRAM_FIFO_POINTERS_WR_PTR_MSK, \
- }, \
- }
-
-#define IWL_DEVICE_DR \
- IWL_DEVICE_DR_COMMON, \
- .uhb_supported = true, \
- .features = IWL_TX_CSUM_NETIF_FLAGS | NETIF_F_RXCSUM, \
- .num_rbds = IWL_NUM_RBDS_DR_EHT, \
- .ht_params = &iwl_22000_ht_params
-
-/*
- * This size was picked according to 8 MSDUs inside 512 A-MSDUs in an
- * A-MPDU, with additional overhead to account for processing time.
- */
-#define IWL_NUM_RBDS_DR_EHT (512 * 16)
-
-const struct iwl_cfg_trans_params iwl_dr_trans_cfg = {
+const struct iwl_mac_cfg iwl_dr_mac_cfg = {
.device_family = IWL_DEVICE_FAMILY_DR,
- .base_params = &iwl_dr_base_params,
+ .base = &iwl_dr_base,
.mq_rx_supported = true,
- .rf_id = true,
.gen2 = true,
.integrated = true,
.umac_prph_offset = 0x300000,
@@ -135,33 +89,5 @@ const struct iwl_cfg_trans_params iwl_dr_trans_cfg = {
.ltr_delay = IWL_CFG_TRANS_LTR_DELAY_2500US,
};
-const char iwl_dr_name[] = "Intel(R) TBD Dr device";
-
-const struct iwl_cfg iwl_cfg_dr = {
- .fw_name_mac = "dr",
- IWL_DEVICE_DR,
-};
-
-const struct iwl_cfg_trans_params iwl_br_trans_cfg = {
- .device_family = IWL_DEVICE_FAMILY_DR,
- .base_params = &iwl_dr_base_params,
- .mq_rx_supported = true,
- .rf_id = true,
- .gen2 = true,
- .integrated = true,
- .umac_prph_offset = 0x300000,
- .xtal_latency = 12000,
- .low_latency_xtal = true,
- .ltr_delay = IWL_CFG_TRANS_LTR_DELAY_2500US,
-};
-
-const char iwl_br_name[] = "Intel(R) TBD Br device";
-
-const struct iwl_cfg iwl_cfg_br = {
- .fw_name_mac = "br",
- IWL_DEVICE_DR,
-};
-
MODULE_FIRMWARE(IWL_DR_A_PE_A_FW_MODULE_FIRMWARE(IWL_DR_UCODE_API_MAX));
-MODULE_FIRMWARE(IWL_BR_A_PET_A_FW_MODULE_FIRMWARE(IWL_DR_UCODE_API_MAX));
-MODULE_FIRMWARE(IWL_BR_A_PE_A_FW_MODULE_FIRMWARE(IWL_DR_UCODE_API_MAX));
+
diff --git a/drivers/net/wireless/intel/iwlwifi/cfg/rf-fm.c b/drivers/net/wireless/intel/iwlwifi/cfg/rf-fm.c
new file mode 100644
index 000000000000..456a666c8dfd
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/cfg/rf-fm.c
@@ -0,0 +1,51 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
+/*
+ * Copyright (C) 2015-2017 Intel Deutschland GmbH
+ * Copyright (C) 2018-2025 Intel Corporation
+ */
+#include "iwl-config.h"
+
+/* NVM versions */
+#define IWL_FM_NVM_VERSION 0x0a1d
+
+#define IWL_DEVICE_FM \
+ .ht_params = { \
+ .stbc = true, \
+ .ldpc = true, \
+ .ht40_bands = BIT(NL80211_BAND_2GHZ) | \
+ BIT(NL80211_BAND_5GHZ), \
+ }, \
+ .led_mode = IWL_LED_RF_STATE, \
+ .non_shared_ant = ANT_B, \
+ .vht_mu_mimo_supported = true, \
+ .uhb_supported = true, \
+ .num_rbds = IWL_NUM_RBDS_EHT, \
+ .nvm_ver = IWL_FM_NVM_VERSION, \
+ .nvm_type = IWL_NVM_EXT
+
+const struct iwl_rf_cfg iwl_rf_fm = {
+ IWL_DEVICE_FM,
+};
+
+const struct iwl_rf_cfg iwl_rf_fm_160mhz = {
+ IWL_DEVICE_FM,
+ .bw_limit = 160,
+};
+
+const char iwl_killer_be1750s_name[] =
+ "Killer(R) Wi-Fi 7 BE1750s 320MHz Wireless Network Adapter (BE201D2W)";
+const char iwl_killer_be1750i_name[] =
+ "Killer(R) Wi-Fi 7 BE1750i 320MHz Wireless Network Adapter (BE201NGW)";
+const char iwl_killer_be1750w_name[] =
+ "Killer(TM) Wi-Fi 7 BE1750w 320MHz Wireless Network Adapter (BE200D2W)";
+const char iwl_killer_be1750x_name[] =
+ "Killer(TM) Wi-Fi 7 BE1750x 320MHz Wireless Network Adapter (BE200NGW)";
+const char iwl_killer_be1790s_name[] =
+ "Killer(R) Wi-Fi 7 BE1790s 320MHz Wireless Network Adapter (BE401D2W)";
+const char iwl_killer_be1790i_name[] =
+ "Killer(R) Wi-Fi 7 BE1790i 320MHz Wireless Network Adapter (BE401NGW)";
+
+const char iwl_be201_name[] = "Intel(R) Wi-Fi 7 BE201 320MHz";
+const char iwl_be200_name[] = "Intel(R) Wi-Fi 7 BE200 320MHz";
+const char iwl_be202_name[] = "Intel(R) Wi-Fi 7 BE202 160MHz";
+const char iwl_be401_name[] = "Intel(R) Wi-Fi 7 BE401 320MHz";
diff --git a/drivers/net/wireless/intel/iwlwifi/cfg/rf-gf.c b/drivers/net/wireless/intel/iwlwifi/cfg/rf-gf.c
new file mode 100644
index 000000000000..f55c286e83be
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/cfg/rf-gf.c
@@ -0,0 +1,42 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
+/*
+ * Copyright (C) 2015-2017 Intel Deutschland GmbH
+ * Copyright (C) 2018-2025 Intel Corporation
+ */
+#include "iwl-config.h"
+
+/* NVM versions */
+#define IWL_GF_NVM_VERSION 0x0a1d
+
+const struct iwl_rf_cfg iwl_rf_gf = {
+ .uhb_supported = true,
+ .led_mode = IWL_LED_RF_STATE,
+ .non_shared_ant = ANT_B,
+ .vht_mu_mimo_supported = true,
+ .ht_params = {
+ .stbc = true,
+ .ldpc = true,
+ .ht40_bands = BIT(NL80211_BAND_2GHZ) |
+ BIT(NL80211_BAND_5GHZ),
+ },
+ .nvm_ver = IWL_GF_NVM_VERSION,
+ .nvm_type = IWL_NVM_EXT,
+ .num_rbds = IWL_NUM_RBDS_HE,
+};
+
+const char iwl_ax210_killer_1675w_name[] =
+ "Killer(R) Wi-Fi 6E AX1675w 160MHz Wireless Network Adapter (210D2W)";
+const char iwl_ax210_killer_1675x_name[] =
+ "Killer(R) Wi-Fi 6E AX1675x 160MHz Wireless Network Adapter (210NGW)";
+const char iwl_ax211_killer_1675s_name[] =
+ "Killer(R) Wi-Fi 6E AX1675s 160MHz Wireless Network Adapter (211D2W)";
+const char iwl_ax211_killer_1675i_name[] =
+ "Killer(R) Wi-Fi 6E AX1675i 160MHz Wireless Network Adapter (211NGW)";
+const char iwl_ax411_killer_1690s_name[] =
+ "Killer(R) Wi-Fi 6E AX1690s 160MHz Wireless Network Adapter (411D2W)";
+const char iwl_ax411_killer_1690i_name[] =
+ "Killer(R) Wi-Fi 6E AX1690i 160MHz Wireless Network Adapter (411NGW)";
+
+const char iwl_ax210_name[] = "Intel(R) Wi-Fi 6E AX210 160MHz";
+const char iwl_ax211_name[] = "Intel(R) Wi-Fi 6E AX211 160MHz";
+const char iwl_ax411_name[] = "Intel(R) Wi-Fi 6E AX411 160MHz";
diff --git a/drivers/net/wireless/intel/iwlwifi/cfg/rf-hr.c b/drivers/net/wireless/intel/iwlwifi/cfg/rf-hr.c
new file mode 100644
index 000000000000..db02664e3917
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/cfg/rf-hr.c
@@ -0,0 +1,42 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
+/*
+ * Copyright (C) 2015-2017 Intel Deutschland GmbH
+ * Copyright (C) 2018-2025 Intel Corporation
+ */
+#include "iwl-config.h"
+
+/* NVM versions */
+#define IWL_HR_NVM_VERSION 0x0a1d
+
+#define IWL_DEVICE_HR \
+ .led_mode = IWL_LED_RF_STATE, \
+ .non_shared_ant = ANT_B, \
+ .vht_mu_mimo_supported = true, \
+ .ht_params = { \
+ .stbc = true, \
+ .ldpc = true, \
+ .ht40_bands = BIT(NL80211_BAND_2GHZ) | \
+ BIT(NL80211_BAND_5GHZ), \
+ }, \
+ .num_rbds = IWL_NUM_RBDS_HE, \
+ .nvm_ver = IWL_HR_NVM_VERSION, \
+ .nvm_type = IWL_NVM_EXT
+
+const struct iwl_rf_cfg iwl_rf_hr1 = {
+ IWL_DEVICE_HR,
+ .tx_with_siso_diversity = true,
+};
+
+const struct iwl_rf_cfg iwl_rf_hr = {
+ IWL_DEVICE_HR,
+};
+
+const struct iwl_rf_cfg iwl_rf_hr_80mhz = {
+ IWL_DEVICE_HR,
+ .bw_limit = 80,
+};
+
+const char iwl_ax101_name[] = "Intel(R) Wi-Fi 6 AX101";
+const char iwl_ax200_name[] = "Intel(R) Wi-Fi 6 AX200 160MHz";
+const char iwl_ax201_name[] = "Intel(R) Wi-Fi 6 AX201 160MHz";
+const char iwl_ax203_name[] = "Intel(R) Wi-Fi 6 AX203";
diff --git a/drivers/net/wireless/intel/iwlwifi/cfg/rf-jf.c b/drivers/net/wireless/intel/iwlwifi/cfg/rf-jf.c
new file mode 100644
index 000000000000..467eaeae6deb
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/cfg/rf-jf.c
@@ -0,0 +1,84 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
+/*
+ * Copyright (C) 2015-2017 Intel Deutschland GmbH
+ * Copyright (C) 2018-2021, 2023, 2025 Intel Corporation
+ */
+#include "iwl-config.h"
+
+/* NVM versions */
+#define IWL_JF_NVM_VERSION 0x0a1d
+
+/* Memory offsets and lengths */
+#define IWL9000_DCCM_OFFSET 0x800000
+#define IWL9000_DCCM_LEN 0x18000
+#define IWL9000_DCCM2_OFFSET 0x880000
+#define IWL9000_DCCM2_LEN 0x8000
+
+static const struct iwl_tt_params iwl_jf_tt_params = {
+ .ct_kill_entry = 115,
+ .ct_kill_exit = 93,
+ .ct_kill_duration = 5,
+ .dynamic_smps_entry = 111,
+ .dynamic_smps_exit = 107,
+ .tx_protection_entry = 112,
+ .tx_protection_exit = 105,
+ .tx_backoff = {
+ {.temperature = 110, .backoff = 200},
+ {.temperature = 111, .backoff = 600},
+ {.temperature = 112, .backoff = 1200},
+ {.temperature = 113, .backoff = 2000},
+ {.temperature = 114, .backoff = 4000},
+ },
+ .support_ct_kill = true,
+ .support_dynamic_smps = true,
+ .support_tx_protection = true,
+ .support_tx_backoff = true,
+};
+
+/* these values are ignored if not with Pu/Th MAC firmware, due to offload */
+#define IWL_DEVICE_JF_PU \
+ .dccm_offset = IWL9000_DCCM_OFFSET, \
+ .dccm_len = IWL9000_DCCM_LEN, \
+ .dccm2_offset = IWL9000_DCCM2_OFFSET, \
+ .dccm2_len = IWL9000_DCCM2_LEN, \
+ .thermal_params = &iwl_jf_tt_params
+
+#define IWL_DEVICE_JF \
+ IWL_DEVICE_JF_PU, \
+ .led_mode = IWL_LED_RF_STATE, \
+ .non_shared_ant = ANT_B, \
+ .num_rbds = IWL_NUM_RBDS_NON_HE, \
+ .vht_mu_mimo_supported = true, \
+ .ht_params = { \
+ .stbc = true, \
+ .ldpc = true, \
+ .ht40_bands = BIT(NL80211_BAND_2GHZ) | \
+ BIT(NL80211_BAND_5GHZ), \
+ }, \
+ .nvm_ver = IWL_JF_NVM_VERSION, \
+ .nvm_type = IWL_NVM_EXT
+
+const struct iwl_rf_cfg iwl_rf_jf = {
+ IWL_DEVICE_JF,
+};
+
+const struct iwl_rf_cfg iwl_rf_jf_80mhz = {
+ IWL_DEVICE_JF,
+ .bw_limit = 80,
+};
+
+const char iwl9260_name[] = "Intel(R) Wireless-AC 9260";
+const char iwl9461_name[] = "Intel(R) Wireless-AC 9461";
+const char iwl9462_name[] = "Intel(R) Wireless-AC 9462";
+const char iwl9560_name[] = "Intel(R) Wireless-AC 9560";
+const char iwl9260_160_name[] = "Intel(R) Wireless-AC 9260 160MHz";
+const char iwl9461_160_name[] = "Intel(R) Wireless-AC 9461 160MHz";
+const char iwl9462_160_name[] = "Intel(R) Wireless-AC 9462 160MHz";
+const char iwl9560_160_name[] = "Intel(R) Wireless-AC 9560 160MHz";
+
+const char iwl9260_killer_1550_name[] =
+ "Killer(R) Wireless-AC 1550 Wireless Network Adapter (9260NGW) 160MHz";
+const char iwl9560_killer_1550i_name[] =
+ "Killer(R) Wireless-AC 1550i Wireless Network Adapter (9560NGW) 160MHz";
+const char iwl9560_killer_1550s_name[] =
+ "Killer(R) Wireless-AC 1550s Wireless Network Adapter (9560D2W) 160MHz";
diff --git a/drivers/net/wireless/intel/iwlwifi/cfg/rf-pe.c b/drivers/net/wireless/intel/iwlwifi/cfg/rf-pe.c
new file mode 100644
index 000000000000..483f21659eff
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/cfg/rf-pe.c
@@ -0,0 +1,16 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
+/*
+ * Copyright (C) 2025 Intel Corporation
+ */
+#include "iwl-config.h"
+
+/* currently iwl_rf_wh/iwl_rf_wh_160mhz are just defines for the FM ones */
+
+const char iwl_killer_bn1850w2_name[] =
+ "Killer(R) Wi-Fi 8 BN1850w2 320MHz Wireless Network Adapter (BN201.D2W)";
+const char iwl_killer_bn1850i_name[] =
+ "Killer(R) Wi-Fi 8 BN1850i 320MHz Wireless Network Adapter (BN201.NGW)";
+
+const char iwl_bn201_name[] = "Intel(R) Wi-Fi 8 BN201";
+const char iwl_be221_name[] = "Intel(R) Wi-Fi 7 BE221";
+const char iwl_be223_name[] = "Intel(R) Wi-Fi 7 BE223";
diff --git a/drivers/net/wireless/intel/iwlwifi/cfg/rf-wh.c b/drivers/net/wireless/intel/iwlwifi/cfg/rf-wh.c
new file mode 100644
index 000000000000..97735175cb0e
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/cfg/rf-wh.c
@@ -0,0 +1,15 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
+/*
+ * Copyright (C) 2025 Intel Corporation
+ */
+#include "iwl-config.h"
+
+/* currently iwl_rf_wh/iwl_rf_wh_160mhz are just defines for the FM ones */
+
+const char iwl_killer_be1775s_name[] =
+ "Killer(R) Wi-Fi 7 BE1775s 320MHz Wireless Network Adapter (BE211D2W)";
+const char iwl_killer_be1775i_name[] =
+ "Killer(R) Wi-Fi 7 BE1775i 320MHz Wireless Network Adapter (BE211NGW)";
+
+const char iwl_be211_name[] = "Intel(R) Wi-Fi 7 BE211 320MHz";
+const char iwl_be213_name[] = "Intel(R) Wi-Fi 7 BE213 160MHz";
diff --git a/drivers/net/wireless/intel/iwlwifi/cfg/sc.c b/drivers/net/wireless/intel/iwlwifi/cfg/sc.c
index c9eeb3f6704e..b2e4d4035296 100644
--- a/drivers/net/wireless/intel/iwlwifi/cfg/sc.c
+++ b/drivers/net/wireless/intel/iwlwifi/cfg/sc.c
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
/*
* Copyright (C) 2015-2017 Intel Deutschland GmbH
- * Copyright (C) 2018-2024 Intel Corporation
+ * Copyright (C) 2018-2025 Intel Corporation
*/
#include <linux/module.h>
#include <linux/stringify.h>
@@ -10,19 +10,15 @@
#include "fw/api/txq.h"
/* Highest firmware API version supported */
-#define IWL_SC_UCODE_API_MAX 96
+#define IWL_SC_UCODE_API_MAX 99
/* Lowest firmware API version supported */
-#define IWL_SC_UCODE_API_MIN 92
+#define IWL_SC_UCODE_API_MIN 97
/* NVM versions */
#define IWL_SC_NVM_VERSION 0x0a1d
/* Memory offsets and lengths */
-#define IWL_SC_DCCM_OFFSET 0x800000 /* LMAC1 */
-#define IWL_SC_DCCM_LEN 0x10000 /* LMAC1 */
-#define IWL_SC_DCCM2_OFFSET 0x880000
-#define IWL_SC_DCCM2_LEN 0x8000
#define IWL_SC_SMEM_OFFSET 0x400000
#define IWL_SC_SMEM_LEN 0xD0000
@@ -43,8 +39,7 @@
#define IWL_SC_A_HR_B_FW_MODULE_FIRMWARE(api) \
IWL_SC_A_HR_B_FW_PRE "-" __stringify(api) ".ucode"
-static const struct iwl_base_params iwl_sc_base_params = {
- .eeprom_size = OTP_LOW_IMAGE_SIZE_32K,
+static const struct iwl_family_base_params iwl_sc_base = {
.num_of_queues = 512,
.max_tfd_queue_size = 65536,
.shadow_ram_support = true,
@@ -53,87 +48,55 @@ static const struct iwl_base_params iwl_sc_base_params = {
.max_event_log_size = 512,
.shadow_reg_enable = true,
.pcie_l1_allowed = true,
+ .smem_offset = IWL_SC_SMEM_OFFSET,
+ .smem_len = IWL_SC_SMEM_LEN,
+ .apmg_not_supported = true,
+ .mac_addr_from_csr = 0x30,
+ .min_umac_error_event_table = 0xD0000,
+ .d3_debug_data_base_addr = 0x401000,
+ .d3_debug_data_length = 60 * 1024,
+ .mon_smem_regs = {
+ .write_ptr = {
+ .addr = LDBG_M2S_BUF_WPTR,
+ .mask = LDBG_M2S_BUF_WPTR_VAL_MSK,
+ },
+ .cycle_cnt = {
+ .addr = LDBG_M2S_BUF_WRAP_CNT,
+ .mask = LDBG_M2S_BUF_WRAP_CNT_VAL_MSK,
+ },
+ },
+ .min_txq_size = 128,
+ .gp2_reg_addr = 0xd02c68,
+ .min_ba_txq_size = IWL_DEFAULT_QUEUE_SIZE_EHT,
+ .mon_dram_regs = {
+ .write_ptr = {
+ .addr = DBGC_CUR_DBGBUF_STATUS,
+ .mask = DBGC_CUR_DBGBUF_STATUS_OFFSET_MSK,
+ },
+ .cycle_cnt = {
+ .addr = DBGC_DBGBUF_WRAP_AROUND,
+ .mask = 0xffffffff,
+ },
+ .cur_frag = {
+ .addr = DBGC_CUR_DBGBUF_STATUS,
+ .mask = DBGC_CUR_DBGBUF_STATUS_IDX_MSK,
+ },
+ },
+ .mon_dbgi_regs = {
+ .write_ptr = {
+ .addr = DBGI_SRAM_FIFO_POINTERS,
+ .mask = DBGI_SRAM_FIFO_POINTERS_WR_PTR_MSK,
+ },
+ },
+ .features = IWL_TX_CSUM_NETIF_FLAGS | NETIF_F_RXCSUM,
+ .ucode_api_max = IWL_SC_UCODE_API_MAX,
+ .ucode_api_min = IWL_SC_UCODE_API_MIN,
};
-#define IWL_DEVICE_BZ_COMMON \
- .ucode_api_max = IWL_SC_UCODE_API_MAX, \
- .ucode_api_min = IWL_SC_UCODE_API_MIN, \
- .led_mode = IWL_LED_RF_STATE, \
- .nvm_hw_section_num = 10, \
- .non_shared_ant = ANT_B, \
- .dccm_offset = IWL_SC_DCCM_OFFSET, \
- .dccm_len = IWL_SC_DCCM_LEN, \
- .dccm2_offset = IWL_SC_DCCM2_OFFSET, \
- .dccm2_len = IWL_SC_DCCM2_LEN, \
- .smem_offset = IWL_SC_SMEM_OFFSET, \
- .smem_len = IWL_SC_SMEM_LEN, \
- .apmg_not_supported = true, \
- .trans.mq_rx_supported = true, \
- .vht_mu_mimo_supported = true, \
- .mac_addr_from_csr = 0x30, \
- .nvm_ver = IWL_SC_NVM_VERSION, \
- .trans.rf_id = true, \
- .trans.gen2 = true, \
- .nvm_type = IWL_NVM_EXT, \
- .dbgc_supported = true, \
- .min_umac_error_event_table = 0xD0000, \
- .d3_debug_data_base_addr = 0x401000, \
- .d3_debug_data_length = 60 * 1024, \
- .mon_smem_regs = { \
- .write_ptr = { \
- .addr = LDBG_M2S_BUF_WPTR, \
- .mask = LDBG_M2S_BUF_WPTR_VAL_MSK, \
- }, \
- .cycle_cnt = { \
- .addr = LDBG_M2S_BUF_WRAP_CNT, \
- .mask = LDBG_M2S_BUF_WRAP_CNT_VAL_MSK, \
- }, \
- }, \
- .trans.umac_prph_offset = 0x300000, \
- .trans.device_family = IWL_DEVICE_FAMILY_SC, \
- .trans.base_params = &iwl_sc_base_params, \
- .min_txq_size = 128, \
- .gp2_reg_addr = 0xd02c68, \
- .min_ba_txq_size = IWL_DEFAULT_QUEUE_SIZE_EHT, \
- .mon_dram_regs = { \
- .write_ptr = { \
- .addr = DBGC_CUR_DBGBUF_STATUS, \
- .mask = DBGC_CUR_DBGBUF_STATUS_OFFSET_MSK, \
- }, \
- .cycle_cnt = { \
- .addr = DBGC_DBGBUF_WRAP_AROUND, \
- .mask = 0xffffffff, \
- }, \
- .cur_frag = { \
- .addr = DBGC_CUR_DBGBUF_STATUS, \
- .mask = DBGC_CUR_DBGBUF_STATUS_IDX_MSK, \
- }, \
- }, \
- .mon_dbgi_regs = { \
- .write_ptr = { \
- .addr = DBGI_SRAM_FIFO_POINTERS, \
- .mask = DBGI_SRAM_FIFO_POINTERS_WR_PTR_MSK, \
- }, \
- }
-
-#define IWL_DEVICE_SC \
- IWL_DEVICE_BZ_COMMON, \
- .uhb_supported = true, \
- .features = IWL_TX_CSUM_NETIF_FLAGS | NETIF_F_RXCSUM, \
- .num_rbds = IWL_NUM_RBDS_SC_EHT, \
- .ht_params = &iwl_22000_ht_params
-
-/*
- * This size was picked according to 8 MSDUs inside 512 A-MSDUs in an
- * A-MPDU, with additional overhead to account for processing time.
- */
-#define IWL_NUM_RBDS_SC_EHT (512 * 16)
-
-const struct iwl_cfg_trans_params iwl_sc_trans_cfg = {
+const struct iwl_mac_cfg iwl_sc_mac_cfg = {
.device_family = IWL_DEVICE_FAMILY_SC,
- .base_params = &iwl_sc_base_params,
+ .base = &iwl_sc_base,
.mq_rx_supported = true,
- .rf_id = true,
.gen2 = true,
.integrated = true,
.umac_prph_offset = 0x300000,
@@ -142,27 +105,6 @@ const struct iwl_cfg_trans_params iwl_sc_trans_cfg = {
.ltr_delay = IWL_CFG_TRANS_LTR_DELAY_2500US,
};
-const char iwl_sc_name[] = "Intel(R) TBD Sc device";
-
-const struct iwl_cfg iwl_cfg_sc = {
- .fw_name_mac = "sc",
- IWL_DEVICE_SC,
-};
-
-const char iwl_sc2_name[] = "Intel(R) TBD Sc2 device";
-
-const struct iwl_cfg iwl_cfg_sc2 = {
- .fw_name_mac = "sc2",
- IWL_DEVICE_SC,
-};
-
-const char iwl_sc2f_name[] = "Intel(R) TBD Sc2f device";
-
-const struct iwl_cfg iwl_cfg_sc2f = {
- .fw_name_mac = "sc2f",
- IWL_DEVICE_SC,
-};
-
IWL_FW_AND_PNVM(IWL_SC_A_FM_B_FW_PRE, IWL_SC_UCODE_API_MAX);
IWL_FW_AND_PNVM(IWL_SC_A_FM_C_FW_PRE, IWL_SC_UCODE_API_MAX);
MODULE_FIRMWARE(IWL_SC_A_HR_A_FW_MODULE_FIRMWARE(IWL_SC_UCODE_API_MAX));
diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/agn.h b/drivers/net/wireless/intel/iwlwifi/dvm/agn.h
index a13add556a7b..1ebc7effcc2a 100644
--- a/drivers/net/wireless/intel/iwlwifi/dvm/agn.h
+++ b/drivers/net/wireless/intel/iwlwifi/dvm/agn.h
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
/*
- * Copyright (C) 2005-2014, 2021, 2024 Intel Corporation
+ * Copyright (C) 2005-2014, 2021, 2024-2025 Intel Corporation
*/
#ifndef __iwl_agn_h__
#define __iwl_agn_h__
@@ -399,7 +399,7 @@ static inline void iwl_dvm_set_pmi(struct iwl_priv *priv, bool state)
* later with iwl_free_nvm_data().
*/
struct iwl_nvm_data *
-iwl_parse_eeprom_data(struct iwl_trans *trans, const struct iwl_cfg *cfg,
+iwl_parse_eeprom_data(struct iwl_trans *trans, const struct iwl_rf_cfg *cfg,
const u8 *eeprom, size_t eeprom_size);
int iwl_read_eeprom(struct iwl_trans *trans, u8 **eeprom, size_t *eeprom_size);
diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/commands.h b/drivers/net/wireless/intel/iwlwifi/dvm/commands.h
index 3f49c0bccb28..96ea6c8dfc89 100644
--- a/drivers/net/wireless/intel/iwlwifi/dvm/commands.h
+++ b/drivers/net/wireless/intel/iwlwifi/dvm/commands.h
@@ -1180,85 +1180,87 @@ struct iwl_dram_scratch {
} __packed;
struct iwl_tx_cmd {
- /*
- * MPDU byte count:
- * MAC header (24/26/30/32 bytes) + 2 bytes pad if 26/30 header size,
- * + 8 byte IV for CCM or TKIP (not used for WEP)
- * + Data payload
- * + 8-byte MIC (not used for CCM/WEP)
- * NOTE: Does not include Tx command bytes, post-MAC pad bytes,
- * MIC (CCM) 8 bytes, ICV (WEP/TKIP/CKIP) 4 bytes, CRC 4 bytes.i
- * Range: 14-2342 bytes.
- */
- __le16 len;
-
- /*
- * MPDU or MSDU byte count for next frame.
- * Used for fragmentation and bursting, but not 11n aggregation.
- * Same as "len", but for next frame. Set to 0 if not applicable.
- */
- __le16 next_frame_len;
-
- __le32 tx_flags; /* TX_CMD_FLG_* */
-
- /* uCode may modify this field of the Tx command (in host DRAM!).
- * Driver must also set dram_lsb_ptr and dram_msb_ptr in this cmd. */
- struct iwl_dram_scratch scratch;
-
- /* Rate for *all* Tx attempts, if TX_CMD_FLG_STA_RATE_MSK is cleared. */
- __le32 rate_n_flags; /* RATE_MCS_* */
-
- /* Index of destination station in uCode's station table */
- u8 sta_id;
-
- /* Type of security encryption: CCM or TKIP */
- u8 sec_ctl; /* TX_CMD_SEC_* */
-
- /*
- * Index into rate table (see REPLY_TX_LINK_QUALITY_CMD) for initial
- * Tx attempt, if TX_CMD_FLG_STA_RATE_MSK is set. Normally "0" for
- * data frames, this field may be used to selectively reduce initial
- * rate (via non-0 value) for special frames (e.g. management), while
- * still supporting rate scaling for all frames.
- */
- u8 initial_rate_index;
- u8 reserved;
- u8 key[16];
- __le16 next_frame_flags;
- __le16 reserved2;
- union {
- __le32 life_time;
- __le32 attempt;
- } stop_time;
-
- /* Host DRAM physical address pointer to "scratch" in this command.
- * Must be dword aligned. "0" in dram_lsb_ptr disables usage. */
- __le32 dram_lsb_ptr;
- u8 dram_msb_ptr;
-
- u8 rts_retry_limit; /*byte 50 */
- u8 data_retry_limit; /*byte 51 */
- u8 tid_tspec;
- union {
- __le16 pm_frame_timeout;
- __le16 attempt_duration;
- } timeout;
-
- /*
- * Duration of EDCA burst Tx Opportunity, in 32-usec units.
- * Set this if txop time is not specified by HCCA protocol (e.g. by AP).
- */
- __le16 driver_txop;
-
+ /* New members MUST be added within the __struct_group() macro below. */
+ __struct_group(iwl_tx_cmd_hdr, __hdr, __packed,
+ /*
+ * MPDU byte count:
+ * MAC header (24/26/30/32 bytes) + 2 bytes pad if 26/30 header size,
+ * + 8 byte IV for CCM or TKIP (not used for WEP)
+ * + Data payload
+ * + 8-byte MIC (not used for CCM/WEP)
+ * NOTE: Does not include Tx command bytes, post-MAC pad bytes,
+ * MIC (CCM) 8 bytes, ICV (WEP/TKIP/CKIP) 4 bytes, CRC 4 bytes.i
+ * Range: 14-2342 bytes.
+ */
+ __le16 len;
+
+ /*
+ * MPDU or MSDU byte count for next frame.
+ * Used for fragmentation and bursting, but not 11n aggregation.
+ * Same as "len", but for next frame. Set to 0 if not applicable.
+ */
+ __le16 next_frame_len;
+
+ __le32 tx_flags; /* TX_CMD_FLG_* */
+
+ /* uCode may modify this field of the Tx command (in host DRAM!).
+ * Driver must also set dram_lsb_ptr and dram_msb_ptr in this cmd. */
+ struct iwl_dram_scratch scratch;
+
+ /* Rate for *all* Tx attempts, if TX_CMD_FLG_STA_RATE_MSK is cleared. */
+ __le32 rate_n_flags; /* RATE_MCS_* */
+
+ /* Index of destination station in uCode's station table */
+ u8 sta_id;
+
+ /* Type of security encryption: CCM or TKIP */
+ u8 sec_ctl; /* TX_CMD_SEC_* */
+
+ /*
+ * Index into rate table (see REPLY_TX_LINK_QUALITY_CMD) for initial
+ * Tx attempt, if TX_CMD_FLG_STA_RATE_MSK is set. Normally "0" for
+ * data frames, this field may be used to selectively reduce initial
+ * rate (via non-0 value) for special frames (e.g. management), while
+ * still supporting rate scaling for all frames.
+ */
+ u8 initial_rate_index;
+ u8 reserved;
+ u8 key[16];
+ __le16 next_frame_flags;
+ __le16 reserved2;
+ union {
+ __le32 life_time;
+ __le32 attempt;
+ } stop_time;
+
+ /* Host DRAM physical address pointer to "scratch" in this command.
+ * Must be dword aligned. "0" in dram_lsb_ptr disables usage. */
+ __le32 dram_lsb_ptr;
+ u8 dram_msb_ptr;
+
+ u8 rts_retry_limit; /*byte 50 */
+ u8 data_retry_limit; /*byte 51 */
+ u8 tid_tspec;
+ union {
+ __le16 pm_frame_timeout;
+ __le16 attempt_duration;
+ } timeout;
+
+ /*
+ * Duration of EDCA burst Tx Opportunity, in 32-usec units.
+ * Set this if txop time is not specified by HCCA protocol (e.g. by AP).
+ */
+ __le16 driver_txop;
+
+ );
/*
* MAC header goes here, followed by 2 bytes padding if MAC header
* length is 26 or 30 bytes, followed by payload data
*/
- union {
- DECLARE_FLEX_ARRAY(u8, payload);
- DECLARE_FLEX_ARRAY(struct ieee80211_hdr, hdr);
- };
+ struct ieee80211_hdr hdr[];
} __packed;
+static_assert(offsetof(struct iwl_tx_cmd, hdr) == sizeof(struct iwl_tx_cmd_hdr),
+ "struct member likely outside of __struct_group()");
/*
* TX command response is sent after *agn* transmission attempts.
@@ -2312,7 +2314,7 @@ struct iwl_scan_cmd {
/* For active scans (set to all-0s for passive scans).
* Does not include payload. Must specify Tx rate; no rate scaling. */
- struct iwl_tx_cmd tx_cmd;
+ struct iwl_tx_cmd_hdr tx_cmd;
/* For directed active scans (set to all-0s otherwise) */
struct iwl_ssid_ie direct_scan[PROBE_OPTION_MAX];
@@ -2423,7 +2425,7 @@ struct iwlagn_beacon_notif {
*/
struct iwl_tx_beacon_cmd {
- struct iwl_tx_cmd tx;
+ struct iwl_tx_cmd_hdr tx;
__le16 tim_idx;
u8 tim_size;
u8 reserved1;
diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/debugfs.c b/drivers/net/wireless/intel/iwlwifi/dvm/debugfs.c
index b246dbd371b3..ec94c43ba28c 100644
--- a/drivers/net/wireless/intel/iwlwifi/dvm/debugfs.c
+++ b/drivers/net/wireless/intel/iwlwifi/dvm/debugfs.c
@@ -2,7 +2,7 @@
/******************************************************************************
*
* Copyright(c) 2008 - 2014 Intel Corporation. All rights reserved.
- * Copyright (C) 2018 Intel Corporation
+ * Copyright (C) 2018, 2025 Intel Corporation
*****************************************************************************/
#include <linux/slab.h>
@@ -1870,7 +1870,7 @@ static ssize_t iwl_dbgfs_ucode_tracing_write(struct file *file,
}
} else {
priv->event_log.ucode_trace = false;
- del_timer_sync(&priv->ucode_trace);
+ timer_delete_sync(&priv->ucode_trace);
}
return count;
@@ -2097,7 +2097,8 @@ static ssize_t iwl_dbgfs_protection_mode_read(struct file *file,
char buf[40];
const size_t bufsz = sizeof(buf);
- if (priv->cfg->ht_params)
+ /* HT devices also have at least one HT40 band */
+ if (priv->cfg->ht_params.ht40_bands)
pos += scnprintf(buf + pos, bufsz - pos,
"use %s for aggregation\n",
(priv->hw_params.use_rts_for_aggregation) ?
@@ -2117,7 +2118,8 @@ static ssize_t iwl_dbgfs_protection_mode_write(struct file *file,
int buf_size;
int rts;
- if (!priv->cfg->ht_params)
+ /* HT devices also have at least one HT40 band */
+ if (!priv->cfg->ht_params.ht40_bands)
return -EINVAL;
memset(buf, 0, sizeof(buf));
diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/dev.h b/drivers/net/wireless/intel/iwlwifi/dvm/dev.h
index 4ac8b862ad41..25b24820466d 100644
--- a/drivers/net/wireless/intel/iwlwifi/dvm/dev.h
+++ b/drivers/net/wireless/intel/iwlwifi/dvm/dev.h
@@ -2,6 +2,7 @@
/******************************************************************************
*
* Copyright(c) 2003 - 2014, 2020, 2023 Intel Corporation. All rights reserved.
+ * Copyright (C) 2025 Intel Corporation
*****************************************************************************/
/*
* Please use this file (dev.h) for driver implementation definitions.
@@ -627,7 +628,7 @@ struct iwl_priv {
struct iwl_trans *trans;
struct device *dev; /* for debug prints only */
- const struct iwl_cfg *cfg;
+ const struct iwl_rf_cfg *cfg;
const struct iwl_fw *fw;
const struct iwl_dvm_cfg *lib;
unsigned long status;
diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/devices.c b/drivers/net/wireless/intel/iwlwifi/dvm/devices.c
index 48a8349680fc..3447ae0b160a 100644
--- a/drivers/net/wireless/intel/iwlwifi/dvm/devices.c
+++ b/drivers/net/wireless/intel/iwlwifi/dvm/devices.c
@@ -2,7 +2,7 @@
/******************************************************************************
*
* Copyright(c) 2008 - 2014 Intel Corporation. All rights reserved.
- * Copyright (C) 2019 Intel Corporation
+ * Copyright (C) 2019, 2025 Intel Corporation
*****************************************************************************/
#include <linux/units.h>
@@ -481,7 +481,7 @@ static void iwl6000_set_ct_threshold(struct iwl_priv *priv)
/* NIC configuration for 6000 series */
static void iwl6000_nic_config(struct iwl_priv *priv)
{
- switch (priv->trans->trans_cfg->device_family) {
+ switch (priv->trans->mac_cfg->device_family) {
case IWL_DEVICE_FAMILY_6005:
case IWL_DEVICE_FAMILY_6030:
case IWL_DEVICE_FAMILY_6000:
diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/eeprom.c b/drivers/net/wireless/intel/iwlwifi/dvm/eeprom.c
index cdc05f7e75a6..2423125e5284 100644
--- a/drivers/net/wireless/intel/iwlwifi/dvm/eeprom.c
+++ b/drivers/net/wireless/intel/iwlwifi/dvm/eeprom.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
/*
- * Copyright (C) 2005-2014, 2018-2019, 2021, 2024 Intel Corporation
+ * Copyright (C) 2005-2014, 2018-2019, 2021, 2024-2025 Intel Corporation
*/
#include <linux/types.h>
#include <linux/slab.h>
@@ -151,7 +151,7 @@ static u16 iwl_eeprom_query16(const u8 *eeprom, size_t eeprom_size, int offset)
{
if (WARN_ON(offset + sizeof(u16) > eeprom_size))
return 0;
- return le16_to_cpup((__le16 *)(eeprom + offset));
+ return le16_to_cpup((const __le16 *)(eeprom + offset));
}
static u32 eeprom_indirect_address(const u8 *eeprom, size_t eeprom_size,
@@ -204,8 +204,8 @@ static u32 eeprom_indirect_address(const u8 *eeprom, size_t eeprom_size,
return (address & ADDRESS_MSK) + (offset << 1);
}
-static const u8 *iwl_eeprom_query_addr(const u8 *eeprom, size_t eeprom_size,
- u32 offset)
+static const void *iwl_eeprom_query_addr(const u8 *eeprom, size_t eeprom_size,
+ u32 offset)
{
u32 address = eeprom_indirect_address(eeprom, eeprom_size, offset);
@@ -218,10 +218,9 @@ static const u8 *iwl_eeprom_query_addr(const u8 *eeprom, size_t eeprom_size,
static int iwl_eeprom_read_calib(const u8 *eeprom, size_t eeprom_size,
struct iwl_nvm_data *data)
{
- struct iwl_eeprom_calib_hdr *hdr;
+ const struct iwl_eeprom_calib_hdr *hdr;
- hdr = (void *)iwl_eeprom_query_addr(eeprom, eeprom_size,
- EEPROM_CALIB_ALL);
+ hdr = iwl_eeprom_query_addr(eeprom, eeprom_size, EEPROM_CALIB_ALL);
if (!hdr)
return -ENODATA;
data->calib_version = hdr->version;
@@ -295,7 +294,7 @@ struct iwl_eeprom_enhanced_txpwr {
} __packed;
static s8 iwl_get_max_txpwr_half_dbm(const struct iwl_nvm_data *data,
- struct iwl_eeprom_enhanced_txpwr *txp)
+ const struct iwl_eeprom_enhanced_txpwr *txp)
{
s8 result = 0; /* (.5 dBm) */
@@ -329,7 +328,7 @@ static s8 iwl_get_max_txpwr_half_dbm(const struct iwl_nvm_data *data,
static void
iwl_eeprom_enh_txp_read_element(struct iwl_nvm_data *data,
- struct iwl_eeprom_enhanced_txpwr *txp,
+ const struct iwl_eeprom_enhanced_txpwr *txp,
int n_channels, s8 max_txpower_avg)
{
int ch_idx;
@@ -360,20 +359,18 @@ static void iwl_eeprom_enhanced_txpower(struct device *dev,
const u8 *eeprom, size_t eeprom_size,
int n_channels)
{
- struct iwl_eeprom_enhanced_txpwr *txp_array, *txp;
+ const struct iwl_eeprom_enhanced_txpwr *txp_array, *txp;
int idx, entries;
- __le16 *txp_len;
+ const __le16 *txp_len;
s8 max_txp_avg_halfdbm;
BUILD_BUG_ON(sizeof(struct iwl_eeprom_enhanced_txpwr) != 8);
/* the length is in 16-bit words, but we want entries */
- txp_len = (__le16 *)iwl_eeprom_query_addr(eeprom, eeprom_size,
- EEPROM_TXP_SZ_OFFS);
+ txp_len = iwl_eeprom_query_addr(eeprom, eeprom_size, EEPROM_TXP_SZ_OFFS);
entries = le16_to_cpup(txp_len) * 2 / EEPROM_TXP_ENTRY_LEN;
- txp_array = (void *)iwl_eeprom_query_addr(eeprom, eeprom_size,
- EEPROM_TXP_OFFS);
+ txp_array = iwl_eeprom_query_addr(eeprom, eeprom_size, EEPROM_TXP_OFFS);
for (idx = 0; idx < entries; idx++) {
txp = &txp_array[idx];
@@ -416,7 +413,7 @@ static void iwl_eeprom_enhanced_txpower(struct device *dev,
}
}
-static void iwl_init_band_reference(const struct iwl_cfg *cfg,
+static void iwl_init_band_reference(const struct iwl_rf_cfg *cfg,
const u8 *eeprom, size_t eeprom_size,
int eeprom_band, int *eeprom_ch_count,
const struct iwl_eeprom_channel **ch_info,
@@ -426,7 +423,7 @@ static void iwl_init_band_reference(const struct iwl_cfg *cfg,
offset |= INDIRECT_ADDRESS | INDIRECT_REGULATORY;
- *ch_info = (void *)iwl_eeprom_query_addr(eeprom, eeprom_size, offset);
+ *ch_info = iwl_eeprom_query_addr(eeprom, eeprom_size, offset);
switch (eeprom_band) {
case 1: /* 2.4GHz band */
@@ -510,7 +507,7 @@ static void iwl_mod_ht40_chan_info(struct device *dev,
#define CHECK_AND_PRINT_I(x) \
((eeprom_ch_info[ch_idx].flags & EEPROM_CHANNEL_##x) ? # x " " : "")
-static int iwl_init_channel_map(struct device *dev, const struct iwl_cfg *cfg,
+static int iwl_init_channel_map(struct device *dev, const struct iwl_rf_cfg *cfg,
struct iwl_nvm_data *data,
const u8 *eeprom, size_t eeprom_size)
{
@@ -749,7 +746,7 @@ static int iwl_nvm_is_otp(struct iwl_trans *trans)
u32 otpgp;
/* OTP only valid for CP/PP and after */
- switch (trans->hw_rev & CSR_HW_REV_TYPE_MSK) {
+ switch (trans->info.hw_rev & CSR_HW_REV_TYPE_MSK) {
case CSR_HW_REV_TYPE_NONE:
IWL_ERR(trans, "Unknown hardware type\n");
return -EIO;
@@ -784,7 +781,7 @@ static int iwl_init_otp_access(struct iwl_trans *trans)
* CSR auto clock gate disable bit -
* this is only applicable for HW with OTP shadow RAM
*/
- if (trans->trans_cfg->base_params->shadow_ram_support)
+ if (trans->mac_cfg->base->shadow_ram_support)
iwl_set_bit(trans, CSR_DBG_LINK_PWR_MGMT_REG,
CSR_RESET_LINK_PWR_MGMT_DISABLED);
@@ -905,7 +902,7 @@ static int iwl_find_otp_image(struct iwl_trans *trans,
}
/* more in the link list, continue */
usedblocks++;
- } while (usedblocks <= trans->trans_cfg->base_params->max_ll_items);
+ } while (usedblocks <= trans->mac_cfg->base->max_ll_items);
/* OTP has no valid blocks */
IWL_DEBUG_EEPROM(trans->dev, "OTP has no valid blocks\n");
@@ -938,7 +935,7 @@ int iwl_read_eeprom(struct iwl_trans *trans, u8 **eeprom, size_t *eeprom_size)
if (nvm_is_otp < 0)
return nvm_is_otp;
- sz = trans->trans_cfg->base_params->eeprom_size;
+ sz = trans->mac_cfg->base->eeprom_size;
IWL_DEBUG_EEPROM(trans->dev, "NVM size = %d\n", sz);
e = kmalloc(sz, GFP_KERNEL);
@@ -973,7 +970,7 @@ int iwl_read_eeprom(struct iwl_trans *trans, u8 **eeprom, size_t *eeprom_size)
CSR_OTP_GP_REG_ECC_CORR_STATUS_MSK |
CSR_OTP_GP_REG_ECC_UNCORR_STATUS_MSK);
/* traversing the linked list if no shadow ram supported */
- if (!trans->trans_cfg->base_params->shadow_ram_support) {
+ if (!trans->mac_cfg->base->shadow_ram_support) {
ret = iwl_find_otp_image(trans, &validblockaddr);
if (ret)
goto err_unlock;
@@ -1027,7 +1024,7 @@ int iwl_read_eeprom(struct iwl_trans *trans, u8 **eeprom, size_t *eeprom_size)
return ret;
}
-static void iwl_init_sbands(struct iwl_trans *trans, const struct iwl_cfg *cfg,
+static void iwl_init_sbands(struct iwl_trans *trans, const struct iwl_rf_cfg *cfg,
struct iwl_nvm_data *data,
const u8 *eeprom, size_t eeprom_size)
{
@@ -1062,7 +1059,7 @@ static void iwl_init_sbands(struct iwl_trans *trans, const struct iwl_cfg *cfg,
/* EEPROM data functions */
struct iwl_nvm_data *
-iwl_parse_eeprom_data(struct iwl_trans *trans, const struct iwl_cfg *cfg,
+iwl_parse_eeprom_data(struct iwl_trans *trans, const struct iwl_rf_cfg *cfg,
const u8 *eeprom, size_t eeprom_size)
{
struct iwl_nvm_data *data;
@@ -1098,14 +1095,14 @@ iwl_parse_eeprom_data(struct iwl_trans *trans, const struct iwl_cfg *cfg,
EEPROM_RAW_TEMPERATURE);
if (!tmp)
goto err_free;
- data->raw_temperature = *(__le16 *)tmp;
+ data->raw_temperature = *(const __le16 *)tmp;
tmp = iwl_eeprom_query_addr(eeprom, eeprom_size,
EEPROM_KELVIN_TEMPERATURE);
if (!tmp)
goto err_free;
- data->kelvin_temperature = *(__le16 *)tmp;
- data->kelvin_voltage = *((__le16 *)tmp + 1);
+ data->kelvin_temperature = *(const __le16 *)tmp;
+ data->kelvin_voltage = *((const __le16 *)tmp + 1);
radio_cfg =
iwl_eeprom_query16(eeprom, eeprom_size, EEPROM_RADIO_CONFIG);
diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/led.c b/drivers/net/wireless/intel/iwlwifi/dvm/led.c
index 5ca85d90a8d6..cec2ebdfd651 100644
--- a/drivers/net/wireless/intel/iwlwifi/dvm/led.c
+++ b/drivers/net/wireless/intel/iwlwifi/dvm/led.c
@@ -2,7 +2,7 @@
/******************************************************************************
*
* Copyright(c) 2003 - 2014 Intel Corporation. All rights reserved.
- * Copyright (C) 2019 Intel Corporation
+ * Copyright (C) 2019, 2025 Intel Corporation
*****************************************************************************/
@@ -116,9 +116,9 @@ static int iwl_led_cmd(struct iwl_priv *priv,
}
led_cmd.on = iwl_blink_compensation(priv, on,
- priv->trans->trans_cfg->base_params->led_compensation);
+ priv->trans->mac_cfg->base->led_compensation);
led_cmd.off = iwl_blink_compensation(priv, off,
- priv->trans->trans_cfg->base_params->led_compensation);
+ priv->trans->mac_cfg->base->led_compensation);
ret = iwl_send_led_cmd(priv, &led_cmd);
if (!ret) {
diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/mac80211.c b/drivers/net/wireless/intel/iwlwifi/dvm/mac80211.c
index 56d19a034c24..0771a46bd552 100644
--- a/drivers/net/wireless/intel/iwlwifi/dvm/mac80211.c
+++ b/drivers/net/wireless/intel/iwlwifi/dvm/mac80211.c
@@ -2,7 +2,7 @@
/******************************************************************************
*
* Copyright(c) 2003 - 2014 Intel Corporation. All rights reserved.
- * Copyright(C) 2018 - 2019, 2022 - 2024 Intel Corporation
+ * Copyright(C) 2018 - 2019, 2022 - 2025 Intel Corporation
*
* Portions of this file are derived from the ipw3945 project, as well
* as portions of the ieee80211 subsystem header files.
@@ -96,7 +96,7 @@ int iwlagn_mac_setup_register(struct iwl_priv *priv,
ieee80211_hw_set(hw, SUPPORT_FAST_XMIT);
ieee80211_hw_set(hw, WANT_MONITOR_VIF);
- if (priv->trans->max_skb_frags)
+ if (priv->trans->info.max_skb_frags)
hw->netdev_features = NETIF_F_HIGHDMA | NETIF_F_SG;
hw->offchannel_tx_hw_queue = IWL_AUX_QUEUE;
@@ -188,7 +188,7 @@ int iwlagn_mac_setup_register(struct iwl_priv *priv,
priv->hw->wiphy->bands[NL80211_BAND_5GHZ] =
&priv->nvm_data->bands[NL80211_BAND_5GHZ];
- hw->wiphy->hw_version = priv->trans->hw_id;
+ hw->wiphy->hw_version = priv->trans->info.hw_id;
iwl_leds_init(priv);
@@ -549,7 +549,7 @@ static int iwlagn_mac_resume(struct ieee80211_hw *hw)
iwlagn_prepare_restart(priv);
- memset((void *)&ctx->active, 0, sizeof(ctx->active));
+ memset((void *)(uintptr_t)&ctx->active, 0, sizeof(ctx->active));
iwl_connection_init_rx_config(priv, ctx);
iwlagn_set_rxon_chain(priv, ctx);
@@ -1091,7 +1091,7 @@ static void iwlagn_mac_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
goto done;
}
- scd_queues = BIT(priv->trans->trans_cfg->base_params->num_of_queues) - 1;
+ scd_queues = BIT(priv->trans->mac_cfg->base->num_of_queues) - 1;
scd_queues &= ~(BIT(IWL_IPAN_CMD_QUEUE_NUM) |
BIT(IWL_DEFAULT_CMD_QUEUE_NUM));
diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/main.c b/drivers/net/wireless/intel/iwlwifi/dvm/main.c
index 30789ba06d9d..1d619384c629 100644
--- a/drivers/net/wireless/intel/iwlwifi/dvm/main.c
+++ b/drivers/net/wireless/intel/iwlwifi/dvm/main.c
@@ -2,7 +2,7 @@
/******************************************************************************
*
* Copyright(c) 2003 - 2014, 2018 - 2022 Intel Corporation. All rights reserved.
- * Copyright(c) 2024 Intel Corporation. All rights reserved.
+ * Copyright(c) 2024-2025 Intel Corporation. All rights reserved.
* Copyright(c) 2015 Intel Deutschland GmbH
*
* Portions of this file are derived from the ipw3945 project, as well
@@ -824,11 +824,11 @@ int iwl_alive_start(struct iwl_priv *priv)
iwlagn_send_tx_ant_config(priv, priv->nvm_data->valid_tx_ant);
if (iwl_is_associated_ctx(ctx) && !priv->wowlan) {
- struct iwl_rxon_cmd *active_rxon =
- (struct iwl_rxon_cmd *)&ctx->active;
+ struct iwl_rxon_cmd *active = (void *)(uintptr_t)&ctx->active;
+
/* apply any changes in staging */
ctx->staging.filter_flags |= RXON_FILTER_ASSOC_MSK;
- active_rxon->filter_flags &= ~RXON_FILTER_ASSOC_MSK;
+ active->filter_flags &= ~RXON_FILTER_ASSOC_MSK;
} else {
struct iwl_rxon_context *tmp;
/* Initialize our rx_config data */
@@ -1082,8 +1082,8 @@ void iwl_cancel_deferred_work(struct iwl_priv *priv)
cancel_work_sync(&priv->bt_full_concurrency);
cancel_work_sync(&priv->bt_runtime_config);
- del_timer_sync(&priv->statistics_periodic);
- del_timer_sync(&priv->ucode_trace);
+ timer_delete_sync(&priv->statistics_periodic);
+ timer_delete_sync(&priv->ucode_trace);
}
static int iwl_init_drv(struct iwl_priv *priv)
@@ -1137,9 +1137,10 @@ static void iwl_uninit_drv(struct iwl_priv *priv)
static void iwl_set_hw_params(struct iwl_priv *priv)
{
- if (priv->cfg->ht_params)
+ /* there are no devices with HT but without HT40 on all bands */
+ if (priv->cfg->ht_params.ht40_bands)
priv->hw_params.use_rts_for_aggregation =
- priv->cfg->ht_params->use_rts_for_aggregation;
+ priv->cfg->ht_params.use_rts_for_aggregation;
/* Device-specific setup */
priv->lib->set_hw_params(priv);
@@ -1173,8 +1174,9 @@ static int iwl_eeprom_init_hw_params(struct iwl_priv *priv)
{
struct iwl_nvm_data *data = priv->nvm_data;
+ /* all HT devices also have HT40 on at least one band */
if (data->sku_cap_11n_enable &&
- !priv->cfg->ht_params) {
+ !priv->cfg->ht_params.ht40_bands) {
IWL_ERR(priv, "Invalid 11n configuration\n");
return -EINVAL;
}
@@ -1224,7 +1226,7 @@ static int iwl_nvm_check_version(struct iwl_nvm_data *data,
}
static struct iwl_op_mode *iwl_op_mode_dvm_start(struct iwl_trans *trans,
- const struct iwl_cfg *cfg,
+ const struct iwl_rf_cfg *cfg,
const struct iwl_fw *fw,
struct dentry *dbgfs_dir)
{
@@ -1233,7 +1235,6 @@ static struct iwl_op_mode *iwl_op_mode_dvm_start(struct iwl_trans *trans,
struct iwl_op_mode *op_mode;
u16 num_mac;
u32 ucode_flags;
- struct iwl_trans_config trans_cfg = {};
static const u8 no_reclaim_cmds[] = {
REPLY_RX_PHY_CMD,
REPLY_RX_MPDU_CMD,
@@ -1248,7 +1249,8 @@ static struct iwl_op_mode *iwl_op_mode_dvm_start(struct iwl_trans *trans,
************************/
hw = iwl_alloc_all();
if (!hw) {
- pr_err("%s: Cannot allocate network device\n", trans->name);
+ pr_err("%s: Cannot allocate network device\n",
+ trans->info.name);
err = -ENOMEM;
goto out;
}
@@ -1261,7 +1263,7 @@ static struct iwl_op_mode *iwl_op_mode_dvm_start(struct iwl_trans *trans,
priv->cfg = cfg;
priv->fw = fw;
- switch (priv->trans->trans_cfg->device_family) {
+ switch (priv->trans->mac_cfg->device_family) {
case IWL_DEVICE_FAMILY_1000:
case IWL_DEVICE_FAMILY_100:
priv->lib = &iwl_dvm_1000_cfg;
@@ -1309,52 +1311,50 @@ static struct iwl_op_mode *iwl_op_mode_dvm_start(struct iwl_trans *trans,
* Populate the state variables that the transport layer needs
* to know about.
*/
- trans_cfg.op_mode = op_mode;
- trans_cfg.no_reclaim_cmds = no_reclaim_cmds;
- trans_cfg.n_no_reclaim_cmds = ARRAY_SIZE(no_reclaim_cmds);
+ BUILD_BUG_ON(sizeof(no_reclaim_cmds) >
+ sizeof(trans->conf.no_reclaim_cmds));
+ memcpy(trans->conf.no_reclaim_cmds, no_reclaim_cmds,
+ sizeof(no_reclaim_cmds));
switch (iwlwifi_mod_params.amsdu_size) {
case IWL_AMSDU_DEF:
case IWL_AMSDU_4K:
- trans_cfg.rx_buf_size = IWL_AMSDU_4K;
+ trans->conf.rx_buf_size = IWL_AMSDU_4K;
break;
case IWL_AMSDU_8K:
- trans_cfg.rx_buf_size = IWL_AMSDU_8K;
+ trans->conf.rx_buf_size = IWL_AMSDU_8K;
break;
case IWL_AMSDU_12K:
default:
- trans_cfg.rx_buf_size = IWL_AMSDU_4K;
+ trans->conf.rx_buf_size = IWL_AMSDU_4K;
pr_err("Unsupported amsdu_size: %d\n",
iwlwifi_mod_params.amsdu_size);
}
- trans_cfg.command_groups = iwl_dvm_groups;
- trans_cfg.command_groups_size = ARRAY_SIZE(iwl_dvm_groups);
+ trans->conf.command_groups = iwl_dvm_groups;
+ trans->conf.command_groups_size = ARRAY_SIZE(iwl_dvm_groups);
- trans_cfg.cmd_fifo = IWLAGN_CMD_FIFO_NUM;
- trans_cfg.cb_data_offs = offsetof(struct ieee80211_tx_info,
- driver_data[2]);
+ trans->conf.cmd_fifo = IWLAGN_CMD_FIFO_NUM;
+ trans->conf.cb_data_offs = offsetof(struct ieee80211_tx_info,
+ driver_data[2]);
WARN_ON(sizeof(priv->transport_queue_stop) * BITS_PER_BYTE <
- priv->trans->trans_cfg->base_params->num_of_queues);
+ priv->trans->mac_cfg->base->num_of_queues);
ucode_flags = fw->ucode_capa.flags;
if (ucode_flags & IWL_UCODE_TLV_FLAGS_PAN) {
priv->sta_key_max_num = STA_KEY_MAX_NUM_PAN;
- trans_cfg.cmd_queue = IWL_IPAN_CMD_QUEUE_NUM;
+ trans->conf.cmd_queue = IWL_IPAN_CMD_QUEUE_NUM;
} else {
priv->sta_key_max_num = STA_KEY_MAX_NUM;
- trans_cfg.cmd_queue = IWL_DEFAULT_CMD_QUEUE_NUM;
+ trans->conf.cmd_queue = IWL_DEFAULT_CMD_QUEUE_NUM;
}
- /* Configure transport layer */
- iwl_trans_configure(priv->trans, &trans_cfg);
+ trans->conf.rx_mpdu_cmd = REPLY_RX_MPDU_CMD;
+ trans->conf.rx_mpdu_cmd_hdr_size = sizeof(struct iwl_rx_mpdu_res_start);
- trans->rx_mpdu_cmd = REPLY_RX_MPDU_CMD;
- trans->rx_mpdu_cmd_hdr_size = sizeof(struct iwl_rx_mpdu_res_start);
- trans->command_groups = trans_cfg.command_groups;
- trans->command_groups_size = trans_cfg.command_groups_size;
+ iwl_trans_op_mode_enter(priv->trans, op_mode);
/* At this point both hw and priv are allocated. */
@@ -1378,18 +1378,18 @@ static struct iwl_op_mode *iwl_op_mode_dvm_start(struct iwl_trans *trans,
* 2. Read REV register
***********************/
IWL_INFO(priv, "Detected %s, REV=0x%X\n",
- priv->trans->name, priv->trans->hw_rev);
+ priv->trans->info.name, priv->trans->info.hw_rev);
err = iwl_trans_start_hw(priv->trans);
if (err)
- goto out_free_hw;
+ goto out_leave_trans;
/* Read the EEPROM */
err = iwl_read_eeprom(priv->trans, &priv->eeprom_blob,
&priv->eeprom_blob_size);
if (err) {
IWL_ERR(priv, "Unable to init EEPROM\n");
- goto out_free_hw;
+ goto out_leave_trans;
}
/* Reset chip to save power until we load uCode during "up". */
@@ -1437,10 +1437,7 @@ static struct iwl_op_mode *iwl_op_mode_dvm_start(struct iwl_trans *trans,
* packaging bug or due to the eeprom check above
*/
priv->sta_key_max_num = STA_KEY_MAX_NUM;
- trans_cfg.cmd_queue = IWL_DEFAULT_CMD_QUEUE_NUM;
-
- /* Configure transport layer again*/
- iwl_trans_configure(priv->trans, &trans_cfg);
+ trans->conf.cmd_queue = IWL_DEFAULT_CMD_QUEUE_NUM;
}
/*******************
@@ -1508,6 +1505,8 @@ out_free_eeprom_blob:
kfree(priv->eeprom_blob);
out_free_eeprom:
kfree(priv->nvm_data);
+out_leave_trans:
+ iwl_trans_op_mode_leave(priv->trans);
out_free_hw:
ieee80211_free_hw(priv->hw);
out:
@@ -1992,7 +1991,7 @@ static void iwl_nic_config(struct iwl_op_mode *op_mode)
/* SKU Control */
iwl_trans_set_bits_mask(priv->trans, CSR_HW_IF_CONFIG_REG,
CSR_HW_IF_CONFIG_REG_MSK_MAC_STEP_DASH,
- CSR_HW_REV_STEP_DASH(priv->trans->hw_rev));
+ CSR_HW_REV_STEP_DASH(priv->trans->info.hw_rev));
/* write radio config values to register */
if (priv->nvm_data->radio_cfg_type <= EEPROM_RF_CONFIG_TYPE_MAX) {
diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/power.c b/drivers/net/wireless/intel/iwlwifi/dvm/power.c
index 6d16a7105656..6b42d6e5f30f 100644
--- a/drivers/net/wireless/intel/iwlwifi/dvm/power.c
+++ b/drivers/net/wireless/intel/iwlwifi/dvm/power.c
@@ -2,7 +2,7 @@
/******************************************************************************
*
* Copyright(c) 2007 - 2014 Intel Corporation. All rights reserved.
- * Copyright (C) 2019 Intel Corporation
+ * Copyright (C) 2019, 2025 Intel Corporation
*
* Portions of this file are derived from the ipw3945 project, as well
* as portions of the ieee80211 subsystem header files.
@@ -64,32 +64,32 @@ struct iwl_power_vec_entry {
/* for DTIM period 0 through IWL_DTIM_RANGE_0_MAX */
/* DTIM 0 - 2 */
static const struct iwl_power_vec_entry range_0[IWL_POWER_NUM] = {
- {{SLP, SLP_TOUT(200), SLP_TOUT(500), SLP_VEC(1, 1, 2, 2, 0xFF)}, 0},
- {{SLP, SLP_TOUT(200), SLP_TOUT(300), SLP_VEC(1, 2, 2, 2, 0xFF)}, 0},
- {{SLP, SLP_TOUT(50), SLP_TOUT(100), SLP_VEC(2, 2, 2, 2, 0xFF)}, 0},
- {{SLP, SLP_TOUT(50), SLP_TOUT(25), SLP_VEC(2, 2, 4, 4, 0xFF)}, 1},
- {{SLP, SLP_TOUT(25), SLP_TOUT(25), SLP_VEC(2, 2, 4, 6, 0xFF)}, 2}
+ {{SLP, SLP_TOUT(200), SLP_TOUT(500), SLP_VEC(1, 1, 2, 2, 0xFF), 0}, 0},
+ {{SLP, SLP_TOUT(200), SLP_TOUT(300), SLP_VEC(1, 2, 2, 2, 0xFF), 0}, 0},
+ {{SLP, SLP_TOUT(50), SLP_TOUT(100), SLP_VEC(2, 2, 2, 2, 0xFF), 0}, 0},
+ {{SLP, SLP_TOUT(50), SLP_TOUT(25), SLP_VEC(2, 2, 4, 4, 0xFF), 0}, 1},
+ {{SLP, SLP_TOUT(25), SLP_TOUT(25), SLP_VEC(2, 2, 4, 6, 0xFF), 0}, 2}
};
/* for DTIM period IWL_DTIM_RANGE_0_MAX + 1 through IWL_DTIM_RANGE_1_MAX */
/* DTIM 3 - 10 */
static const struct iwl_power_vec_entry range_1[IWL_POWER_NUM] = {
- {{SLP, SLP_TOUT(200), SLP_TOUT(500), SLP_VEC(1, 2, 3, 4, 4)}, 0},
- {{SLP, SLP_TOUT(200), SLP_TOUT(300), SLP_VEC(1, 2, 3, 4, 7)}, 0},
- {{SLP, SLP_TOUT(50), SLP_TOUT(100), SLP_VEC(2, 4, 6, 7, 9)}, 0},
- {{SLP, SLP_TOUT(50), SLP_TOUT(25), SLP_VEC(2, 4, 6, 9, 10)}, 1},
- {{SLP, SLP_TOUT(25), SLP_TOUT(25), SLP_VEC(2, 4, 6, 10, 10)}, 2}
+ {{SLP, SLP_TOUT(200), SLP_TOUT(500), SLP_VEC(1, 2, 3, 4, 4), 0}, 0},
+ {{SLP, SLP_TOUT(200), SLP_TOUT(300), SLP_VEC(1, 2, 3, 4, 7), 0}, 0},
+ {{SLP, SLP_TOUT(50), SLP_TOUT(100), SLP_VEC(2, 4, 6, 7, 9), 0}, 0},
+ {{SLP, SLP_TOUT(50), SLP_TOUT(25), SLP_VEC(2, 4, 6, 9, 10), 0}, 1},
+ {{SLP, SLP_TOUT(25), SLP_TOUT(25), SLP_VEC(2, 4, 6, 10, 10), 0}, 2}
};
/* for DTIM period > IWL_DTIM_RANGE_1_MAX */
/* DTIM 11 - */
static const struct iwl_power_vec_entry range_2[IWL_POWER_NUM] = {
- {{SLP, SLP_TOUT(200), SLP_TOUT(500), SLP_VEC(1, 2, 3, 4, 0xFF)}, 0},
- {{SLP, SLP_TOUT(200), SLP_TOUT(300), SLP_VEC(2, 4, 6, 7, 0xFF)}, 0},
- {{SLP, SLP_TOUT(50), SLP_TOUT(100), SLP_VEC(2, 7, 9, 9, 0xFF)}, 0},
- {{SLP, SLP_TOUT(50), SLP_TOUT(25), SLP_VEC(2, 7, 9, 9, 0xFF)}, 0},
- {{SLP, SLP_TOUT(25), SLP_TOUT(25), SLP_VEC(4, 7, 10, 10, 0xFF)}, 0}
+ {{SLP, SLP_TOUT(200), SLP_TOUT(500), SLP_VEC(1, 2, 3, 4, 0xFF), 0}, 0},
+ {{SLP, SLP_TOUT(200), SLP_TOUT(300), SLP_VEC(2, 4, 6, 7, 0xFF), 0}, 0},
+ {{SLP, SLP_TOUT(50), SLP_TOUT(100), SLP_VEC(2, 7, 9, 9, 0xFF), 0}, 0},
+ {{SLP, SLP_TOUT(50), SLP_TOUT(25), SLP_VEC(2, 7, 9, 9, 0xFF), 0}, 0},
+ {{SLP, SLP_TOUT(25), SLP_TOUT(25), SLP_VEC(4, 7, 10, 10, 0xFF), 0}, 0}
};
/* advance power management */
@@ -196,7 +196,7 @@ static void iwl_static_sleep_cmd(struct iwl_priv *priv,
else
cmd->flags &= ~IWL_POWER_SLEEP_OVER_DTIM_MSK;
- if (priv->trans->trans_cfg->base_params->shadow_reg_enable)
+ if (priv->trans->mac_cfg->base->shadow_reg_enable)
cmd->flags |= IWL_POWER_SHADOW_REG_ENA;
else
cmd->flags &= ~IWL_POWER_SHADOW_REG_ENA;
diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/rx.c b/drivers/net/wireless/intel/iwlwifi/dvm/rx.c
index 7f67e602940c..5f8b60824043 100644
--- a/drivers/net/wireless/intel/iwlwifi/dvm/rx.c
+++ b/drivers/net/wireless/intel/iwlwifi/dvm/rx.c
@@ -3,7 +3,7 @@
*
* Copyright(c) 2003 - 2014 Intel Corporation. All rights reserved.
* Copyright(c) 2015 Intel Deutschland GmbH
- * Copyright(c) 2018, 2020-2021 Intel Corporation
+ * Copyright(c) 2018, 2020-2021, 2025 Intel Corporation
*
* Portions of this file are derived from the ipw3945 project, as well
* as portionhelp of the ieee80211 subsystem header files.
@@ -50,7 +50,7 @@ static void iwlagn_rx_csa(struct iwl_priv *priv, struct iwl_rx_cmd_buffer *rxb)
* See iwlagn_mac_channel_switch.
*/
struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_BSS];
- struct iwl_rxon_cmd *rxon = (void *)&ctx->active;
+ struct iwl_rxon_cmd *rxon = (void *)(uintptr_t)&ctx->active;
if (!test_bit(STATUS_CHANNEL_SWITCH_PENDING, &priv->status))
return;
@@ -643,8 +643,8 @@ static void iwlagn_pass_packet_to_mac80211(struct iwl_priv *priv,
fraglen = len - hdrlen;
if (fraglen) {
- int offset = (void *)hdr + hdrlen -
- rxb_addr(rxb) + rxb_offset(rxb);
+ int offset = (u8 *)hdr + hdrlen -
+ (u8 *)rxb_addr(rxb) + rxb_offset(rxb);
skb_add_rx_frag(skb, 0, rxb_steal_page(rxb), offset,
fraglen, rxb->truesize);
diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/rxon.c b/drivers/net/wireless/intel/iwlwifi/dvm/rxon.c
index f80cce37e2c0..2d3c1627f283 100644
--- a/drivers/net/wireless/intel/iwlwifi/dvm/rxon.c
+++ b/drivers/net/wireless/intel/iwlwifi/dvm/rxon.c
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: GPL-2.0-only
/******************************************************************************
*
- * Copyright(c) 2003 - 2014 Intel Corporation. All rights reserved.
+ * Copyright(c) 2003 - 2014, 2025 Intel Corporation. All rights reserved.
* Copyright(c) 2015 Intel Deutschland GmbH
*****************************************************************************/
@@ -341,7 +341,7 @@ static int iwlagn_rxon_disconn(struct iwl_priv *priv,
struct iwl_rxon_context *ctx)
{
int ret;
- struct iwl_rxon_cmd *active = (void *)&ctx->active;
+ struct iwl_rxon_cmd *active = (void *)(uintptr_t)&ctx->active;
if (ctx->ctxid == IWL_RXON_CTX_BSS) {
ret = iwlagn_disable_bss(priv, ctx, &ctx->staging);
@@ -441,7 +441,7 @@ static int iwlagn_rxon_connect(struct iwl_priv *priv,
struct iwl_rxon_context *ctx)
{
int ret;
- struct iwl_rxon_cmd *active = (void *)&ctx->active;
+ struct iwl_rxon_cmd *active = (void *)(uintptr_t)&ctx->active;
/* RXON timing must be before associated RXON */
if (ctx->ctxid == IWL_RXON_CTX_BSS) {
@@ -1023,7 +1023,7 @@ static void iwl_calc_basic_rates(struct iwl_priv *priv,
int iwlagn_commit_rxon(struct iwl_priv *priv, struct iwl_rxon_context *ctx)
{
/* cast away the const for active_rxon in this function */
- struct iwl_rxon_cmd *active = (void *)&ctx->active;
+ struct iwl_rxon_cmd *active = (void *)(uintptr_t)&ctx->active;
bool new_assoc = !!(ctx->staging.filter_flags & RXON_FILTER_ASSOC_MSK);
int ret;
diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/tt.c b/drivers/net/wireless/intel/iwlwifi/dvm/tt.c
index 43e8d04d5a8b..98f0949b3683 100644
--- a/drivers/net/wireless/intel/iwlwifi/dvm/tt.c
+++ b/drivers/net/wireless/intel/iwlwifi/dvm/tt.c
@@ -124,17 +124,6 @@ enum iwl_antenna_ok iwl_tx_ant_restriction(struct iwl_priv *priv)
return restriction->tx_stream;
}
-enum iwl_antenna_ok iwl_rx_ant_restriction(struct iwl_priv *priv)
-{
- struct iwl_tt_mgmt *tt = &priv->thermal_throttle;
- struct iwl_tt_restriction *restriction;
-
- if (!priv->thermal_throttle.advanced_tt)
- return IWL_ANT_OK_MULTI;
- restriction = tt->restriction + tt->state;
- return restriction->rx_stream;
-}
-
#define CT_KILL_EXIT_DURATION (5) /* 5 seconds duration */
#define CT_KILL_WAITING_DURATION (300) /* 300ms duration */
@@ -268,7 +257,7 @@ static void iwl_legacy_tt_handler(struct iwl_priv *priv, s32 temp, bool force)
tt->tt_previous_temp = temp;
#endif
/* stop ct_kill_waiting_tm timer */
- del_timer_sync(&priv->thermal_throttle.ct_kill_waiting_tm);
+ timer_delete_sync(&priv->thermal_throttle.ct_kill_waiting_tm);
if (tt->state != old_state) {
switch (tt->state) {
case IWL_TI_0:
@@ -389,7 +378,7 @@ static void iwl_advance_tt_handler(struct iwl_priv *priv, s32 temp, bool force)
}
}
/* stop ct_kill_waiting_tm timer */
- del_timer_sync(&priv->thermal_throttle.ct_kill_waiting_tm);
+ timer_delete_sync(&priv->thermal_throttle.ct_kill_waiting_tm);
if (changed) {
if (tt->state >= IWL_TI_1) {
/* force PI = IWL_POWER_INDEX_5 in the case of TI > 0 */
@@ -517,7 +506,7 @@ static void iwl_bg_ct_exit(struct work_struct *work)
return;
/* stop ct_kill_exit_tm timer */
- del_timer_sync(&priv->thermal_throttle.ct_kill_exit_tm);
+ timer_delete_sync(&priv->thermal_throttle.ct_kill_exit_tm);
if (tt->state == IWL_TI_CT_KILL) {
IWL_ERR(priv,
@@ -651,9 +640,9 @@ void iwl_tt_exit(struct iwl_priv *priv)
struct iwl_tt_mgmt *tt = &priv->thermal_throttle;
/* stop ct_kill_exit_tm timer if activated */
- del_timer_sync(&priv->thermal_throttle.ct_kill_exit_tm);
+ timer_delete_sync(&priv->thermal_throttle.ct_kill_exit_tm);
/* stop ct_kill_waiting_tm timer if activated */
- del_timer_sync(&priv->thermal_throttle.ct_kill_waiting_tm);
+ timer_delete_sync(&priv->thermal_throttle.ct_kill_waiting_tm);
cancel_work_sync(&priv->tt_work);
cancel_work_sync(&priv->ct_enter);
cancel_work_sync(&priv->ct_exit);
diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/tt.h b/drivers/net/wireless/intel/iwlwifi/dvm/tt.h
index 4af792d41dce..198f934a0d16 100644
--- a/drivers/net/wireless/intel/iwlwifi/dvm/tt.h
+++ b/drivers/net/wireless/intel/iwlwifi/dvm/tt.h
@@ -100,7 +100,6 @@ u8 iwl_tt_current_power_mode(struct iwl_priv *priv);
bool iwl_tt_is_low_power_state(struct iwl_priv *priv);
bool iwl_ht_enabled(struct iwl_priv *priv);
enum iwl_antenna_ok iwl_tx_ant_restriction(struct iwl_priv *priv);
-enum iwl_antenna_ok iwl_rx_ant_restriction(struct iwl_priv *priv);
void iwl_tt_enter_ct_kill(struct iwl_priv *priv);
void iwl_tt_exit_ct_kill(struct iwl_priv *priv);
void iwl_tt_handler(struct iwl_priv *priv);
diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/tx.c b/drivers/net/wireless/intel/iwlwifi/dvm/tx.c
index 111ed1873006..24fefa0e8148 100644
--- a/drivers/net/wireless/intel/iwlwifi/dvm/tx.c
+++ b/drivers/net/wireless/intel/iwlwifi/dvm/tx.c
@@ -3,7 +3,7 @@
*
* Copyright(c) 2008 - 2014 Intel Corporation. All rights reserved.
* Copyright (C) 2019 Intel Corporation
- * Copyright (C) 2023 Intel Corporation
+ * Copyright (C) 2023, 2025 Intel Corporation
*****************************************************************************/
#include <linux/kernel.h>
@@ -463,7 +463,7 @@ static int iwlagn_alloc_agg_txq(struct iwl_priv *priv, int mq)
int q;
for (q = IWLAGN_FIRST_AMPDU_QUEUE;
- q < priv->trans->trans_cfg->base_params->num_of_queues; q++) {
+ q < priv->trans->mac_cfg->base->num_of_queues; q++) {
if (!test_and_set_bit(q, priv->agg_q_alloc)) {
priv->queue_to_mac80211[q] = mq;
return q;
@@ -1277,7 +1277,7 @@ void iwlagn_rx_reply_compressed_ba(struct iwl_priv *priv,
* (in Tx queue's circular buffer) of first TFD/frame in window */
u16 ba_resp_scd_ssn = le16_to_cpu(ba_resp->scd_ssn);
- if (scd_flow >= priv->trans->trans_cfg->base_params->num_of_queues) {
+ if (scd_flow >= priv->trans->mac_cfg->base->num_of_queues) {
IWL_ERR(priv,
"BUG_ON scd_flow is bigger than number of queues\n");
return;
diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/ucode.c b/drivers/net/wireless/intel/iwlwifi/dvm/ucode.c
index bb13ca5d666c..ac90191a3973 100644
--- a/drivers/net/wireless/intel/iwlwifi/dvm/ucode.c
+++ b/drivers/net/wireless/intel/iwlwifi/dvm/ucode.c
@@ -3,6 +3,7 @@
*
* Copyright(c) 2008 - 2014 Intel Corporation. All rights reserved.
* Copyright(c) 2015 Intel Deutschland GmbH
+ * Copyright (C) 2025 Intel Corporation
*****************************************************************************/
#include <linux/kernel.h>
@@ -222,7 +223,7 @@ static int iwl_alive_notify(struct iwl_priv *priv)
int ret;
int i;
- iwl_trans_fw_alive(priv->trans, 0);
+ iwl_trans_fw_alive(priv->trans);
if (priv->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_PAN &&
priv->nvm_data->sku_cap_ipan_enable) {
@@ -293,15 +294,10 @@ int iwl_load_ucode_wait_alive(struct iwl_priv *priv,
{
struct iwl_notification_wait alive_wait;
struct iwl_alive_data alive_data;
- const struct fw_img *fw;
int ret;
enum iwl_ucode_type old_type;
static const u16 alive_cmd[] = { REPLY_ALIVE };
- fw = iwl_get_ucode_image(priv->fw, ucode_type);
- if (WARN_ON(!fw))
- return -EINVAL;
-
old_type = priv->cur_ucode;
priv->cur_ucode = ucode_type;
priv->ucode_loaded = false;
@@ -310,7 +306,7 @@ int iwl_load_ucode_wait_alive(struct iwl_priv *priv,
alive_cmd, ARRAY_SIZE(alive_cmd),
iwl_alive_fn, &alive_data);
- ret = iwl_trans_start_fw(priv->trans, fw, false);
+ ret = iwl_trans_start_fw(priv->trans, priv->fw, ucode_type, false);
if (ret) {
priv->cur_ucode = old_type;
iwl_remove_notification(&priv->notif_wait, &alive_wait);
diff --git a/drivers/net/wireless/intel/iwlwifi/fw/acpi.c b/drivers/net/wireless/intel/iwlwifi/fw/acpi.c
index efa7b673ebc7..bee7d92293b8 100644
--- a/drivers/net/wireless/intel/iwlwifi/fw/acpi.c
+++ b/drivers/net/wireless/intel/iwlwifi/fw/acpi.c
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
/*
* Copyright (C) 2017 Intel Deutschland GmbH
- * Copyright (C) 2019-2024 Intel Corporation
+ * Copyright (C) 2019-2025 Intel Corporation
*/
#include <linux/uuid.h>
#include "iwl-drv.h"
@@ -847,12 +847,12 @@ int iwl_acpi_get_ppag_table(struct iwl_fw_runtime *fwrt)
if (IS_ERR(data))
return PTR_ERR(data);
- /* try to read ppag table rev 3, 2 or 1 (all have the same data size) */
+ /* try to read ppag table rev 1 to 4 (all have the same data size) */
wifi_pkg = iwl_acpi_get_wifi_pkg(fwrt->dev, data,
ACPI_PPAG_WIFI_DATA_SIZE_V2, &tbl_rev);
if (!IS_ERR(wifi_pkg)) {
- if (tbl_rev >= 1 && tbl_rev <= 3) {
+ if (tbl_rev >= 1 && tbl_rev <= 4) {
num_sub_bands = IWL_NUM_SUB_BANDS_V2;
IWL_DEBUG_RADIO(fwrt,
"Reading PPAG table (tbl_rev=%d)\n",
@@ -882,7 +882,7 @@ int iwl_acpi_get_ppag_table(struct iwl_fw_runtime *fwrt)
goto out_free;
read_table:
- fwrt->ppag_ver = tbl_rev;
+ fwrt->ppag_bios_rev = tbl_rev;
flags = &wifi_pkg->package.elements[1];
if (flags->type != ACPI_TYPE_INTEGER) {
@@ -891,7 +891,7 @@ read_table:
}
fwrt->ppag_flags = iwl_bios_get_ppag_flags(flags->integer.value,
- fwrt->ppag_ver);
+ fwrt->ppag_bios_rev);
/*
* read, verify gain values and save them into the PPAG table.
@@ -912,6 +912,7 @@ read_table:
}
}
+ fwrt->ppag_bios_source = BIOS_SOURCE_ACPI;
ret = 0;
out_free:
@@ -919,40 +920,39 @@ out_free:
return ret;
}
-void iwl_acpi_get_phy_filters(struct iwl_fw_runtime *fwrt,
- struct iwl_phy_specific_cfg *filters)
+int iwl_acpi_get_phy_filters(struct iwl_fw_runtime *fwrt)
{
+ struct iwl_phy_specific_cfg *filters = &fwrt->phy_filters;
struct iwl_phy_specific_cfg tmp = {};
- union acpi_object *wifi_pkg, *data;
+ union acpi_object *wifi_pkg, *data __free(kfree);
int tbl_rev, i;
data = iwl_acpi_get_object(fwrt->dev, ACPI_WPFC_METHOD);
if (IS_ERR(data))
- return;
+ return PTR_ERR(data);
wifi_pkg = iwl_acpi_get_wifi_pkg(fwrt->dev, data,
ACPI_WPFC_WIFI_DATA_SIZE,
&tbl_rev);
if (IS_ERR(wifi_pkg))
- goto out_free;
+ return PTR_ERR(wifi_pkg);
if (tbl_rev != 0)
- goto out_free;
+ return -EINVAL;
BUILD_BUG_ON(ARRAY_SIZE(filters->filter_cfg_chains) !=
ACPI_WPFC_WIFI_DATA_SIZE - 1);
for (i = 0; i < ARRAY_SIZE(filters->filter_cfg_chains); i++) {
if (wifi_pkg->package.elements[i + 1].type != ACPI_TYPE_INTEGER)
- goto out_free;
+ return -EINVAL;
tmp.filter_cfg_chains[i] =
cpu_to_le32(wifi_pkg->package.elements[i + 1].integer.value);
}
IWL_DEBUG_RADIO(fwrt, "Loaded WPFC filter config from ACPI\n");
*filters = tmp;
-out_free:
- kfree(data);
+ return 0;
}
IWL_EXPORT_SYMBOL(iwl_acpi_get_phy_filters);
diff --git a/drivers/net/wireless/intel/iwlwifi/fw/acpi.h b/drivers/net/wireless/intel/iwlwifi/fw/acpi.h
index e50b93472dd2..68d8fb5f6357 100644
--- a/drivers/net/wireless/intel/iwlwifi/fw/acpi.h
+++ b/drivers/net/wireless/intel/iwlwifi/fw/acpi.h
@@ -1,7 +1,7 @@
/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
/*
* Copyright (C) 2017 Intel Deutschland GmbH
- * Copyright (C) 2018-2023 Intel Corporation
+ * Copyright (C) 2018-2023, 2025 Intel Corporation
*/
#ifndef __iwl_fw_acpi__
#define __iwl_fw_acpi__
@@ -180,8 +180,7 @@ int iwl_acpi_get_tas_table(struct iwl_fw_runtime *fwrt,
int iwl_acpi_get_ppag_table(struct iwl_fw_runtime *fwrt);
-void iwl_acpi_get_phy_filters(struct iwl_fw_runtime *fwrt,
- struct iwl_phy_specific_cfg *filters);
+int iwl_acpi_get_phy_filters(struct iwl_fw_runtime *fwrt);
void iwl_acpi_get_guid_lock_status(struct iwl_fw_runtime *fwrt);
@@ -244,8 +243,10 @@ static inline int iwl_acpi_get_ppag_table(struct iwl_fw_runtime *fwrt)
return -ENOENT;
}
-/* macro since the second argument doesn't always exist */
-#define iwl_acpi_get_phy_filters(fwrt, filters) do { } while (0)
+static inline int iwl_acpi_get_phy_filters(struct iwl_fw_runtime *fwrt)
+{
+ return -ENOENT;
+}
static inline void iwl_acpi_get_guid_lock_status(struct iwl_fw_runtime *fwrt)
{
diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/alive.h b/drivers/net/wireless/intel/iwlwifi/fw/api/alive.h
index ebe85fdf08d3..3ce477c248ce 100644
--- a/drivers/net/wireless/intel/iwlwifi/fw/api/alive.h
+++ b/drivers/net/wireless/intel/iwlwifi/fw/api/alive.h
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
/*
- * Copyright (C) 2012-2014, 2018, 2020-2021, 2024 Intel Corporation
+ * Copyright (C) 2012-2014, 2018, 2020-2021, 2024-2025 Intel Corporation
* Copyright (C) 2013-2015 Intel Mobile Communications GmbH
* Copyright (C) 2016-2017 Intel Deutschland GmbH
*/
@@ -112,12 +112,22 @@ struct iwl_alive_ntf_v6 {
struct iwl_imr_alive_info imr;
} __packed; /* UCODE_ALIVE_NTFY_API_S_VER_6 */
+struct iwl_alive_ntf {
+ __le16 status;
+ __le16 flags;
+ struct iwl_lmac_alive lmac_data[2];
+ struct iwl_umac_alive umac_data;
+ struct iwl_sku_id sku_id;
+ struct iwl_imr_alive_info imr;
+ __le64 platform_id;
+} __packed; /* UCODE_ALIVE_NTFY_API_S_VER_8 */
+
/**
* enum iwl_extended_cfg_flags - commands driver may send before
* finishing init flow
* @IWL_INIT_DEBUG_CFG: driver is going to send debug config command
* @IWL_INIT_NVM: driver is going to send NVM_ACCESS commands
- * @IWL_INIT_PHY: driver is going to send PHY_DB commands
+ * @IWL_INIT_PHY: driver is going to send the PHY_CONFIGURATION_CMD
*/
enum iwl_extended_cfg_flags {
IWL_INIT_DEBUG_CFG,
diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/commands.h b/drivers/net/wireless/intel/iwlwifi/fw/api/commands.h
index 34a1f97653c0..1c86a858aaab 100644
--- a/drivers/net/wireless/intel/iwlwifi/fw/api/commands.h
+++ b/drivers/net/wireless/intel/iwlwifi/fw/api/commands.h
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2013-2015 Intel Mobile Communications GmbH
* Copyright (C) 2016-2017 Intel Deutschland GmbH
- * Copyright (C) 2018-2022, 2024 Intel Corporation
+ * Copyright (C) 2018-2022, 2024-2025 Intel Corporation
*/
#ifndef __iwl_fw_api_commands_h__
#define __iwl_fw_api_commands_h__
@@ -145,8 +145,8 @@ enum iwl_legacy_cmds {
REMOVE_STA = 0x19,
/**
- * @TX_CMD: uses &struct iwl_tx_cmd or &struct iwl_tx_cmd_gen2 or
- * &struct iwl_tx_cmd_gen3,
+ * @TX_CMD: uses &struct iwl_tx_cmd_v6 or &struct iwl_tx_cmd_v9 or
+ * &struct iwl_tx_cmd,
* response in &struct iwl_tx_resp or
* &struct iwl_tx_resp_v3
*/
@@ -502,11 +502,16 @@ enum iwl_legacy_cmds {
/**
* @DTS_MEASUREMENT_NOTIFICATION:
* &struct iwl_dts_measurement_notif_v1 or
- * &struct iwl_dts_measurement_notif_v2
+ * &struct iwl_dts_measurement_notif
*/
DTS_MEASUREMENT_NOTIFICATION = 0xdd,
/**
+ * @DEBUG_HOST_COMMAND: &struct iwl_dhc_cmd
+ */
+ DEBUG_HOST_COMMAND = 0xf1,
+
+ /**
* @LDBG_CONFIG_CMD: configure continuous trace recording
*/
LDBG_CONFIG_CMD = 0xf6,
diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/context.h b/drivers/net/wireless/intel/iwlwifi/fw/api/context.h
index a9fa5f054ce0..464eed9b5e71 100644
--- a/drivers/net/wireless/intel/iwlwifi/fw/api/context.h
+++ b/drivers/net/wireless/intel/iwlwifi/fw/api/context.h
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
/*
- * Copyright (C) 2012-2014, 2022 Intel Corporation
+ * Copyright (C) 2012-2014, 2022, 2024 Intel Corporation
* Copyright (C) 2013-2015 Intel Mobile Communications GmbH
* Copyright (C) 2016-2017 Intel Deutschland GmbH
*/
@@ -14,6 +14,9 @@
* @FW_CTXT_COLOR_POS: position of the color
* @FW_CTXT_COLOR_MSK: mask of the color
* @FW_CTXT_INVALID: value used to indicate unused/invalid
+ * @FW_CTXT_ID_INVALID: value used to indicate unused/invalid. This can be
+ * used with newer firmware which no longer use the color. Typically,
+ * firmware versions supported by iwlmld can use this value.
*/
enum iwl_ctxt_id_and_color {
FW_CTXT_ID_POS = 0,
@@ -21,6 +24,7 @@ enum iwl_ctxt_id_and_color {
FW_CTXT_COLOR_POS = 8,
FW_CTXT_COLOR_MSK = 0xff << FW_CTXT_COLOR_POS,
FW_CTXT_INVALID = 0xffffffff,
+ FW_CTXT_ID_INVALID = 0xff,
};
#define FW_CMD_ID_AND_COLOR(_id, _color) (((_id) << FW_CTXT_ID_POS) |\
diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/d3.h b/drivers/net/wireless/intel/iwlwifi/fw/api/d3.h
index c2362bc786b2..9c271ea67155 100644
--- a/drivers/net/wireless/intel/iwlwifi/fw/api/d3.h
+++ b/drivers/net/wireless/intel/iwlwifi/fw/api/d3.h
@@ -1006,7 +1006,7 @@ struct iwl_wowlan_wake_pkt_notif {
* struct iwl_mvm_d3_end_notif - d3 end notification
* @flags: See &enum iwl_d0i3_flags
*/
-struct iwl_mvm_d3_end_notif {
+struct iwl_d3_end_notif {
__le32 flags;
} __packed;
diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/datapath.h b/drivers/net/wireless/intel/iwlwifi/fw/api/datapath.h
index 570a3f722510..9c88bb280609 100644
--- a/drivers/net/wireless/intel/iwlwifi/fw/api/datapath.h
+++ b/drivers/net/wireless/intel/iwlwifi/fw/api/datapath.h
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
/*
- * Copyright (C) 2024 Intel Corporation
+ * Copyright (C) 2024-2025 Intel Corporation
* Copyright (C) 2012-2014, 2018-2022 Intel Corporation
* Copyright (C) 2013-2015 Intel Mobile Communications GmbH
* Copyright (C) 2016-2017 Intel Deutschland GmbH
@@ -91,8 +91,14 @@ enum iwl_data_path_subcmd_ids {
SEC_KEY_CMD = 0x18,
/**
+ * @OMI_SEND_STATUS_NOTIF: notification after OMI was sent
+ * uses &struct iwl_omi_send_status_notif
+ */
+ OMI_SEND_STATUS_NOTIF = 0xF2,
+
+ /**
* @ESR_MODE_NOTIF: notification to recommend/force a wanted esr mode,
- * uses &struct iwl_mvm_esr_mode_notif
+ * uses &struct iwl_esr_mode_notif or &struct iwl_esr_mode_notif_v1
*/
ESR_MODE_NOTIF = 0xF3,
@@ -688,4 +694,13 @@ struct iwl_sec_key_cmd {
} __packed u; /* SEC_KEY_OPERATION_API_U_VER_1 */
} __packed; /* SEC_KEY_CMD_API_S_VER_1 */
+/**
+ * struct iwl_omi_send_status_notif - OMI status notification
+ * @success: indicates that the OMI was sent successfully
+ * (currently always set)
+ */
+struct iwl_omi_send_status_notif {
+ __le32 success;
+} __packed; /* OMI_SEND_STATUS_NTFY_API_S_VER_1 */
+
#endif /* __iwl_fw_api_datapath_h__ */
diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/dbg-tlv.h b/drivers/net/wireless/intel/iwlwifi/fw/api/dbg-tlv.h
index 550de6db1834..3173fa96cb48 100644
--- a/drivers/net/wireless/intel/iwlwifi/fw/api/dbg-tlv.h
+++ b/drivers/net/wireless/intel/iwlwifi/fw/api/dbg-tlv.h
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
/*
- * Copyright (C) 2018-2024 Intel Corporation
+ * Copyright (C) 2018-2025 Intel Corporation
*/
#ifndef __iwl_fw_dbg_tlv_h__
#define __iwl_fw_dbg_tlv_h__
@@ -527,6 +527,8 @@ enum iwl_fw_ini_time_point {
* @IWL_FW_INI_APPLY_POLICY_OVERRIDE_DATA: override trigger data.
* Append otherwise
* @IWL_FW_INI_APPLY_POLICY_DUMP_COMPLETE_CMD: send cmd once dump collected
+ * @IWL_FW_INI_APPLY_POLICY_SPLIT_DUMP_RESET: split this dump into regions
+ * before and after the reset handshake
*/
enum iwl_fw_ini_trigger_apply_policy {
IWL_FW_INI_APPLY_POLICY_MATCH_TIME_POINT = BIT(0),
@@ -535,6 +537,7 @@ enum iwl_fw_ini_trigger_apply_policy {
IWL_FW_INI_APPLY_POLICY_OVERRIDE_CFG = BIT(9),
IWL_FW_INI_APPLY_POLICY_OVERRIDE_DATA = BIT(10),
IWL_FW_INI_APPLY_POLICY_DUMP_COMPLETE_CMD = BIT(16),
+ IWL_FW_INI_APPLY_POLICY_SPLIT_DUMP_RESET = BIT(17),
};
/**
@@ -556,12 +559,14 @@ enum iwl_fw_ini_trigger_reset_fw_policy {
* @IWL_FW_INI_DEBUG_DUMP_POLICY_NO_LIMIT: OS has no limit of dump size
* @IWL_FW_INI_DEBUG_DUMP_POLICY_MAX_LIMIT_600KB: mini dump only 600KB region dump
* @IWL_FW_IWL_DEBUG_DUMP_POLICY_MAX_LIMIT_5MB: mini dump 5MB size dump
+ * @IWL_FW_IWL_DEBUG_DUMP_POLICY_BEFORE_RESET: dump this region before reset
+ * handshake (if requested by %IWL_FW_INI_APPLY_POLICY_SPLIT_DUMP_RESET)
*/
enum iwl_fw_ini_dump_policy {
IWL_FW_INI_DEBUG_DUMP_POLICY_NO_LIMIT = BIT(0),
IWL_FW_INI_DEBUG_DUMP_POLICY_MAX_LIMIT_600KB = BIT(1),
IWL_FW_IWL_DEBUG_DUMP_POLICY_MAX_LIMIT_5MB = BIT(2),
-
+ IWL_FW_IWL_DEBUG_DUMP_POLICY_BEFORE_RESET = BIT(3),
};
/**
diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/debug.h b/drivers/net/wireless/intel/iwlwifi/fw/api/debug.h
index aa88e91d117e..0cf1e5124fba 100644
--- a/drivers/net/wireless/intel/iwlwifi/fw/api/debug.h
+++ b/drivers/net/wireless/intel/iwlwifi/fw/api/debug.h
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
/*
- * Copyright (C) 2005-2014, 2018-2024 Intel Corporation
+ * Copyright (C) 2005-2014, 2018-2025 Intel Corporation
* Copyright (C) 2013-2015 Intel Mobile Communications GmbH
* Copyright (C) 2016-2017 Intel Deutschland GmbH
*/
@@ -51,7 +51,7 @@ enum iwl_debug_cmds {
/**
* @GET_TAS_STATUS:
* sends command to fw to get TAS status
- * the response is &struct iwl_mvm_tas_status_resp
+ * the response is &struct iwl_tas_status_resp
*/
GET_TAS_STATUS = 0xA,
/**
@@ -439,25 +439,20 @@ struct iwl_dbg_dump_complete_cmd {
__le32 tp_data;
} __packed; /* FW_DUMP_COMPLETE_CMD_API_S_VER_1 */
-#define TAS_LMAC_BAND_HB 0
-#define TAS_LMAC_BAND_LB 1
-#define TAS_LMAC_BAND_UHB 2
-#define TAS_LMAC_BAND_INVALID 3
-
/**
- * struct iwl_mvm_tas_status_per_mac - tas status per lmac
+ * struct iwl_tas_status_per_mac - tas status per lmac
* @static_status: tas statically enabled or disabled per lmac - TRUE/FALSE
* @static_dis_reason: TAS static disable reason, uses
- * &enum iwl_mvm_tas_statically_disabled_reason
+ * &enum iwl_tas_statically_disabled_reason
* @dynamic_status: Current TAS status. uses
- * &enum iwl_mvm_tas_dyna_status
+ * &enum iwl_tas_dyna_status
* @near_disconnection: is TAS currently near disconnection per lmac? - TRUE/FALSE
* @max_reg_pwr_limit: Regulatory power limits in dBm
* @sar_limit: SAR limits per lmac in dBm
* @band: Band per lmac
* @reserved: reserved
*/
-struct iwl_mvm_tas_status_per_mac {
+struct iwl_tas_status_per_mac {
u8 static_status;
u8 static_dis_reason;
u8 dynamic_status;
@@ -466,35 +461,35 @@ struct iwl_mvm_tas_status_per_mac {
__le16 sar_limit;
u8 band;
u8 reserved[3];
-} __packed; /*DEBUG_GET_TAS_STATUS_PER_MAC_S_VER_1*/
+} __packed; /* DEBUG_GET_TAS_STATUS_PER_MAC_S_VER_1 */
/**
- * struct iwl_mvm_tas_status_resp - Response to GET_TAS_STATUS
+ * struct iwl_tas_status_resp - Response to GET_TAS_STATUS
* @tas_fw_version: TAS FW version
* @is_uhb_for_usa_enable: is UHB enabled in USA? - TRUE/FALSE
* @curr_mcc: current mcc
* @block_list: country block list
* @tas_status_mac: TAS status per lmac, uses
- * &struct iwl_mvm_tas_status_per_mac
+ * &struct iwl_tas_status_per_mac
* @in_dual_radio: is TAS in dual radio? - TRUE/FALSE
* @uhb_allowed_flags: see &enum iwl_tas_uhb_allowed_flags.
* This member is valid only when fw has
* %IWL_UCODE_TLV_CAPA_UHB_CANADA_TAS_SUPPORT capability.
* @reserved: reserved
*/
-struct iwl_mvm_tas_status_resp {
+struct iwl_tas_status_resp {
u8 tas_fw_version;
u8 is_uhb_for_usa_enable;
__le16 curr_mcc;
__le16 block_list[16];
- struct iwl_mvm_tas_status_per_mac tas_status_mac[2];
+ struct iwl_tas_status_per_mac tas_status_mac[2];
u8 in_dual_radio;
u8 uhb_allowed_flags;
u8 reserved[2];
-} __packed; /*DEBUG_GET_TAS_STATUS_RSP_API_S_VER_3*/
+} __packed; /* DEBUG_GET_TAS_STATUS_RSP_API_S_VER_3 */
/**
- * enum iwl_mvm_tas_dyna_status - TAS current running status
+ * enum iwl_tas_dyna_status - TAS current running status
* @TAS_DYNA_INACTIVE: TAS status is inactive
* @TAS_DYNA_INACTIVE_MVM_MODE: TAS is disabled due because FW is in MVM mode
* or is in softap mode.
@@ -507,7 +502,7 @@ struct iwl_mvm_tas_status_resp {
* @TAS_DYNA_ACTIVE: TAS is currently active
* @TAS_DYNA_STATUS_MAX: TAS status max value
*/
-enum iwl_mvm_tas_dyna_status {
+enum iwl_tas_dyna_status {
TAS_DYNA_INACTIVE,
TAS_DYNA_INACTIVE_MVM_MODE,
TAS_DYNA_INACTIVE_TRIGGER_MODE,
@@ -516,19 +511,22 @@ enum iwl_mvm_tas_dyna_status {
TAS_DYNA_ACTIVE,
TAS_DYNA_STATUS_MAX,
-}; /*_TAS_DYNA_STATUS_E*/
+};
/**
- * enum iwl_mvm_tas_statically_disabled_reason - TAS statically disabled reason
+ * enum iwl_tas_statically_disabled_reason - TAS statically disabled reason
* @TAS_DISABLED_DUE_TO_BIOS: TAS is disabled because TAS is disabled in BIOS
* @TAS_DISABLED_DUE_TO_SAR_6DBM: TAS is disabled because SAR limit is less than 6 Dbm
* @TAS_DISABLED_REASON_INVALID: TAS disable reason is invalid
+ * @TAS_DISABLED_DUE_TO_TABLE_SOURCE_INVALID: TAS is disabled due to
+ * table source invalid
* @TAS_DISABLED_REASON_MAX: TAS disable reason max value
*/
-enum iwl_mvm_tas_statically_disabled_reason {
+enum iwl_tas_statically_disabled_reason {
TAS_DISABLED_DUE_TO_BIOS,
TAS_DISABLED_DUE_TO_SAR_6DBM,
TAS_DISABLED_REASON_INVALID,
+ TAS_DISABLED_DUE_TO_TABLE_SOURCE_INVALID,
TAS_DISABLED_REASON_MAX,
}; /*_TAS_STATICALLY_DISABLED_REASON_E*/
diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/dhc.h b/drivers/net/wireless/intel/iwlwifi/fw/api/dhc.h
new file mode 100644
index 000000000000..b6d79c678cd8
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/fw/api/dhc.h
@@ -0,0 +1,226 @@
+/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
+/*
+ * Copyright (C) 2025 Intel Corporation
+ */
+#ifndef __iwl_fw_api_dhc_h__
+#define __iwl_fw_api_dhc_h__
+
+#define DHC_TABLE_MASK_POS (28)
+
+/**
+ * enum iwl_dhc_table_id - DHC table operations index
+ */
+enum iwl_dhc_table_id {
+ /**
+ * @DHC_TABLE_INTEGRATION: select the integration table
+ */
+ DHC_TABLE_INTEGRATION = 2 << DHC_TABLE_MASK_POS,
+ /**
+ * @DHC_TABLE_TOOLS: select the tools table
+ */
+ DHC_TABLE_TOOLS = 0,
+};
+
+/**
+ * enum iwl_dhc_umac_tools_table - tools operations
+ * @DHC_TOOLS_UMAC_GET_TAS_STATUS: Get TAS status.
+ * See @struct iwl_dhc_tas_status_resp
+ */
+enum iwl_dhc_umac_tools_table {
+ DHC_TOOLS_UMAC_GET_TAS_STATUS = 0,
+};
+
+/**
+ * enum iwl_dhc_umac_integration_table - integration operations
+ */
+enum iwl_dhc_umac_integration_table {
+ /**
+ * @DHC_INT_UMAC_TWT_OPERATION: trigger a TWT operation
+ */
+ DHC_INT_UMAC_TWT_OPERATION = 4,
+ /**
+ * @DHC_INTEGRATION_TLC_DEBUG_CONFIG: TLC debug
+ */
+ DHC_INTEGRATION_TLC_DEBUG_CONFIG = 1,
+ /**
+ * @DHC_INTEGRATION_MAX: Maximum UMAC integration table entries
+ */
+ DHC_INTEGRATION_MAX
+};
+
+#define DHC_TARGET_UMAC BIT(27)
+
+/**
+ * struct iwl_dhc_cmd - debug host command
+ * @length: length in DWs of the data structure that is concatenated to the end
+ * of this struct
+ * @index_and_mask: bit 31 is 1 for data set operation else it's 0
+ * bits 28-30 is the index of the table of the operation -
+ * &enum iwl_dhc_table_id *
+ * bit 27 is 0 if the cmd targeted to LMAC and 1 if targeted to UMAC,
+ * (LMAC is 0 for backward compatibility)
+ * bit 26 is 0 if the cmd targeted to LMAC0 and 1 if targeted to LMAC1,
+ * relevant only if bit 27 set to 0
+ * bits 0-25 is a specific entry index in the table specified in bits 28-30
+ *
+ * @data: the concatenated data.
+ */
+struct iwl_dhc_cmd {
+ __le32 length;
+ __le32 index_and_mask;
+ __le32 data[];
+} __packed; /* DHC_CMD_API_S */
+
+/**
+ * struct iwl_dhc_payload_hdr - DHC payload header
+ * @version: a version of a payload
+ * @reserved: reserved for alignment
+ */
+struct iwl_dhc_payload_hdr {
+ u8 version;
+ u8 reserved[3];
+} __packed; /* DHC_PAYLOAD_HDR_API_S_VER_1 */
+
+/**
+ * struct iwl_dhc_tas_status_per_radio - TAS status per radio
+ * @band: &PHY_BAND_5 for high band, PHY_BAND_24 for low band and
+ * &PHY_BAND_6 for ultra high band.
+ * @static_status: TAS statically enabled or disabled
+ * @static_disable_reason: TAS static disable reason, uses
+ * &enum iwl_tas_statically_disabled_reason
+ * @near_disconnection: is TAS currently near disconnection per radio
+ * @dynamic_status_ant_a: Antenna A current TAS status.
+ * uses &enum iwl_tas_dyna_status
+ * @dynamic_status_ant_b: Antenna B current TAS status.
+ * uses &enum iwl_tas_dyna_status
+ * @max_reg_pwr_limit_ant_a: Antenna A regulatory power limits in dBm
+ * @max_reg_pwr_limit_ant_b: Antenna B regulatory power limits in dBm
+ * @sar_limit_ant_a: Antenna A SAR limit per radio in dBm
+ * @sar_limit_ant_b: Antenna B SAR limit per radio in dBm
+ * @reserved: reserved for alignment
+ */
+struct iwl_dhc_tas_status_per_radio {
+ u8 band;
+ u8 static_status;
+ u8 static_disable_reason;
+ u8 near_disconnection;
+ u8 dynamic_status_ant_a;
+ u8 dynamic_status_ant_b;
+ __le16 max_reg_pwr_limit_ant_a;
+ __le16 max_reg_pwr_limit_ant_b;
+ __le16 sar_limit_ant_a;
+ __le16 sar_limit_ant_b;
+ u8 reserved[2];
+} __packed; /* DHC_TAS_STATUS_PER_RADIO_S_VER_1 */
+
+/**
+ * struct iwl_dhc_tas_status_resp - Response to DHC_TOOLS_UMAC_GET_TAS_STATUS
+ * @header: DHC payload header, uses &struct iwl_dhc_payload_hdr
+ * @tas_config_info: see @struct bios_value_u32
+ * @mcc_block_list: block listed country codes
+ * @tas_status_radio: TAS status, uses &struct iwl_dhc_tas_status_per_radio
+ * @curr_mcc: current mcc
+ * @valid_radio_mask: represent entry in tas_status_per_radio is valid.
+ * @reserved: reserved for alignment
+ */
+struct iwl_dhc_tas_status_resp {
+ struct iwl_dhc_payload_hdr header;
+ struct bios_value_u32 tas_config_info;
+ __le16 mcc_block_list[IWL_WTAS_BLACK_LIST_MAX];
+ struct iwl_dhc_tas_status_per_radio tas_status_radio[2];
+ __le16 curr_mcc;
+ u8 valid_radio_mask;
+ u8 reserved;
+} __packed; /* DHC_TAS_STATUS_RSP_API_S_VER_1 */
+
+/**
+ * struct iwl_dhc_cmd_resp_v1 - debug host command response
+ * @status: status of the command
+ * @data: the response data
+ */
+struct iwl_dhc_cmd_resp_v1 {
+ __le32 status;
+ __le32 data[];
+} __packed; /* DHC_RESP_API_S_VER_1 */
+
+/**
+ * struct iwl_dhc_cmd_resp - debug host command response
+ * @status: status of the command
+ * @descriptor: command descriptor (index_and_mask) returned
+ * @data: the response data
+ */
+struct iwl_dhc_cmd_resp {
+ __le32 status;
+ __le32 descriptor;
+ __le32 data[];
+} __packed; /* DHC_RESP_API_S_VER_2 and DHC_RESP_API_S_VER_3 */
+
+/**
+ * enum iwl_dhc_twt_operation_type - describes the TWT operation type
+ *
+ * @DHC_TWT_REQUEST: Send a Request TWT command
+ * @DHC_TWT_SUGGEST: Send a Suggest TWT command
+ * @DHC_TWT_DEMAND: Send a Demand TWT command
+ * @DHC_TWT_GROUPING: Send a Grouping TWT command
+ * @DHC_TWT_ACCEPT: Send a Accept TWT command
+ * @DHC_TWT_ALTERNATE: Send a Alternate TWT command
+ * @DHC_TWT_DICTATE: Send a Dictate TWT command
+ * @DHC_TWT_REJECT: Send a Reject TWT command
+ * @DHC_TWT_TEARDOWN: Send a TearDown TWT command
+ */
+enum iwl_dhc_twt_operation_type {
+ DHC_TWT_REQUEST,
+ DHC_TWT_SUGGEST,
+ DHC_TWT_DEMAND,
+ DHC_TWT_GROUPING,
+ DHC_TWT_ACCEPT,
+ DHC_TWT_ALTERNATE,
+ DHC_TWT_DICTATE,
+ DHC_TWT_REJECT,
+ DHC_TWT_TEARDOWN,
+}; /* DHC_TWT_OPERATION_TYPE_E */
+
+/**
+ * struct iwl_dhc_twt_operation - trigger a TWT operation
+ *
+ * @mac_id: the mac Id on which to trigger TWT operation
+ * @twt_operation: see &enum iwl_dhc_twt_operation_type
+ * @target_wake_time: when should we be on channel
+ * @interval_exp: the exponent for the interval
+ * @interval_mantissa: the mantissa for the interval
+ * @min_wake_duration: the minimum duration for the wake period
+ * @trigger: is the TWT triggered or not
+ * @flow_type: is the TWT announced or not
+ * @flow_id: the TWT flow identifier from 0 to 7
+ * @protection: is the TWT protected
+ * @ndo_paging_indicator: is ndo_paging_indicator set
+ * @responder_pm_mode: is responder_pm_mode set
+ * @negotiation_type: if the responder wants to doze outside the TWT SP
+ * @twt_request: 1 for TWT request, 0 otherwise
+ * @implicit: is TWT implicit
+ * @twt_group_assignment: the TWT group assignment
+ * @twt_channel: the TWT channel
+ * @reserved: reserved
+ */
+struct iwl_dhc_twt_operation {
+ __le32 mac_id;
+ __le32 twt_operation;
+ __le64 target_wake_time;
+ __le32 interval_exp;
+ __le32 interval_mantissa;
+ __le32 min_wake_duration;
+ u8 trigger;
+ u8 flow_type;
+ u8 flow_id;
+ u8 protection;
+ u8 ndo_paging_indicator;
+ u8 responder_pm_mode;
+ u8 negotiation_type;
+ u8 twt_request;
+ u8 implicit;
+ u8 twt_group_assignment;
+ u8 twt_channel;
+ u8 reserved;
+}; /* DHC_TWT_OPERATION_API_S */
+
+#endif /* __iwl_fw_api_dhc_h__ */
diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/location.h b/drivers/net/wireless/intel/iwlwifi/fw/api/location.h
index b8dff139aa05..33541f92c7c7 100644
--- a/drivers/net/wireless/intel/iwlwifi/fw/api/location.h
+++ b/drivers/net/wireless/intel/iwlwifi/fw/api/location.h
@@ -2,10 +2,15 @@
/*
* Copyright (C) 2015-2017 Intel Deutschland GmbH
* Copyright (C) 2018-2022 Intel Corporation
- * Copyright (C) 2024 Intel Corporation
+ * Copyright (C) 2024-2025 Intel Corporation
*/
#ifndef __iwl_fw_api_location_h__
#define __iwl_fw_api_location_h__
+#include <linux/ieee80211.h>
+#include <linux/if_ether.h>
+#include <linux/types.h>
+#include <linux/bits.h>
+#include "rs.h"
/**
* enum iwl_location_subcmd_ids - location group command IDs
@@ -1015,7 +1020,7 @@ struct iwl_tof_range_req_ap_entry_v9 {
} __packed; /* LOCATION_RANGE_REQ_AP_ENTRY_CMD_API_S_VER_9 */
/**
- * struct iwl_tof_range_req_ap_entry_v10 - AP configuration parameters
+ * struct iwl_tof_range_req_ap_entry - AP configuration parameters
* @initiator_ap_flags: see &enum iwl_initiator_ap_flags.
* @band: 0 for 5.2 GHz, 1 for 2.4 GHz, 2 for 6GHz
* @channel_num: AP Channel number
@@ -1063,7 +1068,7 @@ struct iwl_tof_range_req_ap_entry_v9 {
* @min_time_between_msr: For non trigger based NDP ranging, the minimum time
* between measurements in units of milliseconds
*/
-struct iwl_tof_range_req_ap_entry_v10 {
+struct iwl_tof_range_req_ap_entry {
__le32 initiator_ap_flags;
u8 band;
u8 channel_num;
@@ -1134,7 +1139,7 @@ enum iwl_tof_initiator_flags {
IWL_TOF_INITIATOR_FLAGS_NON_ASAP_SUPPORT = BIT(20),
}; /* LOCATION_RANGE_REQ_CMD_API_S_VER_5 */
-#define IWL_MVM_TOF_MAX_APS 5
+#define IWL_TOF_MAX_APS 5
#define IWL_MVM_TOF_MAX_TWO_SIDED_APS 5
/**
@@ -1153,7 +1158,7 @@ enum iwl_tof_initiator_flags {
* when the session is done (successfully / partially).
* one of iwl_tof_response_mode.
* @reserved0: reserved
- * @num_of_ap: Number of APs to measure (error if > IWL_MVM_TOF_MAX_APS)
+ * @num_of_ap: Number of APs to measure (error if > IWL_TOF_MAX_APS)
* @macaddr_random: '0' Use default source MAC address (i.e. p2_p),
* '1' Use MAC Address randomization according to the below
* @range_req_bssid: ranging request BSSID
@@ -1183,7 +1188,7 @@ struct iwl_tof_range_req_cmd_v5 {
u8 ftm_tx_chains;
__le16 common_calib;
__le16 specific_calib;
- struct iwl_tof_range_req_ap_entry_v2 ap[IWL_MVM_TOF_MAX_APS];
+ struct iwl_tof_range_req_ap_entry_v2 ap[IWL_TOF_MAX_APS];
} __packed;
/* LOCATION_RANGE_REQ_CMD_API_S_VER_5 */
@@ -1192,7 +1197,7 @@ struct iwl_tof_range_req_cmd_v5 {
* @initiator_flags: see flags @ iwl_tof_initiator_flags
* @request_id: A Token incremented per request. The same Token will be
* sent back in the range response
- * @num_of_ap: Number of APs to measure (error if > IWL_MVM_TOF_MAX_APS)
+ * @num_of_ap: Number of APs to measure (error if > IWL_TOF_MAX_APS)
* @range_req_bssid: ranging request BSSID
* @macaddr_mask: Bits set to 0 shall be copied from the MAC address template.
* Bits set to 1 shall be randomized by the UMAC
@@ -1216,7 +1221,7 @@ struct iwl_tof_range_req_cmd_v7 {
__le32 tsf_mac_id;
__le16 common_calib;
__le16 specific_calib;
- struct iwl_tof_range_req_ap_entry_v3 ap[IWL_MVM_TOF_MAX_APS];
+ struct iwl_tof_range_req_ap_entry_v3 ap[IWL_TOF_MAX_APS];
} __packed; /* LOCATION_RANGE_REQ_CMD_API_S_VER_7 */
/**
@@ -1224,7 +1229,7 @@ struct iwl_tof_range_req_cmd_v7 {
* @initiator_flags: see flags @ iwl_tof_initiator_flags
* @request_id: A Token incremented per request. The same Token will be
* sent back in the range response
- * @num_of_ap: Number of APs to measure (error if > IWL_MVM_TOF_MAX_APS)
+ * @num_of_ap: Number of APs to measure (error if > IWL_TOF_MAX_APS)
* @range_req_bssid: ranging request BSSID
* @macaddr_mask: Bits set to 0 shall be copied from the MAC address template.
* Bits set to 1 shall be randomized by the UMAC
@@ -1248,7 +1253,7 @@ struct iwl_tof_range_req_cmd_v8 {
__le32 tsf_mac_id;
__le16 common_calib;
__le16 specific_calib;
- struct iwl_tof_range_req_ap_entry_v4 ap[IWL_MVM_TOF_MAX_APS];
+ struct iwl_tof_range_req_ap_entry_v4 ap[IWL_TOF_MAX_APS];
} __packed; /* LOCATION_RANGE_REQ_CMD_API_S_VER_8 */
/**
@@ -1256,7 +1261,7 @@ struct iwl_tof_range_req_cmd_v8 {
* @initiator_flags: see flags @ iwl_tof_initiator_flags
* @request_id: A Token incremented per request. The same Token will be
* sent back in the range response
- * @num_of_ap: Number of APs to measure (error if > IWL_MVM_TOF_MAX_APS)
+ * @num_of_ap: Number of APs to measure (error if > IWL_TOF_MAX_APS)
* @range_req_bssid: ranging request BSSID
* @macaddr_mask: Bits set to 0 shall be copied from the MAC address template.
* Bits set to 1 shall be randomized by the UMAC
@@ -1276,7 +1281,7 @@ struct iwl_tof_range_req_cmd_v9 {
u8 macaddr_template[ETH_ALEN];
__le32 req_timeout_ms;
__le32 tsf_mac_id;
- struct iwl_tof_range_req_ap_entry_v6 ap[IWL_MVM_TOF_MAX_APS];
+ struct iwl_tof_range_req_ap_entry_v6 ap[IWL_TOF_MAX_APS];
} __packed; /* LOCATION_RANGE_REQ_CMD_API_S_VER_9 */
/**
@@ -1284,7 +1289,7 @@ struct iwl_tof_range_req_cmd_v9 {
* @initiator_flags: see flags @ iwl_tof_initiator_flags
* @request_id: A Token incremented per request. The same Token will be
* sent back in the range response
- * @num_of_ap: Number of APs to measure (error if > IWL_MVM_TOF_MAX_APS)
+ * @num_of_ap: Number of APs to measure (error if > IWL_TOF_MAX_APS)
* @range_req_bssid: ranging request BSSID
* @macaddr_mask: Bits set to 0 shall be copied from the MAC address template.
* Bits set to 1 shall be randomized by the UMAC
@@ -1304,7 +1309,7 @@ struct iwl_tof_range_req_cmd_v11 {
u8 macaddr_template[ETH_ALEN];
__le32 req_timeout_ms;
__le32 tsf_mac_id;
- struct iwl_tof_range_req_ap_entry_v7 ap[IWL_MVM_TOF_MAX_APS];
+ struct iwl_tof_range_req_ap_entry_v7 ap[IWL_TOF_MAX_APS];
} __packed; /* LOCATION_RANGE_REQ_CMD_API_S_VER_11 */
/**
@@ -1312,7 +1317,7 @@ struct iwl_tof_range_req_cmd_v11 {
* @initiator_flags: see flags @ iwl_tof_initiator_flags
* @request_id: A Token incremented per request. The same Token will be
* sent back in the range response
- * @num_of_ap: Number of APs to measure (error if > IWL_MVM_TOF_MAX_APS)
+ * @num_of_ap: Number of APs to measure (error if > IWL_TOF_MAX_APS)
* @range_req_bssid: ranging request BSSID
* @macaddr_mask: Bits set to 0 shall be copied from the MAC address template.
* Bits set to 1 shall be randomized by the UMAC
@@ -1332,7 +1337,7 @@ struct iwl_tof_range_req_cmd_v12 {
u8 macaddr_template[ETH_ALEN];
__le32 req_timeout_ms;
__le32 tsf_mac_id;
- struct iwl_tof_range_req_ap_entry_v8 ap[IWL_MVM_TOF_MAX_APS];
+ struct iwl_tof_range_req_ap_entry_v8 ap[IWL_TOF_MAX_APS];
} __packed; /* LOCATION_RANGE_REQ_CMD_API_S_VER_12 */
/**
@@ -1340,7 +1345,7 @@ struct iwl_tof_range_req_cmd_v12 {
* @initiator_flags: see flags @ iwl_tof_initiator_flags
* @request_id: A Token incremented per request. The same Token will be
* sent back in the range response
- * @num_of_ap: Number of APs to measure (error if > IWL_MVM_TOF_MAX_APS)
+ * @num_of_ap: Number of APs to measure (error if > IWL_TOF_MAX_APS)
* @range_req_bssid: ranging request BSSID
* @macaddr_mask: Bits set to 0 shall be copied from the MAC address template.
* Bits set to 1 shall be randomized by the UMAC
@@ -1360,15 +1365,15 @@ struct iwl_tof_range_req_cmd_v13 {
u8 macaddr_template[ETH_ALEN];
__le32 req_timeout_ms;
__le32 tsf_mac_id;
- struct iwl_tof_range_req_ap_entry_v9 ap[IWL_MVM_TOF_MAX_APS];
+ struct iwl_tof_range_req_ap_entry_v9 ap[IWL_TOF_MAX_APS];
} __packed; /* LOCATION_RANGE_REQ_CMD_API_S_VER_13 */
/**
- * struct iwl_tof_range_req_cmd_v14 - start measurement cmd
+ * struct iwl_tof_range_req_cmd - start measurement cmd
* @initiator_flags: see flags @ iwl_tof_initiator_flags
* @request_id: A Token incremented per request. The same Token will be
* sent back in the range response
- * @num_of_ap: Number of APs to measure (error if > IWL_MVM_TOF_MAX_APS)
+ * @num_of_ap: Number of APs to measure (error if > IWL_TOF_MAX_APS)
* @range_req_bssid: ranging request BSSID
* @macaddr_mask: Bits set to 0 shall be copied from the MAC address template.
* Bits set to 1 shall be randomized by the UMAC
@@ -1377,9 +1382,9 @@ struct iwl_tof_range_req_cmd_v13 {
* This is the session time for completing the measurement.
* @tsf_mac_id: report the measurement start time for each ap in terms of the
* TSF of this mac id. 0xff to disable TSF reporting.
- * @ap: per-AP request data, see &struct iwl_tof_range_req_ap_entry_v10.
+ * @ap: per-AP request data, see &struct iwl_tof_range_req_ap_entry.
*/
-struct iwl_tof_range_req_cmd_v14 {
+struct iwl_tof_range_req_cmd {
__le32 initiator_flags;
u8 request_id;
u8 num_of_ap;
@@ -1388,8 +1393,8 @@ struct iwl_tof_range_req_cmd_v14 {
u8 macaddr_template[ETH_ALEN];
__le32 req_timeout_ms;
__le32 tsf_mac_id;
- struct iwl_tof_range_req_ap_entry_v10 ap[IWL_MVM_TOF_MAX_APS];
-} __packed; /* LOCATION_RANGE_REQ_CMD_API_S_VER_13 */
+ struct iwl_tof_range_req_ap_entry ap[IWL_TOF_MAX_APS];
+} __packed; /* LOCATION_RANGE_REQ_CMD_API_S_VER_15 */
/*
* enum iwl_tof_range_request_status - status of the sent request
@@ -1609,7 +1614,7 @@ struct iwl_tof_range_rsp_ap_entry_ntfy_v5 {
} __packed; /* LOCATION_RANGE_RSP_AP_ETRY_NTFY_API_S_VER_5 */
/**
- * struct iwl_tof_range_rsp_ap_entry_ntfy_v6 - AP parameters (response)
+ * struct iwl_tof_range_rsp_ap_entry_ntfy_v7 - AP parameters (response)
* @bssid: BSSID of the AP
* @measure_status: current APs measurement status, one of
* &enum iwl_tof_entry_status.
@@ -1645,7 +1650,7 @@ struct iwl_tof_range_rsp_ap_entry_ntfy_v5 {
* @tx_pn: the last PN used for this responder Tx in case PMF is configured in
* LE byte order.
*/
-struct iwl_tof_range_rsp_ap_entry_ntfy_v6 {
+struct iwl_tof_range_rsp_ap_entry_ntfy_v7 {
u8 bssid[ETH_ALEN];
u8 measure_status;
u8 measure_bw;
@@ -1672,6 +1677,65 @@ struct iwl_tof_range_rsp_ap_entry_ntfy_v6 {
} __packed; /* LOCATION_RANGE_RSP_AP_ETRY_NTFY_API_S_VER_6,
LOCATION_RANGE_RSP_AP_ETRY_NTFY_API_S_VER_7 */
+/**
+ * struct iwl_tof_range_rsp_ap_entry_ntfy - AP parameters (response)
+ * @bssid: BSSID of the AP
+ * @measure_status: current APs measurement status, one of
+ * &enum iwl_tof_entry_status.
+ * @measure_bw: Current AP Bandwidth: 0 20MHz, 1 40MHz, 2 80MHz
+ * @rtt: The Round Trip Time that took for the last measurement for
+ * current AP [pSec]
+ * @rtt_variance: The Variance of the RTT values measured for current AP
+ * @rtt_spread: The Difference between the maximum and the minimum RTT
+ * values measured for current AP in the current session [pSec]
+ * @rssi: RSSI as uploaded in the Channel Estimation notification
+ * @rssi_spread: The Difference between the maximum and the minimum RSSI values
+ * measured for current AP in the current session
+ * @last_burst: 1 if no more FTM sessions are scheduled for this responder
+ * @refusal_period: refusal period in case of
+ * @IWL_TOF_ENTRY_RESPONDER_CANNOT_COLABORATE [sec]
+ * @timestamp: The GP2 Clock [usec] where Channel Estimation notification was
+ * uploaded by the LMAC
+ * @start_tsf: measurement start time in TSF of the mac specified in the range
+ * request
+ * @reserved1: reserved, for backwards compatibility
+ * @t2t3_initiator: as calculated from the algo in the initiator
+ * @t1t4_responder: as calculated from the algo in the responder
+ * @common_calib: Calib val that was used in for this AP measurement
+ * @specific_calib: val that was used in for this AP measurement
+ * @papd_calib_output: The result of the tof papd calibration that was injected
+ * into the algorithm.
+ * @rttConfidence: a value between 0 - 31 that represents the rtt accuracy.
+ * @reserved: for alignment
+ * @rx_pn: the last PN used for this responder Rx in case PMF is configured in
+ * LE byte order.
+ * @tx_pn: the last PN used for this responder Tx in case PMF is configured in
+ * LE byte order.
+ */
+struct iwl_tof_range_rsp_ap_entry_ntfy {
+ u8 bssid[ETH_ALEN];
+ u8 measure_status;
+ u8 measure_bw;
+ __le32 rtt;
+ __le32 rtt_variance;
+ __le32 rtt_spread;
+ s8 rssi;
+ u8 rssi_spread;
+ u8 last_burst;
+ u8 refusal_period;
+ __le32 timestamp;
+ __le32 start_tsf;
+ __le32 reserved1[2];
+ __le32 t2t3_initiator;
+ __le32 t1t4_responder;
+ __le16 common_calib;
+ __le16 specific_calib;
+ __le32 papd_calib_output;
+ u8 rttConfidence;
+ u8 reserved[3];
+ u8 rx_pn[IEEE80211_CCMP_PN_LEN];
+ u8 tx_pn[IEEE80211_CCMP_PN_LEN];
+} __packed; /* LOCATION_RANGE_RSP_AP_ETRY_NTFY_API_S_VER_8 */
/**
* enum iwl_tof_response_status - tof response status
@@ -1695,7 +1759,7 @@ enum iwl_tof_response_status {
* @request_status: status of current measurement session, one of
* &enum iwl_tof_response_status.
* @last_in_batch: reprot policy (when not all responses are uploaded at once)
- * @num_of_aps: Number of APs to measure (error if > IWL_MVM_TOF_MAX_APS)
+ * @num_of_aps: Number of APs to measure (error if > IWL_TOF_MAX_APS)
* @ap: per-AP data
*/
struct iwl_tof_range_rsp_ntfy_v5 {
@@ -1703,7 +1767,7 @@ struct iwl_tof_range_rsp_ntfy_v5 {
u8 request_status;
u8 last_in_batch;
u8 num_of_aps;
- struct iwl_tof_range_rsp_ap_entry_ntfy_v3 ap[IWL_MVM_TOF_MAX_APS];
+ struct iwl_tof_range_rsp_ap_entry_ntfy_v3 ap[IWL_TOF_MAX_APS];
} __packed; /* LOCATION_RANGE_RSP_NTFY_API_S_VER_5 */
/**
@@ -1719,7 +1783,7 @@ struct iwl_tof_range_rsp_ntfy_v6 {
u8 num_of_aps;
u8 last_report;
u8 reserved;
- struct iwl_tof_range_rsp_ap_entry_ntfy_v4 ap[IWL_MVM_TOF_MAX_APS];
+ struct iwl_tof_range_rsp_ap_entry_ntfy_v4 ap[IWL_TOF_MAX_APS];
} __packed; /* LOCATION_RANGE_RSP_NTFY_API_S_VER_6 */
/**
@@ -1735,25 +1799,42 @@ struct iwl_tof_range_rsp_ntfy_v7 {
u8 num_of_aps;
u8 last_report;
u8 reserved;
- struct iwl_tof_range_rsp_ap_entry_ntfy_v5 ap[IWL_MVM_TOF_MAX_APS];
+ struct iwl_tof_range_rsp_ap_entry_ntfy_v5 ap[IWL_TOF_MAX_APS];
} __packed; /* LOCATION_RANGE_RSP_NTFY_API_S_VER_7 */
/**
- * struct iwl_tof_range_rsp_ntfy_v8 - ranging response notification
+ * struct iwl_tof_range_rsp_ntfy_v9 - ranging response notification
* @request_id: A Token ID of the corresponding Range request
* @num_of_aps: Number of APs results
* @last_report: 1 if no more FTM sessions are scheduled, 0 otherwise.
* @reserved: reserved
* @ap: per-AP data
*/
-struct iwl_tof_range_rsp_ntfy_v8 {
+struct iwl_tof_range_rsp_ntfy_v9 {
u8 request_id;
u8 num_of_aps;
u8 last_report;
u8 reserved;
- struct iwl_tof_range_rsp_ap_entry_ntfy_v6 ap[IWL_MVM_TOF_MAX_APS];
+ struct iwl_tof_range_rsp_ap_entry_ntfy_v7 ap[IWL_TOF_MAX_APS];
} __packed; /* LOCATION_RANGE_RSP_NTFY_API_S_VER_8,
- LOCATION_RANGE_RSP_NTFY_API_S_VER_9 */
+ * LOCATION_RANGE_RSP_NTFY_API_S_VER_9
+ */
+
+/**
+ * struct iwl_tof_range_rsp_ntfy - ranging response notification
+ * @request_id: A Token ID of the corresponding Range request
+ * @num_of_aps: Number of APs results
+ * @last_report: 1 if no more FTM sessions are scheduled, 0 otherwise.
+ * @reserved: reserved
+ * @ap: per-AP data
+ */
+struct iwl_tof_range_rsp_ntfy {
+ u8 request_id;
+ u8 num_of_aps;
+ u8 last_report;
+ u8 reserved;
+ struct iwl_tof_range_rsp_ap_entry_ntfy ap[IWL_TOF_MAX_APS];
+} __packed; /* LOCATION_RANGE_RSP_NTFY_API_S_VER_10 */
#define IWL_MVM_TOF_MCSI_BUF_SIZE (245)
/**
diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/mac-cfg.h b/drivers/net/wireless/intel/iwlwifi/fw/api/mac-cfg.h
index 37bb7002c1c9..b9f559dac39f 100644
--- a/drivers/net/wireless/intel/iwlwifi/fw/api/mac-cfg.h
+++ b/drivers/net/wireless/intel/iwlwifi/fw/api/mac-cfg.h
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
/*
- * Copyright (C) 2012-2014, 2018-2019, 2021-2024 Intel Corporation
+ * Copyright (C) 2012-2014, 2018-2019, 2021-2025 Intel Corporation
* Copyright (C) 2013-2015 Intel Mobile Communications GmbH
* Copyright (C) 2016-2017 Intel Deutschland GmbH
*/
@@ -46,7 +46,7 @@ enum iwl_mac_conf_subcmd_ids {
*/
STA_CONFIG_CMD = 0xA,
/**
- * @AUX_STA_CMD: &struct iwl_mvm_aux_sta_cmd
+ * @AUX_STA_CMD: &struct iwl_aux_sta_cmd
*/
AUX_STA_CMD = 0xB,
/**
@@ -62,6 +62,10 @@ enum iwl_mac_conf_subcmd_ids {
*/
ROC_CMD = 0xE,
/**
+ * @TWT_OPERATION_CMD: &struct iwl_twt_operation_cmd
+ */
+ TWT_OPERATION_CMD = 0x10,
+ /**
* @MISSED_BEACONS_NOTIF: &struct iwl_missed_beacons_notif
*/
MISSED_BEACONS_NOTIF = 0xF6,
@@ -307,8 +311,41 @@ enum iwl_mac_config_filter_flags {
}; /* MAC_FILTER_FLAGS_MASK_E_VER_1 */
/**
+ * struct iwl_mac_wifi_gen_support_v2 - parameters of iwl_mac_config_cmd
+ * with support up to eht as in version 2 of the command
+ *
+ * @he_support: does this MAC support HE
+ * @he_ap_support: HE AP enabled, "pseudo HE", no trigger frame handling
+ * @eht_support: does this MAC support EHT. Requires he_support
+ */
+struct iwl_mac_wifi_gen_support_v2 {
+ __le16 he_support;
+ __le16 he_ap_support;
+ __le32 eht_support;
+} __packed;
+
+/**
+ * struct iwl_mac_wifi_gen_support - parameters of iwl_mac_config_cmd
+ * with support up to uhr as in version 3 of the command
+ * ( MAC_CONTEXT_CONFIG_CMD = 0x8 )
+ *
+ * @he_support: does this MAC support HE
+ * @he_ap_support: HE AP enabled, "pseudo HE", no trigger frame handling
+ * @eht_support: does this MAC support EHT. Requires he_support
+ * @uhr_support: does this MAC support UHR. Requires eht_support
+ * @reserved: reserved for alignment and to match version 2's size
+ */
+struct iwl_mac_wifi_gen_support {
+ u8 he_support;
+ u8 he_ap_support;
+ u8 eht_support;
+ u8 uhr_support;
+ __le32 reserved;
+} __packed;
+
+/**
* struct iwl_mac_config_cmd - command structure to configure MAC contexts in
- * MLD API
+ * MLD API for versions 2 and 3
* ( MAC_CONTEXT_CONFIG_CMD = 0x8 )
*
* @id_and_color: ID and color of the MAC
@@ -317,9 +354,8 @@ enum iwl_mac_config_filter_flags {
* @local_mld_addr: mld address
* @reserved_for_local_mld_addr: reserved
* @filter_flags: combination of &enum iwl_mac_config_filter_flags
- * @he_support: does this MAC support HE
- * @he_ap_support: HE AP enabled, "pseudo HE", no trigger frame handling
- * @eht_support: does this MAC support EHT. Requires he_support
+ * @wifi_gen_v2: he/eht parameters as in cmd version 2
+ * @wifi_gen: he/eht/uhr parameters as in cmd version 3
* @nic_not_ack_enabled: mark that the NIC doesn't support receiving
* ACK-enabled AGG, (i.e. both BACK and non-BACK frames in single AGG).
* If the NIC is not ACK_ENABLED it may use the EOF-bit in first non-0
@@ -328,7 +364,6 @@ enum iwl_mac_config_filter_flags {
* @p2p_dev: mac data for p2p device
*/
struct iwl_mac_config_cmd {
- /* COMMON_INDEX_HDR_API_S_VER_1 */
__le32 id_and_color;
__le32 action;
/* MAC_CONTEXT_TYPE_API_E */
@@ -336,16 +371,17 @@ struct iwl_mac_config_cmd {
u8 local_mld_addr[6];
__le16 reserved_for_local_mld_addr;
__le32 filter_flags;
- __le16 he_support;
- __le16 he_ap_support;
- __le32 eht_support;
+ union {
+ struct iwl_mac_wifi_gen_support_v2 wifi_gen_v2;
+ struct iwl_mac_wifi_gen_support wifi_gen;
+ };
__le32 nic_not_ack_enabled;
/* MAC_CONTEXT_CONFIG_SPECIFIC_DATA_API_U_VER_2 */
union {
struct iwl_mac_client_data client;
struct iwl_mac_p2p_dev_data p2p_dev;
};
-} __packed; /* MAC_CONTEXT_CONFIG_CMD_API_S_VER_2 */
+} __packed; /* MAC_CONTEXT_CONFIG_CMD_API_S_VER_2_VER_3 */
/**
* enum iwl_link_ctx_modify_flags - indicate to the fw what fields are being
@@ -453,6 +489,24 @@ enum iwl_link_modify_bandwidth {
};
/**
+ * struct iwl_npca_params - NPCA parameters (non-primary channel access)
+ *
+ * @switch_delay: after switch, delay TX according to destination AP
+ * @switch_back_delay: switch back to control channel before OBSS frame end
+ * @min_dur_threshold: minimum PPDU time to switch to the non-primary
+ * NPCA channel
+ * @flags: NPCA flags - bit 0: puncturing allowed, bit 1: new TX allowed
+ * @reserved: reserved for alignment purposes
+ */
+struct iwl_npca_params {
+ u8 switch_delay;
+ u8 switch_back_delay;
+ __le16 min_dur_threshold;
+ __le16 flags;
+ __le16 reserved;
+} __packed; /* NPCA_PARAM_API_S_VER_1 */
+
+/**
* struct iwl_link_config_cmd - command structure to configure the LINK context
* in MLD API
* ( LINK_CONFIG_CMD =0x9 )
@@ -509,6 +563,8 @@ enum iwl_link_modify_bandwidth {
* IEEE802.11REVme-D5.0
* @ibss_bssid_addr: bssid for ibss
* @reserved_for_ibss_bssid_addr: reserved
+ * @npca_params: NPCA parameters
+ * @prio_edca_params: priority EDCA parameters for enhanced QoS
* @reserved3: reserved for future use
*/
struct iwl_link_config_cmd {
@@ -556,8 +612,10 @@ struct iwl_link_config_cmd {
u8 ul_mu_data_disable;
u8 ibss_bssid_addr[6];
__le16 reserved_for_ibss_bssid_addr;
- __le32 reserved3[8];
-} __packed; /* LINK_CONTEXT_CONFIG_CMD_API_S_VER_1, _VER_2, _VER_3, _VER_4, _VER_5, _VER_6 */
+ struct iwl_npca_params npca_params; /* since _VER_7 */
+ struct iwl_ac_qos prio_edca_params; /* since _VER_7 */
+ __le32 reserved3[4];
+} __packed; /* LINK_CONTEXT_CONFIG_CMD_API_S_VER_1, _VER_2, _VER_3, _VER_4, _VER_5, _VER_6, _VER_7 */
/* Currently FW supports link ids in the range 0-3 and can have
* at most two active links for each vif.
@@ -585,6 +643,62 @@ enum iwl_fw_sta_type {
}; /* STATION_TYPE_E_VER_1 */
/**
+ * struct iwl_sta_cfg_cmd_v1 - cmd structure to add a peer sta to the uCode's
+ * station table
+ * ( STA_CONFIG_CMD = 0xA )
+ *
+ * @sta_id: index of station in uCode's station table
+ * @link_id: the id of the link that is used to communicate with this sta
+ * @peer_mld_address: the peers mld address
+ * @reserved_for_peer_mld_address: reserved
+ * @peer_link_address: the address of the link that is used to communicate
+ * with this sta
+ * @reserved_for_peer_link_address: reserved
+ * @station_type: type of this station. See &enum iwl_fw_sta_type
+ * @assoc_id: for GO only
+ * @beamform_flags: beam forming controls
+ * @mfp: indicates whether the STA uses management frame protection or not.
+ * @mimo: indicates whether the sta uses mimo or not
+ * @mimo_protection: indicates whether the sta uses mimo protection or not
+ * @ack_enabled: indicates that the AP supports receiving ACK-
+ * enabled AGG, i.e. both BACK and non-BACK frames in a single AGG
+ * @trig_rnd_alloc: indicates that trigger based random allocation
+ * is enabled according to UORA element existence
+ * @tx_ampdu_spacing: minimum A-MPDU spacing:
+ * 4 - 2us density, 5 - 4us density, 6 - 8us density, 7 - 16us density
+ * @tx_ampdu_max_size: maximum A-MPDU length: 0 - 8K, 1 - 16K, 2 - 32K,
+ * 3 - 64K, 4 - 128K, 5 - 256K, 6 - 512K, 7 - 1024K.
+ * @sp_length: the size of the SP in actual number of frames
+ * @uapsd_acs: 4 LS bits are trigger enabled ACs, 4 MS bits are the deliver
+ * enabled ACs.
+ * @pkt_ext: optional, exists according to PPE-present bit in the HE/EHT-PHY
+ * capa
+ * @htc_flags: which features are supported in HTC
+ */
+struct iwl_sta_cfg_cmd_v1 {
+ __le32 sta_id;
+ __le32 link_id;
+ u8 peer_mld_address[ETH_ALEN];
+ __le16 reserved_for_peer_mld_address;
+ u8 peer_link_address[ETH_ALEN];
+ __le16 reserved_for_peer_link_address;
+ __le32 station_type;
+ __le32 assoc_id;
+ __le32 beamform_flags;
+ __le32 mfp;
+ __le32 mimo;
+ __le32 mimo_protection;
+ __le32 ack_enabled;
+ __le32 trig_rnd_alloc;
+ __le32 tx_ampdu_spacing;
+ __le32 tx_ampdu_max_size;
+ __le32 sp_length;
+ __le32 uapsd_acs;
+ struct iwl_he_pkt_ext_v2 pkt_ext;
+ __le32 htc_flags;
+} __packed; /* STA_CMD_API_S_VER_1 */
+
+/**
* struct iwl_sta_cfg_cmd - cmd structure to add a peer sta to the uCode's
* station table
* ( STA_CONFIG_CMD = 0xA )
@@ -616,6 +730,14 @@ enum iwl_fw_sta_type {
* @pkt_ext: optional, exists according to PPE-present bit in the HE/EHT-PHY
* capa
* @htc_flags: which features are supported in HTC
+ * @use_ldpc_x2_cw: Indicates whether to use LDPC with double CW
+ * @use_icf: Indicates whether to use ICF instead of RTS
+ * @dps_pad_time: DPS (Dynamic Power Save) padding delay resolution to ensure
+ * proper timing alignment
+ * @dps_trans_delay: DPS minimal time that takes the peer to return to low power
+ * @mic_prep_pad_delay: MIC prep time padding
+ * @mic_compute_pad_delay: MIC compute time padding
+ * @reserved: Reserved for alignment
*/
struct iwl_sta_cfg_cmd {
__le32 sta_id;
@@ -638,10 +760,17 @@ struct iwl_sta_cfg_cmd {
__le32 uapsd_acs;
struct iwl_he_pkt_ext_v2 pkt_ext;
__le32 htc_flags;
-} __packed; /* STA_CMD_API_S_VER_1 */
+ u8 use_ldpc_x2_cw;
+ u8 use_icf;
+ u8 dps_pad_time;
+ u8 dps_trans_delay;
+ u8 mic_prep_pad_delay;
+ u8 mic_compute_pad_delay;
+ u8 reserved[2];
+} __packed; /* STA_CMD_API_S_VER_2 */
/**
- * struct iwl_mvm_aux_sta_cmd - command for AUX STA configuration
+ * struct iwl_aux_sta_cmd - command for AUX STA configuration
* ( AUX_STA_CMD = 0xB )
*
* @sta_id: index of aux sta to configure
@@ -649,7 +778,7 @@ struct iwl_sta_cfg_cmd {
* @mac_addr: mac addr of the auxilary sta
* @reserved_for_mac_addr: reserved
*/
-struct iwl_mvm_aux_sta_cmd {
+struct iwl_aux_sta_cmd {
__le32 sta_id;
__le32 lmac_id;
u8 mac_addr[ETH_ALEN];
@@ -682,9 +811,9 @@ struct iwl_mvm_sta_disable_tx_cmd {
/**
* enum iwl_mvm_fw_esr_recommendation - FW recommendation code
- * @ESR_RECOMMEND_LEAVE: recommendation to leave esr
- * @ESR_FORCE_LEAVE: force exiting esr
- * @ESR_RECOMMEND_ENTER: recommendation to enter esr
+ * @ESR_RECOMMEND_LEAVE: recommendation to leave EMLSR
+ * @ESR_FORCE_LEAVE: force exiting EMLSR
+ * @ESR_RECOMMEND_ENTER: recommendation to enter EMLSR
*/
enum iwl_mvm_fw_esr_recommendation {
ESR_RECOMMEND_LEAVE,
@@ -693,15 +822,46 @@ enum iwl_mvm_fw_esr_recommendation {
}; /* ESR_MODE_RECOMMENDATION_CODE_API_E_VER_1 */
/**
- * struct iwl_mvm_esr_mode_notif - FWs recommendation/force for esr mode
+ * struct iwl_esr_mode_notif_v1 - FW recommendation/force for EMLSR mode
*
- * @action: the action to apply on esr state. See &iwl_mvm_fw_esr_recommendation
+ * @action: the action to apply on EMLSR state.
+ * See &iwl_mvm_fw_esr_recommendation
*/
-struct iwl_mvm_esr_mode_notif {
+struct iwl_esr_mode_notif_v1 {
__le32 action;
} __packed; /* ESR_MODE_RECOMMENDATION_NTFY_API_S_VER_1 */
/**
+ * enum iwl_esr_leave_reason - reasons for leaving EMLSR mode
+ *
+ * @ESR_LEAVE_REASON_OMI_MU_UL_DISALLOWED: OMI MU UL disallowed
+ * @ESR_LEAVE_REASON_NO_TRIG_FOR_ESR_STA: No trigger for EMLSR station
+ * @ESR_LEAVE_REASON_NO_ESR_STA_IN_MU_DL: No EMLSR station in MU DL
+ * @ESR_LEAVE_REASON_BAD_ACTIV_FRAME_TH: Bad activation frame threshold
+ * @ESR_LEAVE_REASON_RTS_IN_DUAL_LISTEN: RTS in dual listen
+ */
+enum iwl_esr_leave_reason {
+ ESR_LEAVE_REASON_OMI_MU_UL_DISALLOWED = BIT(0),
+ ESR_LEAVE_REASON_NO_TRIG_FOR_ESR_STA = BIT(1),
+ ESR_LEAVE_REASON_NO_ESR_STA_IN_MU_DL = BIT(2),
+ ESR_LEAVE_REASON_BAD_ACTIV_FRAME_TH = BIT(3),
+ ESR_LEAVE_REASON_RTS_IN_DUAL_LISTEN = BIT(4),
+};
+
+/**
+ * struct iwl_esr_mode_notif - FW recommendation/force for EMLSR mode
+ *
+ * @action: the action to apply on EMLSR state.
+ * See &iwl_mvm_fw_esr_recommendation
+ * @leave_reason_mask: mask for various reasons to leave EMLSR mode.
+ * See &iwl_esr_leave_reason
+ */
+struct iwl_esr_mode_notif {
+ __le32 action;
+ __le32 leave_reason_mask;
+} __packed; /* ESR_MODE_RECOMMENDATION_NTFY_API_S_VER_2 */
+
+/**
* struct iwl_missed_beacons_notif - sent when by the firmware upon beacon loss
* ( MISSED_BEACONS_NOTIF = 0xF6 )
* @link_id: fw link ID
@@ -748,4 +908,83 @@ struct iwl_esr_trans_fail_notif {
__le32 err_code;
} __packed; /* ESR_TRANSITION_FAILED_NTFY_API_S_VER_1 */
+/*
+ * enum iwl_twt_operation_type: TWT operation in a TWT action frame
+ *
+ * @TWT_OPERATION_REQUEST: TWT Request
+ * @TWT_OPERATION_SUGGEST: TWT Suggest
+ * @TWT_OPERATION_DEMAND: TWT Demand
+ * @TWT_OPERATION_GROUPING: TWT Grouping
+ * @TWT_OPERATION_ACCEPT: TWT Accept
+ * @TWT_OPERATION_ALTERNATE: TWT Alternate
+ * @TWT_OPERATION_DICTATE: TWT Dictate
+ * @TWT_OPERATION_REJECT: TWT Reject
+ * @TWT_OPERATION_TEARDOWN: TWT Teardown
+ * @TWT_OPERATION_UNAVAILABILITY: TWT Unavailability
+ */
+enum iwl_twt_operation_type {
+ TWT_OPERATION_REQUEST,
+ TWT_OPERATION_SUGGEST,
+ TWT_OPERATION_DEMAND,
+ TWT_OPERATION_GROUPING,
+ TWT_OPERATION_ACCEPT,
+ TWT_OPERATION_ALTERNATE,
+ TWT_OPERATION_DICTATE,
+ TWT_OPERATION_REJECT,
+ TWT_OPERATION_TEARDOWN,
+ TWT_OPERATION_UNAVAILABILITY,
+ TWT_OPERATION_MAX,
+}; /* TWT_OPERATION_TYPE_E_VER_1 */
+
+/**
+ * struct iwl_twt_operation_cmd - initiate a TWT session from driver
+ *
+ * @link_id: FW link id to initiate the TWT
+ * @twt_operation: &enum iwl_twt_operation_type
+ * @target_wake_time: TSF time to start the TWT
+ * @interval_exponent: the exponent for the interval
+ * @interval_mantissa: the mantissa for the interval
+ * @minimum_wake_duration: the minimum duration for the wake period
+ * @trigger: is the TWT triggered or not
+ * @flow_type: is the TWT announced (0) or not (1)
+ * @flow_id: the TWT flow identifier 0 - 7
+ * @twt_protection: is the TWT protected
+ * @ndp_paging_indicator: is ndp paging indicator set
+ * @responder_pm_mode: is responder pm mode set
+ * @negotiation_type: if the responder wants to doze outside the TWT SP
+ * @twt_request: 1 for TWT request (STA), 0 for TWT response (AP)
+ * @implicit: is TWT implicit
+ * @twt_group_assignment: the TWT group assignment
+ * @twt_channel: the TWT channel
+ * @restricted_info_present: is this a restricted TWT
+ * @dl_bitmap_valid: is DL (download) bitmap valid (restricted TWT)
+ * @ul_bitmap_valid: is UL (upload) bitmap valid (restricted TWT)
+ * @dl_tid_bitmap: DL TID bitmap (restricted TWT)
+ * @ul_tid_bitmap: UL TID bitmap (restricted TWT)
+ */
+struct iwl_twt_operation_cmd {
+ __le32 link_id;
+ __le32 twt_operation;
+ __le64 target_wake_time;
+ __le32 interval_exponent;
+ __le32 interval_mantissa;
+ __le32 minimum_wake_duration;
+ u8 trigger;
+ u8 flow_type;
+ u8 flow_id;
+ u8 twt_protection;
+ u8 ndp_paging_indicator;
+ u8 responder_pm_mode;
+ u8 negotiation_type;
+ u8 twt_request;
+ u8 implicit;
+ u8 twt_group_assignment;
+ u8 twt_channel;
+ u8 restricted_info_present;
+ u8 dl_bitmap_valid;
+ u8 ul_bitmap_valid;
+ u8 dl_tid_bitmap;
+ u8 ul_tid_bitmap;
+} __packed; /* TWT_OPERATION_API_S_VER_1 */
+
#endif /* __iwl_fw_api_mac_cfg_h__ */
diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/mac.h b/drivers/net/wireless/intel/iwlwifi/fw/api/mac.h
index 26301c0b06a1..2a174c00b712 100644
--- a/drivers/net/wireless/intel/iwlwifi/fw/api/mac.h
+++ b/drivers/net/wireless/intel/iwlwifi/fw/api/mac.h
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
/*
- * Copyright (C) 2012-2014, 2018-2022, 2024 Intel Corporation
+ * Copyright (C) 2012-2014, 2018-2022, 2024-2025 Intel Corporation
* Copyright (C) 2017 Intel Deutschland GmbH
*/
#ifndef __iwl_fw_api_mac_h__
@@ -287,9 +287,9 @@ struct iwl_ac_qos {
__le16 cw_min;
__le16 cw_max;
u8 aifsn;
- u8 fifos_mask;
+ u8 fifos_mask; /* not in use since _VER_3 */
__le16 edca_txop;
-} __packed; /* AC_QOS_API_S_VER_2 */
+} __packed; /* AC_QOS_API_S_VER_2, _VER_3 */
/**
* struct iwl_mac_ctx_cmd - command structure to configure MAC contexts
diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/phy-ctxt.h b/drivers/net/wireless/intel/iwlwifi/fw/api/phy-ctxt.h
index 4d8a12799c4d..4594a7c94bd6 100644
--- a/drivers/net/wireless/intel/iwlwifi/fw/api/phy-ctxt.h
+++ b/drivers/net/wireless/intel/iwlwifi/fw/api/phy-ctxt.h
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
/*
- * Copyright (C) 2012-2014, 2018, 2020-2024 Intel Corporation
+ * Copyright (C) 2012-2014, 2018, 2020-2025 Intel Corporation
* Copyright (C) 2013-2015 Intel Mobile Communications GmbH
* Copyright (C) 2016-2017 Intel Deutschland GmbH
*/
@@ -146,6 +146,7 @@ struct iwl_phy_context_cmd_v1 {
* @sbb_ctrl_channel_loc: location of the control channel
* @puncture_mask: bitmap of punctured subchannels
* @dsp_cfg_flags: set to 0
+ * @secondary_ctrl_chnl_loc: location of secondary control channel
* @reserved: reserved to align to 64 bit
*/
struct iwl_phy_context_cmd {
@@ -164,11 +165,13 @@ struct iwl_phy_context_cmd {
};
};
__le32 dsp_cfg_flags;
- __le32 reserved;
+ u8 secondary_ctrl_chnl_loc;
+ u8 reserved[3];
} __packed; /* PHY_CONTEXT_CMD_API_VER_3,
* PHY_CONTEXT_CMD_API_VER_4,
* PHY_CONTEXT_CMD_API_VER_5,
- * PHY_CONTEXT_CMD_API_VER_6
+ * PHY_CONTEXT_CMD_API_VER_6,
+ * PHY_CONTEXT_CMD_API_S_VER_7
*/
#endif /* __iwl_fw_api_phy_ctxt_h__ */
diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/phy.h b/drivers/net/wireless/intel/iwlwifi/fw/api/phy.h
index c73d4d597857..f63b25b03b7e 100644
--- a/drivers/net/wireless/intel/iwlwifi/fw/api/phy.h
+++ b/drivers/net/wireless/intel/iwlwifi/fw/api/phy.h
@@ -1,11 +1,13 @@
/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
/*
- * Copyright (C) 2012-2014, 2019-2022, 2024 Intel Corporation
+ * Copyright (C) 2012-2014, 2019-2022, 2024-2025 Intel Corporation
* Copyright (C) 2013-2015 Intel Mobile Communications GmbH
* Copyright (C) 2016-2017 Intel Deutschland GmbH
*/
#ifndef __iwl_fw_api_phy_h__
#define __iwl_fw_api_phy_h__
+#include <linux/types.h>
+#include <linux/bits.h>
/**
* enum iwl_phy_ops_subcmd_ids - PHY group commands
@@ -19,7 +21,7 @@ enum iwl_phy_ops_subcmd_ids {
CMD_DTS_MEASUREMENT_TRIGGER_WIDE = 0x0,
/**
- * @CTDP_CONFIG_CMD: &struct iwl_mvm_ctdp_cmd
+ * @CTDP_CONFIG_CMD: &struct iwl_ctdp_cmd
*/
CTDP_CONFIG_CMD = 0x03,
@@ -55,7 +57,7 @@ enum iwl_phy_ops_subcmd_ids {
/**
* @DTS_MEASUREMENT_NOTIF_WIDE:
* &struct iwl_dts_measurement_notif_v1 or
- * &struct iwl_dts_measurement_notif_v2
+ * &struct iwl_dts_measurement_notif
*/
DTS_MEASUREMENT_NOTIF_WIDE = 0xFF,
};
@@ -152,13 +154,13 @@ struct iwl_dts_measurement_notif_v1 {
} __packed; /* TEMPERATURE_MEASUREMENT_TRIGGER_NTFY_S_VER_1*/
/**
- * struct iwl_dts_measurement_notif_v2 - measurements notification
+ * struct iwl_dts_measurement_notif - measurements notification
*
* @temp: the measured temperature
* @voltage: the measured voltage
* @threshold_idx: the trip index that was crossed
*/
-struct iwl_dts_measurement_notif_v2 {
+struct iwl_dts_measurement_notif {
__le32 temp;
__le32 voltage;
__le32 threshold_idx;
@@ -195,25 +197,25 @@ struct ct_kill_notif {
} __packed; /* CT_KILL_NOTIFICATION_API_S_VER_1, CT_KILL_NOTIFICATION_API_S_VER_2 */
/**
-* enum iwl_mvm_ctdp_cmd_operation - CTDP command operations
+* enum iwl_ctdp_cmd_operation - CTDP command operations
* @CTDP_CMD_OPERATION_START: update the current budget
* @CTDP_CMD_OPERATION_STOP: stop ctdp
* @CTDP_CMD_OPERATION_REPORT: get the average budget
*/
-enum iwl_mvm_ctdp_cmd_operation {
+enum iwl_ctdp_cmd_operation {
CTDP_CMD_OPERATION_START = 0x1,
CTDP_CMD_OPERATION_STOP = 0x2,
CTDP_CMD_OPERATION_REPORT = 0x4,
};/* CTDP_CMD_OPERATION_TYPE_E */
/**
- * struct iwl_mvm_ctdp_cmd - track and manage the FW power consumption budget
+ * struct iwl_ctdp_cmd - track and manage the FW power consumption budget
*
- * @operation: see &enum iwl_mvm_ctdp_cmd_operation
+ * @operation: see &enum iwl_ctdp_cmd_operation
* @budget: the budget in milliwatt
* @window_size: defined in API but not used
*/
-struct iwl_mvm_ctdp_cmd {
+struct iwl_ctdp_cmd {
__le32 operation;
__le32 budget;
__le32 window_size;
diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/power.h b/drivers/net/wireless/intel/iwlwifi/fw/api/power.h
index 37ec26596ee7..23140205ccb9 100644
--- a/drivers/net/wireless/intel/iwlwifi/fw/api/power.h
+++ b/drivers/net/wireless/intel/iwlwifi/fw/api/power.h
@@ -1,12 +1,14 @@
/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
/*
- * Copyright (C) 2012-2014, 2018-2024 Intel Corporation
+ * Copyright (C) 2012-2014, 2018-2025 Intel Corporation
* Copyright (C) 2013-2014 Intel Mobile Communications GmbH
* Copyright (C) 2015-2017 Intel Deutschland GmbH
*/
#ifndef __iwl_fw_api_power_h__
#define __iwl_fw_api_power_h__
+#include "nvm-reg.h"
+
/* Power Management Commands, Responses, Notifications */
/**
@@ -54,7 +56,7 @@ struct iwl_ltr_config_cmd_v1 {
* @flags: See &enum iwl_ltr_config_flags
* @static_long: static LTR Long register value.
* @static_short: static LTR Short register value.
- * @ltr_cfg_values: LTR parameters table values (in usec) in folowing order:
+ * @ltr_cfg_values: LTR parameters table values (in usec) in following order:
* TX, RX, Short Idle, Long Idle. Used only if %LTR_CFG_FLAG_UPDATE_VALUES
* is set.
* @ltr_short_idle_timeout: LTR Short Idle timeout (in usec). Used only if
@@ -89,6 +91,7 @@ struct iwl_ltr_config_cmd {
* @POWER_FLAGS_LPRX_ENA_MSK: Low Power RX enable.
* @POWER_FLAGS_UAPSD_MISBEHAVING_ENA_MSK: AP/GO's uAPSD misbehaving
* detection enablement
+ * @POWER_FLAGS_ENABLE_SMPS_MSK: SMPS is allowed for this vif
*/
enum iwl_power_flags {
POWER_FLAGS_POWER_SAVE_ENA_MSK = BIT(0),
@@ -99,6 +102,7 @@ enum iwl_power_flags {
POWER_FLAGS_ADVANCE_PM_ENA_MSK = BIT(9),
POWER_FLAGS_LPRX_ENA_MSK = BIT(11),
POWER_FLAGS_UAPSD_MISBEHAVING_ENA_MSK = BIT(12),
+ POWER_FLAGS_ENABLE_SMPS_MSK = BIT(14),
};
#define IWL_POWER_VEC_SIZE 5
@@ -216,7 +220,6 @@ struct iwl_mac_power_cmd {
/* CONTEXT_DESC_API_T_VER_1 */
__le32 id_and_color;
- /* CLIENT_PM_POWER_TABLE_S_VER_1 */
__le16 flags;
__le16 keep_alive_seconds;
__le32 rx_data_timeout;
@@ -237,7 +240,7 @@ struct iwl_mac_power_cmd {
u8 heavy_rx_thld_percentage;
u8 limited_ps_threshold;
u8 reserved;
-} __packed;
+} __packed; /* CLIENT_PM_POWER_TABLE_S_VER_1, VER_2 */
/*
* struct iwl_uapsd_misbehaving_ap_notif - FW sends this notification when
@@ -628,28 +631,37 @@ enum iwl_ppag_flags {
/**
* union iwl_ppag_table_cmd - union for all versions of PPAG command
- * @v1: version 1
- * @v2: version 2
- * version 3, 4, 5 and 6 are the same structure as v2,
+ * @v1: command version 1 structure.
+ * @v2: command version from 2 to 6 are same structure as v2.
* but has a different format of the flags bitmap
+ * @v3: command version 7 structure.
* @v1.flags: values from &enum iwl_ppag_flags
* @v1.gain: table of antenna gain values per chain and sub-band
* @v1.reserved: reserved
* @v2.flags: values from &enum iwl_ppag_flags
* @v2.gain: table of antenna gain values per chain and sub-band
- * @v2.reserved: reserved
+ * @v3.ppag_config_info: see @struct bios_value_u32
+ * @v3.gain: table of antenna gain values per chain and sub-band
+ * @v3.reserved: reserved
*/
union iwl_ppag_table_cmd {
struct {
__le32 flags;
s8 gain[IWL_NUM_CHAIN_LIMITS][IWL_NUM_SUB_BANDS_V1];
s8 reserved[2];
- } v1;
+ } __packed v1; /* PER_PLAT_ANTENNA_GAIN_CMD_API_S_VER_1 */
struct {
__le32 flags;
s8 gain[IWL_NUM_CHAIN_LIMITS][IWL_NUM_SUB_BANDS_V2];
s8 reserved[2];
- } v2;
+ } __packed v2; /* PER_PLAT_ANTENNA_GAIN_CMD_API_S_VER_2, VER3, VER4,
+ * VER5, VER6
+ */
+ struct {
+ struct bios_value_u32 ppag_config_info;
+ s8 gain[IWL_NUM_CHAIN_LIMITS][IWL_NUM_SUB_BANDS_V2];
+ s8 reserved[2];
+ } __packed v3; /* PER_PLAT_ANTENNA_GAIN_CMD_API_S_VER_7 */
} __packed;
#define IWL_PPAG_CMD_V4_MASK (IWL_PPAG_ETSI_MASK | IWL_PPAG_CHINA_MASK)
@@ -657,6 +669,15 @@ union iwl_ppag_table_cmd {
IWL_PPAG_ETSI_LPI_UHB_MASK | \
IWL_PPAG_USA_LPI_UHB_MASK)
+#define IWL_PPAG_CMD_V6_MASK (IWL_PPAG_CMD_V5_MASK | \
+ IWL_PPAG_ETSI_VLP_UHB_MASK | \
+ IWL_PPAG_ETSI_SP_UHB_MASK | \
+ IWL_PPAG_USA_VLP_UHB_MASK | \
+ IWL_PPAG_USA_SP_UHB_MASK | \
+ IWL_PPAG_CANADA_LPI_UHB_MASK | \
+ IWL_PPAG_CANADA_VLP_UHB_MASK | \
+ IWL_PPAG_CANADA_SP_UHB_MASK)
+
#define MCC_TO_SAR_OFFSET_TABLE_ROW_SIZE 26
#define MCC_TO_SAR_OFFSET_TABLE_COL_SIZE 13
@@ -690,7 +711,7 @@ struct iwl_sar_offset_mapping_cmd {
* Roaming Energy Delta Threshold, otherwise use normal Energy Delta
* Threshold. Typical energy threshold is -72dBm.
* @bf_temp_threshold: This threshold determines the type of temperature
- * filtering (Slow or Fast) that is selected (Units are in Celsuis):
+ * filtering (Slow or Fast) that is selected (Units are in Celsius):
* If the current temperature is above this threshold - Fast filter
* will be used, If the current temperature is below this threshold -
* Slow filter will be used.
@@ -698,12 +719,12 @@ struct iwl_sar_offset_mapping_cmd {
* calculated for this and the last passed beacon is greater than this
* threshold. Zero value means that the temperature change is ignored for
* beacon filtering; beacons will not be forced to be sent to driver
- * regardless of whether its temerature has been changed.
+ * regardless of whether its temperature has been changed.
* @bf_temp_slow_filter: Send Beacon to driver if delta in temperature values
* calculated for this and the last passed beacon is greater than this
* threshold. Zero value means that the temperature change is ignored for
* beacon filtering; beacons will not be forced to be sent to driver
- * regardless of whether its temerature has been changed.
+ * regardless of whether its temperature has been changed.
* @bf_enable_beacon_filter: 1, beacon filtering is enabled; 0, disabled.
* @bf_debug_flag: beacon filtering debug configuration
* @bf_escape_timer: Send beacons to to driver if no beacons were passed
diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/rs.h b/drivers/net/wireless/intel/iwlwifi/fw/api/rs.h
index 1a60f0cdf972..3222cbcbe1ab 100644
--- a/drivers/net/wireless/intel/iwlwifi/fw/api/rs.h
+++ b/drivers/net/wireless/intel/iwlwifi/fw/api/rs.h
@@ -1,11 +1,13 @@
/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
/*
- * Copyright (C) 2012-2014, 2018-2022, 2024 Intel Corporation
+ * Copyright (C) 2012-2014, 2018-2022, 2024-2025 Intel Corporation
* Copyright (C) 2017 Intel Deutschland GmbH
*/
#ifndef __iwl_fw_api_rs_h__
#define __iwl_fw_api_rs_h__
-
+#include <linux/bitfield.h>
+#include <linux/types.h>
+#include <linux/bits.h>
#include "mac.h"
/**
@@ -213,7 +215,8 @@ enum iwl_tlc_update_flags {
* @sta_id: station id
* @reserved: reserved
* @flags: bitmap of notifications reported
- * @rate: current initial rate
+ * @rate: current initial rate, format depends on the notification
+ * version
* @amsdu_size: Max AMSDU size, in bytes
* @amsdu_enabled: bitmap for per-TID AMSDU enablement
*/
@@ -224,8 +227,60 @@ struct iwl_tlc_update_notif {
__le32 rate;
__le32 amsdu_size;
__le32 amsdu_enabled;
-} __packed; /* TLC_MNG_UPDATE_NTFY_API_S_VER_2 */
+} __packed; /* TLC_MNG_UPDATE_NTFY_API_S_VER_2, _VER_3, _VER_4 */
+/**
+ * enum iwl_tlc_debug_types - debug options
+ */
+enum iwl_tlc_debug_types {
+ /**
+ * @IWL_TLC_DEBUG_FIXED_RATE: set fixed rate for rate scaling
+ */
+ IWL_TLC_DEBUG_FIXED_RATE,
+ /**
+ * @IWL_TLC_DEBUG_AGG_DURATION_LIM: time limit for a BA
+ * session, in usec
+ */
+ IWL_TLC_DEBUG_AGG_DURATION_LIM,
+ /**
+ * @IWL_TLC_DEBUG_AGG_FRAME_CNT_LIM: set max number of frames
+ * in an aggregation
+ */
+ IWL_TLC_DEBUG_AGG_FRAME_CNT_LIM,
+ /**
+ * @IWL_TLC_DEBUG_TPC_ENABLED: enable or disable tpc
+ */
+ IWL_TLC_DEBUG_TPC_ENABLED,
+ /**
+ * @IWL_TLC_DEBUG_TPC_STATS: get number of frames Tx'ed in each
+ * tpc step
+ */
+ IWL_TLC_DEBUG_TPC_STATS,
+ /**
+ * @IWL_TLC_DEBUG_RTS_DISABLE: disable RTS (bool true/false).
+ */
+ IWL_TLC_DEBUG_RTS_DISABLE,
+ /**
+ * @IWL_TLC_DEBUG_PARTIAL_FIXED_RATE: set partial fixed rate to fw
+ */
+ IWL_TLC_DEBUG_PARTIAL_FIXED_RATE,
+}; /* TLC_MNG_DEBUG_TYPES_API_E */
+
+#define MAX_DATA_IN_DHC_TLC_CMD 10
+
+/**
+ * struct iwl_dhc_tlc_cmd - fixed debug config
+ * @sta_id: bit 0 - enable/disable, bits 1 - 7 hold station id
+ * @reserved1: reserved
+ * @type: type id of %enum iwl_tlc_debug_types
+ * @data: data to send
+ */
+struct iwl_dhc_tlc_cmd {
+ u8 sta_id;
+ u8 reserved1[3];
+ __le32 type;
+ __le32 data[MAX_DATA_IN_DHC_TLC_CMD];
+} __packed; /* TLC_MNG_DEBUG_CMD_S */
#define IWL_MAX_MCS_DISPLAY_SIZE 12
@@ -375,6 +430,7 @@ enum {
/* Bit 4-5: (0) SISO, (1) MIMO2 (2) MIMO3 */
#define RATE_VHT_MCS_RATE_CODE_MSK 0xf
+#define RATE_VHT_MCS_NSS_MSK 0x30
/*
* Legacy OFDM rate format for bits 7:0
@@ -489,7 +545,7 @@ enum {
#define RATE_MCS_CTS_REQUIRED_POS (31)
#define RATE_MCS_CTS_REQUIRED_MSK (0x1 << RATE_MCS_CTS_REQUIRED_POS)
-/* rate_n_flags bit field version 2
+/* rate_n_flags bit field version 2 and 3
*
* The 32-bit value has different layouts in the low 8 bits depending on the
* format. There are three formats, HT, VHT and legacy (11abg, with subformats
@@ -501,23 +557,25 @@ enum {
* (0) Legacy CCK (1) Legacy OFDM (2) High-throughput (HT)
* (3) Very High-throughput (VHT) (4) High-efficiency (HE)
* (5) Extremely High-throughput (EHT)
+ * (6) Ultra High Reliability (UHR) (v3 rate format only)
*/
#define RATE_MCS_MOD_TYPE_POS 8
#define RATE_MCS_MOD_TYPE_MSK (0x7 << RATE_MCS_MOD_TYPE_POS)
-#define RATE_MCS_CCK_MSK (0 << RATE_MCS_MOD_TYPE_POS)
-#define RATE_MCS_LEGACY_OFDM_MSK (1 << RATE_MCS_MOD_TYPE_POS)
-#define RATE_MCS_HT_MSK (2 << RATE_MCS_MOD_TYPE_POS)
-#define RATE_MCS_VHT_MSK (3 << RATE_MCS_MOD_TYPE_POS)
-#define RATE_MCS_HE_MSK (4 << RATE_MCS_MOD_TYPE_POS)
-#define RATE_MCS_EHT_MSK (5 << RATE_MCS_MOD_TYPE_POS)
+#define RATE_MCS_MOD_TYPE_CCK (0 << RATE_MCS_MOD_TYPE_POS)
+#define RATE_MCS_MOD_TYPE_LEGACY_OFDM (1 << RATE_MCS_MOD_TYPE_POS)
+#define RATE_MCS_MOD_TYPE_HT (2 << RATE_MCS_MOD_TYPE_POS)
+#define RATE_MCS_MOD_TYPE_VHT (3 << RATE_MCS_MOD_TYPE_POS)
+#define RATE_MCS_MOD_TYPE_HE (4 << RATE_MCS_MOD_TYPE_POS)
+#define RATE_MCS_MOD_TYPE_EHT (5 << RATE_MCS_MOD_TYPE_POS)
+#define RATE_MCS_MOD_TYPE_UHR (6 << RATE_MCS_MOD_TYPE_POS)
/*
* Legacy CCK rate format for bits 0:3:
*
- * (0) 0xa - 1 Mbps
- * (1) 0x14 - 2 Mbps
- * (2) 0x37 - 5.5 Mbps
- * (3) 0x6e - 11 nbps
+ * (0) 1 Mbps
+ * (1) 2 Mbps
+ * (2) 5.5 Mbps
+ * (3) 11 Mbps
*
* Legacy OFDM rate format for bis 3:0:
*
@@ -534,15 +592,19 @@ enum {
#define RATE_LEGACY_RATE_MSK 0x7
/*
- * HT, VHT, HE, EHT rate format for bits 3:0
- * 3-0: MCS
- *
+ * HT, VHT, HE, EHT, UHR rate format
+ * Version 2: (not applicable for UHR)
+ * 3-0: MCS
+ * 4: NSS==2 indicator
+ * Version 3:
+ * 4-0: MCS
+ * 5: NSS==2 indicator
*/
#define RATE_HT_MCS_CODE_MSK 0x7
-#define RATE_MCS_NSS_POS 4
-#define RATE_MCS_NSS_MSK (1 << RATE_MCS_NSS_POS)
-#define RATE_MCS_CODE_MSK 0xf
-#define RATE_HT_MCS_INDEX(r) ((((r) & RATE_MCS_NSS_MSK) >> 1) | \
+#define RATE_MCS_NSS_MSK_V2 0x10
+#define RATE_MCS_NSS_MSK 0x20
+#define RATE_MCS_CODE_MSK 0x1f
+#define RATE_HT_MCS_INDEX(r) ((((r) & RATE_MCS_NSS_MSK) >> 2) | \
((r) & RATE_HT_MCS_CODE_MSK))
/* Bits 7-5: reserved */
@@ -758,11 +820,38 @@ struct iwl_lq_cmd {
}; /* LINK_QUALITY_CMD_API_S_VER_1 */
u8 iwl_fw_rate_idx_to_plcp(int idx);
-u32 iwl_new_rate_from_v1(u32 rate_v1);
const struct iwl_rate_mcs_info *iwl_rate_mcs(int idx);
const char *iwl_rs_pretty_ant(u8 ant);
const char *iwl_rs_pretty_bw(int bw);
int rs_pretty_print_rate(char *buf, int bufsz, const u32 rate);
bool iwl_he_is_sgi(u32 rate_n_flags);
+static inline u32 iwl_v3_rate_from_v2_v3(__le32 rate, bool fw_v3)
+{
+ u32 val;
+
+ if (fw_v3)
+ return le32_to_cpu(rate);
+
+ val = le32_to_cpu(rate) & ~RATE_MCS_NSS_MSK_V2;
+ val |= u32_encode_bits(le32_get_bits(rate, RATE_MCS_NSS_MSK_V2),
+ RATE_MCS_NSS_MSK);
+
+ return val;
+}
+
+static inline __le32 iwl_v3_rate_to_v2_v3(u32 rate, bool fw_v3)
+{
+ __le32 val;
+
+ if (fw_v3)
+ return cpu_to_le32(rate);
+
+ val = cpu_to_le32(rate & ~RATE_MCS_NSS_MSK);
+ val |= le32_encode_bits(u32_get_bits(rate, RATE_MCS_NSS_MSK),
+ RATE_MCS_NSS_MSK_V2);
+
+ return val;
+}
+
#endif /* __iwl_fw_api_rs_h__ */
diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/rx.h b/drivers/net/wireless/intel/iwlwifi/fw/api/rx.h
index 691c879cb90d..7cf6d6ac7430 100644
--- a/drivers/net/wireless/intel/iwlwifi/fw/api/rx.h
+++ b/drivers/net/wireless/intel/iwlwifi/fw/api/rx.h
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
/*
- * Copyright (C) 2012-2014, 2018-2024 Intel Corporation
+ * Copyright (C) 2012-2014, 2018-2025 Intel Corporation
* Copyright (C) 2013-2015 Intel Mobile Communications GmbH
* Copyright (C) 2015-2017 Intel Deutschland GmbH
*/
@@ -193,10 +193,11 @@ enum iwl_rx_mpdu_amsdu_info {
IWL_RX_MPDU_AMSDU_LAST_SUBFRAME = 0x80,
};
-#define RX_MPDU_BAND_POS 6
-#define RX_MPDU_BAND_MASK 0xC0
-#define BAND_IN_RX_STATUS(_val) \
- (((_val) & RX_MPDU_BAND_MASK) >> RX_MPDU_BAND_POS)
+enum iwl_rx_mpdu_mac_phy_band {
+ IWL_RX_MPDU_MAC_PHY_BAND_MAC_MASK = 0x0f,
+ IWL_RX_MPDU_MAC_PHY_BAND_PHY_MASK = 0x30,
+ IWL_RX_MPDU_MAC_PHY_BAND_BAND_MASK = 0xc0,
+};
enum iwl_rx_l3_proto_values {
IWL_RX_L3_TYPE_NONE,
@@ -639,7 +640,9 @@ struct iwl_rx_mpdu_desc_v3 {
*/
__le32 reserved[1];
} __packed; /* RX_MPDU_RES_START_API_S_VER_3,
- RX_MPDU_RES_START_API_S_VER_5 */
+ * RX_MPDU_RES_START_API_S_VER_5,
+ * RX_MPDU_RES_START_API_S_VER_6
+ */
/**
* struct iwl_rx_mpdu_desc - RX MPDU descriptor
@@ -668,9 +671,10 @@ struct iwl_rx_mpdu_desc {
*/
__le16 phy_info;
/**
- * @mac_phy_idx: MAC/PHY index
+ * @mac_phy_band: MAC ID, PHY ID, band;
+ * see &enum iwl_rx_mpdu_mac_phy_band
*/
- u8 mac_phy_idx;
+ u8 mac_phy_band;
/* DW4 */
union {
struct {
@@ -722,8 +726,10 @@ struct iwl_rx_mpdu_desc {
struct iwl_rx_mpdu_desc_v3 v3;
};
} __packed; /* RX_MPDU_RES_START_API_S_VER_3,
- RX_MPDU_RES_START_API_S_VER_4,
- RX_MPDU_RES_START_API_S_VER_5 */
+ * RX_MPDU_RES_START_API_S_VER_4,
+ * RX_MPDU_RES_START_API_S_VER_5,
+ * RX_MPDU_RES_START_API_S_VER_6
+ */
#define IWL_RX_DESC_SIZE_V1 offsetofend(struct iwl_rx_mpdu_desc, v1)
@@ -819,7 +825,7 @@ struct iwl_rx_no_data {
* 15:8 chain-B, measured at FINA time (FINA_ENERGY), 16:23 channel
* @on_air_rise_time: GP2 during on air rise
* @fr_time: frame time
- * @rate: rate/mcs of frame
+ * @rate: rate/mcs of frame, format depends on the notification version
* @phy_info: &enum iwl_rx_phy_eht_data0 and &enum iwl_rx_phy_info_type
* @rx_vec: DW-12:9 raw RX vectors from DSP according to modulation type.
* for VHT: OFDM_RX_VECTOR_SIGA1_OUT, OFDM_RX_VECTOR_SIGA2_OUT
@@ -835,9 +841,7 @@ struct iwl_rx_no_data_ver_3 {
__le32 rate;
__le32 phy_info[2];
__le32 rx_vec[4];
-} __packed; /* RX_NO_DATA_NTFY_API_S_VER_1,
- RX_NO_DATA_NTFY_API_S_VER_2
- RX_NO_DATA_NTFY_API_S_VER_3 */
+} __packed; /* RX_NO_DATA_NTFY_API_S_VER_3, _VER_4 */
struct iwl_frame_release {
u8 baid;
diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/sta.h b/drivers/net/wireless/intel/iwlwifi/fw/api/sta.h
index d7f8a276b683..ecbcd5084cd8 100644
--- a/drivers/net/wireless/intel/iwlwifi/fw/api/sta.h
+++ b/drivers/net/wireless/intel/iwlwifi/fw/api/sta.h
@@ -191,6 +191,7 @@ enum iwl_sta_sleep_flag {
#define STA_KEY_IDX_INVALID (0xff)
#define STA_KEY_MAX_DATA_KEY_NUM (4)
#define IWL_MAX_GLOBAL_KEYS (4)
+#define IWL_MAX_NUM_IGTKS 2
#define STA_KEY_LEN_WEP40 (5)
#define STA_KEY_LEN_WEP104 (13)
diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/stats.h b/drivers/net/wireless/intel/iwlwifi/fw/api/stats.h
index 0a9f14fb04be..00713a991879 100644
--- a/drivers/net/wireless/intel/iwlwifi/fw/api/stats.h
+++ b/drivers/net/wireless/intel/iwlwifi/fw/api/stats.h
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
/*
- * Copyright (C) 2012-2014, 2018, 2020 - 2021, 2023 - 2024 Intel Corporation
+ * Copyright (C) 2012-2014, 2018, 2020-2021, 2023-2025 Intel Corporation
* Copyright (C) 2013-2015 Intel Mobile Communications GmbH
* Copyright (C) 2016-2017 Intel Deutschland GmbH
*/
@@ -584,6 +584,9 @@ struct iwl_stats_ntfy_per_phy {
__le32 last_tx_ch_width_indx;
} __packed; /* STATISTICS_NTFY_PER_PHY_API_S_VER_1 */
+/* unknown channel load (due to not being active on channel) */
+#define IWL_STATS_UNKNOWN_CHANNEL_LOAD 0xffffffff
+
/**
* struct iwl_stats_ntfy_per_sta
*
diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/tdls.h b/drivers/net/wireless/intel/iwlwifi/fw/api/tdls.h
index cfa6532a3cdd..58d5a6ef633e 100644
--- a/drivers/net/wireless/intel/iwlwifi/fw/api/tdls.h
+++ b/drivers/net/wireless/intel/iwlwifi/fw/api/tdls.h
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
/*
- * Copyright (C) 2012-2014, 2018, 2024 Intel Corporation
+ * Copyright (C) 2012-2014, 2018, 2024-2025 Intel Corporation
* Copyright (C) 2013-2015 Intel Mobile Communications GmbH
* Copyright (C) 2016-2017 Intel Deutschland GmbH
*/
@@ -50,7 +50,7 @@ struct iwl_tdls_channel_switch_timing {
*/
struct iwl_tdls_channel_switch_frame {
__le32 switch_time_offset;
- struct iwl_tx_cmd tx_cmd;
+ struct iwl_tx_cmd_v6 tx_cmd;
u8 data[IWL_TDLS_CH_SW_FRAME_MAX_SIZE];
} __packed; /* TDLS_STA_CHANNEL_SWITCH_FRAME_API_S_VER_1 */
@@ -131,7 +131,7 @@ struct iwl_tdls_config_cmd {
struct iwl_tdls_sta_info sta_info[IWL_TDLS_STA_COUNT];
__le32 pti_req_data_offset;
- struct iwl_tx_cmd pti_req_tx_cmd;
+ struct iwl_tx_cmd_v6 pti_req_tx_cmd;
u8 pti_req_template[];
} __packed; /* TDLS_CONFIG_CMD_API_S_VER_1 */
diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/time-event.h b/drivers/net/wireless/intel/iwlwifi/fw/api/time-event.h
index 18d030334a6a..f586379d66dd 100644
--- a/drivers/net/wireless/intel/iwlwifi/fw/api/time-event.h
+++ b/drivers/net/wireless/intel/iwlwifi/fw/api/time-event.h
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
/*
- * Copyright (C) 2012-2014, 2018-2020, 2022-2024 Intel Corporation
+ * Copyright (C) 2012-2014, 2018-2020, 2022-2025 Intel Corporation
* Copyright (C) 2013-2015 Intel Mobile Communications GmbH
* Copyright (C) 2016-2017 Intel Deutschland GmbH
*/
@@ -351,7 +351,7 @@ enum iwl_roc_activity {
}; /* ROC_ACTIVITY_API_E_VER_1 */
/*
- * ROC command
+ * ROC command v5
*
* Command requests the firmware to remain on a channel for a certain duration.
*
@@ -366,7 +366,7 @@ enum iwl_roc_activity {
* @max_delay: max delay the ROC can start in TU
* @duration: remain on channel duration in TU
*/
-struct iwl_roc_req {
+struct iwl_roc_req_v5 {
__le32 action;
__le32 activity;
__le32 sta_id;
@@ -375,7 +375,41 @@ struct iwl_roc_req {
__le16 reserved;
__le32 max_delay;
__le32 duration;
-} __packed; /* ROC_CMD_API_S_VER_3 */
+} __packed; /* ROC_CMD_API_S_VER_5 */
+
+/*
+ * ROC command
+ *
+ * Command requests the firmware to remain on a channel for a certain duration.
+ *
+ * ( MAC_CONF_GROUP 0x3, ROC_CMD 0xE )
+ *
+ * @action: action to perform, see &enum iwl_ctxt_action
+ * @activity: type of activity, see &enum iwl_roc_activity
+ * @sta_id: station id, resumed during "Remain On Channel" activity.
+ * @channel_info: &struct iwl_fw_channel_info
+ * @node_addr: node MAC address for Rx filtering
+ * @reserved1: align to a dword
+ * @max_delay: max delay the ROC can start in TU
+ * @duration: remain on channel duration in TU
+ * @interval: interval between repetitions (when repetitions > 1).
+ * @repetitions: number of repetitions
+ * 0xFF: infinite repetitions. 0 or 1: single repetition.
+ * @reserved2: align to a dword
+ */
+struct iwl_roc_req {
+ __le32 action;
+ __le32 activity;
+ __le32 sta_id;
+ struct iwl_fw_channel_info channel_info;
+ u8 node_addr[ETH_ALEN];
+ __le16 reserved1;
+ __le32 max_delay;
+ __le32 duration;
+ __le32 interval;
+ u8 repetitions;
+ u8 reserved2[3];
+} __packed; /* ROC_CMD_API_S_VER_6 */
/*
* ROC notification
diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/tx.h b/drivers/net/wireless/intel/iwlwifi/fw/api/tx.h
index 0a39e4b6eb62..557832563f89 100644
--- a/drivers/net/wireless/intel/iwlwifi/fw/api/tx.h
+++ b/drivers/net/wireless/intel/iwlwifi/fw/api/tx.h
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
/*
- * Copyright (C) 2012-2014, 2018-2024 Intel Corporation
+ * Copyright (C) 2012-2014, 2018-2025 Intel Corporation
* Copyright (C) 2016-2017 Intel Deutschland GmbH
*/
#ifndef __iwl_fw_api_tx_h__
@@ -151,7 +151,7 @@ enum iwl_tx_cmd_sec_ctrl {
#define IWL_LOW_RETRY_LIMIT 7
/**
- * enum iwl_tx_offload_assist_flags_pos - set %iwl_tx_cmd offload_assist values
+ * enum iwl_tx_offload_assist_flags_pos - set %iwl_tx_cmd_v6 offload_assist values
* @TX_CMD_OFFLD_IP_HDR: offset to start of IP header (in words)
* from mac header end. For normal case it is 4 words for SNAP.
* note: tx_cmd, mac header and pad are not counted in the offset.
@@ -181,7 +181,7 @@ enum iwl_tx_offload_assist_flags_pos {
/* TODO: complete documentation for try_cnt and btkill_cnt */
/**
- * struct iwl_tx_cmd - TX command struct to FW
+ * struct iwl_tx_cmd_v6 - TX command struct to FW
* ( TX_CMD = 0x1c )
* @len: in bytes of the payload, see below for details
* @offload_assist: TX offload configuration
@@ -221,7 +221,7 @@ enum iwl_tx_offload_assist_flags_pos {
* After the struct fields the MAC header is placed, plus any padding,
* and then the actial payload.
*/
-struct iwl_tx_cmd {
+struct iwl_tx_cmd_v6 {
__le16 len;
__le16 offload_assist;
__le32 tx_flags;
@@ -258,7 +258,7 @@ struct iwl_dram_sec_info {
} __packed; /* DRAM_SEC_INFO_API_S_VER_1 */
/**
- * struct iwl_tx_cmd_gen2 - TX command struct to FW for 22000 devices
+ * struct iwl_tx_cmd_v9 - TX command struct to FW for 22000 devices
* ( TX_CMD = 0x1c )
* @len: in bytes of the payload, see below for details
* @offload_assist: TX offload configuration
@@ -268,7 +268,7 @@ struct iwl_dram_sec_info {
* cleared. Combination of RATE_MCS_*
* @hdr: 802.11 header
*/
-struct iwl_tx_cmd_gen2 {
+struct iwl_tx_cmd_v9 {
__le16 len;
__le16 offload_assist;
__le32 flags;
@@ -279,18 +279,18 @@ struct iwl_tx_cmd_gen2 {
TX_CMD_API_S_VER_9 */
/**
- * struct iwl_tx_cmd_gen3 - TX command struct to FW for AX210+ devices
+ * struct iwl_tx_cmd - TX command struct to FW for AX210+ devices
* ( TX_CMD = 0x1c )
* @len: in bytes of the payload, see below for details
* @flags: combination of &enum iwl_tx_cmd_flags
* @offload_assist: TX offload configuration
* @dram_info: FW internal DRAM storage
* @rate_n_flags: rate for *all* Tx attempts, if TX_CMD_FLG_STA_RATE_MSK is
- * cleared. Combination of RATE_MCS_*
+ * cleared. Combination of RATE_MCS_*; format depends on version
* @reserved: reserved
* @hdr: 802.11 header
*/
-struct iwl_tx_cmd_gen3 {
+struct iwl_tx_cmd {
__le16 len;
__le16 flags;
__le32 offload_assist;
@@ -298,7 +298,9 @@ struct iwl_tx_cmd_gen3 {
__le32 rate_n_flags;
u8 reserved[8];
struct ieee80211_hdr hdr[];
-} __packed; /* TX_CMD_API_S_VER_8, TX_CMD_API_S_VER_10 */
+} __packed; /* TX_CMD_API_S_VER_10,
+ * TX_CMD_API_S_VER_11
+ */
/*
* TX response related data
@@ -549,7 +551,7 @@ struct iwl_tx_resp_v3 {
* @failure_rts: num of failures due to unsuccessful RTS
* @failure_frame: num failures due to no ACK (unused for agg)
* @initial_rate: for non-agg: rate of the successful Tx. For agg: rate of the
- * Tx of all the batch. RATE_MCS_*
+ * Tx of all the batch. RATE_MCS_*; format depends on command version
* @wireless_media_time: for non-agg: RTS + CTS + frame tx attempts time + ACK.
* for agg: RTS + CTS + aggregation tx time + block-ack time.
* in usec.
@@ -600,8 +602,10 @@ struct iwl_tx_resp {
__le16 reserved2;
struct agg_tx_status status;
} __packed; /* TX_RSP_API_S_VER_6,
- TX_RSP_API_S_VER_7,
- TX_RSP_API_S_VER_8 */
+ * TX_RSP_API_S_VER_7,
+ * TX_RSP_API_S_VER_8,
+ * TX_RSP_API_S_VER_9
+ */
/**
* struct iwl_mvm_ba_notif - notifies about reception of BA
@@ -701,7 +705,8 @@ enum iwl_mvm_ba_resp_flags {
* @rts_retry_cnt: RTS retry count
* @reserved: reserved (for alignment)
* @wireless_time: Wireless-media time
- * @tx_rate: the rate the aggregation was sent at
+ * @tx_rate: the rate the aggregation was sent at. Format depends on command
+ * version.
* @tfd_cnt: number of TFD-Q elements
* @ra_tid_cnt: number of RATID-Q elements
* @tfd: array of TFD queue status updates. See &iwl_compressed_ba_tfd
@@ -730,7 +735,8 @@ struct iwl_compressed_ba_notif {
DECLARE_FLEX_ARRAY(struct iwl_compressed_ba_tfd, tfd);
};
} __packed; /* COMPRESSED_BA_RES_API_S_VER_4,
- COMPRESSED_BA_RES_API_S_VER_5 */
+ COMPRESSED_BA_RES_API_S_VER_6,
+ COMPRESSED_BA_RES_API_S_VER_7 */
/**
* struct iwl_mac_beacon_cmd_v6 - beacon template command
@@ -742,7 +748,7 @@ struct iwl_compressed_ba_notif {
* @frame: the template of the beacon frame
*/
struct iwl_mac_beacon_cmd_v6 {
- struct iwl_tx_cmd tx;
+ struct iwl_tx_cmd_v6 tx;
__le32 template_id;
__le32 tim_idx;
__le32 tim_size;
@@ -761,7 +767,7 @@ struct iwl_mac_beacon_cmd_v6 {
* @frame: the template of the beacon frame
*/
struct iwl_mac_beacon_cmd_v7 {
- struct iwl_tx_cmd tx;
+ struct iwl_tx_cmd_v6 tx;
__le32 template_id;
__le32 tim_idx;
__le32 tim_size;
diff --git a/drivers/net/wireless/intel/iwlwifi/fw/dbg.c b/drivers/net/wireless/intel/iwlwifi/fw/dbg.c
index fb2ea38e89ac..ea739ebe7cb0 100644
--- a/drivers/net/wireless/intel/iwlwifi/fw/dbg.c
+++ b/drivers/net/wireless/intel/iwlwifi/fw/dbg.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
/*
- * Copyright (C) 2005-2014, 2018-2024 Intel Corporation
+ * Copyright (C) 2005-2014, 2018-2025 Intel Corporation
* Copyright (C) 2013-2015 Intel Mobile Communications GmbH
* Copyright (C) 2015-2017 Intel Deutschland GmbH
*/
@@ -187,7 +187,7 @@ static void iwl_fw_dump_rxf(struct iwl_fw_runtime *fwrt,
/* Pull RXF2 */
iwl_fwrt_dump_rxf(fwrt, dump_data, cfg->rxfifo2_size,
RXF_DIFF_FROM_PREV +
- fwrt->trans->trans_cfg->umac_prph_offset, 1);
+ fwrt->trans->mac_cfg->umac_prph_offset, 1);
/* Pull LMAC2 RXF1 */
if (fwrt->smem_cfg.num_lmacs > 1)
iwl_fwrt_dump_rxf(fwrt, dump_data,
@@ -558,41 +558,71 @@ static void iwl_dump_prph(struct iwl_fw_runtime *fwrt,
}
/*
- * alloc_sgtable - allocates scallerlist table in the given size,
- * fills it with pages and returns it
+ * alloc_sgtable - allocates (chained) scatterlist in the given size,
+ * fills it with pages and returns it
* @size: the size (in bytes) of the table
-*/
-static struct scatterlist *alloc_sgtable(int size)
+ */
+static struct scatterlist *alloc_sgtable(ssize_t size)
{
- int alloc_size, nents, i;
- struct page *new_page;
- struct scatterlist *iter;
- struct scatterlist *table;
+ struct scatterlist *result = NULL, *prev;
+ int nents, i, n_prev;
nents = DIV_ROUND_UP(size, PAGE_SIZE);
- table = kcalloc(nents, sizeof(*table), GFP_KERNEL);
- if (!table)
- return NULL;
- sg_init_table(table, nents);
- iter = table;
- for_each_sg(table, iter, sg_nents(table), i) {
- new_page = alloc_page(GFP_KERNEL);
- if (!new_page) {
- /* release all previous allocated pages in the table */
- iter = table;
- for_each_sg(table, iter, sg_nents(table), i) {
- new_page = sg_page(iter);
- if (new_page)
- __free_page(new_page);
- }
- kfree(table);
+
+#define N_ENTRIES_PER_PAGE (PAGE_SIZE / sizeof(*result))
+ /*
+ * We need an additional entry for table chaining,
+ * this ensures the loop can finish i.e. we can
+ * fit at least two entries per page (obviously,
+ * many more really fit.)
+ */
+ BUILD_BUG_ON(N_ENTRIES_PER_PAGE < 2);
+
+ while (nents > 0) {
+ struct scatterlist *new, *iter;
+ int n_fill, n_alloc;
+
+ if (nents <= N_ENTRIES_PER_PAGE) {
+ /* last needed table */
+ n_fill = nents;
+ n_alloc = nents;
+ nents = 0;
+ } else {
+ /* fill a page with entries */
+ n_alloc = N_ENTRIES_PER_PAGE;
+ /* reserve one for chaining */
+ n_fill = n_alloc - 1;
+ nents -= n_fill;
+ }
+
+ new = kcalloc(n_alloc, sizeof(*new), GFP_KERNEL);
+ if (!new) {
+ if (result)
+ _devcd_free_sgtable(result);
return NULL;
}
- alloc_size = min_t(int, size, PAGE_SIZE);
- size -= PAGE_SIZE;
- sg_set_page(iter, new_page, alloc_size, 0);
+ sg_init_table(new, n_alloc);
+
+ if (!result)
+ result = new;
+ else
+ sg_chain(prev, n_prev, new);
+ prev = new;
+ n_prev = n_alloc;
+
+ for_each_sg(new, iter, n_fill, i) {
+ struct page *new_page = alloc_page(GFP_KERNEL);
+
+ if (!new_page) {
+ _devcd_free_sgtable(result);
+ return NULL;
+ }
+
+ sg_set_page(iter, new_page, PAGE_SIZE, 0);
+ }
}
- return table;
+
+ return result;
}
static void iwl_fw_get_prph_len(struct iwl_fw_runtime *fwrt,
@@ -624,10 +654,10 @@ static void iwl_fw_prph_handler(struct iwl_fw_runtime *fwrt, void *ptr,
{
u32 range_len;
- if (fwrt->trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_AX210) {
+ if (fwrt->trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_AX210) {
range_len = ARRAY_SIZE(iwl_prph_dump_addr_ax210);
handler(fwrt, iwl_prph_dump_addr_ax210, range_len, ptr);
- } else if (fwrt->trans->trans_cfg->device_family >=
+ } else if (fwrt->trans->mac_cfg->device_family >=
IWL_DEVICE_FAMILY_22000) {
range_len = ARRAY_SIZE(iwl_prph_dump_addr_22000);
handler(fwrt, iwl_prph_dump_addr_22000, range_len, ptr);
@@ -635,7 +665,7 @@ static void iwl_fw_prph_handler(struct iwl_fw_runtime *fwrt, void *ptr,
range_len = ARRAY_SIZE(iwl_prph_dump_addr_comm);
handler(fwrt, iwl_prph_dump_addr_comm, range_len, ptr);
- if (fwrt->trans->trans_cfg->mq_rx_supported) {
+ if (fwrt->trans->mac_cfg->mq_rx_supported) {
range_len = ARRAY_SIZE(iwl_prph_dump_addr_9000);
handler(fwrt, iwl_prph_dump_addr_9000, range_len, ptr);
}
@@ -779,13 +809,14 @@ iwl_fw_error_dump_file(struct iwl_fw_runtime *fwrt,
const struct iwl_fw_dbg_mem_seg_tlv *fw_mem = fwrt->fw->dbg.mem_tlv;
struct iwl_fwrt_shared_mem_cfg *mem_cfg = &fwrt->smem_cfg;
u32 file_len, fifo_len = 0, prph_len = 0, radio_len = 0;
- u32 smem_len = fwrt->fw->dbg.n_mem_tlv ? 0 : fwrt->trans->cfg->smem_len;
+ u32 smem_len = fwrt->fw->dbg.n_mem_tlv ? 0 : fwrt->trans->mac_cfg->base->smem_len;
u32 sram2_len = fwrt->fw->dbg.n_mem_tlv ?
0 : fwrt->trans->cfg->dccm2_len;
int i;
/* SRAM - include stack CCM if driver knows the values for it */
- if (!fwrt->trans->cfg->dccm_offset || !fwrt->trans->cfg->dccm_len) {
+ if (!fwrt->trans->cfg->dccm_offset ||
+ !fwrt->trans->cfg->dccm_len) {
const struct fw_img *img;
if (fwrt->cur_fw_img >= IWL_UCODE_TYPE_MAX)
@@ -808,7 +839,7 @@ iwl_fw_error_dump_file(struct iwl_fw_runtime *fwrt,
iwl_fw_prph_handler(fwrt, &prph_len,
iwl_fw_get_prph_len);
- if (fwrt->trans->trans_cfg->device_family ==
+ if (fwrt->trans->mac_cfg->device_family ==
IWL_DEVICE_FAMILY_7000 &&
iwl_fw_dbg_type_on(fwrt, IWL_FW_ERROR_DUMP_RADIO_REG))
radio_len = sizeof(*dump_data) + RADIO_REG_MAX_READ;
@@ -846,7 +877,7 @@ iwl_fw_error_dump_file(struct iwl_fw_runtime *fwrt,
if (iwl_fw_dbg_is_d3_debug_enabled(fwrt) && fwrt->dump.d3_debug_data) {
file_len += sizeof(*dump_data) +
- fwrt->trans->cfg->d3_debug_data_length * 2;
+ fwrt->trans->mac_cfg->base->d3_debug_data_length * 2;
}
/* If we only want a monitor dump, reset the file length */
@@ -874,13 +905,14 @@ iwl_fw_error_dump_file(struct iwl_fw_runtime *fwrt,
dump_data->len = cpu_to_le32(sizeof(*dump_info));
dump_info = (void *)dump_data->data;
dump_info->hw_type =
- cpu_to_le32(CSR_HW_REV_TYPE(fwrt->trans->hw_rev));
+ cpu_to_le32(CSR_HW_REV_TYPE(fwrt->trans->info.hw_rev));
dump_info->hw_step =
- cpu_to_le32(fwrt->trans->hw_rev_step);
+ cpu_to_le32(fwrt->trans->info.hw_rev_step);
memcpy(dump_info->fw_human_readable, fwrt->fw->human_readable,
sizeof(dump_info->fw_human_readable));
- strscpy_pad(dump_info->dev_human_readable, fwrt->trans->name,
- sizeof(dump_info->dev_human_readable));
+ strscpy_pad(dump_info->dev_human_readable,
+ fwrt->trans->info.name,
+ sizeof(dump_info->dev_human_readable));
strscpy_pad(dump_info->bus_human_readable, fwrt->dev->bus->name,
sizeof(dump_info->bus_human_readable));
dump_info->num_of_lmacs = fwrt->smem_cfg.num_lmacs;
@@ -966,7 +998,7 @@ iwl_fw_error_dump_file(struct iwl_fw_runtime *fwrt,
}
iwl_fw_dump_mem(fwrt, &dump_data, smem_len,
- fwrt->trans->cfg->smem_offset,
+ fwrt->trans->mac_cfg->base->smem_offset,
IWL_FW_ERROR_DUMP_MEM_SMEM);
iwl_fw_dump_mem(fwrt, &dump_data, sram2_len,
@@ -975,8 +1007,8 @@ iwl_fw_error_dump_file(struct iwl_fw_runtime *fwrt,
}
if (iwl_fw_dbg_is_d3_debug_enabled(fwrt) && fwrt->dump.d3_debug_data) {
- u32 addr = fwrt->trans->cfg->d3_debug_data_base_addr;
- size_t data_size = fwrt->trans->cfg->d3_debug_data_length;
+ u32 addr = fwrt->trans->mac_cfg->base->d3_debug_data_base_addr;
+ size_t data_size = fwrt->trans->mac_cfg->base->d3_debug_data_length;
dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_D3_DEBUG_DATA);
dump_data->len = cpu_to_le32(data_size * 2);
@@ -1079,7 +1111,7 @@ static int iwl_dump_ini_prph_phy_iter_common(struct iwl_fw_runtime *fwrt,
range->internal_base_addr = cpu_to_le32(addr);
range->range_data_size = size;
- if (fwrt->trans->trans_cfg->device_family < IWL_DEVICE_FAMILY_AX210)
+ if (fwrt->trans->mac_cfg->device_family < IWL_DEVICE_FAMILY_AX210)
indirect_wr_addr = WMAL_INDRCT_CMD1;
indirect_wr_addr += le32_to_cpu(offset);
@@ -1236,7 +1268,7 @@ static int iwl_dump_ini_paging_iter(struct iwl_fw_runtime *fwrt,
/* all paged index start from 1 to skip CSS section */
idx++;
- if (!fwrt->trans->trans_cfg->gen2)
+ if (!fwrt->trans->mac_cfg->gen2)
return _iwl_dump_ini_paging_iter(fwrt, range_ptr, range_len, idx);
range = range_ptr;
@@ -1760,7 +1792,7 @@ iwl_dump_ini_mon_fill_header(struct iwl_fw_runtime *fwrt, u32 alloc_id,
data->write_ptr = iwl_get_mon_reg(fwrt, alloc_id,
&addrs->write_ptr);
- if (fwrt->trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_AX210) {
+ if (fwrt->trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_AX210) {
u32 wrt_ptr = le32_to_cpu(data->write_ptr);
data->write_ptr = cpu_to_le32(wrt_ptr >> 2);
@@ -1787,7 +1819,7 @@ iwl_dump_ini_mon_dram_fill_header(struct iwl_fw_runtime *fwrt,
u32 alloc_id = le32_to_cpu(reg->dram_alloc_id);
return iwl_dump_ini_mon_fill_header(fwrt, alloc_id, mon_dump,
- &fwrt->trans->cfg->mon_dram_regs);
+ &fwrt->trans->mac_cfg->base->mon_dram_regs);
}
static void *
@@ -1800,7 +1832,7 @@ iwl_dump_ini_mon_smem_fill_header(struct iwl_fw_runtime *fwrt,
u32 alloc_id = le32_to_cpu(reg->internal_buffer.alloc_id);
return iwl_dump_ini_mon_fill_header(fwrt, alloc_id, mon_dump,
- &fwrt->trans->cfg->mon_smem_regs);
+ &fwrt->trans->mac_cfg->base->mon_smem_regs);
}
static void *
@@ -1814,7 +1846,7 @@ iwl_dump_ini_mon_dbgi_fill_header(struct iwl_fw_runtime *fwrt,
/* no offset calculation later */
IWL_FW_INI_ALLOCATION_ID_DBGC1,
mon_dump,
- &fwrt->trans->cfg->mon_dbgi_regs);
+ &fwrt->trans->mac_cfg->base->mon_dbgi_regs);
}
static void *
@@ -1879,7 +1911,7 @@ iwl_dump_ini_mem_block_ranges(struct iwl_fw_runtime *fwrt,
static u32 iwl_dump_ini_paging_ranges(struct iwl_fw_runtime *fwrt,
struct iwl_dump_ini_region_data *reg_data)
{
- if (fwrt->trans->trans_cfg->gen2) {
+ if (fwrt->trans->mac_cfg->gen2) {
if (fwrt->trans->init_dram.paging_cnt)
return fwrt->trans->init_dram.paging_cnt - 1;
else
@@ -1991,7 +2023,7 @@ iwl_dump_ini_paging_get_size(struct iwl_fw_runtime *fwrt,
/* start from 1 to skip CSS section */
for (i = 1; i <= iwl_dump_ini_paging_ranges(fwrt, reg_data); i++) {
size += range_header_len;
- if (fwrt->trans->trans_cfg->gen2)
+ if (fwrt->trans->mac_cfg->gen2)
size += fwrt->trans->init_dram.paging[i].size;
else
size += fwrt->fw_paging_db[i].fw_paging_size;
@@ -2345,7 +2377,7 @@ static u32 iwl_dump_ini_info(struct iwl_fw_runtime *fwrt,
struct iwl_fw_ini_dump_cfg_name *cfg_name;
u32 size = sizeof(*tlv) + sizeof(*dump);
u32 num_of_cfg_names = 0;
- u32 hw_type;
+ u32 hw_type, is_cdb, is_jacket;
list_for_each_entry(node, &fwrt->trans->dbg.debug_info_tlv_list, list) {
size += sizeof(*cfg_name);
@@ -2373,33 +2405,24 @@ static u32 iwl_dump_ini_info(struct iwl_fw_runtime *fwrt,
dump->ver_type = cpu_to_le32(fwrt->dump.fw_ver.type);
dump->ver_subtype = cpu_to_le32(fwrt->dump.fw_ver.subtype);
- dump->hw_step = cpu_to_le32(fwrt->trans->hw_rev_step);
+ dump->hw_step = cpu_to_le32(fwrt->trans->info.hw_rev_step);
- /*
- * Several HWs all have type == 0x42, so we'll override this value
- * according to the detected HW
- */
- hw_type = CSR_HW_REV_TYPE(fwrt->trans->hw_rev);
- if (hw_type == IWL_AX210_HW_TYPE) {
- u32 prph_val = iwl_read_umac_prph(fwrt->trans, WFPM_OTP_CFG1_ADDR);
- u32 is_jacket = !!(prph_val & WFPM_OTP_CFG1_IS_JACKET_BIT);
- u32 is_cdb = !!(prph_val & WFPM_OTP_CFG1_IS_CDB_BIT);
- u32 masked_bits = is_jacket | (is_cdb << 1);
+ hw_type = CSR_HW_REV_TYPE(fwrt->trans->info.hw_rev);
+
+ is_cdb = CSR_HW_RFID_IS_CDB(fwrt->trans->info.hw_rf_id);
+ is_jacket = !!(iwl_read_umac_prph(fwrt->trans, WFPM_OTP_CFG1_ADDR) &
+ WFPM_OTP_CFG1_IS_JACKET_BIT);
+
+ /* Use bits 12 and 13 to indicate jacket/CDB, respectively */
+ hw_type |= (is_jacket | (is_cdb << 1)) << IWL_JACKET_CDB_SHIFT;
- /*
- * The HW type depends on certain bits in this case, so add
- * these bits to the HW type. We won't have collisions since we
- * add these bits after the highest possible bit in the mask.
- */
- hw_type |= masked_bits << IWL_AX210_HW_TYPE_ADDITION_SHIFT;
- }
dump->hw_type = cpu_to_le32(hw_type);
dump->rf_id_flavor =
- cpu_to_le32(CSR_HW_RFID_FLAVOR(fwrt->trans->hw_rf_id));
- dump->rf_id_dash = cpu_to_le32(CSR_HW_RFID_DASH(fwrt->trans->hw_rf_id));
- dump->rf_id_step = cpu_to_le32(CSR_HW_RFID_STEP(fwrt->trans->hw_rf_id));
- dump->rf_id_type = cpu_to_le32(CSR_HW_RFID_TYPE(fwrt->trans->hw_rf_id));
+ cpu_to_le32(CSR_HW_RFID_FLAVOR(fwrt->trans->info.hw_rf_id));
+ dump->rf_id_dash = cpu_to_le32(CSR_HW_RFID_DASH(fwrt->trans->info.hw_rf_id));
+ dump->rf_id_step = cpu_to_le32(CSR_HW_RFID_STEP(fwrt->trans->info.hw_rf_id));
+ dump->rf_id_type = cpu_to_le32(CSR_HW_RFID_TYPE(fwrt->trans->info.hw_rf_id));
dump->lmac_major = cpu_to_le32(fwrt->dump.fw_ver.lmac_major);
dump->lmac_minor = cpu_to_le32(fwrt->dump.fw_ver.lmac_minor);
@@ -2588,29 +2611,34 @@ static const struct iwl_dump_ini_mem_ops iwl_dump_ini_region_ops[] = {
},
};
-static u32 iwl_dump_ini_trigger(struct iwl_fw_runtime *fwrt,
- struct iwl_fwrt_dump_data *dump_data,
- struct list_head *list)
+enum iwl_dump_ini_region_selector {
+ IWL_INI_DUMP_ALL_REGIONS,
+ IWL_INI_DUMP_EARLY_REGIONS,
+ IWL_INI_DUMP_LATE_REGIONS,
+};
+
+static bool iwl_dump_due_to_error(enum iwl_fw_ini_time_point tp_id)
{
- struct iwl_fw_ini_trigger_tlv *trigger = dump_data->trig;
- enum iwl_fw_ini_time_point tp_id = le32_to_cpu(trigger->time_point);
- struct iwl_dump_ini_region_data reg_data = {
- .dump_data = dump_data,
- };
- struct iwl_dump_ini_region_data imr_reg_data = {
- .dump_data = dump_data,
- };
- int i;
- u32 size = 0;
- u64 regions_mask = le64_to_cpu(trigger->regions_mask) &
- ~(fwrt->trans->dbg.unsupported_region_msk);
+ return tp_id == IWL_FW_INI_TIME_POINT_FW_ASSERT ||
+ tp_id == IWL_FW_INI_TIME_POINT_FW_HW_ERROR;
+}
- BUILD_BUG_ON(sizeof(trigger->regions_mask) != sizeof(regions_mask));
- BUILD_BUG_ON((sizeof(trigger->regions_mask) * BITS_PER_BYTE) <
- ARRAY_SIZE(fwrt->trans->dbg.active_regions));
+static u32
+iwl_dump_ini_dump_regions(struct iwl_fw_runtime *fwrt,
+ struct iwl_fwrt_dump_data *dump_data,
+ struct list_head *list,
+ enum iwl_fw_ini_time_point tp_id,
+ u64 regions_mask,
+ struct iwl_dump_ini_region_data *imr_reg_data,
+ enum iwl_dump_ini_region_selector which)
+{
+ u32 size = 0;
- for (i = 0; i < ARRAY_SIZE(fwrt->trans->dbg.active_regions); i++) {
- u32 reg_type;
+ for (int i = 0; i < ARRAY_SIZE(fwrt->trans->dbg.active_regions); i++) {
+ struct iwl_dump_ini_region_data reg_data = {
+ .dump_data = dump_data,
+ };
+ u32 reg_type, dp;
struct iwl_fw_ini_region_tlv *reg;
if (!(BIT_ULL(i) & regions_mask))
@@ -2628,6 +2656,8 @@ static u32 iwl_dump_ini_trigger(struct iwl_fw_runtime *fwrt,
if (reg_type >= ARRAY_SIZE(iwl_dump_ini_region_ops))
continue;
+ dp = le32_get_bits(reg->id, IWL_FW_INI_REGION_DUMP_POLICY_MASK);
+
if ((reg_type == IWL_FW_INI_REGION_PERIPHERY_PHY ||
reg_type == IWL_FW_INI_REGION_PERIPHERY_PHY_RANGE ||
reg_type == IWL_FW_INI_REGION_PERIPHERY_SNPS_DPHYIP) &&
@@ -2637,6 +2667,20 @@ static u32 iwl_dump_ini_trigger(struct iwl_fw_runtime *fwrt,
tp_id);
continue;
}
+
+ switch (which) {
+ case IWL_INI_DUMP_ALL_REGIONS:
+ break;
+ case IWL_INI_DUMP_EARLY_REGIONS:
+ if (!(dp & IWL_FW_IWL_DEBUG_DUMP_POLICY_BEFORE_RESET))
+ continue;
+ break;
+ case IWL_INI_DUMP_LATE_REGIONS:
+ if (dp & IWL_FW_IWL_DEBUG_DUMP_POLICY_BEFORE_RESET)
+ continue;
+ break;
+ }
+
/*
* DRAM_IMR can be collected only for FW/HW error timepoint
* when fw is not alive. In addition, it must be collected
@@ -2644,9 +2688,9 @@ static u32 iwl_dump_ini_trigger(struct iwl_fw_runtime *fwrt,
* debug data which also need to be collected.
*/
if (reg_type == IWL_FW_INI_REGION_DRAM_IMR) {
- if (tp_id == IWL_FW_INI_TIME_POINT_FW_ASSERT ||
- tp_id == IWL_FW_INI_TIME_POINT_FW_HW_ERROR)
- imr_reg_data.reg_tlv = fwrt->trans->dbg.active_regions[i];
+ if (iwl_dump_due_to_error(tp_id))
+ imr_reg_data->reg_tlv =
+ fwrt->trans->dbg.active_regions[i];
else
IWL_INFO(fwrt,
"WRT: trying to collect DRAM_IMR at time point: %d, skipping\n",
@@ -2659,9 +2703,48 @@ static u32 iwl_dump_ini_trigger(struct iwl_fw_runtime *fwrt,
size += iwl_dump_ini_mem(fwrt, list, &reg_data,
&iwl_dump_ini_region_ops[reg_type]);
}
+
+ return size;
+}
+
+static u32 iwl_dump_ini_trigger(struct iwl_fw_runtime *fwrt,
+ struct iwl_fwrt_dump_data *dump_data,
+ struct list_head *list)
+{
+ struct iwl_fw_ini_trigger_tlv *trigger = dump_data->trig;
+ enum iwl_fw_ini_time_point tp_id = le32_to_cpu(trigger->time_point);
+ struct iwl_dump_ini_region_data imr_reg_data = {
+ .dump_data = dump_data,
+ };
+ u32 size = 0;
+ u64 regions_mask = le64_to_cpu(trigger->regions_mask) &
+ ~(fwrt->trans->dbg.unsupported_region_msk);
+
+ BUILD_BUG_ON(sizeof(trigger->regions_mask) != sizeof(regions_mask));
+ BUILD_BUG_ON((sizeof(trigger->regions_mask) * BITS_PER_BYTE) <
+ ARRAY_SIZE(fwrt->trans->dbg.active_regions));
+
+ if (trigger->apply_policy &
+ cpu_to_le32(IWL_FW_INI_APPLY_POLICY_SPLIT_DUMP_RESET)) {
+ size += iwl_dump_ini_dump_regions(fwrt, dump_data, list, tp_id,
+ regions_mask, &imr_reg_data,
+ IWL_INI_DUMP_EARLY_REGIONS);
+ iwl_trans_pcie_fw_reset_handshake(fwrt->trans);
+ size += iwl_dump_ini_dump_regions(fwrt, dump_data, list, tp_id,
+ regions_mask, &imr_reg_data,
+ IWL_INI_DUMP_LATE_REGIONS);
+ } else {
+ if (fw_has_capa(&fwrt->fw->ucode_capa,
+ IWL_UCODE_TLV_CAPA_RESET_DURING_ASSERT) &&
+ iwl_dump_due_to_error(tp_id))
+ iwl_trans_pcie_fw_reset_handshake(fwrt->trans);
+ size += iwl_dump_ini_dump_regions(fwrt, dump_data, list, tp_id,
+ regions_mask, &imr_reg_data,
+ IWL_INI_DUMP_ALL_REGIONS);
+ }
/* collect DRAM_IMR region in the last */
if (imr_reg_data.reg_tlv)
- size += iwl_dump_ini_mem(fwrt, list, &reg_data,
+ size += iwl_dump_ini_mem(fwrt, list, &imr_reg_data,
&iwl_dump_ini_region_ops[IWL_FW_INI_REGION_DRAM_IMR]);
if (size) {
@@ -3042,9 +3125,8 @@ int iwl_fw_start_dbg_conf(struct iwl_fw_runtime *fwrt, u8 conf_id)
}
IWL_EXPORT_SYMBOL(iwl_fw_start_dbg_conf);
-void iwl_send_dbg_dump_complete_cmd(struct iwl_fw_runtime *fwrt,
- u32 timepoint,
- u32 timepoint_data)
+static void iwl_send_dbg_dump_complete_cmd(struct iwl_fw_runtime *fwrt,
+ u32 timepoint, u32 timepoint_data)
{
struct iwl_dbg_dump_complete_cmd hcmd_data;
struct iwl_host_cmd hcmd = {
@@ -3072,6 +3154,7 @@ static void iwl_fw_dbg_collect_sync(struct iwl_fw_runtime *fwrt, u8 wk_idx)
struct iwl_fw_dbg_params params = {0};
struct iwl_fwrt_dump_data *dump_data =
&fwrt->dump.wks[wk_idx].dump_data;
+
if (!test_bit(wk_idx, &fwrt->dump.active_wks))
return;
@@ -3096,9 +3179,9 @@ static void iwl_fw_dbg_collect_sync(struct iwl_fw_runtime *fwrt, u8 wk_idx)
IWL_DEBUG_FW_INFO(fwrt, "WRT: Data collection start\n");
if (iwl_trans_dbg_ini_valid(fwrt->trans))
- iwl_fw_error_ini_dump(fwrt, &fwrt->dump.wks[wk_idx].dump_data);
+ iwl_fw_error_ini_dump(fwrt, dump_data);
else
- iwl_fw_error_dump(fwrt, &fwrt->dump.wks[wk_idx].dump_data);
+ iwl_fw_error_dump(fwrt, dump_data);
IWL_DEBUG_FW_INFO(fwrt, "WRT: Data collection done\n");
iwl_fw_dbg_stop_restart_recording(fwrt, &params, false);
@@ -3115,7 +3198,6 @@ static void iwl_fw_dbg_collect_sync(struct iwl_fw_runtime *fwrt, u8 wk_idx)
if (fwrt->trans->dbg.last_tp_resetfw == IWL_FW_INI_RESET_FW_MODE_STOP_FW_ONLY)
iwl_force_nmi(fwrt->trans);
-
out:
if (iwl_trans_dbg_ini_valid(fwrt->trans)) {
iwl_fw_error_dump_data_free(dump_data);
@@ -3202,13 +3284,13 @@ void iwl_fw_error_dump_wk(struct work_struct *work)
void iwl_fw_dbg_read_d3_debug_data(struct iwl_fw_runtime *fwrt)
{
- const struct iwl_cfg *cfg = fwrt->trans->cfg;
+ const struct iwl_mac_cfg *mac_cfg = fwrt->trans->mac_cfg;
if (!iwl_fw_dbg_is_d3_debug_enabled(fwrt))
return;
if (!fwrt->dump.d3_debug_data) {
- fwrt->dump.d3_debug_data = kmalloc(cfg->d3_debug_data_length,
+ fwrt->dump.d3_debug_data = kmalloc(mac_cfg->base->d3_debug_data_length,
GFP_KERNEL);
if (!fwrt->dump.d3_debug_data) {
IWL_ERR(fwrt,
@@ -3218,15 +3300,15 @@ void iwl_fw_dbg_read_d3_debug_data(struct iwl_fw_runtime *fwrt)
}
/* if the buffer holds previous debug data it is overwritten */
- iwl_trans_read_mem_bytes(fwrt->trans, cfg->d3_debug_data_base_addr,
+ iwl_trans_read_mem_bytes(fwrt->trans, mac_cfg->base->d3_debug_data_base_addr,
fwrt->dump.d3_debug_data,
- cfg->d3_debug_data_length);
+ mac_cfg->base->d3_debug_data_length);
if (fwrt->sanitize_ops && fwrt->sanitize_ops->frob_mem)
fwrt->sanitize_ops->frob_mem(fwrt->sanitize_ctx,
- cfg->d3_debug_data_base_addr,
+ mac_cfg->base->d3_debug_data_base_addr,
fwrt->dump.d3_debug_data,
- cfg->d3_debug_data_length);
+ mac_cfg->base->d3_debug_data_length);
}
IWL_EXPORT_SYMBOL(iwl_fw_dbg_read_d3_debug_data);
@@ -3261,7 +3343,7 @@ static int iwl_fw_dbg_suspend_resume_hcmd(struct iwl_trans *trans, bool suspend)
static void iwl_fw_dbg_stop_recording(struct iwl_trans *trans,
struct iwl_fw_dbg_params *params)
{
- if (trans->trans_cfg->device_family == IWL_DEVICE_FAMILY_7000) {
+ if (trans->mac_cfg->device_family == IWL_DEVICE_FAMILY_7000) {
iwl_set_bits_prph(trans, MON_BUFF_SAMPLE_CTL, 0x100);
return;
}
@@ -3285,7 +3367,7 @@ static int iwl_fw_dbg_restart_recording(struct iwl_trans *trans,
if (!params)
return -EIO;
- if (trans->trans_cfg->device_family == IWL_DEVICE_FAMILY_7000) {
+ if (trans->mac_cfg->device_family == IWL_DEVICE_FAMILY_7000) {
iwl_clear_bits_prph(trans, MON_BUFF_SAMPLE_CTL, 0x100);
iwl_clear_bits_prph(trans, MON_BUFF_SAMPLE_CTL, 0x1);
iwl_set_bits_prph(trans, MON_BUFF_SAMPLE_CTL, 0x1);
@@ -3387,7 +3469,7 @@ void iwl_fw_disable_dbg_asserts(struct iwl_fw_runtime *fwrt)
GENMASK(31, IWL_FW_DBG_DOMAIN_POS + 1));
/* supported starting from 9000 devices */
- if (fwrt->trans->trans_cfg->device_family < IWL_DEVICE_FAMILY_9000)
+ if (fwrt->trans->mac_cfg->device_family < IWL_DEVICE_FAMILY_9000)
return;
if (fwrt->trans->dbg.yoyo_bin_loaded || (preset && preset != 1))
diff --git a/drivers/net/wireless/intel/iwlwifi/fw/dbg.h b/drivers/net/wireless/intel/iwlwifi/fw/dbg.h
index 87998374f459..8034c9ecba69 100644
--- a/drivers/net/wireless/intel/iwlwifi/fw/dbg.h
+++ b/drivers/net/wireless/intel/iwlwifi/fw/dbg.h
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
/*
- * Copyright (C) 2005-2014, 2018-2019, 2021-2024 Intel Corporation
+ * Copyright (C) 2005-2014, 2018-2019, 2021-2025 Intel Corporation
* Copyright (C) 2013-2015 Intel Mobile Communications GmbH
* Copyright (C) 2015-2017 Intel Deutschland GmbH
*/
@@ -201,7 +201,7 @@ static inline bool iwl_fw_dbg_is_d3_debug_enabled(struct iwl_fw_runtime *fwrt)
{
return fw_has_capa(&fwrt->fw->ucode_capa,
IWL_UCODE_TLV_CAPA_D3_DEBUG) &&
- fwrt->trans->cfg->d3_debug_data_length && fwrt->ops &&
+ fwrt->trans->mac_cfg->base->d3_debug_data_length && fwrt->ops &&
fwrt->ops->d3_debug_enable &&
fwrt->ops->d3_debug_enable(fwrt->ops_ctx) &&
iwl_fw_dbg_type_on(fwrt, IWL_FW_ERROR_DUMP_D3_DEBUG_DATA);
@@ -210,7 +210,7 @@ static inline bool iwl_fw_dbg_is_d3_debug_enabled(struct iwl_fw_runtime *fwrt)
static inline bool iwl_fw_dbg_is_paging_enabled(struct iwl_fw_runtime *fwrt)
{
return iwl_fw_dbg_type_on(fwrt, IWL_FW_ERROR_DUMP_PAGING) &&
- !fwrt->trans->trans_cfg->gen2 &&
+ !fwrt->trans->mac_cfg->gen2 &&
fwrt->cur_fw_img < IWL_UCODE_TYPE_MAX &&
fwrt->fw->img[fwrt->cur_fw_img].paging_mem_size &&
fwrt->fw_paging_db[0].fw_paging_block;
@@ -324,9 +324,6 @@ static inline void iwl_fwrt_update_fw_versions(struct iwl_fw_runtime *fwrt,
}
void iwl_fwrt_dump_error_logs(struct iwl_fw_runtime *fwrt);
-void iwl_send_dbg_dump_complete_cmd(struct iwl_fw_runtime *fwrt,
- u32 timepoint,
- u32 timepoint_data);
bool iwl_fwrt_read_err_table(struct iwl_trans *trans, u32 base, u32 *err_id);
void iwl_fw_disable_dbg_asserts(struct iwl_fw_runtime *fwrt);
void iwl_fw_dbg_clear_monitor_buf(struct iwl_fw_runtime *fwrt);
diff --git a/drivers/net/wireless/intel/iwlwifi/fw/debugfs.c b/drivers/net/wireless/intel/iwlwifi/fw/debugfs.c
index f0c813d675f4..c70f2a20f7d5 100644
--- a/drivers/net/wireless/intel/iwlwifi/fw/debugfs.c
+++ b/drivers/net/wireless/intel/iwlwifi/fw/debugfs.c
@@ -311,7 +311,7 @@ static ssize_t iwl_dbgfs_fw_ver_read(struct iwl_fw_runtime *fwrt,
pos += scnprintf(pos, endpos - pos, "FW: %s\n",
fwrt->fw->human_readable);
pos += scnprintf(pos, endpos - pos, "Device: %s\n",
- fwrt->trans->name);
+ fwrt->trans->info.name);
pos += scnprintf(pos, endpos - pos, "Bus: %s\n",
fwrt->dev->bus->name);
diff --git a/drivers/net/wireless/intel/iwlwifi/fw/dhc-utils.h b/drivers/net/wireless/intel/iwlwifi/fw/dhc-utils.h
new file mode 100644
index 000000000000..983acee5cd7d
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/fw/dhc-utils.h
@@ -0,0 +1,75 @@
+/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
+/*
+ * Copyright (C) 2021, 2025 Intel Corporation
+ */
+#ifndef __iwl_fw_dhc_utils_h__
+#define __iwl_fw_dhc_utils_h__
+
+#include <linux/types.h>
+#include "fw/img.h"
+#include "api/commands.h"
+#include "api/dhc.h"
+
+/**
+ * iwl_dhc_resp_status - return status of DHC response
+ * @fw: firwmware image information
+ * @pkt: response packet, must not be %NULL
+ *
+ * Returns: the status value of the DHC command or (u32)-1 if the
+ * response was too short.
+ */
+static inline u32 iwl_dhc_resp_status(const struct iwl_fw *fw,
+ struct iwl_rx_packet *pkt)
+{
+ if (iwl_fw_lookup_notif_ver(fw, IWL_ALWAYS_LONG_GROUP,
+ DEBUG_HOST_COMMAND, 1) >= 2) {
+ struct iwl_dhc_cmd_resp *resp = (void *)pkt->data;
+
+ if (iwl_rx_packet_payload_len(pkt) < sizeof(*resp))
+ return (u32)-1;
+
+ return le32_to_cpu(resp->status);
+ } else {
+ struct iwl_dhc_cmd_resp_v1 *resp = (void *)pkt->data;
+
+ if (iwl_rx_packet_payload_len(pkt) < sizeof(*resp))
+ return (u32)-1;
+
+ return le32_to_cpu(resp->status);
+ }
+}
+
+/**
+ * iwl_dhc_resp_data - return data pointer of DHC response
+ * @fw: firwmware image information
+ * @pkt: response packet, must not be %NULL
+ * @len: where to store the length
+ *
+ * Returns: The data pointer, or an ERR_PTR() if the data was
+ * not valid (too short).
+ */
+static inline void *iwl_dhc_resp_data(const struct iwl_fw *fw,
+ struct iwl_rx_packet *pkt,
+ unsigned int *len)
+{
+ if (iwl_fw_lookup_notif_ver(fw, IWL_ALWAYS_LONG_GROUP,
+ DEBUG_HOST_COMMAND, 1) >= 2) {
+ struct iwl_dhc_cmd_resp *resp = (void *)pkt->data;
+
+ if (iwl_rx_packet_payload_len(pkt) < sizeof(*resp))
+ return ERR_PTR(-EINVAL);
+
+ *len = iwl_rx_packet_payload_len(pkt) - sizeof(*resp);
+ return (void *)&resp->data;
+ } else {
+ struct iwl_dhc_cmd_resp_v1 *resp = (void *)pkt->data;
+
+ if (iwl_rx_packet_payload_len(pkt) < sizeof(*resp))
+ return ERR_PTR(-EINVAL);
+
+ *len = iwl_rx_packet_payload_len(pkt) - sizeof(*resp);
+ return (void *)&resp->data;
+ }
+}
+
+#endif /* __iwl_fw_dhc_utils_h__ */
diff --git a/drivers/net/wireless/intel/iwlwifi/fw/dump.c b/drivers/net/wireless/intel/iwlwifi/fw/dump.c
index 8e0c85a1240d..3ec42a4ea801 100644
--- a/drivers/net/wireless/intel/iwlwifi/fw/dump.c
+++ b/drivers/net/wireless/intel/iwlwifi/fw/dump.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
/*
- * Copyright (C) 2012-2014, 2018-2024 Intel Corporation
+ * Copyright (C) 2012-2014, 2018-2025 Intel Corporation
* Copyright (C) 2013-2014 Intel Mobile Communications GmbH
* Copyright (C) 2015-2017 Intel Deutschland GmbH
*/
@@ -417,10 +417,10 @@ static void iwl_fwrt_dump_iml_error_log(struct iwl_fw_runtime *fwrt)
struct iwl_trans *trans = fwrt->trans;
u32 error, data1;
- if (fwrt->trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_22000) {
+ if (fwrt->trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_22000) {
error = UMAG_SB_CPU_2_STATUS;
data1 = UMAG_SB_CPU_1_STATUS;
- } else if (fwrt->trans->trans_cfg->device_family >=
+ } else if (fwrt->trans->mac_cfg->device_family >=
IWL_DEVICE_FAMILY_8000) {
error = SB_CPU_2_STATUS;
data1 = SB_CPU_1_STATUS;
@@ -439,7 +439,7 @@ static void iwl_fwrt_dump_iml_error_log(struct iwl_fw_runtime *fwrt)
IWL_ERR(fwrt, "0x%08X | IML/ROM data1\n",
iwl_read_umac_prph(trans, data1));
- if (fwrt->trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_22000)
+ if (fwrt->trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_22000)
IWL_ERR(fwrt, "0x%08X | IML/ROM WFPM_AUTH_KEY_0\n",
iwl_read_umac_prph(trans, SB_MODIFY_CFG_FLAG));
}
@@ -508,7 +508,7 @@ void iwl_fwrt_dump_error_logs(struct iwl_fw_runtime *fwrt)
iwl_fwrt_dump_rcm_error_log(fwrt, 1);
iwl_fwrt_dump_iml_error_log(fwrt);
iwl_fwrt_dump_fseq_regs(fwrt);
- if (fwrt->trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_22000) {
+ if (fwrt->trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_22000) {
pc_data = fwrt->trans->dbg.pc_data;
if (!iwl_trans_grab_nic_access(fwrt->trans))
@@ -522,7 +522,7 @@ void iwl_fwrt_dump_error_logs(struct iwl_fw_runtime *fwrt)
iwl_trans_release_nic_access(fwrt->trans);
}
- if (fwrt->trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_BZ) {
+ if (fwrt->trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_BZ) {
u32 scratch = iwl_read32(fwrt->trans, CSR_FUNC_SCRATCH);
IWL_ERR(fwrt, "Function Scratch status:\n");
@@ -540,6 +540,9 @@ bool iwl_fwrt_read_err_table(struct iwl_trans *trans, u32 base, u32 *err_id)
} err_info = {};
int ret;
+ if (err_id)
+ *err_id = 0;
+
if (!base)
return false;
diff --git a/drivers/net/wireless/intel/iwlwifi/fw/error-dump.h b/drivers/net/wireless/intel/iwlwifi/fw/error-dump.h
index 3af275133da0..cf41021d59ad 100644
--- a/drivers/net/wireless/intel/iwlwifi/fw/error-dump.h
+++ b/drivers/net/wireless/intel/iwlwifi/fw/error-dump.h
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
/*
- * Copyright (C) 2014, 2018-2024 Intel Corporation
+ * Copyright (C) 2014, 2018-2025 Intel Corporation
* Copyright (C) 2014-2015 Intel Mobile Communications GmbH
* Copyright (C) 2016-2017 Intel Deutschland GmbH
*/
@@ -372,10 +372,7 @@ struct iwl_fw_ini_dump_cfg_name {
u8 cfg_name[IWL_FW_INI_MAX_CFG_NAME];
} __packed;
-/* AX210's HW type */
-#define IWL_AX210_HW_TYPE 0x42
-/* How many bits to roll when adding to the HW type of AX210 HW */
-#define IWL_AX210_HW_TYPE_ADDITION_SHIFT 12
+#define IWL_JACKET_CDB_SHIFT 12
/* struct iwl_fw_ini_dump_info - ini dump information
* @version: dump version
diff --git a/drivers/net/wireless/intel/iwlwifi/fw/file.h b/drivers/net/wireless/intel/iwlwifi/fw/file.h
index 9860903ecd3f..5a1ec880ed72 100644
--- a/drivers/net/wireless/intel/iwlwifi/fw/file.h
+++ b/drivers/net/wireless/intel/iwlwifi/fw/file.h
@@ -102,6 +102,7 @@ enum iwl_ucode_tlv_type {
IWL_UCODE_TLV_SEC_TABLE_ADDR = 66,
IWL_UCODE_TLV_D3_KEK_KCK_ADDR = 67,
IWL_UCODE_TLV_CURRENT_PC = 68,
+ IWL_UCODE_TLV_FSEQ_BIN_VERSION = 72,
IWL_UCODE_TLV_FW_NUM_STATIONS = IWL_UCODE_TLV_CONST_BASE + 0,
IWL_UCODE_TLV_FW_NUM_LINKS = IWL_UCODE_TLV_CONST_BASE + 1,
@@ -402,6 +403,7 @@ typedef unsigned int __bitwise iwl_ucode_tlv_capa_t;
* @IWL_UCODE_TLV_CAPA_BIOS_OVERRIDE_5G9_FOR_CA: supports (de)activating 5G9
* for CA from BIOS.
* @IWL_UCODE_TLV_CAPA_UHB_CANADA_TAS_SUPPORT: supports %TAS_UHB_ALLOWED_CANADA
+ * @IWL_UCODE_TLV_CAPA_EXT_FSEQ_IMAGE_SUPPORT: external FSEQ image support
*
* @NUM_IWL_UCODE_TLV_CAPA: number of bits used
*/
@@ -504,6 +506,14 @@ enum iwl_ucode_tlv_capa {
IWL_UCODE_TLV_CAPA_MONITOR_PASSIVE_CHANS = (__force iwl_ucode_tlv_capa_t)122,
IWL_UCODE_TLV_CAPA_BIOS_OVERRIDE_5G9_FOR_CA = (__force iwl_ucode_tlv_capa_t)123,
IWL_UCODE_TLV_CAPA_UHB_CANADA_TAS_SUPPORT = (__force iwl_ucode_tlv_capa_t)124,
+ IWL_UCODE_TLV_CAPA_EXT_FSEQ_IMAGE_SUPPORT = (__force iwl_ucode_tlv_capa_t)125,
+
+ /* set 4 */
+ /**
+ * @IWL_UCODE_TLV_CAPA_RESET_DURING_ASSERT: FW reset handshake is needed
+ * during assert handling even if the dump isn't split
+ */
+ IWL_UCODE_TLV_CAPA_RESET_DURING_ASSERT = (__force iwl_ucode_tlv_capa_t)(4 * 32 + 0),
NUM_IWL_UCODE_TLV_CAPA
/*
* This construction make both sparse (which cannot increment the previous
@@ -994,6 +1004,10 @@ struct iwl_fw_dump_exclude {
__le32 addr, size;
};
+struct iwl_fw_fseq_bin_version {
+ __le32 major, minor;
+}; /* FW_TLV_FSEQ_BIN_VERSION_S */
+
static inline size_t _iwl_tlv_array_len(const struct iwl_ucode_tlv *tlv,
size_t fixed_size, size_t var_size)
{
@@ -1011,4 +1025,18 @@ static inline size_t _iwl_tlv_array_len(const struct iwl_ucode_tlv *tlv,
#define iwl_tlv_array_len_with_size(_tlv_ptr, _struct_ptr, _size) \
_iwl_tlv_array_len((_tlv_ptr), sizeof(*(_struct_ptr)), _size)
+
+/* external FSEQ file */
+#define IWL_FSEQ_FILE "intel/fseq-%04x-%04x"
+#define IWL_FSEQ_MAGIC "INTEL-CNV-FSEQ\n\0"
+
+struct iwl_fseq_file {
+ char magic[16];
+ char version[16];
+ __le32 bt_len;
+ __le32 wifi_len;
+ u8 reserved[8];
+ u8 data[];
+} __packed;
+
#endif /* __iwl_fw_file_h__ */
diff --git a/drivers/net/wireless/intel/iwlwifi/fw/init.c b/drivers/net/wireless/intel/iwlwifi/fw/init.c
index de87e0e3e072..d1d8058ad29f 100644
--- a/drivers/net/wireless/intel/iwlwifi/fw/init.c
+++ b/drivers/net/wireless/intel/iwlwifi/fw/init.c
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
/*
* Copyright (C) 2017 Intel Deutschland GmbH
- * Copyright (C) 2019-2021, 2024 Intel Corporation
+ * Copyright (C) 2019-2021, 2024-2025 Intel Corporation
*/
#include "iwl-drv.h"
#include "runtime.h"
@@ -72,7 +72,7 @@ int iwl_set_soc_latency(struct iwl_fw_runtime *fwrt)
* values in VER_1, this is backwards-compatible with VER_2,
* as long as we don't set any other bits.
*/
- if (!fwrt->trans->trans_cfg->integrated)
+ if (!fwrt->trans->mac_cfg->integrated)
cmd.flags = cpu_to_le32(SOC_CONFIG_CMD_FLAGS_DISCRETE);
BUILD_BUG_ON(IWL_CFG_TRANS_LTR_DELAY_NONE !=
@@ -84,17 +84,17 @@ int iwl_set_soc_latency(struct iwl_fw_runtime *fwrt)
BUILD_BUG_ON(IWL_CFG_TRANS_LTR_DELAY_1820US !=
SOC_FLAGS_LTR_APPLY_DELAY_1820);
- if (fwrt->trans->trans_cfg->ltr_delay != IWL_CFG_TRANS_LTR_DELAY_NONE &&
- !WARN_ON(!fwrt->trans->trans_cfg->integrated))
- cmd.flags |= le32_encode_bits(fwrt->trans->trans_cfg->ltr_delay,
+ if (fwrt->trans->mac_cfg->ltr_delay != IWL_CFG_TRANS_LTR_DELAY_NONE &&
+ !WARN_ON(!fwrt->trans->mac_cfg->integrated))
+ cmd.flags |= le32_encode_bits(fwrt->trans->mac_cfg->ltr_delay,
SOC_FLAGS_LTR_APPLY_DELAY_MASK);
if (iwl_fw_lookup_cmd_ver(fwrt->fw, SCAN_REQ_UMAC,
IWL_FW_CMD_VER_UNKNOWN) >= 2 &&
- fwrt->trans->trans_cfg->low_latency_xtal)
+ fwrt->trans->mac_cfg->low_latency_xtal)
cmd.flags |= cpu_to_le32(SOC_CONFIG_CMD_FLAGS_LOW_LATENCY);
- cmd.latency = cpu_to_le32(fwrt->trans->trans_cfg->xtal_latency);
+ cmd.latency = cpu_to_le32(fwrt->trans->mac_cfg->xtal_latency);
ret = iwl_trans_send_cmd(fwrt->trans, &hcmd);
if (ret)
@@ -116,14 +116,14 @@ int iwl_configure_rxq(struct iwl_fw_runtime *fwrt)
* The default queue is configured via context info, so if we
* have a single queue, there's nothing to do here.
*/
- if (fwrt->trans->num_rx_queues == 1)
+ if (fwrt->trans->info.num_rxqs == 1)
return 0;
- if (fwrt->trans->trans_cfg->device_family < IWL_DEVICE_FAMILY_22000)
+ if (fwrt->trans->mac_cfg->device_family < IWL_DEVICE_FAMILY_22000)
return 0;
/* skip the default queue */
- num_queues = fwrt->trans->num_rx_queues - 1;
+ num_queues = fwrt->trans->info.num_rxqs - 1;
size = struct_size(cmd, data, num_queues);
diff --git a/drivers/net/wireless/intel/iwlwifi/fw/paging.c b/drivers/net/wireless/intel/iwlwifi/fw/paging.c
index a7b7cae874a2..826409f6f710 100644
--- a/drivers/net/wireless/intel/iwlwifi/fw/paging.c
+++ b/drivers/net/wireless/intel/iwlwifi/fw/paging.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
/*
- * Copyright (C) 2012-2014, 2018-2019, 2021 Intel Corporation
+ * Copyright (C) 2012-2014, 2018-2019, 2021, 2025 Intel Corporation
* Copyright (C) 2013-2015 Intel Mobile Communications GmbH
* Copyright (C) 2016-2017 Intel Deutschland GmbH
*/
@@ -267,7 +267,7 @@ int iwl_init_paging(struct iwl_fw_runtime *fwrt, enum iwl_ucode_type type)
const struct fw_img *fw = &fwrt->fw->img[type];
int ret;
- if (fwrt->trans->trans_cfg->gen2)
+ if (fwrt->trans->mac_cfg->gen2)
return 0;
/*
diff --git a/drivers/net/wireless/intel/iwlwifi/fw/pnvm.c b/drivers/net/wireless/intel/iwlwifi/fw/pnvm.c
index 1195e708caa9..4f3c2f7f4f5b 100644
--- a/drivers/net/wireless/intel/iwlwifi/fw/pnvm.c
+++ b/drivers/net/wireless/intel/iwlwifi/fw/pnvm.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
/*
- * Copyright(c) 2020-2024 Intel Corporation
+ * Copyright(c) 2020-2025 Intel Corporation
*/
#include "iwl-drv.h"
@@ -96,8 +96,8 @@ static int iwl_pnvm_handle_section(struct iwl_trans *trans, const u8 *data,
"Got IWL_UCODE_TLV_HW_TYPE mac_type 0x%0x rf_id 0x%0x\n",
mac_type, rf_id);
- if (mac_type == CSR_HW_REV_TYPE(trans->hw_rev) &&
- rf_id == CSR_HW_RFID_TYPE(trans->hw_rf_id))
+ if (mac_type == CSR_HW_REV_TYPE(trans->info.hw_rev) &&
+ rf_id == CSR_HW_RFID_TYPE(trans->info.hw_rf_id))
hw_match = true;
break;
case IWL_UCODE_TLV_SEC_RT: {
@@ -152,8 +152,8 @@ done:
if (!hw_match) {
IWL_DEBUG_FW(trans,
"HW mismatch, skipping PNVM section (need mac_type 0x%x rf_id 0x%x)\n",
- CSR_HW_REV_TYPE(trans->hw_rev),
- CSR_HW_RFID_TYPE(trans->hw_rf_id));
+ CSR_HW_REV_TYPE(trans->info.hw_rev),
+ CSR_HW_RFID_TYPE(trans->info.hw_rf_id));
return -ENOENT;
}
@@ -167,7 +167,8 @@ done:
static int iwl_pnvm_parse(struct iwl_trans *trans, const u8 *data,
size_t len,
- struct iwl_pnvm_image *pnvm_data)
+ struct iwl_pnvm_image *pnvm_data,
+ __le32 sku_id[3])
{
const struct iwl_ucode_tlv *tlv;
@@ -190,23 +191,23 @@ static int iwl_pnvm_parse(struct iwl_trans *trans, const u8 *data,
}
if (tlv_type == IWL_UCODE_TLV_PNVM_SKU) {
- const struct iwl_sku_id *sku_id =
+ const struct iwl_sku_id *tlv_sku_id =
(const void *)(data + sizeof(*tlv));
IWL_DEBUG_FW(trans,
"Got IWL_UCODE_TLV_PNVM_SKU len %d\n",
tlv_len);
IWL_DEBUG_FW(trans, "sku_id 0x%0x 0x%0x 0x%0x\n",
- le32_to_cpu(sku_id->data[0]),
- le32_to_cpu(sku_id->data[1]),
- le32_to_cpu(sku_id->data[2]));
+ le32_to_cpu(tlv_sku_id->data[0]),
+ le32_to_cpu(tlv_sku_id->data[1]),
+ le32_to_cpu(tlv_sku_id->data[2]));
data += sizeof(*tlv) + ALIGN(tlv_len, 4);
len -= ALIGN(tlv_len, 4);
trans->reduced_cap_sku = false;
- rf_type = CSR_HW_RFID_TYPE(trans->hw_rf_id);
- if ((trans->sku_id[0] & IWL_PNVM_REDUCED_CAP_BIT) &&
+ rf_type = CSR_HW_RFID_TYPE(trans->info.hw_rf_id);
+ if ((sku_id[0] & cpu_to_le32(IWL_PNVM_REDUCED_CAP_BIT)) &&
rf_type == IWL_CFG_RF_TYPE_FM)
trans->reduced_cap_sku = true;
@@ -214,9 +215,9 @@ static int iwl_pnvm_parse(struct iwl_trans *trans, const u8 *data,
"Reduced SKU device %d\n",
trans->reduced_cap_sku);
- if (trans->sku_id[0] == le32_to_cpu(sku_id->data[0]) &&
- trans->sku_id[1] == le32_to_cpu(sku_id->data[1]) &&
- trans->sku_id[2] == le32_to_cpu(sku_id->data[2])) {
+ if (sku_id[0] == tlv_sku_id->data[0] &&
+ sku_id[1] == tlv_sku_id->data[1] &&
+ sku_id[2] == tlv_sku_id->data[2]) {
int ret;
ret = iwl_pnvm_handle_section(trans, data, len,
@@ -263,13 +264,14 @@ static int iwl_pnvm_get_from_fs(struct iwl_trans *trans, u8 **data, size_t *len)
return 0;
}
-static u8 *iwl_get_pnvm_image(struct iwl_trans *trans_p, size_t *len)
+static u8 *iwl_get_pnvm_image(struct iwl_trans *trans_p, size_t *len,
+ __le32 sku_id[3])
{
struct pnvm_sku_package *package;
u8 *image = NULL;
/* Get PNVM from BIOS for non-Intel SKU */
- if (trans_p->sku_id[2]) {
+ if (sku_id[2]) {
package = iwl_uefi_get_pnvm(trans_p, len);
if (!IS_ERR_OR_NULL(package)) {
if (*len >= sizeof(*package)) {
@@ -294,8 +296,10 @@ static u8 *iwl_get_pnvm_image(struct iwl_trans *trans_p, size_t *len)
return image;
}
-static void iwl_pnvm_load_pnvm_to_trans(struct iwl_trans *trans,
- const struct iwl_ucode_capabilities *capa)
+static void
+iwl_pnvm_load_pnvm_to_trans(struct iwl_trans *trans,
+ const struct iwl_ucode_capabilities *capa,
+ __le32 sku_id[3])
{
struct iwl_pnvm_image *pnvm_data = NULL;
u8 *data = NULL;
@@ -309,7 +313,7 @@ static void iwl_pnvm_load_pnvm_to_trans(struct iwl_trans *trans,
if (trans->pnvm_loaded)
goto set;
- data = iwl_get_pnvm_image(trans, &length);
+ data = iwl_get_pnvm_image(trans, &length, sku_id);
if (!data) {
trans->fail_to_parse_pnvm_image = true;
return;
@@ -319,7 +323,7 @@ static void iwl_pnvm_load_pnvm_to_trans(struct iwl_trans *trans,
if (!pnvm_data)
goto free;
- ret = iwl_pnvm_parse(trans, data, length, pnvm_data);
+ ret = iwl_pnvm_parse(trans, data, length, pnvm_data, sku_id);
if (ret) {
trans->fail_to_parse_pnvm_image = true;
goto free;
@@ -339,7 +343,8 @@ free:
static void
iwl_pnvm_load_reduce_power_to_trans(struct iwl_trans *trans,
- const struct iwl_ucode_capabilities *capa)
+ const struct iwl_ucode_capabilities *capa,
+ __le32 sku_id[3])
{
struct iwl_pnvm_image *pnvm_data = NULL;
u8 *data = NULL;
@@ -362,7 +367,8 @@ iwl_pnvm_load_reduce_power_to_trans(struct iwl_trans *trans,
if (!pnvm_data)
goto free;
- ret = iwl_uefi_reduce_power_parse(trans, data, length, pnvm_data);
+ ret = iwl_uefi_reduce_power_parse(trans, data, length, pnvm_data,
+ sku_id);
if (ret) {
trans->failed_to_load_reduce_power_image = true;
goto free;
@@ -386,18 +392,19 @@ free:
int iwl_pnvm_load(struct iwl_trans *trans,
struct iwl_notif_wait_data *notif_wait,
- const struct iwl_ucode_capabilities *capa)
+ const struct iwl_ucode_capabilities *capa,
+ __le32 sku_id[3])
{
struct iwl_notification_wait pnvm_wait;
static const u16 ntf_cmds[] = { WIDE_ID(REGULATORY_AND_NVM_GROUP,
PNVM_INIT_COMPLETE_NTFY) };
/* if the SKU_ID is empty, there's nothing to do */
- if (!trans->sku_id[0] && !trans->sku_id[1] && !trans->sku_id[2])
+ if (!sku_id[0] && !sku_id[1] && !sku_id[2])
return 0;
- iwl_pnvm_load_pnvm_to_trans(trans, capa);
- iwl_pnvm_load_reduce_power_to_trans(trans, capa);
+ iwl_pnvm_load_pnvm_to_trans(trans, capa, sku_id);
+ iwl_pnvm_load_reduce_power_to_trans(trans, capa, sku_id);
iwl_init_notification_wait(notif_wait, &pnvm_wait,
ntf_cmds, ARRAY_SIZE(ntf_cmds),
diff --git a/drivers/net/wireless/intel/iwlwifi/fw/pnvm.h b/drivers/net/wireless/intel/iwlwifi/fw/pnvm.h
index 1bac3466154c..9540926e8a0f 100644
--- a/drivers/net/wireless/intel/iwlwifi/fw/pnvm.h
+++ b/drivers/net/wireless/intel/iwlwifi/fw/pnvm.h
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
/*
- * Copyright(c) 2020-2023 Intel Corporation
+ * Copyright(c) 2020-2023, 2025 Intel Corporation
*/
#ifndef __IWL_PNVM_H__
#define __IWL_PNVM_H__
@@ -14,7 +14,8 @@
int iwl_pnvm_load(struct iwl_trans *trans,
struct iwl_notif_wait_data *notif_wait,
- const struct iwl_ucode_capabilities *capa);
+ const struct iwl_ucode_capabilities *capa,
+ __le32 sku_id[3]);
static inline
void iwl_pnvm_get_fs_name(struct iwl_trans *trans,
diff --git a/drivers/net/wireless/intel/iwlwifi/fw/regulatory.c b/drivers/net/wireless/intel/iwlwifi/fw/regulatory.c
index ea435ee94312..74b90bd92c48 100644
--- a/drivers/net/wireless/intel/iwlwifi/fw/regulatory.c
+++ b/drivers/net/wireless/intel/iwlwifi/fw/regulatory.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
/*
- * Copyright (C) 2023 Intel Corporation
+ * Copyright (C) 2023, 2025 Intel Corporation
*/
#include <linux/dmi.h>
#include "iwl-drv.h"
@@ -34,6 +34,7 @@ IWL_BIOS_TABLE_LOADER(wrds_table);
IWL_BIOS_TABLE_LOADER(ewrd_table);
IWL_BIOS_TABLE_LOADER(wgds_table);
IWL_BIOS_TABLE_LOADER(ppag_table);
+IWL_BIOS_TABLE_LOADER(phy_filters);
IWL_BIOS_TABLE_LOADER_DATA(tas_table, struct iwl_tas_data);
IWL_BIOS_TABLE_LOADER_DATA(pwr_limit, u64);
IWL_BIOS_TABLE_LOADER_DATA(mcc, char);
@@ -180,9 +181,9 @@ bool iwl_sar_geo_support(struct iwl_fw_runtime *fwrt)
*/
return IWL_UCODE_SERIAL(fwrt->fw->ucode_ver) >= 38 ||
(IWL_UCODE_SERIAL(fwrt->fw->ucode_ver) == 17 &&
- fwrt->trans->hw_rev != CSR_HW_REV_TYPE_3160) ||
+ fwrt->trans->info.hw_rev != CSR_HW_REV_TYPE_3160) ||
(IWL_UCODE_SERIAL(fwrt->fw->ucode_ver) == 29 &&
- ((fwrt->trans->hw_rev & CSR_HW_REV_TYPE_MSK) ==
+ ((fwrt->trans->info.hw_rev & CSR_HW_REV_TYPE_MSK) ==
CSR_HW_REV_TYPE_7265D));
}
IWL_EXPORT_SYMBOL(iwl_sar_geo_support);
@@ -313,7 +314,7 @@ int iwl_fill_ppag_table(struct iwl_fw_runtime *fwrt,
bool send_ppag_always;
/* many firmware images for JF lie about this */
- if (CSR_HW_RFID_TYPE(fwrt->trans->hw_rf_id) ==
+ if (CSR_HW_RFID_TYPE(fwrt->trans->info.hw_rf_id) ==
CSR_HW_RFID_TYPE(CSR_HW_RF_ID_TYPE_JF))
return -EOPNOTSUPP;
@@ -338,31 +339,35 @@ int iwl_fill_ppag_table(struct iwl_fw_runtime *fwrt,
return -EINVAL;
}
- /* The 'flags' field is the same in v1 and in v2 so we can just
- * use v1 to access it.
- */
- cmd->v1.flags = cpu_to_le32(fwrt->ppag_flags);
-
IWL_DEBUG_RADIO(fwrt, "PPAG cmd ver is %d\n", cmd_ver);
if (cmd_ver == 1) {
num_sub_bands = IWL_NUM_SUB_BANDS_V1;
gain = cmd->v1.gain[0];
*cmd_size = sizeof(cmd->v1);
- if (fwrt->ppag_ver >= 1) {
+ cmd->v1.flags = cpu_to_le32(fwrt->ppag_flags);
+ if (fwrt->ppag_bios_rev >= 1) {
/* in this case FW supports revision 0 */
IWL_DEBUG_RADIO(fwrt,
"PPAG table rev is %d, send truncated table\n",
- fwrt->ppag_ver);
+ fwrt->ppag_bios_rev);
}
} else if (cmd_ver >= 2 && cmd_ver <= 6) {
num_sub_bands = IWL_NUM_SUB_BANDS_V2;
gain = cmd->v2.gain[0];
*cmd_size = sizeof(cmd->v2);
- if (fwrt->ppag_ver == 0) {
+ cmd->v2.flags = cpu_to_le32(fwrt->ppag_flags);
+ if (fwrt->ppag_bios_rev == 0) {
/* in this case FW supports revisions 1,2 or 3 */
IWL_DEBUG_RADIO(fwrt,
"PPAG table rev is 0, send padded table\n");
}
+ } else if (cmd_ver == 7) {
+ num_sub_bands = IWL_NUM_SUB_BANDS_V2;
+ gain = cmd->v3.gain[0];
+ *cmd_size = sizeof(cmd->v3);
+ cmd->v3.ppag_config_info.table_source = fwrt->ppag_bios_source;
+ cmd->v3.ppag_config_info.table_revision = fwrt->ppag_bios_rev;
+ cmd->v3.ppag_config_info.value = cpu_to_le32(fwrt->ppag_flags);
} else {
IWL_DEBUG_RADIO(fwrt, "Unsupported PPAG command version\n");
return -EINVAL;
@@ -371,9 +376,11 @@ int iwl_fill_ppag_table(struct iwl_fw_runtime *fwrt,
/* ppag mode */
IWL_DEBUG_RADIO(fwrt,
"PPAG MODE bits were read from bios: %d\n",
- le32_to_cpu(cmd->v1.flags));
+ fwrt->ppag_flags);
- if (cmd_ver == 5)
+ if (cmd_ver == 6)
+ cmd->v1.flags &= cpu_to_le32(IWL_PPAG_CMD_V6_MASK);
+ else if (cmd_ver == 5)
cmd->v1.flags &= cpu_to_le32(IWL_PPAG_CMD_V5_MASK);
else if (cmd_ver < 5)
cmd->v1.flags &= cpu_to_le32(IWL_PPAG_CMD_V4_MASK);
@@ -381,16 +388,20 @@ int iwl_fill_ppag_table(struct iwl_fw_runtime *fwrt,
if ((cmd_ver == 1 &&
!fw_has_capa(&fwrt->fw->ucode_capa,
IWL_UCODE_TLV_CAPA_PPAG_CHINA_BIOS_SUPPORT)) ||
- (cmd_ver == 2 && fwrt->ppag_ver >= 2)) {
+ (cmd_ver == 2 && fwrt->ppag_bios_rev >= 2)) {
cmd->v1.flags &= cpu_to_le32(IWL_PPAG_ETSI_MASK);
IWL_DEBUG_RADIO(fwrt, "masking ppag China bit\n");
} else {
IWL_DEBUG_RADIO(fwrt, "isn't masking ppag China bit\n");
}
+ /* The 'flags' field is the same in v1 and v2 so we can just
+ * use v1 to access it.
+ */
IWL_DEBUG_RADIO(fwrt,
"PPAG MODE bits going to be sent: %d\n",
- le32_to_cpu(cmd->v1.flags));
+ (cmd_ver < 7) ? le32_to_cpu(cmd->v1.flags) :
+ le32_to_cpu(cmd->v3.ppag_config_info.value));
for (i = 0; i < IWL_NUM_CHAIN_LIMITS; i++) {
for (j = 0; j < num_sub_bands; j++) {
@@ -456,13 +467,31 @@ iwl_parse_tas_selection(const u32 tas_selection_in, const u8 tbl_rev)
}
IWL_EXPORT_SYMBOL(iwl_parse_tas_selection);
-static __le32 iwl_get_lari_config_bitmap(struct iwl_fw_runtime *fwrt)
+bool iwl_add_mcc_to_tas_block_list(u16 *list, u8 *size, u16 mcc)
+{
+ for (int i = 0; i < *size; i++) {
+ if (list[i] == mcc)
+ return true;
+ }
+
+ /* Verify that there is room for another country
+ * If *size == IWL_WTAS_BLACK_LIST_MAX, then the table is full.
+ */
+ if (*size >= IWL_WTAS_BLACK_LIST_MAX)
+ return false;
+
+ list[*size++] = mcc;
+ return true;
+}
+IWL_EXPORT_SYMBOL(iwl_add_mcc_to_tas_block_list);
+
+__le32 iwl_get_lari_config_bitmap(struct iwl_fw_runtime *fwrt)
{
int ret;
u32 val;
__le32 config_bitmap = 0;
- switch (CSR_HW_RFID_TYPE(fwrt->trans->hw_rf_id)) {
+ switch (CSR_HW_RFID_TYPE(fwrt->trans->info.hw_rf_id)) {
case IWL_CFG_RF_TYPE_HR1:
case IWL_CFG_RF_TYPE_HR2:
case IWL_CFG_RF_TYPE_JF1:
@@ -503,6 +532,7 @@ static __le32 iwl_get_lari_config_bitmap(struct iwl_fw_runtime *fwrt)
return config_bitmap;
}
+IWL_EXPORT_SYMBOL(iwl_get_lari_config_bitmap);
static size_t iwl_get_lari_config_cmd_size(u8 cmd_ver)
{
@@ -553,6 +583,10 @@ int iwl_fill_lari_config(struct iwl_fw_runtime *fwrt,
WIDE_ID(REGULATORY_AND_NVM_GROUP,
LARI_CONFIG_CHANGE), 1);
+ if (WARN_ONCE(cmd_ver > 12,
+ "Don't add newer versions to this function\n"))
+ return -EINVAL;
+
memset(cmd, 0, sizeof(*cmd));
*cmd_size = iwl_get_lari_config_cmd_size(cmd_ver);
@@ -674,3 +708,34 @@ bool iwl_puncturing_is_allowed_in_bios(u32 puncturing, u16 mcc)
}
}
IWL_EXPORT_SYMBOL(iwl_puncturing_is_allowed_in_bios);
+
+bool iwl_rfi_is_enabled_in_bios(struct iwl_fw_runtime *fwrt)
+{
+ /* default behaviour is disabled */
+ u32 value = 0;
+ int ret = iwl_bios_get_dsm(fwrt, DSM_FUNC_RFI_CONFIG, &value);
+
+ if (ret < 0) {
+ IWL_DEBUG_RADIO(fwrt, "Failed to get DSM RFI, ret=%d\n", ret);
+ return false;
+ }
+
+ value &= DSM_VALUE_RFI_DISABLE;
+ /* RFI BIOS CONFIG value can be 0 or 3 only.
+ * i.e 0 means DDR and DLVR enabled. 3 means DDR and DLVR disabled.
+ * 1 and 2 are invalid BIOS configurations, So, it's not possible to
+ * disable ddr/dlvr separately.
+ */
+ if (!value) {
+ IWL_DEBUG_RADIO(fwrt, "DSM RFI is evaluated to enable\n");
+ return true;
+ } else if (value == DSM_VALUE_RFI_DISABLE) {
+ IWL_DEBUG_RADIO(fwrt, "DSM RFI is evaluated to disable\n");
+ } else {
+ IWL_DEBUG_RADIO(fwrt,
+ "DSM RFI got invalid value, value=%d\n", value);
+ }
+
+ return false;
+}
+IWL_EXPORT_SYMBOL(iwl_rfi_is_enabled_in_bios);
diff --git a/drivers/net/wireless/intel/iwlwifi/fw/regulatory.h b/drivers/net/wireless/intel/iwlwifi/fw/regulatory.h
index b355d7bef14c..9bed3d573b1e 100644
--- a/drivers/net/wireless/intel/iwlwifi/fw/regulatory.h
+++ b/drivers/net/wireless/intel/iwlwifi/fw/regulatory.h
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
/*
- * Copyright (C) 2023-2024 Intel Corporation
+ * Copyright (C) 2023-2025 Intel Corporation
*/
#ifndef __fw_regulatory_h__
@@ -167,6 +167,8 @@ enum iwl_dsm_values_rfi {
#define DSM_VALUE_RFI_DISABLE (DSM_VALUE_RFI_DLVR_DISABLE |\
DSM_VALUE_RFI_DDR_DISABLE)
+bool iwl_rfi_is_enabled_in_bios(struct iwl_fw_runtime *fwrt);
+
enum iwl_dsm_masks_reg {
DSM_MASK_CHINA_22_REG = BIT(2)
};
@@ -190,6 +192,7 @@ int iwl_fill_ppag_table(struct iwl_fw_runtime *fwrt,
bool iwl_is_ppag_approved(struct iwl_fw_runtime *fwrt);
bool iwl_is_tas_approved(void);
+bool iwl_add_mcc_to_tas_block_list(u16 *list, u8 *size, u16 mcc);
struct iwl_tas_selection_data
iwl_parse_tas_selection(const u32 tas_selection, const u8 tbl_rev);
@@ -212,6 +215,7 @@ int iwl_bios_get_mcc(struct iwl_fw_runtime *fwrt, char *mcc);
int iwl_bios_get_eckv(struct iwl_fw_runtime *fwrt, u32 *ext_clk);
int iwl_bios_get_wbem(struct iwl_fw_runtime *fwrt, u32 *value);
+__le32 iwl_get_lari_config_bitmap(struct iwl_fw_runtime *fwrt);
int iwl_fill_lari_config(struct iwl_fw_runtime *fwrt,
struct iwl_lari_config_change_cmd *cmd,
size_t *cmd_size);
@@ -220,10 +224,14 @@ int iwl_bios_get_dsm(struct iwl_fw_runtime *fwrt, enum iwl_dsm_funcs func,
u32 *value);
static inline u32 iwl_bios_get_ppag_flags(const u32 ppag_modes,
- const u8 ppag_ver)
+ const u8 ppag_bios_rev)
{
- return ppag_modes & (ppag_ver < 3 ? IWL_PPAG_ETSI_CHINA_MASK :
- IWL_PPAG_REV3_MASK);
+ /* For revision 4 and above driver is pipe */
+ if (ppag_bios_rev >= 4)
+ return ppag_modes;
+
+ return ppag_modes & (ppag_bios_rev < 3 ? IWL_PPAG_ETSI_CHINA_MASK :
+ IWL_PPAG_REV3_MASK);
}
bool iwl_puncturing_is_allowed_in_bios(u32 puncturing, u16 mcc);
@@ -232,22 +240,25 @@ bool iwl_puncturing_is_allowed_in_bios(u32 puncturing, u16 mcc);
#define IWL_DSBR_PERMANENT_URM_MASK BIT(9)
int iwl_bios_get_dsbr(struct iwl_fw_runtime *fwrt, u32 *value);
+int iwl_bios_get_phy_filters(struct iwl_fw_runtime *fwrt);
static inline void iwl_bios_setup_step(struct iwl_trans *trans,
struct iwl_fw_runtime *fwrt)
{
u32 dsbr;
- if (!trans->trans_cfg->integrated)
+ if (!trans->mac_cfg->integrated)
return;
- if (trans->trans_cfg->device_family < IWL_DEVICE_FAMILY_BZ)
+ if (trans->mac_cfg->device_family < IWL_DEVICE_FAMILY_BZ)
return;
if (iwl_bios_get_dsbr(fwrt, &dsbr))
dsbr = 0;
- trans->dsbr_urm_fw_dependent = !!(dsbr & IWL_DSBR_FW_MODIFIED_URM_MASK);
- trans->dsbr_urm_permanent = !!(dsbr & IWL_DSBR_PERMANENT_URM_MASK);
+ trans->conf.dsbr_urm_fw_dependent =
+ !!(dsbr & IWL_DSBR_FW_MODIFIED_URM_MASK);
+ trans->conf.dsbr_urm_permanent =
+ !!(dsbr & IWL_DSBR_PERMANENT_URM_MASK);
}
#endif /* __fw_regulatory_h__ */
diff --git a/drivers/net/wireless/intel/iwlwifi/fw/rs.c b/drivers/net/wireless/intel/iwlwifi/fw/rs.c
index 8f99e501629e..746f2acffb8f 100644
--- a/drivers/net/wireless/intel/iwlwifi/fw/rs.c
+++ b/drivers/net/wireless/intel/iwlwifi/fw/rs.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
/*
- * Copyright (C) 2021-2022 Intel Corporation
+ * Copyright (C) 2021-2022, 2025 Intel Corporation
*/
#include <net/mac80211.h>
@@ -91,104 +91,6 @@ const char *iwl_rs_pretty_bw(int bw)
}
IWL_EXPORT_SYMBOL(iwl_rs_pretty_bw);
-static u32 iwl_legacy_rate_to_fw_idx(u32 rate_n_flags)
-{
- int rate = rate_n_flags & RATE_LEGACY_RATE_MSK_V1;
- int idx;
- bool ofdm = !(rate_n_flags & RATE_MCS_CCK_MSK_V1);
- int offset = ofdm ? IWL_FIRST_OFDM_RATE : 0;
- int last = ofdm ? IWL_RATE_COUNT_LEGACY : IWL_FIRST_OFDM_RATE;
-
- for (idx = offset; idx < last; idx++)
- if (iwl_fw_rate_idx_to_plcp(idx) == rate)
- return idx - offset;
- return IWL_RATE_INVALID;
-}
-
-u32 iwl_new_rate_from_v1(u32 rate_v1)
-{
- u32 rate_v2 = 0;
- u32 dup = 0;
-
- if (rate_v1 == 0)
- return rate_v1;
- /* convert rate */
- if (rate_v1 & RATE_MCS_HT_MSK_V1) {
- u32 nss = 0;
-
- rate_v2 |= RATE_MCS_HT_MSK;
- rate_v2 |=
- rate_v1 & RATE_HT_MCS_RATE_CODE_MSK_V1;
- nss = (rate_v1 & RATE_HT_MCS_MIMO2_MSK) >>
- RATE_HT_MCS_NSS_POS_V1;
- rate_v2 |= nss << RATE_MCS_NSS_POS;
- } else if (rate_v1 & RATE_MCS_VHT_MSK_V1 ||
- rate_v1 & RATE_MCS_HE_MSK_V1) {
- rate_v2 |= rate_v1 & RATE_VHT_MCS_RATE_CODE_MSK;
-
- rate_v2 |= rate_v1 & RATE_MCS_NSS_MSK;
-
- if (rate_v1 & RATE_MCS_HE_MSK_V1) {
- u32 he_type_bits = rate_v1 & RATE_MCS_HE_TYPE_MSK_V1;
- u32 he_type = he_type_bits >> RATE_MCS_HE_TYPE_POS_V1;
- u32 he_106t = (rate_v1 & RATE_MCS_HE_106T_MSK_V1) >>
- RATE_MCS_HE_106T_POS_V1;
- u32 he_gi_ltf = (rate_v1 & RATE_MCS_HE_GI_LTF_MSK_V1) >>
- RATE_MCS_HE_GI_LTF_POS;
-
- if ((he_type_bits == RATE_MCS_HE_TYPE_SU ||
- he_type_bits == RATE_MCS_HE_TYPE_EXT_SU) &&
- he_gi_ltf == RATE_MCS_HE_SU_4_LTF)
- /* the new rate have an additional bit to
- * represent the value 4 rather then using SGI
- * bit for this purpose - as it was done in the old
- * rate */
- he_gi_ltf += (rate_v1 & RATE_MCS_SGI_MSK_V1) >>
- RATE_MCS_SGI_POS_V1;
-
- rate_v2 |= he_gi_ltf << RATE_MCS_HE_GI_LTF_POS;
- rate_v2 |= he_type << RATE_MCS_HE_TYPE_POS;
- rate_v2 |= he_106t << RATE_MCS_HE_106T_POS;
- rate_v2 |= rate_v1 & RATE_HE_DUAL_CARRIER_MODE_MSK;
- rate_v2 |= RATE_MCS_HE_MSK;
- } else {
- rate_v2 |= RATE_MCS_VHT_MSK;
- }
- /* if legacy format */
- } else {
- u32 legacy_rate = iwl_legacy_rate_to_fw_idx(rate_v1);
-
- if (WARN_ON_ONCE(legacy_rate == IWL_RATE_INVALID))
- legacy_rate = (rate_v1 & RATE_MCS_CCK_MSK_V1) ?
- IWL_FIRST_CCK_RATE : IWL_FIRST_OFDM_RATE;
-
- rate_v2 |= legacy_rate;
- if (!(rate_v1 & RATE_MCS_CCK_MSK_V1))
- rate_v2 |= RATE_MCS_LEGACY_OFDM_MSK;
- }
-
- /* convert flags */
- if (rate_v1 & RATE_MCS_LDPC_MSK_V1)
- rate_v2 |= RATE_MCS_LDPC_MSK;
- rate_v2 |= (rate_v1 & RATE_MCS_CHAN_WIDTH_MSK_V1) |
- (rate_v1 & RATE_MCS_ANT_AB_MSK) |
- (rate_v1 & RATE_MCS_STBC_MSK) |
- (rate_v1 & RATE_MCS_BF_MSK);
-
- dup = (rate_v1 & RATE_MCS_DUP_MSK_V1) >> RATE_MCS_DUP_POS_V1;
- if (dup) {
- rate_v2 |= RATE_MCS_DUP_MSK;
- rate_v2 |= dup << RATE_MCS_CHAN_WIDTH_POS;
- }
-
- if ((!(rate_v1 & RATE_MCS_HE_MSK_V1)) &&
- (rate_v1 & RATE_MCS_SGI_MSK_V1))
- rate_v2 |= RATE_MCS_SGI_MSK;
-
- return rate_v2;
-}
-IWL_EXPORT_SYMBOL(iwl_new_rate_from_v1);
-
int rs_pretty_print_rate(char *buf, int bufsz, const u32 rate)
{
char *type;
@@ -197,37 +99,40 @@ int rs_pretty_print_rate(char *buf, int bufsz, const u32 rate)
u32 bw = (rate & RATE_MCS_CHAN_WIDTH_MSK) >>
RATE_MCS_CHAN_WIDTH_POS;
u32 format = rate & RATE_MCS_MOD_TYPE_MSK;
+ int index = 0;
bool sgi;
- if (format == RATE_MCS_CCK_MSK ||
- format == RATE_MCS_LEGACY_OFDM_MSK) {
- int legacy_rate = rate & RATE_LEGACY_RATE_MSK;
- int index = format == RATE_MCS_CCK_MSK ?
- legacy_rate :
- legacy_rate + IWL_FIRST_OFDM_RATE;
+ switch (format) {
+ case RATE_MCS_MOD_TYPE_LEGACY_OFDM:
+ index = IWL_FIRST_OFDM_RATE;
+ fallthrough;
+ case RATE_MCS_MOD_TYPE_CCK:
+ index += rate & RATE_LEGACY_RATE_MSK;
return scnprintf(buf, bufsz, "Legacy | ANT: %s Rate: %s Mbps",
iwl_rs_pretty_ant(ant),
iwl_rate_mcs(index)->mbps);
- }
-
- if (format == RATE_MCS_VHT_MSK)
+ case RATE_MCS_MOD_TYPE_VHT:
type = "VHT";
- else if (format == RATE_MCS_HT_MSK)
+ break;
+ case RATE_MCS_MOD_TYPE_HT:
type = "HT";
- else if (format == RATE_MCS_HE_MSK)
+ break;
+ case RATE_MCS_MOD_TYPE_HE:
type = "HE";
- else if (format == RATE_MCS_EHT_MSK)
+ break;
+ case RATE_MCS_MOD_TYPE_EHT:
type = "EHT";
- else
+ break;
+ default:
type = "Unknown"; /* shouldn't happen */
+ }
- mcs = format == RATE_MCS_HT_MSK ?
+ mcs = format == RATE_MCS_MOD_TYPE_HT ?
RATE_HT_MCS_INDEX(rate) :
rate & RATE_MCS_CODE_MSK;
- nss = ((rate & RATE_MCS_NSS_MSK)
- >> RATE_MCS_NSS_POS) + 1;
- sgi = format == RATE_MCS_HE_MSK ?
+ nss = u32_get_bits(rate, RATE_MCS_NSS_MSK);
+ sgi = format == RATE_MCS_MOD_TYPE_HE ?
iwl_he_is_sgi(rate) :
rate & RATE_MCS_SGI_MSK;
diff --git a/drivers/net/wireless/intel/iwlwifi/fw/runtime.h b/drivers/net/wireless/intel/iwlwifi/fw/runtime.h
index 048877fa7c71..0444a736c2b2 100644
--- a/drivers/net/wireless/intel/iwlwifi/fw/runtime.h
+++ b/drivers/net/wireless/intel/iwlwifi/fw/runtime.h
@@ -1,7 +1,7 @@
/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
/*
* Copyright (C) 2017 Intel Deutschland GmbH
- * Copyright (C) 2018-2024 Intel Corporation
+ * Copyright (C) 2018-2025 Intel Corporation
*/
#ifndef __iwl_fw_runtime_h__
#define __iwl_fw_runtime_h__
@@ -104,11 +104,15 @@ struct iwl_txf_iter_data {
* the driver by calling &iwl_fw_set_current_image()
* @dump: debug dump data
* @uats_table: AP type table
+ * @uats_valid: is AP type table valid
* @uefi_tables_lock_status: The status of the WIFI GUID UEFI variables lock:
* 0: Unlocked, 1 and 2: Locked.
* Only read the UEFI variables if locked.
* @sar_profiles: sar profiles as read from WRDS/EWRD BIOS tables
* @geo_profiles: geographic profiles as read from WGDS BIOS table
+ * @phy_filters: specific phy filters as read from WPFC BIOS table
+ * @ppag_bios_rev: PPAG BIOS revision
+ * @ppag_bios_source: see &enum bios_source
*/
struct iwl_fw_runtime {
struct iwl_trans *trans;
@@ -177,11 +181,14 @@ struct iwl_fw_runtime {
bool geo_enabled;
struct iwl_ppag_chain ppag_chains[IWL_NUM_CHAIN_LIMITS];
u32 ppag_flags;
- u8 ppag_ver;
+ u8 ppag_bios_rev;
+ u8 ppag_bios_source;
struct iwl_sar_offset_mapping_cmd sgom_table;
bool sgom_enabled;
struct iwl_mcc_allowed_ap_type_cmd uats_table;
+ bool uats_valid;
u8 uefi_tables_lock_status;
+ struct iwl_phy_specific_cfg phy_filters;
};
void iwl_fw_runtime_init(struct iwl_fw_runtime *fwrt, struct iwl_trans *trans,
diff --git a/drivers/net/wireless/intel/iwlwifi/fw/smem.c b/drivers/net/wireless/intel/iwlwifi/fw/smem.c
index 3f1272014daf..90fd69b4860c 100644
--- a/drivers/net/wireless/intel/iwlwifi/fw/smem.c
+++ b/drivers/net/wireless/intel/iwlwifi/fw/smem.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
/*
- * Copyright (C) 2012-2014, 2018-2021 Intel Corporation
+ * Copyright (C) 2012-2014, 2018-2021, 2025 Intel Corporation
* Copyright (C) 2013-2015 Intel Mobile Communications GmbH
* Copyright (C) 2016-2017 Intel Deutschland GmbH
*/
@@ -102,7 +102,7 @@ void iwl_get_shared_mem_conf(struct iwl_fw_runtime *fwrt)
}
pkt = cmd.resp_pkt;
- if (fwrt->trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_22000)
+ if (fwrt->trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_22000)
iwl_parse_shared_mem_22000(fwrt, pkt);
else
iwl_parse_shared_mem(fwrt, pkt);
diff --git a/drivers/net/wireless/intel/iwlwifi/fw/uefi.c b/drivers/net/wireless/intel/iwlwifi/fw/uefi.c
index 434eed4130b9..48126ec6b94b 100644
--- a/drivers/net/wireless/intel/iwlwifi/fw/uefi.c
+++ b/drivers/net/wireless/intel/iwlwifi/fw/uefi.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
/*
- * Copyright(c) 2021-2024 Intel Corporation
+ * Copyright(c) 2021-2025 Intel Corporation
*/
#include "iwl-drv.h"
@@ -219,7 +219,8 @@ done:
int iwl_uefi_reduce_power_parse(struct iwl_trans *trans,
const u8 *data, size_t len,
- struct iwl_pnvm_image *pnvm_data)
+ struct iwl_pnvm_image *pnvm_data,
+ __le32 sku_id[3])
{
const struct iwl_ucode_tlv *tlv;
@@ -241,23 +242,23 @@ int iwl_uefi_reduce_power_parse(struct iwl_trans *trans,
}
if (tlv_type == IWL_UCODE_TLV_PNVM_SKU) {
- const struct iwl_sku_id *sku_id =
+ const struct iwl_sku_id *tlv_sku_id =
(const void *)(data + sizeof(*tlv));
IWL_DEBUG_FW(trans,
"Got IWL_UCODE_TLV_PNVM_SKU len %d\n",
tlv_len);
IWL_DEBUG_FW(trans, "sku_id 0x%0x 0x%0x 0x%0x\n",
- le32_to_cpu(sku_id->data[0]),
- le32_to_cpu(sku_id->data[1]),
- le32_to_cpu(sku_id->data[2]));
+ le32_to_cpu(tlv_sku_id->data[0]),
+ le32_to_cpu(tlv_sku_id->data[1]),
+ le32_to_cpu(tlv_sku_id->data[2]));
data += sizeof(*tlv) + ALIGN(tlv_len, 4);
len -= ALIGN(tlv_len, 4);
- if (trans->sku_id[0] == le32_to_cpu(sku_id->data[0]) &&
- trans->sku_id[1] == le32_to_cpu(sku_id->data[1]) &&
- trans->sku_id[2] == le32_to_cpu(sku_id->data[2])) {
+ if (sku_id[0] == tlv_sku_id->data[0] &&
+ sku_id[1] == tlv_sku_id->data[1] &&
+ sku_id[2] == tlv_sku_id->data[2]) {
int ret = iwl_uefi_reduce_power_section(trans,
data, len,
pnvm_data);
@@ -310,11 +311,12 @@ static int iwl_uefi_step_parse(struct uefi_cnv_common_step_data *common_step_dat
if (common_step_data->revision != 1)
return -EINVAL;
- trans->mbx_addr_0_step = (u32)common_step_data->revision |
+ trans->conf.mbx_addr_0_step =
+ (u32)common_step_data->revision |
(u32)common_step_data->cnvi_eq_channel << 8 |
(u32)common_step_data->cnvr_eq_channel << 16 |
(u32)common_step_data->radio1 << 24;
- trans->mbx_addr_1_step = (u32)common_step_data->radio2;
+ trans->conf.mbx_addr_1_step = (u32)common_step_data->radio2;
return 0;
}
@@ -323,7 +325,7 @@ void iwl_uefi_get_step_table(struct iwl_trans *trans)
struct uefi_cnv_common_step_data *data;
int ret;
- if (trans->trans_cfg->device_family < IWL_DEVICE_FAMILY_AX210)
+ if (trans->mac_cfg->device_family < IWL_DEVICE_FAMILY_AX210)
return;
data = iwl_uefi_get_verified_variable_guid(trans, &IWL_EFI_WIFI_BT_GUID,
@@ -402,11 +404,14 @@ static int iwl_uefi_uats_parse(struct uefi_cnv_wlan_uats_data *uats_data,
memcpy(fwrt->uats_table.offset_map, uats_data->offset_map,
sizeof(fwrt->uats_table.offset_map));
+
+ fwrt->uats_valid = true;
+
return 0;
}
-int iwl_uefi_get_uats_table(struct iwl_trans *trans,
- struct iwl_fw_runtime *fwrt)
+void iwl_uefi_get_uats_table(struct iwl_trans *trans,
+ struct iwl_fw_runtime *fwrt)
{
struct uefi_cnv_wlan_uats_data *data;
int ret;
@@ -414,17 +419,12 @@ int iwl_uefi_get_uats_table(struct iwl_trans *trans,
data = iwl_uefi_get_verified_variable(trans, IWL_UEFI_UATS_NAME,
"UATS", sizeof(*data), NULL);
if (IS_ERR(data))
- return -EINVAL;
+ return;
ret = iwl_uefi_uats_parse(data, fwrt);
- if (ret < 0) {
+ if (ret < 0)
IWL_DEBUG_FW(trans, "Cannot read UATS table. rev is invalid\n");
- kfree(data);
- return ret;
- }
-
kfree(data);
- return 0;
}
IWL_EXPORT_SYMBOL(iwl_uefi_get_uats_table);
@@ -554,13 +554,14 @@ int iwl_uefi_get_ppag_table(struct iwl_fw_runtime *fwrt)
goto out;
}
- fwrt->ppag_ver = data->revision;
+ fwrt->ppag_bios_rev = data->revision;
fwrt->ppag_flags = iwl_bios_get_ppag_flags(data->ppag_modes,
- fwrt->ppag_ver);
+ fwrt->ppag_bios_rev);
BUILD_BUG_ON(sizeof(fwrt->ppag_chains) != sizeof(data->ppag_chains));
memcpy(&fwrt->ppag_chains, &data->ppag_chains,
sizeof(data->ppag_chains));
+ fwrt->ppag_bios_source = BIOS_SOURCE_UEFI;
out:
kfree(data);
return ret;
@@ -678,14 +679,16 @@ int iwl_uefi_get_eckv(struct iwl_fw_runtime *fwrt, u32 *extl_clk)
struct uefi_cnv_var_eckv *data;
int ret = 0;
- data = iwl_uefi_get_verified_variable(fwrt->trans, IWL_UEFI_ECKV_NAME,
- "ECKV", sizeof(*data), NULL);
+ data = iwl_uefi_get_verified_variable_guid(fwrt->trans,
+ &IWL_EFI_WIFI_BT_GUID,
+ IWL_UEFI_ECKV_NAME,
+ "ECKV", sizeof(*data), NULL);
if (IS_ERR(data))
return -EINVAL;
if (data->revision != IWL_UEFI_ECKV_REVISION) {
ret = -EINVAL;
- IWL_DEBUG_RADIO(fwrt, "Unsupported UEFI WRDD revision:%d\n",
+ IWL_DEBUG_RADIO(fwrt, "Unsupported UEFI ECKV revision:%d\n",
data->revision);
goto out;
}
@@ -745,6 +748,10 @@ int iwl_uefi_get_dsm(struct iwl_fw_runtime *fwrt, enum iwl_dsm_funcs func,
}
*value = data->functions[func];
+
+ IWL_DEBUG_RADIO(fwrt,
+ "UEFI: DSM func=%d: value=%d\n", func, *value);
+
ret = 0;
out:
kfree(data);
@@ -805,3 +812,31 @@ out:
kfree(data);
return ret;
}
+
+int iwl_uefi_get_phy_filters(struct iwl_fw_runtime *fwrt)
+{
+ struct uefi_cnv_wpfc_data *data __free(kfree);
+ struct iwl_phy_specific_cfg *filters = &fwrt->phy_filters;
+
+ data = iwl_uefi_get_verified_variable(fwrt->trans, IWL_UEFI_WPFC_NAME,
+ "WPFC", sizeof(*data), NULL);
+ if (IS_ERR(data))
+ return -EINVAL;
+
+ if (data->revision != 0) {
+ IWL_DEBUG_RADIO(fwrt, "Unsupported UEFI WPFC revision:%d\n",
+ data->revision);
+ return -EINVAL;
+ }
+
+ BUILD_BUG_ON(ARRAY_SIZE(filters->filter_cfg_chains) !=
+ ARRAY_SIZE(data->chains));
+
+ for (int i = 0; i < ARRAY_SIZE(filters->filter_cfg_chains); i++) {
+ filters->filter_cfg_chains[i] = cpu_to_le32(data->chains[i]);
+ IWL_DEBUG_RADIO(fwrt, "WPFC: chain %d: %u\n", i, data->chains[i]);
+ }
+
+ IWL_DEBUG_RADIO(fwrt, "Loaded WPFC config from UEFI\n");
+ return 0;
+}
diff --git a/drivers/net/wireless/intel/iwlwifi/fw/uefi.h b/drivers/net/wireless/intel/iwlwifi/fw/uefi.h
index 0c8943a8bd01..5a4c557e47c7 100644
--- a/drivers/net/wireless/intel/iwlwifi/fw/uefi.h
+++ b/drivers/net/wireless/intel/iwlwifi/fw/uefi.h
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
/*
- * Copyright(c) 2021-2024 Intel Corporation
+ * Copyright(c) 2021-2025 Intel Corporation
*/
#ifndef __iwl_fw_uefi__
#define __iwl_fw_uefi__
@@ -19,11 +19,12 @@
#define IWL_UEFI_WTAS_NAME L"UefiCnvWlanWTAS"
#define IWL_UEFI_SPLC_NAME L"UefiCnvWlanSPLC"
#define IWL_UEFI_WRDD_NAME L"UefiCnvWlanWRDD"
-#define IWL_UEFI_ECKV_NAME L"UefiCnvWlanECKV"
+#define IWL_UEFI_ECKV_NAME L"UefiCnvCommonECKV"
#define IWL_UEFI_DSM_NAME L"UefiCnvWlanGeneralCfg"
#define IWL_UEFI_WBEM_NAME L"UefiCnvWlanWBEM"
#define IWL_UEFI_PUNCTURING_NAME L"UefiCnvWlanPuncturing"
#define IWL_UEFI_DSBR_NAME L"UefiCnvCommonDSBR"
+#define IWL_UEFI_WPFC_NAME L"WPFC"
#define IWL_SGOM_MAP_SIZE 339
@@ -33,7 +34,7 @@
#define IWL_UEFI_EWRD_REVISION 2
#define IWL_UEFI_WGDS_REVISION 3
#define IWL_UEFI_MIN_PPAG_REV 1
-#define IWL_UEFI_MAX_PPAG_REV 3
+#define IWL_UEFI_MAX_PPAG_REV 4
#define IWL_UEFI_MIN_WTAS_REVISION 1
#define IWL_UEFI_MAX_WTAS_REVISION 2
#define IWL_UEFI_SPLC_REVISION 0
@@ -230,6 +231,18 @@ struct uefi_cnv_wlan_dsbr_data {
u32 config;
} __packed;
+/**
+ * struct uefi_cnv_wpfc_data - BIOS Wi-Fi PHY filter Configuration
+ * @revision: the revision of the table
+ * @chains: configuration of each of the chains (a-d)
+ *
+ * specific PHY filter configuration
+ */
+struct uefi_cnv_wpfc_data {
+ u8 revision;
+ u32 chains[4];
+} __packed;
+
/*
* This is known to be broken on v4.19 and to work on v5.4. Until we
* figure out why this is the case and how to make it work, simply
@@ -240,7 +253,8 @@ void *iwl_uefi_get_pnvm(struct iwl_trans *trans, size_t *len);
u8 *iwl_uefi_get_reduced_power(struct iwl_trans *trans, size_t *len);
int iwl_uefi_reduce_power_parse(struct iwl_trans *trans,
const u8 *data, size_t len,
- struct iwl_pnvm_image *pnvm_data);
+ struct iwl_pnvm_image *pnvm_data,
+ __le32 sku_id[3]);
void iwl_uefi_get_step_table(struct iwl_trans *trans);
int iwl_uefi_handle_tlv_mem_desc(struct iwl_trans *trans, const u8 *data,
u32 tlv_len, struct iwl_pnvm_image *pnvm_data);
@@ -258,10 +272,11 @@ int iwl_uefi_get_wbem(struct iwl_fw_runtime *fwrt, u32 *value);
int iwl_uefi_get_dsm(struct iwl_fw_runtime *fwrt, enum iwl_dsm_funcs func,
u32 *value);
void iwl_uefi_get_sgom_table(struct iwl_trans *trans, struct iwl_fw_runtime *fwrt);
-int iwl_uefi_get_uats_table(struct iwl_trans *trans,
- struct iwl_fw_runtime *fwrt);
+void iwl_uefi_get_uats_table(struct iwl_trans *trans,
+ struct iwl_fw_runtime *fwrt);
int iwl_uefi_get_puncturing(struct iwl_fw_runtime *fwrt);
int iwl_uefi_get_dsbr(struct iwl_fw_runtime *fwrt, u32 *value);
+int iwl_uefi_get_phy_filters(struct iwl_fw_runtime *fwrt);
#else /* CONFIG_EFI */
static inline void *iwl_uefi_get_pnvm(struct iwl_trans *trans, size_t *len)
{
@@ -271,7 +286,8 @@ static inline void *iwl_uefi_get_pnvm(struct iwl_trans *trans, size_t *len)
static inline int
iwl_uefi_reduce_power_parse(struct iwl_trans *trans,
const u8 *data, size_t len,
- struct iwl_pnvm_image *pnvm_data)
+ struct iwl_pnvm_image *pnvm_data,
+ __le32 sku_id[3])
{
return -EOPNOTSUPP;
}
@@ -352,11 +368,9 @@ void iwl_uefi_get_sgom_table(struct iwl_trans *trans, struct iwl_fw_runtime *fwr
{
}
-static inline
-int iwl_uefi_get_uats_table(struct iwl_trans *trans,
- struct iwl_fw_runtime *fwrt)
+static inline void
+iwl_uefi_get_uats_table(struct iwl_trans *trans, struct iwl_fw_runtime *fwrt)
{
- return 0;
}
static inline
@@ -370,5 +384,10 @@ int iwl_uefi_get_dsbr(struct iwl_fw_runtime *fwrt, u32 *value)
{
return -ENOENT;
}
+
+static inline int iwl_uefi_get_phy_filters(struct iwl_fw_runtime *fwrt)
+{
+ return -ENOENT;
+}
#endif /* CONFIG_EFI */
#endif /* __iwl_fw_uefi__ */
diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-config.h b/drivers/net/wireless/intel/iwlwifi/iwl-config.h
index 2b6a80142aba..91f22ce36d74 100644
--- a/drivers/net/wireless/intel/iwlwifi/iwl-config.h
+++ b/drivers/net/wireless/intel/iwlwifi/iwl-config.h
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2005-2014, 2018-2021 Intel Corporation
* Copyright (C) 2016-2017 Intel Deutschland GmbH
- * Copyright (C) 2018-2024 Intel Corporation
+ * Copyright (C) 2018-2025 Intel Corporation
*/
#ifndef __IWL_CONFIG_H__
#define __IWL_CONFIG_H__
@@ -115,7 +115,29 @@ static inline u8 num_of_ant(u8 mask)
}
/**
- * struct iwl_base_params - params not likely to change within a device family
+ * struct iwl_fw_mon_reg - FW monitor register info
+ * @addr: register address
+ * @mask: register mask
+ */
+struct iwl_fw_mon_reg {
+ u32 addr;
+ u32 mask;
+};
+
+/**
+ * struct iwl_fw_mon_regs - FW monitor registers
+ * @write_ptr: write pointer register
+ * @cycle_cnt: cycle count register
+ * @cur_frag: current fragment in use
+ */
+struct iwl_fw_mon_regs {
+ struct iwl_fw_mon_reg write_ptr;
+ struct iwl_fw_mon_reg cycle_cnt;
+ struct iwl_fw_mon_reg cur_frag;
+};
+
+/**
+ * struct iwl_family_base_params - base parameters for an entire family
* @max_ll_items: max number of OTP blocks
* @shadow_ram_support: shadow support for OTP memory
* @led_compensation: compensate on the led on/off time per HW according
@@ -124,12 +146,34 @@ static inline u8 num_of_ant(u8 mask)
* @wd_timeout: TX queues watchdog timeout
* @max_event_log_size: size of event log buffer size for ucode event logging
* @shadow_reg_enable: HW shadow register support
+ * @apmg_not_supported: there's no APMG
* @apmg_wake_up_wa: should the MAC access REQ be asserted when a command
* is in flight. This is due to a HW bug in 7260, 3160 and 7265.
* @scd_chain_ext_wa: should the chain extension feature in SCD be disabled.
* @max_tfd_queue_size: max number of entries in tfd queue.
+ * @eeprom_size: EEPROM size
+ * @num_of_queues: number of HW TX queues supported
+ * @pcie_l1_allowed: PCIe L1 state is allowed
+ * @pll_cfg: PLL configuration needed
+ * @nvm_hw_section_num: the ID of the HW NVM section
+ * @features: hw features, any combination of feature_passlist
+ * @smem_offset: offset from which the SMEM begins
+ * @smem_len: the length of SMEM
+ * @mac_addr_from_csr: read HW address from CSR registers at this offset
+ * @d3_debug_data_base_addr: base address where D3 debug data is stored
+ * @d3_debug_data_length: length of the D3 debug data
+ * @min_ba_txq_size: minimum number of slots required in a TX queue used
+ * for aggregation
+ * @min_txq_size: minimum number of slots required in a TX queue
+ * @gp2_reg_addr: GP2 (timer) register address
+ * @min_umac_error_event_table: minimum SMEM location of UMAC error table
+ * @mon_dbgi_regs: monitor DBGI registers
+ * @mon_dram_regs: monitor DRAM registers
+ * @mon_smem_regs: monitor SMEM registers
+ * @ucode_api_max: Highest version of uCode API supported by driver.
+ * @ucode_api_min: Lowest version of uCode API supported by driver.
*/
-struct iwl_base_params {
+struct iwl_family_base_params {
unsigned int wd_timeout;
u16 eeprom_size;
@@ -140,6 +184,7 @@ struct iwl_base_params {
shadow_reg_enable:1,
pcie_l1_allowed:1,
apmg_wake_up_wa:1,
+ apmg_not_supported:1,
scd_chain_ext_wa:1;
u16 num_of_queues; /* def: HW dependent */
@@ -147,6 +192,22 @@ struct iwl_base_params {
u8 max_ll_items;
u8 led_compensation;
+ u8 ucode_api_max;
+ u8 ucode_api_min;
+ u32 mac_addr_from_csr:10;
+ u8 nvm_hw_section_num;
+ netdev_features_t features;
+ u32 smem_offset;
+ u32 smem_len;
+ u32 min_umac_error_event_table;
+ u32 d3_debug_data_base_addr;
+ u32 d3_debug_data_length;
+ u32 min_txq_size;
+ u32 gp2_reg_addr;
+ u32 min_ba_txq_size;
+ const struct iwl_fw_mon_regs mon_dram_regs;
+ const struct iwl_fw_mon_regs mon_smem_regs;
+ const struct iwl_fw_mon_regs mon_dbgi_regs;
};
/*
@@ -238,7 +299,7 @@ struct iwl_pwr_tx_backoff {
u32 backoff;
};
-enum iwl_cfg_trans_ltr_delay {
+enum iwl_mac_cfg_ltr_delay {
IWL_CFG_TRANS_LTR_DELAY_NONE = 0,
IWL_CFG_TRANS_LTR_DELAY_200US = 1,
IWL_CFG_TRANS_LTR_DELAY_2500US = 2,
@@ -246,35 +307,33 @@ enum iwl_cfg_trans_ltr_delay {
};
/**
- * struct iwl_cfg_trans_params - information needed to start the trans
+ * struct iwl_mac_cfg - information about the MAC-specific device part
*
* These values are specific to the device ID and do not change when
* multiple configs are used for a single device ID. They values are
* used, among other things, to boot the NIC so that the HW REV or
* RFID can be read before deciding the remaining parameters to use.
*
- * @base_params: pointer to basic parameters
+ * @base: pointer to basic parameters
* @device_family: the device family
* @umac_prph_offset: offset to add to UMAC periphery address
* @xtal_latency: power up latency to get the xtal stabilized
* @extra_phy_cfg_flags: extra configuration flags to pass to the PHY
- * @rf_id: need to read rf_id to determine the firmware image
* @gen2: 22000 and on transport operation
* @mq_rx_supported: multi-queue rx support
* @integrated: discrete or integrated
* @low_latency_xtal: use the low latency xtal if supported
* @bisr_workaround: BISR hardware workaround (for 22260 series devices)
- * @ltr_delay: LTR delay parameter, &enum iwl_cfg_trans_ltr_delay.
+ * @ltr_delay: LTR delay parameter, &enum iwl_mac_cfg_ltr_delay.
* @imr_enabled: use the IMR if supported.
*/
-struct iwl_cfg_trans_params {
- const struct iwl_base_params *base_params;
+struct iwl_mac_cfg {
+ const struct iwl_family_base_params *base;
enum iwl_device_family device_family;
u32 umac_prph_offset;
u32 xtal_latency;
u32 extra_phy_cfg_flags;
- u32 rf_id:1,
- gen2:1,
+ u32 gen2:1,
mq_rx_supported:1,
integrated:1,
low_latency_xtal:1,
@@ -283,36 +342,21 @@ struct iwl_cfg_trans_params {
imr_enabled:1;
};
-/**
- * struct iwl_fw_mon_reg - FW monitor register info
- * @addr: register address
- * @mask: register mask
- */
-struct iwl_fw_mon_reg {
- u32 addr;
- u32 mask;
-};
-
-/**
- * struct iwl_fw_mon_regs - FW monitor registers
- * @write_ptr: write pointer register
- * @cycle_cnt: cycle count register
- * @cur_frag: current fragment in use
+/*
+ * These sizes were picked according to 8 MSDUs inside 64/256/612 A-MSDUs
+ * in an A-MPDU, with additional overhead to account for processing time.
+ * They will be doubled for MACs starting from So/Ty that don't support
+ * putting multiple frames into a single buffer.
*/
-struct iwl_fw_mon_regs {
- struct iwl_fw_mon_reg write_ptr;
- struct iwl_fw_mon_reg cycle_cnt;
- struct iwl_fw_mon_reg cur_frag;
-};
+#define IWL_NUM_RBDS_NON_HE (64 * 8)
+#define IWL_NUM_RBDS_HE (256 * 8)
+#define IWL_NUM_RBDS_EHT (512 * 8)
/**
- * struct iwl_cfg
- * @trans: the trans-specific configuration part
- * @name: Official name of the device
+ * struct iwl_rf_cfg
* @fw_name_pre: Firmware filename prefix. The api version and extension
* (.ucode) will be added to filename before loading from disk. The
* filename is constructed as <fw_name_pre>-<api>.ucode.
- * @fw_name_mac: MAC name for this config, the remaining pieces of the
* name will be generated dynamically
* @ucode_api_max: Highest version of uCode API supported by driver.
* @ucode_api_min: Lowest version of uCode API supported by driver.
@@ -323,34 +367,25 @@ struct iwl_fw_mon_regs {
* @non_shared_ant: the antenna that is for WiFi only
* @nvm_ver: NVM version
* @nvm_calib_ver: NVM calibration version
+ * @bw_limit: bandwidth limit for this device, if non-zero
* @ht_params: point to ht parameters
+ * @eeprom_params: EEPROM parameters (old devices)
+ * @thermal_params: Thermal throttling parameters
+ * @lp_xtal_workaround: low-power crystal workaround needed
* @led_mode: 0=blinking, 1=On(RF On)/Off(RF Off)
* @rx_with_siso_diversity: 1x1 device with rx antenna diversity
* @tx_with_siso_diversity: 1x1 device with tx antenna diversity
* @internal_wimax_coex: internal wifi/wimax combo device
- * @high_temp: Is this NIC is designated to be in high temperature.
* @host_interrupt_operation_mode: device needs host interrupt operation
* mode set
- * @nvm_hw_section_num: the ID of the HW NVM section
- * @mac_addr_from_csr: read HW address from CSR registers at this offset
- * @features: hw features, any combination of feature_passlist
* @pwr_tx_backoffs: translation table between power limits and backoffs
- * @max_tx_agg_size: max TX aggregation size of the ADDBA request/response
* @dccm_offset: offset from which DCCM begins
* @dccm_len: length of DCCM (including runtime stack CCM)
* @dccm2_offset: offset from which the second DCCM begins
* @dccm2_len: length of the second DCCM
- * @smem_offset: offset from which the SMEM begins
- * @smem_len: the length of SMEM
* @vht_mu_mimo_supported: VHT MU-MIMO support
- * @cdb: CDB support
* @nvm_type: see &enum iwl_nvm_type
- * @d3_debug_data_base_addr: base address where D3 debug data is stored
- * @d3_debug_data_length: length of the D3 debug data
- * @min_txq_size: minimum number of slots required in a TX queue
* @uhb_supported: ultra high band channels supported
- * @min_ba_txq_size: minimum number of slots required in a TX queue which
- * based on hardware support (HE - 256, EHT - 1K).
* @num_rbds: number of receive buffer descriptors to use
* (only used for multi-queue capable devices)
*
@@ -358,60 +393,38 @@ struct iwl_fw_mon_regs {
* API differences in uCode shouldn't be handled here but through TLVs
* and/or the uCode API version instead.
*/
-struct iwl_cfg {
- struct iwl_cfg_trans_params trans;
+struct iwl_rf_cfg {
/* params specific to an individual device within a device family */
- const char *name;
const char *fw_name_pre;
- const char *fw_name_mac;
/* params likely to change within a device family */
- const struct iwl_ht_params *ht_params;
+ const struct iwl_ht_params ht_params;
const struct iwl_eeprom_params *eeprom_params;
const struct iwl_pwr_tx_backoff *pwr_tx_backoffs;
- const char *default_nvm_file_C_step;
const struct iwl_tt_params *thermal_params;
enum iwl_led_mode led_mode;
enum iwl_nvm_type nvm_type;
u32 max_data_size;
u32 max_inst_size;
- netdev_features_t features;
u32 dccm_offset;
u32 dccm_len;
u32 dccm2_offset;
u32 dccm2_len;
- u32 smem_offset;
- u32 smem_len;
u16 nvm_ver;
u16 nvm_calib_ver;
+ u16 bw_limit;
u32 rx_with_siso_diversity:1,
tx_with_siso_diversity:1,
internal_wimax_coex:1,
host_interrupt_operation_mode:1,
- high_temp:1,
- mac_addr_from_csr:10,
lp_xtal_workaround:1,
- apmg_not_supported:1,
vht_mu_mimo_supported:1,
- cdb:1,
- dbgc_supported:1,
uhb_supported:1;
u8 valid_tx_ant;
u8 valid_rx_ant;
u8 non_shared_ant;
- u8 nvm_hw_section_num;
- u8 max_tx_agg_size;
u8 ucode_api_max;
u8 ucode_api_min;
u16 num_rbds;
- u32 min_umac_error_event_table;
- u32 d3_debug_data_base_addr;
- u32 d3_debug_data_length;
- u32 min_txq_size;
- u32 gp2_reg_addr;
- u32 min_ba_txq_size;
- const struct iwl_fw_mon_regs mon_dram_regs;
- const struct iwl_fw_mon_regs mon_smem_regs;
- const struct iwl_fw_mon_regs mon_dbgi_regs;
};
#define IWL_CFG_ANY (~0)
@@ -419,8 +432,10 @@ struct iwl_cfg {
#define IWL_CFG_MAC_TYPE_PU 0x31
#define IWL_CFG_MAC_TYPE_TH 0x32
#define IWL_CFG_MAC_TYPE_QU 0x33
+#define IWL_CFG_MAC_TYPE_CC 0x34
#define IWL_CFG_MAC_TYPE_QUZ 0x35
#define IWL_CFG_MAC_TYPE_SO 0x37
+#define IWL_CFG_MAC_TYPE_TY 0x42
#define IWL_CFG_MAC_TYPE_SOF 0x43
#define IWL_CFG_MAC_TYPE_MA 0x44
#define IWL_CFG_MAC_TYPE_BZ 0x46
@@ -432,8 +447,6 @@ struct iwl_cfg {
#define IWL_CFG_MAC_TYPE_BR 0x4C
#define IWL_CFG_MAC_TYPE_DR 0x4D
-#define IWL_CFG_RF_TYPE_TH 0x105
-#define IWL_CFG_RF_TYPE_TH1 0x108
#define IWL_CFG_RF_TYPE_JF2 0x105
#define IWL_CFG_RF_TYPE_JF1 0x108
#define IWL_CFG_RF_TYPE_HR2 0x10A
@@ -451,12 +464,6 @@ struct iwl_cfg {
#define IWL_CFG_RF_ID_HR 0x7
#define IWL_CFG_RF_ID_HR1 0x4
-#define IWL_CFG_NO_160 0x1
-#define IWL_CFG_160 0x0
-
-#define IWL_CFG_NO_320 0x1
-#define IWL_CFG_320 0x0
-
#define IWL_CFG_CORES_BT 0x0
#define IWL_CFG_CORES_BT_GNSS 0x5
@@ -467,55 +474,133 @@ struct iwl_cfg {
#define IWL_CFG_IS_JACKET 0x1
#define IWL_SUBDEVICE_RF_ID(subdevice) ((u16)((subdevice) & 0x00F0) >> 4)
-#define IWL_SUBDEVICE_NO_160(subdevice) ((u16)((subdevice) & 0x0200) >> 9)
+#define IWL_SUBDEVICE_BW_LIM(subdevice) ((u16)((subdevice) & 0x0200) >> 9)
#define IWL_SUBDEVICE_CORES(subdevice) ((u16)((subdevice) & 0x1C00) >> 10)
struct iwl_dev_info {
+ const struct iwl_rf_cfg *cfg;
+ const char *name;
u16 device;
u16 subdevice;
- u16 mac_type;
- u16 rf_type;
- u8 mac_step;
- u8 rf_step;
- u8 rf_id;
- u8 no_160;
- u8 cores;
- u8 cdb;
- u8 jacket;
- const struct iwl_cfg *cfg;
- const char *name;
+ u32 subdevice_m_l:4,
+ subdevice_m_h:4,
+ match_rf_type:1,
+ rf_type:9,
+ match_bw_limit:1,
+ bw_limit:1,
+ match_rf_step:1,
+ rf_step:4,
+ match_rf_id:1,
+ rf_id:4,
+ match_cdb:1,
+ cdb:1;
};
#if IS_ENABLED(CONFIG_IWLWIFI_KUNIT_TESTS)
extern const struct iwl_dev_info iwl_dev_info_table[];
extern const unsigned int iwl_dev_info_table_size;
const struct iwl_dev_info *
-iwl_pci_find_dev_info(u16 device, u16 subsystem_device,
- u16 mac_type, u8 mac_step, u16 rf_type, u8 cdb,
- u8 jacket, u8 rf_id, u8 no_160, u8 cores, u8 rf_step);
+iwl_pci_find_dev_info(u16 device, u16 subsystem_device, u16 rf_type, u8 cdb,
+ u8 rf_id, u8 bw_limit, u8 rf_step);
extern const struct pci_device_id iwl_hw_card_ids[];
#endif
/*
* This list declares the config structures for all devices.
*/
-extern const struct iwl_cfg_trans_params iwl9000_trans_cfg;
-extern const struct iwl_cfg_trans_params iwl9560_trans_cfg;
-extern const struct iwl_cfg_trans_params iwl9560_long_latency_trans_cfg;
-extern const struct iwl_cfg_trans_params iwl9560_shared_clk_trans_cfg;
-extern const struct iwl_cfg_trans_params iwl_qu_trans_cfg;
-extern const struct iwl_cfg_trans_params iwl_qu_medium_latency_trans_cfg;
-extern const struct iwl_cfg_trans_params iwl_qu_long_latency_trans_cfg;
-extern const struct iwl_cfg_trans_params iwl_ax200_trans_cfg;
-extern const struct iwl_cfg_trans_params iwl_so_trans_cfg;
-extern const struct iwl_cfg_trans_params iwl_so_long_latency_trans_cfg;
-extern const struct iwl_cfg_trans_params iwl_so_long_latency_imr_trans_cfg;
-extern const struct iwl_cfg_trans_params iwl_ma_trans_cfg;
-extern const struct iwl_cfg_trans_params iwl_bz_trans_cfg;
-extern const struct iwl_cfg_trans_params iwl_gl_trans_cfg;
-extern const struct iwl_cfg_trans_params iwl_sc_trans_cfg;
-extern const struct iwl_cfg_trans_params iwl_dr_trans_cfg;
-extern const struct iwl_cfg_trans_params iwl_br_trans_cfg;
+extern const struct iwl_mac_cfg iwl1000_mac_cfg;
+extern const struct iwl_mac_cfg iwl5000_mac_cfg;
+extern const struct iwl_mac_cfg iwl2000_mac_cfg;
+extern const struct iwl_mac_cfg iwl2030_mac_cfg;
+extern const struct iwl_mac_cfg iwl105_mac_cfg;
+extern const struct iwl_mac_cfg iwl135_mac_cfg;
+extern const struct iwl_mac_cfg iwl5150_mac_cfg;
+extern const struct iwl_mac_cfg iwl6005_mac_cfg;
+extern const struct iwl_mac_cfg iwl6030_mac_cfg;
+extern const struct iwl_mac_cfg iwl6000i_mac_cfg;
+extern const struct iwl_mac_cfg iwl6050_mac_cfg;
+extern const struct iwl_mac_cfg iwl6150_mac_cfg;
+extern const struct iwl_mac_cfg iwl6000_mac_cfg;
+extern const struct iwl_mac_cfg iwl7000_mac_cfg;
+extern const struct iwl_mac_cfg iwl8000_mac_cfg;
+extern const struct iwl_mac_cfg iwl9000_mac_cfg;
+extern const struct iwl_mac_cfg iwl9560_mac_cfg;
+extern const struct iwl_mac_cfg iwl9560_long_latency_mac_cfg;
+extern const struct iwl_mac_cfg iwl9560_shared_clk_mac_cfg;
+extern const struct iwl_mac_cfg iwl_qu_mac_cfg;
+extern const struct iwl_mac_cfg iwl_qu_medium_latency_mac_cfg;
+extern const struct iwl_mac_cfg iwl_qu_long_latency_mac_cfg;
+extern const struct iwl_mac_cfg iwl_ax200_mac_cfg;
+extern const struct iwl_mac_cfg iwl_ty_mac_cfg;
+extern const struct iwl_mac_cfg iwl_so_mac_cfg;
+extern const struct iwl_mac_cfg iwl_so_long_latency_mac_cfg;
+extern const struct iwl_mac_cfg iwl_so_long_latency_imr_mac_cfg;
+extern const struct iwl_mac_cfg iwl_ma_mac_cfg;
+extern const struct iwl_mac_cfg iwl_bz_mac_cfg;
+extern const struct iwl_mac_cfg iwl_gl_mac_cfg;
+extern const struct iwl_mac_cfg iwl_sc_mac_cfg;
+extern const struct iwl_mac_cfg iwl_dr_mac_cfg;
+
+extern const char iwl1000_bgn_name[];
+extern const char iwl1000_bg_name[];
+extern const char iwl100_bgn_name[];
+extern const char iwl100_bg_name[];
+extern const char iwl2000_2bgn_name[];
+extern const char iwl2000_2bgn_d_name[];
+extern const char iwl2030_2bgn_name[];
+extern const char iwl105_bgn_name[];
+extern const char iwl105_bgn_d_name[];
+extern const char iwl135_bgn_name[];
+extern const char iwl5300_agn_name[];
+extern const char iwl5100_bgn_name[];
+extern const char iwl5100_abg_name[];
+extern const char iwl5100_agn_name[];
+extern const char iwl5350_agn_name[];
+extern const char iwl5150_agn_name[];
+extern const char iwl5150_abg_name[];
+extern const char iwl6005_2agn_name[];
+extern const char iwl6005_2abg_name[];
+extern const char iwl6005_2bg_name[];
+extern const char iwl6005_2agn_sff_name[];
+extern const char iwl6005_2agn_d_name[];
+extern const char iwl6005_2agn_mow1_name[];
+extern const char iwl6005_2agn_mow2_name[];
+extern const char iwl6030_2agn_name[];
+extern const char iwl6030_2abg_name[];
+extern const char iwl6030_2bgn_name[];
+extern const char iwl6030_2bg_name[];
+extern const char iwl6035_2agn_name[];
+extern const char iwl6035_2agn_sff_name[];
+extern const char iwl1030_bgn_name[];
+extern const char iwl1030_bg_name[];
+extern const char iwl130_bgn_name[];
+extern const char iwl130_bg_name[];
+extern const char iwl6000i_2agn_name[];
+extern const char iwl6000i_2abg_name[];
+extern const char iwl6000i_2bg_name[];
+extern const char iwl6050_2agn_name[];
+extern const char iwl6050_2abg_name[];
+extern const char iwl6150_bgn_name[];
+extern const char iwl6150_bg_name[];
+extern const char iwl6000_3agn_name[];
+extern const char iwl7260_2ac_name[];
+extern const char iwl7260_2n_name[];
+extern const char iwl7260_n_name[];
+extern const char iwl3160_2ac_name[];
+extern const char iwl3160_2n_name[];
+extern const char iwl3160_n_name[];
+extern const char iwl3165_2ac_name[];
+extern const char iwl3168_2ac_name[];
+extern const char iwl7265_2ac_name[];
+extern const char iwl7265_2n_name[];
+extern const char iwl7265_n_name[];
+extern const char iwl8260_2n_name[];
+extern const char iwl8260_2ac_name[];
+extern const char iwl8265_2ac_name[];
+extern const char iwl8275_2ac_name[];
+extern const char iwl4165_2ac_name[];
+extern const char iwl_killer_1435i_name[];
+extern const char iwl_killer_1434_kix_name[];
extern const char iwl9162_name[];
extern const char iwl9260_name[];
extern const char iwl9260_1_name[];
@@ -534,7 +619,6 @@ extern const char iwl9560_killer_1550i_name[];
extern const char iwl9560_killer_1550s_name[];
extern const char iwl_ax200_name[];
extern const char iwl_ax203_name[];
-extern const char iwl_ax204_name[];
extern const char iwl_ax201_name[];
extern const char iwl_ax101_name[];
extern const char iwl_ax200_killer_1650w_name[];
@@ -549,128 +633,84 @@ extern const char iwl_ax211_killer_1675s_name[];
extern const char iwl_ax211_killer_1675i_name[];
extern const char iwl_ax411_killer_1690s_name[];
extern const char iwl_ax411_killer_1690i_name[];
+extern const char iwl_ax210_name[];
extern const char iwl_ax211_name[];
-extern const char iwl_ax221_name[];
-extern const char iwl_ax231_name[];
extern const char iwl_ax411_name[];
-extern const char iwl_bz_name[];
-extern const char iwl_fm_name[];
-extern const char iwl_wh_name[];
-extern const char iwl_gl_name[];
-extern const char iwl_mtp_name[];
-extern const char iwl_sc_name[];
-extern const char iwl_sc2_name[];
-extern const char iwl_sc2f_name[];
-extern const char iwl_dr_name[];
-extern const char iwl_br_name[];
+extern const char iwl_killer_be1750s_name[];
+extern const char iwl_killer_be1750i_name[];
+extern const char iwl_killer_be1750w_name[];
+extern const char iwl_killer_be1750x_name[];
+extern const char iwl_killer_be1790s_name[];
+extern const char iwl_killer_be1790i_name[];
+extern const char iwl_be201_name[];
+extern const char iwl_be200_name[];
+extern const char iwl_be202_name[];
+extern const char iwl_be401_name[];
+extern const char iwl_be213_name[];
+extern const char iwl_killer_be1775s_name[];
+extern const char iwl_killer_be1775i_name[];
+extern const char iwl_be211_name[];
+extern const char iwl_killer_bn1850w2_name[];
+extern const char iwl_killer_bn1850i_name[];
+extern const char iwl_bn201_name[];
+extern const char iwl_be221_name[];
+extern const char iwl_be223_name[];
#if IS_ENABLED(CONFIG_IWLDVM)
-extern const struct iwl_cfg iwl5300_agn_cfg;
-extern const struct iwl_cfg iwl5100_agn_cfg;
-extern const struct iwl_cfg iwl5350_agn_cfg;
-extern const struct iwl_cfg iwl5100_bgn_cfg;
-extern const struct iwl_cfg iwl5100_abg_cfg;
-extern const struct iwl_cfg iwl5150_agn_cfg;
-extern const struct iwl_cfg iwl5150_abg_cfg;
-extern const struct iwl_cfg iwl6005_2agn_cfg;
-extern const struct iwl_cfg iwl6005_2abg_cfg;
-extern const struct iwl_cfg iwl6005_2bg_cfg;
-extern const struct iwl_cfg iwl6005_2agn_sff_cfg;
-extern const struct iwl_cfg iwl6005_2agn_d_cfg;
-extern const struct iwl_cfg iwl6005_2agn_mow1_cfg;
-extern const struct iwl_cfg iwl6005_2agn_mow2_cfg;
-extern const struct iwl_cfg iwl1030_bgn_cfg;
-extern const struct iwl_cfg iwl1030_bg_cfg;
-extern const struct iwl_cfg iwl6030_2agn_cfg;
-extern const struct iwl_cfg iwl6030_2abg_cfg;
-extern const struct iwl_cfg iwl6030_2bgn_cfg;
-extern const struct iwl_cfg iwl6030_2bg_cfg;
-extern const struct iwl_cfg iwl6000i_2agn_cfg;
-extern const struct iwl_cfg iwl6000i_2abg_cfg;
-extern const struct iwl_cfg iwl6000i_2bg_cfg;
-extern const struct iwl_cfg iwl6000_3agn_cfg;
-extern const struct iwl_cfg iwl6050_2agn_cfg;
-extern const struct iwl_cfg iwl6050_2abg_cfg;
-extern const struct iwl_cfg iwl6150_bgn_cfg;
-extern const struct iwl_cfg iwl6150_bg_cfg;
-extern const struct iwl_cfg iwl1000_bgn_cfg;
-extern const struct iwl_cfg iwl1000_bg_cfg;
-extern const struct iwl_cfg iwl100_bgn_cfg;
-extern const struct iwl_cfg iwl100_bg_cfg;
-extern const struct iwl_cfg iwl130_bgn_cfg;
-extern const struct iwl_cfg iwl130_bg_cfg;
-extern const struct iwl_cfg iwl2000_2bgn_cfg;
-extern const struct iwl_cfg iwl2000_2bgn_d_cfg;
-extern const struct iwl_cfg iwl2030_2bgn_cfg;
-extern const struct iwl_cfg iwl6035_2agn_cfg;
-extern const struct iwl_cfg iwl6035_2agn_sff_cfg;
-extern const struct iwl_cfg iwl105_bgn_cfg;
-extern const struct iwl_cfg iwl105_bgn_d_cfg;
-extern const struct iwl_cfg iwl135_bgn_cfg;
+extern const struct iwl_rf_cfg iwl5300_agn_cfg;
+extern const struct iwl_rf_cfg iwl5350_agn_cfg;
+extern const struct iwl_rf_cfg iwl5100_n_cfg;
+extern const struct iwl_rf_cfg iwl5100_abg_cfg;
+extern const struct iwl_rf_cfg iwl5150_agn_cfg;
+extern const struct iwl_rf_cfg iwl5150_abg_cfg;
+extern const struct iwl_rf_cfg iwl6005_non_n_cfg;
+extern const struct iwl_rf_cfg iwl6005_n_cfg;
+extern const struct iwl_rf_cfg iwl6030_n_cfg;
+extern const struct iwl_rf_cfg iwl6030_non_n_cfg;
+extern const struct iwl_rf_cfg iwl6000i_2agn_cfg;
+extern const struct iwl_rf_cfg iwl6000i_non_n_cfg;
+extern const struct iwl_rf_cfg iwl6000i_non_n_cfg;
+extern const struct iwl_rf_cfg iwl6000_3agn_cfg;
+extern const struct iwl_rf_cfg iwl6050_2agn_cfg;
+extern const struct iwl_rf_cfg iwl6050_2abg_cfg;
+extern const struct iwl_rf_cfg iwl6150_bgn_cfg;
+extern const struct iwl_rf_cfg iwl6150_bg_cfg;
+extern const struct iwl_rf_cfg iwl1000_bgn_cfg;
+extern const struct iwl_rf_cfg iwl1000_bg_cfg;
+extern const struct iwl_rf_cfg iwl100_bgn_cfg;
+extern const struct iwl_rf_cfg iwl100_bg_cfg;
+extern const struct iwl_rf_cfg iwl130_bgn_cfg;
+extern const struct iwl_rf_cfg iwl130_bg_cfg;
+extern const struct iwl_rf_cfg iwl2000_2bgn_cfg;
+extern const struct iwl_rf_cfg iwl2030_2bgn_cfg;
+extern const struct iwl_rf_cfg iwl6035_2agn_cfg;
+extern const struct iwl_rf_cfg iwl105_bgn_cfg;
+extern const struct iwl_rf_cfg iwl135_bgn_cfg;
#endif /* CONFIG_IWLDVM */
#if IS_ENABLED(CONFIG_IWLMVM)
-extern const struct iwl_ht_params iwl_22000_ht_params;
-extern const struct iwl_cfg iwl7260_2ac_cfg;
-extern const struct iwl_cfg iwl7260_2ac_cfg_high_temp;
-extern const struct iwl_cfg iwl7260_2n_cfg;
-extern const struct iwl_cfg iwl7260_n_cfg;
-extern const struct iwl_cfg iwl3160_2ac_cfg;
-extern const struct iwl_cfg iwl3160_2n_cfg;
-extern const struct iwl_cfg iwl3160_n_cfg;
-extern const struct iwl_cfg iwl3165_2ac_cfg;
-extern const struct iwl_cfg iwl3168_2ac_cfg;
-extern const struct iwl_cfg iwl7265_2ac_cfg;
-extern const struct iwl_cfg iwl7265_2n_cfg;
-extern const struct iwl_cfg iwl7265_n_cfg;
-extern const struct iwl_cfg iwl7265d_2ac_cfg;
-extern const struct iwl_cfg iwl7265d_2n_cfg;
-extern const struct iwl_cfg iwl7265d_n_cfg;
-extern const struct iwl_cfg iwl8260_2n_cfg;
-extern const struct iwl_cfg iwl8260_2ac_cfg;
-extern const struct iwl_cfg iwl8265_2ac_cfg;
-extern const struct iwl_cfg iwl8275_2ac_cfg;
-extern const struct iwl_cfg iwl4165_2ac_cfg;
-extern const struct iwl_cfg iwl9260_2ac_cfg;
-extern const struct iwl_cfg iwl9560_qu_b0_jf_b0_cfg;
-extern const struct iwl_cfg iwl9560_qu_c0_jf_b0_cfg;
-extern const struct iwl_cfg iwl9560_quz_a0_jf_b0_cfg;
-extern const struct iwl_cfg iwl9560_2ac_cfg_soc;
-extern const struct iwl_cfg iwl_qu_b0_hr1_b0;
-extern const struct iwl_cfg iwl_qu_c0_hr1_b0;
-extern const struct iwl_cfg iwl_quz_a0_hr1_b0;
-extern const struct iwl_cfg iwl_qu_b0_hr_b0;
-extern const struct iwl_cfg iwl_qu_c0_hr_b0;
-extern const struct iwl_cfg iwl_ax200_cfg_cc;
-extern const struct iwl_cfg iwl_ax201_cfg_qu_hr;
-extern const struct iwl_cfg iwl_ax201_cfg_qu_c0_hr_b0;
-extern const struct iwl_cfg iwl_ax201_cfg_quz_hr;
-extern const struct iwl_cfg iwl_ax1650i_cfg_quz_hr;
-extern const struct iwl_cfg iwl_ax1650s_cfg_quz_hr;
-extern const struct iwl_cfg killer1650s_2ax_cfg_qu_b0_hr_b0;
-extern const struct iwl_cfg killer1650i_2ax_cfg_qu_b0_hr_b0;
-extern const struct iwl_cfg killer1650s_2ax_cfg_qu_c0_hr_b0;
-extern const struct iwl_cfg killer1650i_2ax_cfg_qu_c0_hr_b0;
-extern const struct iwl_cfg killer1650x_2ax_cfg;
-extern const struct iwl_cfg killer1650w_2ax_cfg;
-extern const struct iwl_cfg iwlax210_2ax_cfg_so_jf_b0;
-extern const struct iwl_cfg iwlax211_2ax_cfg_so_gf_a0;
-extern const struct iwl_cfg iwlax211_2ax_cfg_so_gf_a0_long;
-extern const struct iwl_cfg iwlax210_2ax_cfg_ty_gf_a0;
-extern const struct iwl_cfg iwlax411_2ax_cfg_so_gf4_a0;
-extern const struct iwl_cfg iwlax411_2ax_cfg_so_gf4_a0_long;
-
-extern const struct iwl_cfg iwl_cfg_ma;
-
-extern const struct iwl_cfg iwl_cfg_so_a0_hr_a0;
-extern const struct iwl_cfg iwl_cfg_quz_a0_hr_b0;
-
-extern const struct iwl_cfg iwl_cfg_bz;
-extern const struct iwl_cfg iwl_cfg_gl;
-
-extern const struct iwl_cfg iwl_cfg_sc;
-extern const struct iwl_cfg iwl_cfg_sc2;
-extern const struct iwl_cfg iwl_cfg_sc2f;
-extern const struct iwl_cfg iwl_cfg_dr;
-extern const struct iwl_cfg iwl_cfg_br;
+extern const struct iwl_rf_cfg iwl7260_cfg;
+extern const struct iwl_rf_cfg iwl7260_high_temp_cfg;
+extern const struct iwl_rf_cfg iwl3160_cfg;
+extern const struct iwl_rf_cfg iwl3165_2ac_cfg;
+extern const struct iwl_rf_cfg iwl3168_2ac_cfg;
+extern const struct iwl_rf_cfg iwl7265_cfg;
+extern const struct iwl_rf_cfg iwl7265d_cfg;
+extern const struct iwl_rf_cfg iwl8260_cfg;
+extern const struct iwl_rf_cfg iwl8265_cfg;
+extern const struct iwl_rf_cfg iwl_rf_jf;
+extern const struct iwl_rf_cfg iwl_rf_jf_80mhz;
+extern const struct iwl_rf_cfg iwl_rf_hr1;
+extern const struct iwl_rf_cfg iwl_rf_hr;
+extern const struct iwl_rf_cfg iwl_rf_hr_80mhz;
+
+extern const struct iwl_rf_cfg iwl_rf_gf;
#endif /* CONFIG_IWLMVM */
+#if IS_ENABLED(CONFIG_IWLMLD)
+extern const struct iwl_rf_cfg iwl_rf_fm;
+extern const struct iwl_rf_cfg iwl_rf_fm_160mhz;
+#define iwl_rf_wh iwl_rf_fm
+#define iwl_rf_wh_160mhz iwl_rf_fm_160mhz
+#define iwl_rf_pe iwl_rf_fm
+#endif /* CONFIG_IWLMLD */
+
#endif /* __IWL_CONFIG_H__ */
diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-context-info-gen3.h b/drivers/net/wireless/intel/iwlwifi/iwl-context-info-v2.h
index cd25a1b9f2ff..8c5c0ea46181 100644
--- a/drivers/net/wireless/intel/iwlwifi/iwl-context-info-gen3.h
+++ b/drivers/net/wireless/intel/iwlwifi/iwl-context-info-v2.h
@@ -1,9 +1,9 @@
/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
/*
- * Copyright (C) 2018, 2020-2024 Intel Corporation
+ * Copyright (C) 2018, 2020-2025 Intel Corporation
*/
-#ifndef __iwl_context_info_file_gen3_h__
-#define __iwl_context_info_file_gen3_h__
+#ifndef __iwl_context_info_file_v2_h__
+#define __iwl_context_info_file_v2_h__
#include "iwl-context-info.h"
@@ -58,6 +58,7 @@ enum iwl_prph_scratch_mtr_format {
* @IWL_PRPH_SCRATCH_RB_SIZE_EXT_16K: 16kB RB size
* @IWL_PRPH_SCRATCH_SCU_FORCE_ACTIVE: Indicate fw to set SCU_FORCE_ACTIVE
* upon reset.
+ * @IWL_PRPH_SCRATCH_TOP_RESET: request TOP reset
*/
enum iwl_prph_scratch_flags {
IWL_PRPH_SCRATCH_IMR_DEBUG_EN = BIT(1),
@@ -74,16 +75,21 @@ enum iwl_prph_scratch_flags {
IWL_PRPH_SCRATCH_RB_SIZE_EXT_12K = 9 << 20,
IWL_PRPH_SCRATCH_RB_SIZE_EXT_16K = 10 << 20,
IWL_PRPH_SCRATCH_SCU_FORCE_ACTIVE = BIT(29),
+ IWL_PRPH_SCRATCH_TOP_RESET = BIT(30),
};
/**
* enum iwl_prph_scratch_ext_flags - PRPH scratch control ext flags
+ * @IWL_PRPH_SCRATCH_EXT_EXT_FSEQ: external FSEQ image provided
* @IWL_PRPH_SCRATCH_EXT_URM_FW: switch to URM mode based on fw setting
* @IWL_PRPH_SCRATCH_EXT_URM_PERM: switch to permanent URM mode
+ * @IWL_PRPH_SCRATCH_EXT_32KHZ_CLK_VALID: use external 32 KHz clock
*/
enum iwl_prph_scratch_ext_flags {
- IWL_PRPH_SCRATCH_EXT_URM_FW = BIT(4),
- IWL_PRPH_SCRATCH_EXT_URM_PERM = BIT(5),
+ IWL_PRPH_SCRATCH_EXT_EXT_FSEQ = BIT(0),
+ IWL_PRPH_SCRATCH_EXT_URM_FW = BIT(4),
+ IWL_PRPH_SCRATCH_EXT_URM_PERM = BIT(5),
+ IWL_PRPH_SCRATCH_EXT_32KHZ_CLK_VALID = BIT(8),
};
/**
@@ -200,6 +206,19 @@ struct iwl_prph_scratch_ctrl_cfg {
struct iwl_prph_scratch_step_cfg step_cfg;
} __packed; /* PERIPH_SCRATCH_CTRL_CFG_S */
+#define IWL_NUM_DRAM_FSEQ_ENTRIES 8
+
+/**
+ * struct iwl_context_info_dram_fseq - images DRAM map (with fseq)
+ * each entry in the map represents a DRAM chunk of up to 32 KB
+ * @common: UMAC/LMAC/virtual images
+ * @fseq_img: FSEQ image DRAM map
+ */
+struct iwl_context_info_dram_fseq {
+ struct iwl_context_info_dram_nonfseq common;
+ __le64 fseq_img[IWL_NUM_DRAM_FSEQ_ENTRIES];
+} __packed; /* PERIPH_SCRATCH_DRAM_MAP_S */
+
/**
* struct iwl_prph_scratch - peripheral scratch mapping
* @ctrl_cfg: control and configuration of prph scratch
@@ -213,7 +232,7 @@ struct iwl_prph_scratch {
__le32 fseq_override;
__le32 step_analog_params;
__le32 reserved[8];
- struct iwl_context_info_dram dram;
+ struct iwl_context_info_dram_fseq dram;
} __packed; /* PERIPH_SCRATCH_S */
/**
@@ -231,7 +250,7 @@ struct iwl_prph_info {
} __packed; /* PERIPH_INFO_S */
/**
- * struct iwl_context_info_gen3 - device INIT configuration
+ * struct iwl_context_info_v2 - device INIT configuration
* @version: version of the context information
* @size: size of context information in DWs
* @config: context in which the peripheral would execute - a subset of
@@ -274,7 +293,7 @@ struct iwl_prph_info {
* @prph_scratch_size: the size of the peripheral scratch structure in DWs
* @reserved: reserved
*/
-struct iwl_context_info_gen3 {
+struct iwl_context_info_v2 {
__le16 version;
__le16 size;
__le32 config;
@@ -304,22 +323,22 @@ struct iwl_context_info_gen3 {
__le32 reserved;
} __packed; /* IPC_CONTEXT_INFO_S */
-int iwl_pcie_ctxt_info_gen3_init(struct iwl_trans *trans,
- const struct fw_img *fw);
-void iwl_pcie_ctxt_info_gen3_free(struct iwl_trans *trans, bool alive);
+int iwl_pcie_ctxt_info_v2_alloc(struct iwl_trans *trans,
+ const struct iwl_fw *fw,
+ const struct fw_img *img);
+void iwl_pcie_ctxt_info_v2_kick(struct iwl_trans *trans);
+void iwl_pcie_ctxt_info_v2_free(struct iwl_trans *trans, bool alive);
-int iwl_trans_pcie_ctx_info_gen3_load_pnvm(struct iwl_trans *trans,
- const struct iwl_pnvm_image *pnvm_payloads,
- const struct iwl_ucode_capabilities *capa);
-void iwl_trans_pcie_ctx_info_gen3_set_pnvm(struct iwl_trans *trans,
- const struct iwl_ucode_capabilities *capa);
+int iwl_trans_pcie_ctx_info_v2_load_pnvm(struct iwl_trans *trans,
+ const struct iwl_pnvm_image *pnvm_payloads,
+ const struct iwl_ucode_capabilities *capa);
+void iwl_trans_pcie_ctx_info_v2_set_pnvm(struct iwl_trans *trans,
+ const struct iwl_ucode_capabilities *capa);
int
-iwl_trans_pcie_ctx_info_gen3_load_reduce_power(struct iwl_trans *trans,
- const struct iwl_pnvm_image *payloads,
- const struct iwl_ucode_capabilities *capa);
+iwl_trans_pcie_ctx_info_v2_load_reduce_power(struct iwl_trans *trans,
+ const struct iwl_pnvm_image *payloads,
+ const struct iwl_ucode_capabilities *capa);
void
-iwl_trans_pcie_ctx_info_gen3_set_reduce_power(struct iwl_trans *trans,
- const struct iwl_ucode_capabilities *capa);
-int iwl_trans_pcie_ctx_info_gen3_set_step(struct iwl_trans *trans,
- u32 mbx_addr_0_step, u32 mbx_addr_1_step);
-#endif /* __iwl_context_info_file_gen3_h__ */
+iwl_trans_pcie_ctx_info_v2_set_reduce_power(struct iwl_trans *trans,
+ const struct iwl_ucode_capabilities *capa);
+#endif /* __iwl_context_info_file_v2_h__ */
diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-context-info.h b/drivers/net/wireless/intel/iwlwifi/iwl-context-info.h
index dfd44fabf237..7ae0fbdef208 100644
--- a/drivers/net/wireless/intel/iwlwifi/iwl-context-info.h
+++ b/drivers/net/wireless/intel/iwlwifi/iwl-context-info.h
@@ -1,7 +1,7 @@
/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
/*
* Copyright (C) 2017 Intel Deutschland GmbH
- * Copyright (C) 2018-2020, 2022, 2024 Intel Corporation
+ * Copyright (C) 2018-2020, 2022, 2024-2025 Intel Corporation
*/
#ifndef __iwl_context_info_file_h__
#define __iwl_context_info_file_h__
@@ -78,13 +78,13 @@ struct iwl_context_info_control {
} __packed;
/**
- * struct iwl_context_info_dram - images DRAM map
+ * struct iwl_context_info_dram_nonfseq - images DRAM map
* each entry in the map represents a DRAM chunk of up to 32 KB
* @umac_img: UMAC image DRAM map
* @lmac_img: LMAC image DRAM map
* @virtual_img: paged image DRAM map
*/
-struct iwl_context_info_dram {
+struct iwl_context_info_dram_nonfseq {
__le64 umac_img[IWL_MAX_DRAM_ENTRY];
__le64 lmac_img[IWL_MAX_DRAM_ENTRY];
__le64 virtual_img[IWL_MAX_DRAM_ENTRY];
@@ -177,16 +177,16 @@ struct iwl_context_info {
struct iwl_context_info_early_dbg_cfg edbg_cfg;
struct iwl_context_info_pnvm_cfg pnvm_cfg;
__le32 reserved2[16];
- struct iwl_context_info_dram dram;
+ struct iwl_context_info_dram_nonfseq dram;
__le32 reserved3[16];
-} __packed;
+} __packed; /* BOOT_LOADER_CONTEXT_INFO_S */
-int iwl_pcie_ctxt_info_init(struct iwl_trans *trans, const struct fw_img *fw);
+int iwl_pcie_ctxt_info_init(struct iwl_trans *trans, const struct fw_img *img);
void iwl_pcie_ctxt_info_free(struct iwl_trans *trans);
void iwl_pcie_ctxt_info_free_paging(struct iwl_trans *trans);
int iwl_pcie_init_fw_sec(struct iwl_trans *trans,
const struct fw_img *fw,
- struct iwl_context_info_dram *ctxt_dram);
+ struct iwl_context_info_dram_nonfseq *ctxt_dram);
void *iwl_pcie_ctxt_info_dma_alloc_coherent(struct iwl_trans *trans,
size_t size,
dma_addr_t *phys);
diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-csr.h b/drivers/net/wireless/intel/iwlwifi/iwl-csr.h
index be9e464c9b7b..0fd452cb94ae 100644
--- a/drivers/net/wireless/intel/iwlwifi/iwl-csr.h
+++ b/drivers/net/wireless/intel/iwlwifi/iwl-csr.h
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
/*
- * Copyright (C) 2005-2014, 2018-2024 Intel Corporation
+ * Copyright (C) 2005-2014, 2018-2025 Intel Corporation
* Copyright (C) 2013-2014 Intel Mobile Communications GmbH
* Copyright (C) 2016 Intel Deutschland GmbH
*/
@@ -107,6 +107,13 @@
/* GIO Chicken Bits (PCI Express bus link power management) */
#define CSR_GIO_CHICKEN_BITS (CSR_BASE+0x100)
+#define CSR_IPC_STATE (CSR_BASE + 0x110)
+#define CSR_IPC_STATE_RESET 0x00000030
+#define CSR_IPC_STATE_RESET_NONE 0
+#define CSR_IPC_STATE_RESET_SW_READY 1
+#define CSR_IPC_STATE_RESET_TOP_READY 2
+#define CSR_IPC_STATE_RESET_TOP_FOLLOWER 3
+
#define CSR_IPC_SLEEP_CONTROL (CSR_BASE + 0x114)
#define CSR_IPC_SLEEP_CONTROL_SUSPEND 0x3
#define CSR_IPC_SLEEP_CONTROL_RESUME 0
@@ -148,6 +155,7 @@
* during a error FW error.
*/
#define CSR_FUNC_SCRATCH_INIT_VALUE (0x01010101)
+#define CSR_FUNC_SCRATCH_POWER_OFF_MASK 0xFFFF
/* Bits for CSR_HW_IF_CONFIG_REG */
#define CSR_HW_IF_CONFIG_REG_MSK_MAC_STEP_DASH (0x0000000F)
@@ -193,17 +201,19 @@
#define CSR_INT_BIT_RF_KILL (1 << 7) /* HW RFKILL switch GP_CNTRL[27] toggled */
#define CSR_INT_BIT_CT_KILL (1 << 6) /* Critical temp (chip too hot) rfkill */
#define CSR_INT_BIT_SW_RX (1 << 3) /* Rx, command responses */
+#define CSR_INT_BIT_RESET_DONE (1 << 2) /* reset handshake with firmware is done */
#define CSR_INT_BIT_WAKEUP (1 << 1) /* NIC controller waking up (pwr mgmt) */
#define CSR_INT_BIT_ALIVE (1 << 0) /* uCode interrupts once it initializes */
-#define CSR_INI_SET_MASK (CSR_INT_BIT_FH_RX | \
- CSR_INT_BIT_HW_ERR | \
- CSR_INT_BIT_FH_TX | \
- CSR_INT_BIT_SW_ERR | \
- CSR_INT_BIT_RF_KILL | \
- CSR_INT_BIT_SW_RX | \
- CSR_INT_BIT_WAKEUP | \
- CSR_INT_BIT_ALIVE | \
+#define CSR_INI_SET_MASK (CSR_INT_BIT_FH_RX | \
+ CSR_INT_BIT_HW_ERR | \
+ CSR_INT_BIT_FH_TX | \
+ CSR_INT_BIT_SW_ERR | \
+ CSR_INT_BIT_RF_KILL | \
+ CSR_INT_BIT_SW_RX | \
+ CSR_INT_BIT_WAKEUP | \
+ CSR_INT_BIT_RESET_DONE | \
+ CSR_INT_BIT_ALIVE | \
CSR_INT_BIT_RX_PERIODIC)
/* interrupt flags in FH (flow handler) (PCI busmaster DMA) */
@@ -640,7 +650,7 @@ enum msix_hw_int_causes {
* HW address related registers *
*****************************************************************************/
-#define CSR_ADDR_BASE(trans) ((trans)->cfg->mac_addr_from_csr)
+#define CSR_ADDR_BASE(trans) ((trans)->mac_cfg->base->mac_addr_from_csr)
#define CSR_MAC_ADDR0_OTP(trans) (CSR_ADDR_BASE(trans) + 0x00)
#define CSR_MAC_ADDR1_OTP(trans) (CSR_ADDR_BASE(trans) + 0x04)
#define CSR_MAC_ADDR0_STRAP(trans) (CSR_ADDR_BASE(trans) + 0x08)
diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-dbg-tlv.c b/drivers/net/wireless/intel/iwlwifi/iwl-dbg-tlv.c
index 08d990ba8a79..5c8f6dc9a3e0 100644
--- a/drivers/net/wireless/intel/iwlwifi/iwl-dbg-tlv.c
+++ b/drivers/net/wireless/intel/iwlwifi/iwl-dbg-tlv.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
/*
- * Copyright (C) 2018-2024 Intel Corporation
+ * Copyright (C) 2018-2025 Intel Corporation
*/
#include <linux/firmware.h>
#include "iwl-drv.h"
@@ -503,7 +503,7 @@ void iwl_dbg_tlv_load_bin(struct device *dev, struct iwl_trans *trans)
int res;
if (!iwlwifi_mod_params.enable_ini ||
- trans->trans_cfg->device_family <= IWL_DEVICE_FAMILY_8000)
+ trans->mac_cfg->device_family <= IWL_DEVICE_FAMILY_8000)
return;
res = firmware_request_nowarn(&fw, yoyo_bin, dev);
@@ -603,11 +603,11 @@ static int iwl_dbg_tlv_alloc_fragments(struct iwl_fw_runtime *fwrt,
return 0;
num_frags = le32_to_cpu(fw_mon_cfg->max_frags_num);
- if (fwrt->trans->trans_cfg->device_family < IWL_DEVICE_FAMILY_AX210) {
+ if (fwrt->trans->mac_cfg->device_family < IWL_DEVICE_FAMILY_AX210) {
if (alloc_id != IWL_FW_INI_ALLOCATION_ID_DBGC1)
return -EIO;
num_frags = 1;
- } else if (fwrt->trans->trans_cfg->device_family < IWL_DEVICE_FAMILY_BZ &&
+ } else if (fwrt->trans->mac_cfg->device_family < IWL_DEVICE_FAMILY_BZ &&
alloc_id > IWL_FW_INI_ALLOCATION_ID_DBGC3) {
return -EIO;
}
@@ -1241,7 +1241,7 @@ iwl_dbg_tlv_tp_trigger(struct iwl_fw_runtime *fwrt, bool sync,
fwrt->trans->dbg.restart_required = false;
- if (fwrt->trans->trans_cfg->device_family ==
+ if (fwrt->trans->mac_cfg->device_family ==
IWL_DEVICE_FAMILY_9000) {
fwrt->trans->dbg.restart_required = true;
} else if (tp == IWL_FW_INI_TIME_POINT_FW_ASSERT &&
@@ -1372,15 +1372,15 @@ void _iwl_dbg_tlv_time_point(struct iwl_fw_runtime *fwrt,
switch (tp_id) {
case IWL_FW_INI_TIME_POINT_EARLY:
iwl_dbg_tlv_init_cfg(fwrt);
- iwl_dbg_tlv_apply_config(fwrt, conf_list);
iwl_dbg_tlv_update_drams(fwrt);
iwl_dbg_tlv_tp_trigger(fwrt, sync, trig_list, tp_data, NULL);
+ iwl_dbg_tlv_apply_config(fwrt, conf_list);
break;
case IWL_FW_INI_TIME_POINT_AFTER_ALIVE:
iwl_dbg_tlv_apply_buffers(fwrt);
iwl_dbg_tlv_send_hcmds(fwrt, hcmd_list);
- iwl_dbg_tlv_apply_config(fwrt, conf_list);
iwl_dbg_tlv_tp_trigger(fwrt, sync, trig_list, tp_data, NULL);
+ iwl_dbg_tlv_apply_config(fwrt, conf_list);
break;
case IWL_FW_INI_TIME_POINT_PERIODIC:
iwl_dbg_tlv_set_periodic_trigs(fwrt);
@@ -1390,14 +1390,14 @@ void _iwl_dbg_tlv_time_point(struct iwl_fw_runtime *fwrt,
case IWL_FW_INI_TIME_POINT_MISSED_BEACONS:
case IWL_FW_INI_TIME_POINT_FW_DHC_NOTIFICATION:
iwl_dbg_tlv_send_hcmds(fwrt, hcmd_list);
- iwl_dbg_tlv_apply_config(fwrt, conf_list);
iwl_dbg_tlv_tp_trigger(fwrt, sync, trig_list, tp_data,
iwl_dbg_tlv_check_fw_pkt);
+ iwl_dbg_tlv_apply_config(fwrt, conf_list);
break;
default:
iwl_dbg_tlv_send_hcmds(fwrt, hcmd_list);
- iwl_dbg_tlv_apply_config(fwrt, conf_list);
iwl_dbg_tlv_tp_trigger(fwrt, sync, trig_list, tp_data, NULL);
+ iwl_dbg_tlv_apply_config(fwrt, conf_list);
break;
}
}
diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-debug.h b/drivers/net/wireless/intel/iwlwifi/iwl-debug.h
index bf52c2edaad1..ea32dc88584f 100644
--- a/drivers/net/wireless/intel/iwlwifi/iwl-debug.h
+++ b/drivers/net/wireless/intel/iwlwifi/iwl-debug.h
@@ -2,7 +2,7 @@
/******************************************************************************
*
* Copyright(c) 2003 - 2014 Intel Corporation. All rights reserved.
- * Copyright(c) 2018 - 2021, 2024 Intel Corporation
+ * Copyright(c) 2018 - 2021, 2024-2025 Intel Corporation
*
* Portions of this file are derived from the ipw3945 project.
*****************************************************************************/
@@ -156,6 +156,7 @@ do { \
#define IWL_DL_FW 0x00010000
#define IWL_DL_RF_KILL 0x00020000
#define IWL_DL_TPT 0x00040000
+#define IWL_DL_PTP 0x00080000
/* 0x00F00000 - 0x00100000 */
#define IWL_DL_RATE 0x00100000
#define IWL_DL_CALIB 0x00200000
@@ -165,7 +166,7 @@ do { \
#define IWL_DL_RX 0x01000000
#define IWL_DL_ISR 0x02000000
#define IWL_DL_HT 0x04000000
-#define IWL_DL_EXTERNAL 0x08000000
+#define IWL_DL_EHT 0x08000000
/* 0xF0000000 - 0x10000000 */
#define IWL_DL_11H 0x10000000
#define IWL_DL_STATS 0x20000000
@@ -175,7 +176,6 @@ do { \
#define IWL_DEBUG_INFO(p, f, a...) IWL_DEBUG(p, IWL_DL_INFO, f, ## a)
#define IWL_DEBUG_TDLS(p, f, a...) IWL_DEBUG(p, IWL_DL_TDLS, f, ## a)
#define IWL_DEBUG_MAC80211(p, f, a...) IWL_DEBUG(p, IWL_DL_MAC80211, f, ## a)
-#define IWL_DEBUG_EXTERNAL(p, f, a...) IWL_DEBUG(p, IWL_DL_EXTERNAL, f, ## a)
#define IWL_DEBUG_TEMP(p, f, a...) IWL_DEBUG(p, IWL_DL_TEMP, f, ## a)
#define IWL_DEBUG_SCAN(p, f, a...) IWL_DEBUG(p, IWL_DL_SCAN, f, ## a)
#define IWL_DEBUG_RX(p, f, a...) IWL_DEBUG(p, IWL_DL_RX, f, ## a)
@@ -216,5 +216,6 @@ do { \
#define IWL_DEBUG_LAR(p, f, a...) IWL_DEBUG(p, IWL_DL_LAR, f, ## a)
#define IWL_DEBUG_FW_INFO(p, f, a...) \
IWL_DEBUG(p, IWL_DL_INFO | IWL_DL_FW, f, ## a)
-
+#define IWL_DEBUG_PTP(p, f, a...) IWL_DEBUG(p, IWL_DL_PTP, f, ## a)
+#define IWL_DEBUG_EHT(p, f, a...) IWL_DEBUG(p, IWL_DL_EHT, f, ## a)
#endif
diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-devtrace.h b/drivers/net/wireless/intel/iwlwifi/iwl-devtrace.h
index 76166e1b10e5..99789c7cef3b 100644
--- a/drivers/net/wireless/intel/iwlwifi/iwl-devtrace.h
+++ b/drivers/net/wireless/intel/iwlwifi/iwl-devtrace.h
@@ -3,7 +3,7 @@
*
* Copyright(c) 2009 - 2014 Intel Corporation. All rights reserved.
* Copyright(C) 2016 Intel Deutschland GmbH
- * Copyright(c) 2018, 2023 Intel Corporation
+ * Copyright(c) 2018, 2023, 2025 Intel Corporation
*****************************************************************************/
#ifndef __IWLWIFI_DEVICE_TRACE
@@ -54,11 +54,11 @@ static inline size_t iwl_rx_trace_len(const struct iwl_trans *trans,
struct ieee80211_hdr *hdr = NULL;
size_t hdr_offset;
- if (cmd->cmd != trans->rx_mpdu_cmd)
+ if (cmd->cmd != trans->conf.rx_mpdu_cmd)
return len;
hdr_offset = sizeof(struct iwl_cmd_header) +
- trans->rx_mpdu_cmd_hdr_size;
+ trans->conf.rx_mpdu_cmd_hdr_size;
if (out_hdr_offset)
*out_hdr_offset = hdr_offset;
@@ -67,7 +67,8 @@ static inline size_t iwl_rx_trace_len(const struct iwl_trans *trans,
if (!ieee80211_is_data(hdr->frame_control))
return len;
/* maybe try to identify EAPOL frames? */
- return sizeof(__le32) + sizeof(*cmd) + trans->rx_mpdu_cmd_hdr_size +
+ return sizeof(__le32) + sizeof(*cmd) +
+ trans->conf.rx_mpdu_cmd_hdr_size +
ieee80211_hdrlen(hdr->frame_control);
}
diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-drv.c b/drivers/net/wireless/intel/iwlwifi/iwl-drv.c
index d3a65f33097c..9504a0cb8b13 100644
--- a/drivers/net/wireless/intel/iwlwifi/iwl-drv.c
+++ b/drivers/net/wireless/intel/iwlwifi/iwl-drv.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
/*
- * Copyright (C) 2005-2014, 2018-2024 Intel Corporation
+ * Copyright (C) 2005-2014, 2018-2025 Intel Corporation
* Copyright (C) 2013-2015 Intel Mobile Communications GmbH
* Copyright (C) 2016-2017 Intel Deutschland GmbH
*/
@@ -75,6 +75,9 @@ struct iwl_drv {
enum {
DVM_OP_MODE,
MVM_OP_MODE,
+#if IS_ENABLED(CONFIG_IWLMLD)
+ MLD_OP_MODE,
+#endif
};
/* Protects the table contents, i.e. the ops pointer & drv list */
@@ -86,6 +89,9 @@ static struct iwlwifi_opmode_table {
} iwlwifi_opmode_table[] = { /* ops set when driver is initialized */
[DVM_OP_MODE] = { .name = "iwldvm", .ops = NULL },
[MVM_OP_MODE] = { .name = "iwlmvm", .ops = NULL },
+#if IS_ENABLED(CONFIG_IWLMLD)
+ [MLD_OP_MODE] = { .name = "iwlmld", .ops = NULL },
+#endif
};
#define IWL_DEFAULT_SCAN_CHANNELS 40
@@ -168,22 +174,88 @@ static inline char iwl_drv_get_step(int step)
return 'a' + step;
}
+static bool iwl_drv_is_wifi7_supported(struct iwl_trans *trans)
+{
+ return CSR_HW_RFID_TYPE(trans->info.hw_rf_id) >= IWL_CFG_RF_TYPE_FM;
+}
+
const char *iwl_drv_get_fwname_pre(struct iwl_trans *trans, char *buf)
{
char mac_step, rf_step;
- const char *rf, *cdb;
+ const char *mac, *rf, *cdb;
if (trans->cfg->fw_name_pre)
return trans->cfg->fw_name_pre;
- if (WARN_ON(!trans->cfg->fw_name_mac))
- return "unconfigured";
+ mac_step = iwl_drv_get_step(trans->info.hw_rev_step);
- mac_step = iwl_drv_get_step(trans->hw_rev_step);
+ switch (CSR_HW_REV_TYPE(trans->info.hw_rev)) {
+ case IWL_CFG_MAC_TYPE_PU:
+ mac = "9000-pu";
+ mac_step = 'b';
+ break;
+ case IWL_CFG_MAC_TYPE_TH:
+ mac = "9260-th";
+ mac_step = 'b';
+ break;
+ case IWL_CFG_MAC_TYPE_QU:
+ mac = "Qu";
+ break;
+ case IWL_CFG_MAC_TYPE_CC:
+ /* special case - no RF since it's fixed (discrete) */
+ scnprintf(buf, FW_NAME_PRE_BUFSIZE, "iwlwifi-cc-a0");
+ return buf;
+ case IWL_CFG_MAC_TYPE_QUZ:
+ mac = "QuZ";
+ /* all QuZ use A0 firmware */
+ mac_step = 'a';
+ break;
+ case IWL_CFG_MAC_TYPE_SO:
+ case IWL_CFG_MAC_TYPE_SOF:
+ mac = "so";
+ mac_step = 'a';
+ break;
+ case IWL_CFG_MAC_TYPE_TY:
+ mac = "ty";
+ mac_step = 'a';
+ break;
+ case IWL_CFG_MAC_TYPE_MA:
+ mac = "ma";
+ break;
+ case IWL_CFG_MAC_TYPE_BZ:
+ case IWL_CFG_MAC_TYPE_BZ_W:
+ mac = "bz";
+ break;
+ case IWL_CFG_MAC_TYPE_GL:
+ mac = "gl";
+ break;
+ case IWL_CFG_MAC_TYPE_SC:
+ mac = "sc";
+ break;
+ case IWL_CFG_MAC_TYPE_SC2:
+ mac = "sc2";
+ break;
+ case IWL_CFG_MAC_TYPE_SC2F:
+ mac = "sc2f";
+ break;
+ case IWL_CFG_MAC_TYPE_BR:
+ mac = "br";
+ break;
+ case IWL_CFG_MAC_TYPE_DR:
+ mac = "dr";
+ break;
+ default:
+ return "unknown-mac";
+ }
- rf_step = iwl_drv_get_step(CSR_HW_RFID_STEP(trans->hw_rf_id));
+ rf_step = iwl_drv_get_step(CSR_HW_RFID_STEP(trans->info.hw_rf_id));
- switch (CSR_HW_RFID_TYPE(trans->hw_rf_id)) {
+ switch (CSR_HW_RFID_TYPE(trans->info.hw_rf_id)) {
+ case IWL_CFG_RF_TYPE_JF1:
+ case IWL_CFG_RF_TYPE_JF2:
+ rf = "jf";
+ rf_step = 'b';
+ break;
case IWL_CFG_RF_TYPE_HR1:
case IWL_CFG_RF_TYPE_HR2:
rf = "hr";
@@ -191,29 +263,26 @@ const char *iwl_drv_get_fwname_pre(struct iwl_trans *trans, char *buf)
break;
case IWL_CFG_RF_TYPE_GF:
rf = "gf";
+ rf_step = 'a';
break;
case IWL_CFG_RF_TYPE_FM:
rf = "fm";
break;
case IWL_CFG_RF_TYPE_WH:
- if (SILICON_Z_STEP ==
- CSR_HW_RFID_STEP(trans->hw_rf_id)) {
- rf = "whtc";
- rf_step = 'a';
- } else {
- rf = "wh";
- }
+ rf = "wh";
+ break;
+ case IWL_CFG_RF_TYPE_PE:
+ rf = "pe";
break;
default:
return "unknown-rf";
}
- cdb = CSR_HW_RFID_IS_CDB(trans->hw_rf_id) ? "4" : "";
+ cdb = CSR_HW_RFID_IS_CDB(trans->info.hw_rf_id) ? "4" : "";
scnprintf(buf, FW_NAME_PRE_BUFSIZE,
"iwlwifi-%s-%c0-%s%s-%c0",
- trans->cfg->fw_name_mac, mac_step,
- rf, cdb, rf_step);
+ mac, mac_step, rf, cdb, rf_step);
return buf;
}
@@ -222,39 +291,64 @@ IWL_EXPORT_SYMBOL(iwl_drv_get_fwname_pre);
static void iwl_req_fw_callback(const struct firmware *ucode_raw,
void *context);
+static void iwl_get_ucode_api_versions(struct iwl_trans *trans,
+ unsigned int *api_min,
+ unsigned int *api_max)
+{
+ const struct iwl_family_base_params *base = trans->mac_cfg->base;
+ const struct iwl_rf_cfg *cfg = trans->cfg;
+
+ if (!base->ucode_api_max) {
+ *api_min = cfg->ucode_api_min;
+ *api_max = cfg->ucode_api_max;
+ return;
+ }
+
+ if (!cfg->ucode_api_max) {
+ *api_min = base->ucode_api_min;
+ *api_max = base->ucode_api_max;
+ return;
+ }
+
+ *api_min = max(cfg->ucode_api_min, base->ucode_api_min);
+ *api_max = min(cfg->ucode_api_max, base->ucode_api_max);
+}
+
static int iwl_request_firmware(struct iwl_drv *drv, bool first)
{
- const struct iwl_cfg *cfg = drv->trans->cfg;
char _fw_name_pre[FW_NAME_PRE_BUFSIZE];
+ unsigned int ucode_api_max, ucode_api_min;
const char *fw_name_pre;
- if (drv->trans->trans_cfg->device_family == IWL_DEVICE_FAMILY_9000 &&
- (drv->trans->hw_rev_step != SILICON_B_STEP &&
- drv->trans->hw_rev_step != SILICON_C_STEP)) {
+ iwl_get_ucode_api_versions(drv->trans, &ucode_api_min, &ucode_api_max);
+
+ if (drv->trans->mac_cfg->device_family == IWL_DEVICE_FAMILY_9000 &&
+ (drv->trans->info.hw_rev_step != SILICON_B_STEP &&
+ drv->trans->info.hw_rev_step != SILICON_C_STEP)) {
IWL_ERR(drv,
"Only HW steps B and C are currently supported (0x%0x)\n",
- drv->trans->hw_rev);
+ drv->trans->info.hw_rev);
return -EINVAL;
}
fw_name_pre = iwl_drv_get_fwname_pre(drv->trans, _fw_name_pre);
if (first)
- drv->fw_index = cfg->ucode_api_max;
+ drv->fw_index = ucode_api_max;
else
drv->fw_index--;
- if (drv->fw_index < cfg->ucode_api_min) {
+ if (drv->fw_index < ucode_api_min) {
IWL_ERR(drv, "no suitable firmware found!\n");
- if (cfg->ucode_api_min == cfg->ucode_api_max) {
+ if (ucode_api_min == ucode_api_max) {
IWL_ERR(drv, "%s-%d is required\n", fw_name_pre,
- cfg->ucode_api_max);
+ ucode_api_max);
} else {
IWL_ERR(drv, "minimum version required: %s-%d\n",
- fw_name_pre, cfg->ucode_api_min);
+ fw_name_pre, ucode_api_min);
IWL_ERR(drv, "maximum version supported: %s-%d\n",
- fw_name_pre, cfg->ucode_api_max);
+ fw_name_pre, ucode_api_max);
}
IWL_ERR(drv,
@@ -316,6 +410,7 @@ struct iwl_firmware_pieces {
size_t dbg_trigger_tlv_len[FW_DBG_TRIGGER_MAX];
struct iwl_fw_dbg_mem_seg_tlv *dbg_mem_tlv;
size_t n_mem_tlv;
+ u32 major;
};
static void alloc_sec_data(struct iwl_firmware_pieces *pieces,
@@ -957,19 +1052,19 @@ static int iwl_parse_tlv_firmware(struct iwl_drv *drv,
break;
case IWL_UCODE_TLV_FW_VERSION: {
const __le32 *ptr = (const void *)tlv_data;
- u32 major, minor;
+ u32 minor;
u8 local_comp;
if (tlv_len != sizeof(u32) * 3)
goto invalid_tlv_len;
- major = le32_to_cpup(ptr++);
+ pieces->major = le32_to_cpup(ptr++);
minor = le32_to_cpup(ptr++);
local_comp = le32_to_cpup(ptr);
snprintf(drv->fw.fw_version,
sizeof(drv->fw.fw_version),
- "%u.%08x.%u %s", major, minor,
+ "%u.%08x.%u %s", pieces->major, minor,
local_comp, iwl_reduced_fw_name(drv));
break;
}
@@ -1181,7 +1276,7 @@ static int iwl_parse_tlv_firmware(struct iwl_drv *drv,
if (tlv_len != sizeof(*fseq_ver))
goto invalid_tlv_len;
- IWL_INFO(drv, "TLV_FW_FSEQ_VERSION: %s\n",
+ IWL_INFO(drv, "TLV_FW_FSEQ_VERSION: %.32s\n",
fseq_ver->version);
}
break;
@@ -1223,7 +1318,7 @@ static int iwl_parse_tlv_firmware(struct iwl_drv *drv,
if (tlv_len != sizeof(*dbg_ptrs))
goto invalid_tlv_len;
- if (drv->trans->trans_cfg->device_family <
+ if (drv->trans->mac_cfg->device_family <
IWL_DEVICE_FAMILY_22000)
break;
drv->trans->dbg.umac_error_event_table =
@@ -1239,7 +1334,7 @@ static int iwl_parse_tlv_firmware(struct iwl_drv *drv,
if (tlv_len != sizeof(*dbg_ptrs))
goto invalid_tlv_len;
- if (drv->trans->trans_cfg->device_family <
+ if (drv->trans->mac_cfg->device_family <
IWL_DEVICE_FAMILY_22000)
break;
drv->trans->dbg.lmac_error_event_table[0] =
@@ -1361,7 +1456,7 @@ static int iwl_alloc_ucode(struct iwl_drv *drv,
static int validate_sec_sizes(struct iwl_drv *drv,
struct iwl_firmware_pieces *pieces,
- const struct iwl_cfg *cfg)
+ const struct iwl_rf_cfg *cfg)
{
IWL_DEBUG_INFO(drv, "f/w package hdr runtime inst size = %zd\n",
get_sec_size(pieces, IWL_UCODE_REGULAR,
@@ -1468,6 +1563,8 @@ static void _iwl_op_mode_stop(struct iwl_drv *drv)
}
}
+#define IWL_MLD_SUPPORTED_FW_VERSION 97
+
/*
* iwl_req_fw_callback - callback when firmware was loaded
*
@@ -1482,14 +1579,15 @@ static void iwl_req_fw_callback(const struct firmware *ucode_raw, void *context)
struct iwlwifi_opmode_table *op;
int err;
struct iwl_firmware_pieces *pieces;
- const unsigned int api_max = drv->trans->cfg->ucode_api_max;
- const unsigned int api_min = drv->trans->cfg->ucode_api_min;
+ unsigned int api_min, api_max;
size_t trigger_tlv_sz[FW_DBG_TRIGGER_MAX];
u32 api_ver;
int i;
bool usniffer_images = false;
bool failure = true;
+ iwl_get_ucode_api_versions(drv->trans, &api_min, &api_max);
+
fw->ucode_capa.max_probe_length = IWL_DEFAULT_MAX_PROBE_LENGTH;
fw->ucode_capa.standard_phy_calibration_size =
IWL_DEFAULT_STANDARD_PHY_CALIBRATE_TBL_SIZE;
@@ -1683,14 +1781,14 @@ static void iwl_req_fw_callback(const struct firmware *ucode_raw, void *context)
fw->init_evtlog_size = (pieces->init_evtlog_size - 16)/12;
else
fw->init_evtlog_size =
- drv->trans->trans_cfg->base_params->max_event_log_size;
+ drv->trans->mac_cfg->base->max_event_log_size;
fw->init_errlog_ptr = pieces->init_errlog_ptr;
fw->inst_evtlog_ptr = pieces->inst_evtlog_ptr;
if (pieces->inst_evtlog_size)
fw->inst_evtlog_size = (pieces->inst_evtlog_size - 16)/12;
else
fw->inst_evtlog_size =
- drv->trans->trans_cfg->base_params->max_event_log_size;
+ drv->trans->mac_cfg->base->max_event_log_size;
fw->inst_errlog_ptr = pieces->inst_errlog_ptr;
/*
@@ -1720,6 +1818,20 @@ static void iwl_req_fw_callback(const struct firmware *ucode_raw, void *context)
break;
}
+#if IS_ENABLED(CONFIG_IWLMLD)
+ if (pieces->major >= IWL_MLD_SUPPORTED_FW_VERSION &&
+ iwl_drv_is_wifi7_supported(drv->trans))
+ op = &iwlwifi_opmode_table[MLD_OP_MODE];
+#else
+ if (pieces->major >= IWL_MLD_SUPPORTED_FW_VERSION &&
+ iwl_drv_is_wifi7_supported(drv->trans)) {
+ IWL_ERR(drv,
+ "IWLMLD needs to be compiled to support this firmware\n");
+ mutex_unlock(&iwlwifi_opmode_table_mtx);
+ goto out_unbind;
+ }
+#endif
+
IWL_INFO(drv, "loaded firmware version %s op_mode %s\n",
drv->fw.fw_version, op->name);
diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-drv.h b/drivers/net/wireless/intel/iwlwifi/iwl-drv.h
index 854957bdf79d..595300a14639 100644
--- a/drivers/net/wireless/intel/iwlwifi/iwl-drv.h
+++ b/drivers/net/wireless/intel/iwlwifi/iwl-drv.h
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
/*
- * Copyright (C) 2005-2014, 2020-2021, 2023 Intel Corporation
+ * Copyright (C) 2005-2014, 2020-2021, 2023, 2025 Intel Corporation
* Copyright (C) 2013-2014 Intel Mobile Communications GmbH
*/
#ifndef __iwl_drv_h__
@@ -53,7 +53,7 @@
struct iwl_drv;
struct iwl_trans;
-struct iwl_cfg;
+struct iwl_rf_cfg;
/**
* iwl_drv_start - start the drv
*
diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-fh.h b/drivers/net/wireless/intel/iwlwifi/iwl-fh.h
index 5c8f1868db64..0f6de08b7473 100644
--- a/drivers/net/wireless/intel/iwlwifi/iwl-fh.h
+++ b/drivers/net/wireless/intel/iwlwifi/iwl-fh.h
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
/*
- * Copyright (C) 2005-2014, 2018-2021, 2023-2024 Intel Corporation
+ * Copyright (C) 2005-2014, 2018-2021, 2023-2025 Intel Corporation
* Copyright (C) 2015-2017 Intel Deutschland GmbH
*/
#ifndef __iwl_fh_h__
@@ -71,7 +71,7 @@
static inline unsigned int FH_MEM_CBBC_QUEUE(struct iwl_trans *trans,
unsigned int chnl)
{
- if (trans->trans_cfg->gen2) {
+ if (trans->mac_cfg->gen2) {
WARN_ON_ONCE(chnl >= 64);
return TFH_TFDQ_CBB_TABLE + 8 * chnl;
}
@@ -378,14 +378,14 @@ static inline unsigned int FH_MEM_CBBC_QUEUE(struct iwl_trans *trans,
* Once the RXF-to-DRAM DMA is active, this flag is immediately turned off.
*/
#define RFH_GEN_STATUS 0xA09808
-#define RFH_GEN_STATUS_GEN3 0xA07824
+#define RFH_GEN_STATUS_AX210 0xA07824
#define RBD_FETCH_IDLE BIT(29)
#define SRAM_DMA_IDLE BIT(30)
#define RXF_DMA_IDLE BIT(31)
/* DMA configuration */
#define RFH_RXF_DMA_CFG 0xA09820
-#define RFH_RXF_DMA_CFG_GEN3 0xA07880
+#define RFH_RXF_DMA_CFG_AX210 0xA07880
/* RB size */
#define RFH_RXF_DMA_RB_SIZE_MASK (0x000F0000) /* bits 16-19 */
#define RFH_RXF_DMA_RB_SIZE_POS 16
@@ -588,13 +588,12 @@ struct iwl_rb_status {
#define TFD_QUEUE_SIZE_MAX (256)
-#define TFD_QUEUE_SIZE_MAX_GEN3 (65536)
/* cb size is the exponent - 3 */
#define TFD_QUEUE_CB_SIZE(x) (ilog2(x) - 3)
#define TFD_QUEUE_SIZE_BC_DUP (64)
#define TFD_QUEUE_BC_SIZE (TFD_QUEUE_SIZE_MAX + TFD_QUEUE_SIZE_BC_DUP)
-#define TFD_QUEUE_BC_SIZE_GEN3_AX210 1024
-#define TFD_QUEUE_BC_SIZE_GEN3_BZ (1024 * 4)
+#define TFD_QUEUE_BC_SIZE_AX210 1024
+#define TFD_QUEUE_BC_SIZE_BZ (1024 * 4)
#define IWL_TX_DMA_MASK DMA_BIT_MASK(36)
#define IWL_NUM_OF_TBS 20
#define IWL_TFH_NUM_TBS 25
@@ -717,30 +716,19 @@ struct iwl_tfh_tfd {
/* Fixed (non-configurable) rx data from phy */
/**
- * struct iwlagn_scd_bc_tbl - scheduler byte count table
+ * struct iwl_bc_tbl_entry - scheduler byte count table entry
* base physical address provided by SCD_DRAM_BASE_ADDR
* For devices up to 22000:
* @tfd_offset:
* For devices up to 22000:
* 0-12 - tx command byte count
* 12-16 - station index
- * For 22000:
+ * For 22000 and on:
* 0-12 - tx command byte count
* 12-13 - number of 64 byte chunks
* 14-16 - reserved
*/
-struct iwlagn_scd_bc_tbl {
- __le16 tfd_offset[TFD_QUEUE_BC_SIZE];
-} __packed;
-
-/**
- * struct iwl_gen3_bc_tbl_entry - scheduler byte count table entry gen3
- * For AX210 and on:
- * @tfd_offset: 0-12 - tx command byte count
- * 12-13 - number of 64 byte chunks
- * 14-16 - reserved
- */
-struct iwl_gen3_bc_tbl_entry {
+struct iwl_bc_tbl_entry {
__le16 tfd_offset;
} __packed;
diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-io.c b/drivers/net/wireless/intel/iwlwifi/iwl-io.c
index 0653ca8b974a..80591809164e 100644
--- a/drivers/net/wireless/intel/iwlwifi/iwl-io.c
+++ b/drivers/net/wireless/intel/iwlwifi/iwl-io.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
/*
- * Copyright (C) 2003-2014, 2018-2022, 2024 Intel Corporation
+ * Copyright (C) 2003-2014, 2018-2022, 2024-2025 Intel Corporation
* Copyright (C) 2015-2016 Intel Deutschland GmbH
*/
#include <linux/delay.h>
@@ -211,13 +211,13 @@ IWL_EXPORT_SYMBOL(iwl_clear_bits_prph);
void iwl_force_nmi(struct iwl_trans *trans)
{
- if (trans->trans_cfg->device_family < IWL_DEVICE_FAMILY_9000)
+ if (trans->mac_cfg->device_family < IWL_DEVICE_FAMILY_9000)
iwl_write_prph_delay(trans, DEVICE_SET_NMI_REG,
DEVICE_SET_NMI_VAL_DRV, 1);
- else if (trans->trans_cfg->device_family < IWL_DEVICE_FAMILY_AX210)
+ else if (trans->mac_cfg->device_family < IWL_DEVICE_FAMILY_AX210)
iwl_write_umac_prph(trans, UREG_NIC_SET_NMI_DRIVER,
UREG_NIC_SET_NMI_DRIVER_NMI_FROM_DRIVER);
- else if (trans->trans_cfg->device_family < IWL_DEVICE_FAMILY_BZ)
+ else if (trans->mac_cfg->device_family < IWL_DEVICE_FAMILY_BZ)
iwl_write_umac_prph(trans, UREG_DOORBELL_TO_ISR6,
UREG_DOORBELL_TO_ISR6_NMI_BIT);
else
@@ -260,7 +260,7 @@ struct reg {
static int iwl_dump_rfh(struct iwl_trans *trans, char **buf)
{
int i, q;
- int num_q = trans->num_rx_queues;
+ int num_q = trans->info.num_rxqs;
static const u32 rfh_tbl[] = {
RFH_RXF_DMA_CFG,
RFH_GEN_CFG,
@@ -368,7 +368,7 @@ int iwl_dump_fh(struct iwl_trans *trans, char **buf)
FH_TSSR_TX_ERROR_REG
};
- if (trans->trans_cfg->mq_rx_supported)
+ if (trans->mac_cfg->mq_rx_supported)
return iwl_dump_rfh(trans, buf);
#ifdef CONFIG_IWLWIFI_DEBUGFS
@@ -423,7 +423,7 @@ static void iwl_dump_host_monitor_block(struct iwl_trans *trans,
static void iwl_dump_host_monitor(struct iwl_trans *trans)
{
- switch (trans->trans_cfg->device_family) {
+ switch (trans->mac_cfg->device_family) {
case IWL_DEVICE_FAMILY_22000:
case IWL_DEVICE_FAMILY_AX210:
IWL_ERR(trans, "CSR_RESET = 0x%x\n",
@@ -445,11 +445,11 @@ static void iwl_dump_host_monitor(struct iwl_trans *trans)
int iwl_finish_nic_init(struct iwl_trans *trans)
{
- const struct iwl_cfg_trans_params *cfg_trans = trans->trans_cfg;
+ const struct iwl_mac_cfg *mac_cfg = trans->mac_cfg;
u32 poll_ready;
int err;
- if (cfg_trans->bisr_workaround) {
+ if (mac_cfg->bisr_workaround) {
/* ensure the TOP FSM isn't still in previous reset */
mdelay(2);
}
@@ -458,7 +458,7 @@ int iwl_finish_nic_init(struct iwl_trans *trans)
* Set "initialization complete" bit to move adapter from
* D0U* --> D0A* (powered-up active) state.
*/
- if (cfg_trans->device_family >= IWL_DEVICE_FAMILY_BZ) {
+ if (mac_cfg->device_family >= IWL_DEVICE_FAMILY_BZ) {
iwl_set_bit(trans, CSR_GP_CNTRL,
CSR_GP_CNTRL_REG_FLAG_BZ_MAC_ACCESS_REQ |
CSR_GP_CNTRL_REG_FLAG_MAC_INIT);
@@ -469,7 +469,7 @@ int iwl_finish_nic_init(struct iwl_trans *trans)
poll_ready = CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY;
}
- if (cfg_trans->device_family == IWL_DEVICE_FAMILY_8000)
+ if (mac_cfg->device_family == IWL_DEVICE_FAMILY_8000)
udelay(2);
/*
@@ -484,7 +484,7 @@ int iwl_finish_nic_init(struct iwl_trans *trans)
iwl_dump_host_monitor(trans);
}
- if (cfg_trans->bisr_workaround) {
+ if (mac_cfg->bisr_workaround) {
/* ensure BISR shift has finished */
udelay(200);
}
diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-io.h b/drivers/net/wireless/intel/iwlwifi/iwl-io.h
index 37b3bd62897e..f4833c5fe86e 100644
--- a/drivers/net/wireless/intel/iwlwifi/iwl-io.h
+++ b/drivers/net/wireless/intel/iwlwifi/iwl-io.h
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
/*
- * Copyright (C) 2018-2021 Intel Corporation
+ * Copyright (C) 2018-2021, 2025 Intel Corporation
*/
#ifndef __iwl_io_h__
#define __iwl_io_h__
@@ -64,38 +64,38 @@ int iwl_dump_fh(struct iwl_trans *trans, char **buf);
*/
static inline u32 iwl_umac_prph(struct iwl_trans *trans, u32 ofs)
{
- return ofs + trans->trans_cfg->umac_prph_offset;
+ return ofs + trans->mac_cfg->umac_prph_offset;
}
static inline u32 iwl_read_umac_prph_no_grab(struct iwl_trans *trans, u32 ofs)
{
return iwl_read_prph_no_grab(trans, ofs +
- trans->trans_cfg->umac_prph_offset);
+ trans->mac_cfg->umac_prph_offset);
}
static inline u32 iwl_read_umac_prph(struct iwl_trans *trans, u32 ofs)
{
- return iwl_read_prph(trans, ofs + trans->trans_cfg->umac_prph_offset);
+ return iwl_read_prph(trans, ofs + trans->mac_cfg->umac_prph_offset);
}
static inline void iwl_write_umac_prph_no_grab(struct iwl_trans *trans, u32 ofs,
u32 val)
{
- iwl_write_prph_no_grab(trans, ofs + trans->trans_cfg->umac_prph_offset,
+ iwl_write_prph_no_grab(trans, ofs + trans->mac_cfg->umac_prph_offset,
val);
}
static inline void iwl_write_umac_prph(struct iwl_trans *trans, u32 ofs,
u32 val)
{
- iwl_write_prph(trans, ofs + trans->trans_cfg->umac_prph_offset, val);
+ iwl_write_prph(trans, ofs + trans->mac_cfg->umac_prph_offset, val);
}
static inline int iwl_poll_umac_prph_bit(struct iwl_trans *trans, u32 addr,
u32 bits, u32 mask, int timeout)
{
return iwl_poll_prph_bit(trans, addr +
- trans->trans_cfg->umac_prph_offset,
+ trans->mac_cfg->umac_prph_offset,
bits, mask, timeout);
}
diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c
index 9f7e013252fe..0592f0f59d1c 100644
--- a/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c
+++ b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
/*
- * Copyright (C) 2005-2014, 2018-2023 Intel Corporation
+ * Copyright (C) 2005-2014, 2018-2023, 2025 Intel Corporation
* Copyright (C) 2013-2015 Intel Mobile Communications GmbH
* Copyright (C) 2016-2017 Intel Deutschland GmbH
*/
@@ -143,6 +143,9 @@ static struct ieee80211_rate iwl_cfg80211_rates[] = {
* @NVM_CHANNEL_VALID: channel is usable for this SKU/geo
* @NVM_CHANNEL_IBSS: usable as an IBSS channel and deprecated
* when %IWL_NVM_SBANDS_FLAGS_LAR enabled.
+ * @NVM_CHANNEL_ALLOW_20MHZ_ACTIVITY: active scanning allowed and
+ * AP allowed only in 20 MHz. Valid only
+ * when %IWL_NVM_SBANDS_FLAGS_LAR enabled.
* @NVM_CHANNEL_ACTIVE: active scanning allowed and allows IBSS
* when %IWL_NVM_SBANDS_FLAGS_LAR enabled.
* @NVM_CHANNEL_RADAR: radar detection required
@@ -159,20 +162,21 @@ static struct ieee80211_rate iwl_cfg80211_rates[] = {
* @NVM_CHANNEL_AFC: client support connection to UHB AFC AP
*/
enum iwl_nvm_channel_flags {
- NVM_CHANNEL_VALID = BIT(0),
- NVM_CHANNEL_IBSS = BIT(1),
- NVM_CHANNEL_ACTIVE = BIT(3),
- NVM_CHANNEL_RADAR = BIT(4),
- NVM_CHANNEL_INDOOR_ONLY = BIT(5),
- NVM_CHANNEL_GO_CONCURRENT = BIT(6),
- NVM_CHANNEL_UNIFORM = BIT(7),
- NVM_CHANNEL_20MHZ = BIT(8),
- NVM_CHANNEL_40MHZ = BIT(9),
- NVM_CHANNEL_80MHZ = BIT(10),
- NVM_CHANNEL_160MHZ = BIT(11),
- NVM_CHANNEL_DC_HIGH = BIT(12),
- NVM_CHANNEL_VLP = BIT(13),
- NVM_CHANNEL_AFC = BIT(14),
+ NVM_CHANNEL_VALID = BIT(0),
+ NVM_CHANNEL_IBSS = BIT(1),
+ NVM_CHANNEL_ALLOW_20MHZ_ACTIVITY = BIT(2),
+ NVM_CHANNEL_ACTIVE = BIT(3),
+ NVM_CHANNEL_RADAR = BIT(4),
+ NVM_CHANNEL_INDOOR_ONLY = BIT(5),
+ NVM_CHANNEL_GO_CONCURRENT = BIT(6),
+ NVM_CHANNEL_UNIFORM = BIT(7),
+ NVM_CHANNEL_20MHZ = BIT(8),
+ NVM_CHANNEL_40MHZ = BIT(9),
+ NVM_CHANNEL_80MHZ = BIT(10),
+ NVM_CHANNEL_160MHZ = BIT(11),
+ NVM_CHANNEL_DC_HIGH = BIT(12),
+ NVM_CHANNEL_VLP = BIT(13),
+ NVM_CHANNEL_AFC = BIT(14),
};
/**
@@ -332,7 +336,7 @@ static inline void iwl_nvm_print_channel_flags(struct device *dev, u32 level,
}
static u32 iwl_get_channel_flags(u8 ch_num, int ch_idx, enum nl80211_band band,
- u32 nvm_flags, const struct iwl_cfg *cfg)
+ u32 nvm_flags, const struct iwl_rf_cfg *cfg)
{
u32 flags = IEEE80211_CHAN_NO_HT40;
@@ -399,7 +403,7 @@ static int iwl_init_channel_map(struct iwl_trans *trans,
const void * const nvm_ch_flags,
u32 sbands_flags, bool v4)
{
- const struct iwl_cfg *cfg = trans->cfg;
+ const struct iwl_rf_cfg *cfg = trans->cfg;
struct device *dev = trans->dev;
int ch_idx;
int n_channels = 0;
@@ -500,7 +504,7 @@ static void iwl_init_vht_hw_capab(struct iwl_trans *trans,
struct ieee80211_sta_vht_cap *vht_cap,
u8 tx_chains, u8 rx_chains)
{
- const struct iwl_cfg *cfg = trans->cfg;
+ const struct iwl_rf_cfg *cfg = trans->cfg;
int num_rx_ants = num_of_ant(rx_chains);
int num_tx_ants = num_of_ant(tx_chains);
@@ -513,7 +517,7 @@ static void iwl_init_vht_hw_capab(struct iwl_trans *trans,
IEEE80211_VHT_MAX_AMPDU_1024K <<
IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_SHIFT;
- if (!trans->cfg->ht_params->stbc)
+ if (!trans->cfg->ht_params.stbc)
vht_cap->cap &= ~IEEE80211_VHT_CAP_RXSTBC_MASK;
if (data->vht160_supported)
@@ -523,7 +527,7 @@ static void iwl_init_vht_hw_capab(struct iwl_trans *trans,
if (cfg->vht_mu_mimo_supported)
vht_cap->cap |= IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE;
- if (cfg->ht_params->ldpc)
+ if (cfg->ht_params.ldpc)
vht_cap->cap |= IEEE80211_VHT_CAP_RXLDPC;
if (data->sku_cap_mimo_disabled) {
@@ -531,21 +535,21 @@ static void iwl_init_vht_hw_capab(struct iwl_trans *trans,
num_tx_ants = 1;
}
- if (trans->cfg->ht_params->stbc && num_tx_ants > 1)
+ if (trans->cfg->ht_params.stbc && num_tx_ants > 1)
vht_cap->cap |= IEEE80211_VHT_CAP_TXSTBC;
else
vht_cap->cap |= IEEE80211_VHT_CAP_TX_ANTENNA_PATTERN;
switch (iwlwifi_mod_params.amsdu_size) {
case IWL_AMSDU_DEF:
- if (trans->trans_cfg->mq_rx_supported)
+ if (trans->mac_cfg->mq_rx_supported)
vht_cap->cap |=
IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_11454;
else
vht_cap->cap |= IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_3895;
break;
case IWL_AMSDU_2K:
- if (trans->trans_cfg->mq_rx_supported)
+ if (trans->mac_cfg->mq_rx_supported)
vht_cap->cap |=
IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_11454;
else
@@ -684,10 +688,13 @@ static const struct ieee80211_sband_iftype_data iwl_he_eht_capa[] = {
.has_eht = true,
.eht_cap_elem = {
.mac_cap_info[0] =
+ IEEE80211_EHT_MAC_CAP0_EPCS_PRIO_ACCESS |
IEEE80211_EHT_MAC_CAP0_OM_CONTROL |
IEEE80211_EHT_MAC_CAP0_TRIG_TXOP_SHARING_MODE1 |
IEEE80211_EHT_MAC_CAP0_TRIG_TXOP_SHARING_MODE2 |
IEEE80211_EHT_MAC_CAP0_SCS_TRAFFIC_DESC,
+ .mac_cap_info[1] =
+ IEEE80211_EHT_MAC_CAP1_UNSOL_EPCS_PRIO_ACCESS,
.phy_cap_info[0] =
IEEE80211_EHT_PHY_CAP0_242_TONE_RU_GT20MHZ |
IEEE80211_EHT_PHY_CAP0_NDP_4_EHT_LFT_32_GI |
@@ -913,11 +920,8 @@ iwl_nvm_fixup_sband_iftd(struct iwl_trans *trans,
{
bool is_ap = iftype_data->types_mask & (BIT(NL80211_IFTYPE_AP) |
BIT(NL80211_IFTYPE_P2P_GO));
- bool no_320;
-
- no_320 = (!trans->trans_cfg->integrated &&
- trans->pcie_link_speed < PCI_EXP_LNKSTA_CLS_8_0GB) ||
- trans->reduced_cap_sku;
+ bool slow_pcie = (!trans->mac_cfg->integrated &&
+ trans->info.pcie_link_speed < PCI_EXP_LNKSTA_CLS_8_0GB);
if (!data->sku_cap_11be_enable || iwlwifi_mod_params.disable_11be)
iftype_data->eht_cap.has_eht = false;
@@ -944,7 +948,8 @@ iwl_nvm_fixup_sband_iftd(struct iwl_trans *trans,
IEEE80211_EHT_MAC_CAP0_MAX_MPDU_LEN_MASK);
break;
case NL80211_BAND_6GHZ:
- if (!no_320) {
+ if (!trans->reduced_cap_sku &&
+ (!trans->cfg->bw_limit || trans->cfg->bw_limit >= 320)) {
iftype_data->eht_cap.eht_cap_elem.phy_cap_info[0] |=
IEEE80211_EHT_PHY_CAP0_320MHZ_IN_6GHZ;
iftype_data->eht_cap.eht_cap_elem.phy_cap_info[1] |=
@@ -986,6 +991,14 @@ iwl_nvm_fixup_sband_iftd(struct iwl_trans *trans,
iftype_data->eht_cap.eht_cap_elem.phy_cap_info[4] |= 0x10;
}
}
+
+ if (slow_pcie) {
+ struct ieee80211_eht_mcs_nss_supp *mcs_nss =
+ &iftype_data->eht_cap.eht_mcs_nss_supp;
+
+ mcs_nss->bw._320.rx_tx_mcs11_max_nss = 0;
+ mcs_nss->bw._320.rx_tx_mcs13_max_nss = 0;
+ }
} else {
struct ieee80211_he_mcs_nss_supp *he_mcs_nss_supp =
&iftype_data->he_cap.he_mcs_nss_supp;
@@ -1023,11 +1036,11 @@ iwl_nvm_fixup_sband_iftd(struct iwl_trans *trans,
cpu_to_le16(IEEE80211_HE_MCS_NOT_SUPPORTED << 2);
}
- if (trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_AX210 && !is_ap)
+ if (trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_AX210 && !is_ap)
iftype_data->he_cap.he_cap_elem.phy_cap_info[2] |=
IEEE80211_HE_PHY_CAP2_UL_MU_FULL_MU_MIMO;
- switch (CSR_HW_RFID_TYPE(trans->hw_rf_id)) {
+ switch (CSR_HW_RFID_TYPE(trans->info.hw_rf_id)) {
case IWL_CFG_RF_TYPE_GF:
case IWL_CFG_RF_TYPE_FM:
case IWL_CFG_RF_TYPE_WH:
@@ -1039,7 +1052,7 @@ iwl_nvm_fixup_sband_iftd(struct iwl_trans *trans,
break;
}
- if (CSR_HW_REV_TYPE(trans->hw_rev) == IWL_CFG_MAC_TYPE_GL &&
+ if (CSR_HW_REV_TYPE(trans->info.hw_rev) == IWL_CFG_MAC_TYPE_GL &&
iftype_data->eht_cap.has_eht) {
iftype_data->eht_cap.eht_cap_elem.mac_cap_info[0] &=
~(IEEE80211_EHT_MAC_CAP0_TRIG_TXOP_SHARING_MODE1 |
@@ -1068,13 +1081,13 @@ iwl_nvm_fixup_sband_iftd(struct iwl_trans *trans,
iftype_data->he_cap.he_cap_elem.mac_cap_info[2] |=
IEEE80211_HE_MAC_CAP2_BCAST_TWT;
- if (trans->trans_cfg->device_family == IWL_DEVICE_FAMILY_22000 &&
+ if (trans->mac_cfg->device_family == IWL_DEVICE_FAMILY_22000 &&
!is_ap) {
iftype_data->vendor_elems.data = iwl_vendor_caps;
iftype_data->vendor_elems.len = ARRAY_SIZE(iwl_vendor_caps);
}
- if (!trans->cfg->ht_params->stbc) {
+ if (!trans->cfg->ht_params.stbc) {
iftype_data->he_cap.he_cap_elem.phy_cap_info[2] &=
~IEEE80211_HE_PHY_CAP2_STBC_RX_UNDER_80MHZ;
iftype_data->he_cap.he_cap_elem.phy_cap_info[7] &=
@@ -1086,19 +1099,23 @@ iwl_nvm_fixup_sband_iftd(struct iwl_trans *trans,
iftype_data->eht_cap.eht_mcs_nss_supp.bw._320.rx_tx_mcs13_max_nss = 0;
}
- if (trans->no_160)
+ if (trans->cfg->bw_limit && trans->cfg->bw_limit < 160)
iftype_data->he_cap.he_cap_elem.phy_cap_info[0] &=
~IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_160MHZ_IN_5G;
- if (trans->reduced_cap_sku) {
+ if ((trans->cfg->bw_limit && trans->cfg->bw_limit < 320) ||
+ trans->reduced_cap_sku) {
memset(&iftype_data->eht_cap.eht_mcs_nss_supp.bw._320, 0,
sizeof(iftype_data->eht_cap.eht_mcs_nss_supp.bw._320));
+ iftype_data->eht_cap.eht_cap_elem.phy_cap_info[2] &=
+ ~IEEE80211_EHT_PHY_CAP2_SOUNDING_DIM_320MHZ_MASK;
+ }
+
+ if (trans->reduced_cap_sku) {
iftype_data->eht_cap.eht_mcs_nss_supp.bw._80.rx_tx_mcs13_max_nss = 0;
iftype_data->eht_cap.eht_mcs_nss_supp.bw._160.rx_tx_mcs13_max_nss = 0;
iftype_data->eht_cap.eht_cap_elem.phy_cap_info[8] &=
~IEEE80211_EHT_PHY_CAP8_RX_4096QAM_WIDER_BW_DL_OFDMA;
- iftype_data->eht_cap.eht_cap_elem.phy_cap_info[2] &=
- ~IEEE80211_EHT_PHY_CAP2_SOUNDING_DIM_320MHZ_MASK;
}
}
@@ -1234,7 +1251,7 @@ static void iwl_init_sbands(struct iwl_trans *trans,
n_used, n_channels);
}
-static int iwl_get_sku(const struct iwl_cfg *cfg, const __le16 *nvm_sw,
+static int iwl_get_sku(const struct iwl_rf_cfg *cfg, const __le16 *nvm_sw,
const __le16 *phy_sku)
{
if (cfg->nvm_type != IWL_NVM_EXT)
@@ -1243,7 +1260,7 @@ static int iwl_get_sku(const struct iwl_cfg *cfg, const __le16 *nvm_sw,
return le32_to_cpup((const __le32 *)(phy_sku + SKU_FAMILY_8000));
}
-static int iwl_get_nvm_version(const struct iwl_cfg *cfg, const __le16 *nvm_sw)
+static int iwl_get_nvm_version(const struct iwl_rf_cfg *cfg, const __le16 *nvm_sw)
{
if (cfg->nvm_type != IWL_NVM_EXT)
return le16_to_cpup(nvm_sw + NVM_VERSION);
@@ -1252,7 +1269,7 @@ static int iwl_get_nvm_version(const struct iwl_cfg *cfg, const __le16 *nvm_sw)
NVM_VERSION_EXT_NVM));
}
-static int iwl_get_radio_cfg(const struct iwl_cfg *cfg, const __le16 *nvm_sw,
+static int iwl_get_radio_cfg(const struct iwl_rf_cfg *cfg, const __le16 *nvm_sw,
const __le16 *phy_sku)
{
if (cfg->nvm_type != IWL_NVM_EXT)
@@ -1262,7 +1279,7 @@ static int iwl_get_radio_cfg(const struct iwl_cfg *cfg, const __le16 *nvm_sw,
}
-static int iwl_get_n_hw_addrs(const struct iwl_cfg *cfg, const __le16 *nvm_sw)
+static int iwl_get_n_hw_addrs(const struct iwl_rf_cfg *cfg, const __le16 *nvm_sw)
{
int n_hw_addr;
@@ -1274,7 +1291,7 @@ static int iwl_get_n_hw_addrs(const struct iwl_cfg *cfg, const __le16 *nvm_sw)
return n_hw_addr & N_HW_ADDR_MASK;
}
-static void iwl_set_radio_cfg(const struct iwl_cfg *cfg,
+static void iwl_set_radio_cfg(const struct iwl_rf_cfg *cfg,
struct iwl_nvm_data *data,
u32 radio_cfg)
{
@@ -1333,7 +1350,7 @@ static void iwl_set_hw_address_from_csr(struct iwl_trans *trans,
}
static void iwl_set_hw_address_family_8000(struct iwl_trans *trans,
- const struct iwl_cfg *cfg,
+ const struct iwl_rf_cfg *cfg,
struct iwl_nvm_data *data,
const __le16 *mac_override,
const __be16 *nvm_hw)
@@ -1382,11 +1399,12 @@ static void iwl_set_hw_address_family_8000(struct iwl_trans *trans,
}
static int iwl_set_hw_address(struct iwl_trans *trans,
- const struct iwl_cfg *cfg,
+ const struct iwl_rf_cfg *cfg,
struct iwl_nvm_data *data, const __be16 *nvm_hw,
const __le16 *mac_override)
{
- if (cfg->mac_addr_from_csr) {
+ const struct iwl_mac_cfg *mac_cfg = trans->mac_cfg;
+ if (mac_cfg->base->mac_addr_from_csr) {
iwl_set_hw_address_from_csr(trans, data);
} else if (cfg->nvm_type != IWL_NVM_EXT) {
const u8 *hw_addr = (const u8 *)(nvm_hw + HW_ADDR);
@@ -1416,7 +1434,7 @@ static int iwl_set_hw_address(struct iwl_trans *trans,
}
static bool
-iwl_nvm_no_wide_in_5ghz(struct iwl_trans *trans, const struct iwl_cfg *cfg,
+iwl_nvm_no_wide_in_5ghz(struct iwl_trans *trans, const struct iwl_rf_cfg *cfg,
const __be16 *nvm_hw)
{
/*
@@ -1428,7 +1446,7 @@ iwl_nvm_no_wide_in_5ghz(struct iwl_trans *trans, const struct iwl_cfg *cfg,
* in 5GHz otherwise the FW will throw a sysassert when we try
* to use them.
*/
- if (trans->trans_cfg->device_family == IWL_DEVICE_FAMILY_7000) {
+ if (trans->mac_cfg->device_family == IWL_DEVICE_FAMILY_7000) {
/*
* Unlike the other sections in the NVM, the hw
* section uses big-endian.
@@ -1448,7 +1466,7 @@ iwl_nvm_no_wide_in_5ghz(struct iwl_trans *trans, const struct iwl_cfg *cfg,
}
struct iwl_nvm_data *
-iwl_parse_mei_nvm_data(struct iwl_trans *trans, const struct iwl_cfg *cfg,
+iwl_parse_mei_nvm_data(struct iwl_trans *trans, const struct iwl_rf_cfg *cfg,
const struct iwl_mei_nvm *mei_nvm,
const struct iwl_fw *fw, u8 tx_ant, u8 rx_ant)
{
@@ -1512,7 +1530,7 @@ iwl_parse_mei_nvm_data(struct iwl_trans *trans, const struct iwl_cfg *cfg,
IWL_EXPORT_SYMBOL(iwl_parse_mei_nvm_data);
struct iwl_nvm_data *
-iwl_parse_nvm_data(struct iwl_trans *trans, const struct iwl_cfg *cfg,
+iwl_parse_nvm_data(struct iwl_trans *trans, const struct iwl_rf_cfg *cfg,
const struct iwl_fw *fw,
const __be16 *nvm_hw, const __le16 *nvm_sw,
const __le16 *nvm_calib, const __le16 *regulatory,
@@ -1612,7 +1630,7 @@ IWL_EXPORT_SYMBOL(iwl_parse_nvm_data);
static u32 iwl_nvm_get_regdom_bw_flags(const u16 *nvm_chan,
int ch_idx, u16 nvm_flags,
struct iwl_reg_capa reg_capa,
- const struct iwl_cfg *cfg)
+ const struct iwl_rf_cfg *cfg)
{
u32 flags = NL80211_RRF_NO_HT40;
@@ -1647,6 +1665,10 @@ static u32 iwl_nvm_get_regdom_bw_flags(const u16 *nvm_chan,
if (nvm_flags & NVM_CHANNEL_INDOOR_ONLY)
flags |= NL80211_RRF_NO_OUTDOOR;
+ if (nvm_flags & NVM_CHANNEL_ALLOW_20MHZ_ACTIVITY &&
+ flags & NL80211_RRF_NO_IR)
+ flags |= NL80211_RRF_ALLOW_20MHZ_ACTIVITY;
+
/* Set the GO concurrent flag only in case that NO_IR is set.
* Otherwise it is meaningless
*/
@@ -1723,10 +1745,12 @@ static struct iwl_reg_capa iwl_get_reg_capa(u32 flags, u8 resp_ver)
}
struct ieee80211_regdomain *
-iwl_parse_nvm_mcc_info(struct device *dev, const struct iwl_cfg *cfg,
+iwl_parse_nvm_mcc_info(struct iwl_trans *trans,
int num_of_ch, __le32 *channels, u16 fw_mcc,
u16 geo_info, u32 cap, u8 resp_ver)
{
+ const struct iwl_rf_cfg *cfg = trans->cfg;
+ struct device *dev = trans->dev;
int ch_idx;
u16 ch_flags;
u32 reg_rule_flags, prev_reg_rule_flags = 0;
@@ -1981,8 +2005,8 @@ int iwl_read_external_nvm(struct iwl_trans *trans,
le32_to_cpu(dword_buff[3]));
/* nvm file validation, dword_buff[2] holds the file version */
- if (trans->trans_cfg->device_family == IWL_DEVICE_FAMILY_8000 &&
- trans->hw_rev_step == SILICON_C_STEP &&
+ if (trans->mac_cfg->device_family == IWL_DEVICE_FAMILY_8000 &&
+ trans->info.hw_rev_step == SILICON_C_STEP &&
le32_to_cpu(dword_buff[2]) < 0xE4A) {
ret = -EFAULT;
goto out;
@@ -2049,7 +2073,7 @@ int iwl_read_external_nvm(struct iwl_trans *trans,
break;
}
- iwl_nvm_fixups(trans->hw_id, section_id, temp, section_size);
+ iwl_nvm_fixups(trans->info.hw_id, section_id, temp, section_size);
kfree(nvm_sections[section_id].data);
nvm_sections[section_id].data = temp;
@@ -2152,7 +2176,7 @@ struct iwl_nvm_data *iwl_get_nvm(struct iwl_trans *trans,
!!(mac_flags & NVM_MAC_SKU_FLAGS_BAND_5_2_ENABLED);
nvm->sku_cap_mimo_disabled =
!!(mac_flags & NVM_MAC_SKU_FLAGS_MIMO_DISABLED);
- if (CSR_HW_RFID_TYPE(trans->hw_rf_id) >= IWL_CFG_RF_TYPE_FM)
+ if (CSR_HW_RFID_TYPE(trans->info.hw_rf_id) >= IWL_CFG_RF_TYPE_FM)
nvm->sku_cap_11be_enable = true;
/* Initialize PHY sku data */
diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.h b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.h
index 0c6c3fb8c6dd..9ce9fa4e78fd 100644
--- a/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.h
+++ b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.h
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
/*
- * Copyright (C) 2005-2015, 2018-2024 Intel Corporation
+ * Copyright (C) 2005-2015, 2018-2025 Intel Corporation
* Copyright (C) 2016-2017 Intel Deutschland GmbH
*/
#ifndef __iwl_nvm_parse_h__
@@ -30,7 +30,7 @@ enum iwl_nvm_sbands_flags {
* later with iwl_free_nvm_data().
*/
struct iwl_nvm_data *
-iwl_parse_nvm_data(struct iwl_trans *trans, const struct iwl_cfg *cfg,
+iwl_parse_nvm_data(struct iwl_trans *trans, const struct iwl_rf_cfg *cfg,
const struct iwl_fw *fw,
const __be16 *nvm_hw, const __le16 *nvm_sw,
const __le16 *nvm_calib, const __le16 *regulatory,
@@ -46,9 +46,17 @@ iwl_parse_nvm_data(struct iwl_trans *trans, const struct iwl_cfg *cfg,
* accordingly. An ERR_PTR is returned on error.
* If not given to the regulatory core, the user is responsible for freeing
* the regdomain returned here with kfree.
+ *
+ * @trans: the transport
+ * @num_of_ch: the number of channels
+ * @channels: channel map
+ * @fw_mcc: firmware country code
+ * @geo_info: geo info value
+ * @cap: capability
+ * @resp_ver: FW response version
*/
struct ieee80211_regdomain *
-iwl_parse_nvm_mcc_info(struct device *dev, const struct iwl_cfg *cfg,
+iwl_parse_nvm_mcc_info(struct iwl_trans *trans,
int num_of_ch, __le32 *channels, u16 fw_mcc,
u16 geo_info, u32 cap, u8 resp_ver);
@@ -87,7 +95,7 @@ struct iwl_nvm_data *iwl_get_nvm(struct iwl_trans *trans,
* iwl_parse_mei_nvm_data - parse the mei_nvm_data and get an iwl_nvm_data
*/
struct iwl_nvm_data *
-iwl_parse_mei_nvm_data(struct iwl_trans *trans, const struct iwl_cfg *cfg,
+iwl_parse_mei_nvm_data(struct iwl_trans *trans, const struct iwl_rf_cfg *cfg,
const struct iwl_mei_nvm *mei_nvm,
const struct iwl_fw *fw, u8 set_tx_ant, u8 set_rx_ant);
diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-nvm-utils.c b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-utils.c
index b3c25acd3691..ec312c90ff85 100644
--- a/drivers/net/wireless/intel/iwlwifi/iwl-nvm-utils.c
+++ b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-utils.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
/*
- * Copyright (C) 2005-2014, 2018-2021, 2023 Intel Corporation
+ * Copyright (C) 2005-2014, 2018-2021, 2023, 2025 Intel Corporation
* Copyright (C) 2015 Intel Mobile Communications GmbH
*/
#include <linux/types.h>
@@ -42,7 +42,7 @@ void iwl_init_ht_hw_capab(struct iwl_trans *trans,
enum nl80211_band band,
u8 tx_chains, u8 rx_chains)
{
- const struct iwl_cfg *cfg = trans->cfg;
+ const struct iwl_rf_cfg *cfg = trans->cfg;
int max_bit_rate = 0;
tx_chains = hweight8(tx_chains);
@@ -53,7 +53,8 @@ void iwl_init_ht_hw_capab(struct iwl_trans *trans,
if (!(data->sku_cap_11n_enable) ||
(iwlwifi_mod_params.disable_11n & IWL_DISABLE_HT_ALL) ||
- !cfg->ht_params) {
+ /* there are no devices with HT but without HT40 entirely */
+ !cfg->ht_params.ht40_bands) {
ht_info->ht_supported = false;
return;
}
@@ -64,17 +65,17 @@ void iwl_init_ht_hw_capab(struct iwl_trans *trans,
ht_info->ht_supported = true;
ht_info->cap = IEEE80211_HT_CAP_DSSSCCK40;
- if (cfg->ht_params->stbc) {
+ if (cfg->ht_params.stbc) {
ht_info->cap |= (1 << IEEE80211_HT_CAP_RX_STBC_SHIFT);
if (tx_chains > 1)
ht_info->cap |= IEEE80211_HT_CAP_TX_STBC;
}
- if (cfg->ht_params->ldpc)
+ if (cfg->ht_params.ldpc)
ht_info->cap |= IEEE80211_HT_CAP_LDPC_CODING;
- if (trans->trans_cfg->mq_rx_supported ||
+ if (trans->mac_cfg->mq_rx_supported ||
iwlwifi_mod_params.amsdu_size >= IWL_AMSDU_8K)
ht_info->cap |= IEEE80211_HT_CAP_MAX_AMSDU;
@@ -90,13 +91,13 @@ void iwl_init_ht_hw_capab(struct iwl_trans *trans,
if (rx_chains >= 3)
ht_info->mcs.rx_mask[2] = 0xFF;
- if (cfg->ht_params->ht_greenfield_support)
+ if (cfg->ht_params.ht_greenfield_support)
ht_info->cap |= IEEE80211_HT_CAP_GRN_FLD;
ht_info->cap |= IEEE80211_HT_CAP_SGI_20;
max_bit_rate = MAX_BIT_RATE_20_MHZ;
- if (cfg->ht_params->ht40_bands & BIT(band)) {
+ if (cfg->ht_params.ht40_bands & BIT(band)) {
ht_info->cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40;
ht_info->cap |= IEEE80211_HT_CAP_SGI_40;
max_bit_rate = MAX_BIT_RATE_40_MHZ;
diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-op-mode.h b/drivers/net/wireless/intel/iwlwifi/iwl-op-mode.h
index 34eca1a568ea..5dc299296d6d 100644
--- a/drivers/net/wireless/intel/iwlwifi/iwl-op-mode.h
+++ b/drivers/net/wireless/intel/iwlwifi/iwl-op-mode.h
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
/*
- * Copyright (C) 2005-2014, 2018-2021, 2024 Intel Corporation
+ * Copyright (C) 2005-2014, 2018-2021, 2024-2025 Intel Corporation
* Copyright (C) 2013-2014 Intel Mobile Communications GmbH
* Copyright (C) 2015 Intel Deutschland GmbH
*/
@@ -17,7 +17,7 @@ struct sk_buff;
struct iwl_device_cmd;
struct iwl_rx_cmd_buffer;
struct iwl_fw;
-struct iwl_cfg;
+struct iwl_rf_cfg;
/**
* DOC: Operational mode - what is it ?
@@ -52,12 +52,20 @@ struct iwl_cfg;
* any debug collection must happen synchronously as
* the device will be shut down
* @IWL_ERR_TYPE_CMD_QUEUE_FULL: command queue was full
+ * @IWL_ERR_TYPE_TOP_RESET_BY_BT: TOP reset initiated by BT
+ * @IWL_ERR_TYPE_TOP_FATAL_ERROR: TOP fatal error
+ * @IWL_ERR_TYPE_TOP_RESET_FAILED: TOP reset failed
+ * @IWL_ERR_TYPE_DEBUGFS: error/reset indication from debugfs
*/
enum iwl_fw_error_type {
IWL_ERR_TYPE_IRQ,
IWL_ERR_TYPE_NMI_FORCED,
IWL_ERR_TYPE_RESET_HS_TIMEOUT,
IWL_ERR_TYPE_CMD_QUEUE_FULL,
+ IWL_ERR_TYPE_TOP_RESET_BY_BT,
+ IWL_ERR_TYPE_TOP_FATAL_ERROR,
+ IWL_ERR_TYPE_TOP_RESET_FAILED,
+ IWL_ERR_TYPE_DEBUGFS,
};
/**
@@ -142,7 +150,7 @@ struct iwl_fw_error_dump_mode {
*/
struct iwl_op_mode_ops {
struct iwl_op_mode *(*start)(struct iwl_trans *trans,
- const struct iwl_cfg *cfg,
+ const struct iwl_rf_cfg *cfg,
const struct iwl_fw *fw,
struct dentry *dbgfs_dir);
void (*stop)(struct iwl_op_mode *op_mode);
@@ -242,6 +250,9 @@ static inline void iwl_op_mode_dump_error(struct iwl_op_mode *op_mode,
{
might_sleep();
+ if (WARN_ON(mode->type == IWL_ERR_TYPE_TOP_RESET_BY_BT))
+ return;
+
if (op_mode->ops->dump_error)
op_mode->ops->dump_error(op_mode, mode);
}
diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-trans.c b/drivers/net/wireless/intel/iwlwifi/iwl-trans.c
index 49c8507d1a6b..8a40801cf0dd 100644
--- a/drivers/net/wireless/intel/iwlwifi/iwl-trans.c
+++ b/drivers/net/wireless/intel/iwlwifi/iwl-trans.c
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2015 Intel Mobile Communications GmbH
* Copyright (C) 2016-2017 Intel Deutschland GmbH
- * Copyright (C) 2019-2021, 2023-2024 Intel Corporation
+ * Copyright (C) 2019-2021, 2023-2025 Intel Corporation
*/
#include <linux/kernel.h>
#include <linux/bsearch.h>
@@ -15,12 +15,13 @@
#include <linux/dmapool.h>
#include "fw/api/commands.h"
#include "pcie/internal.h"
-#include "iwl-context-info-gen3.h"
+#include "iwl-context-info-v2.h"
struct iwl_trans_dev_restart_data {
struct list_head list;
unsigned int restart_count;
time64_t last_error;
+ bool backoff;
char name[];
};
@@ -81,14 +82,14 @@ void iwl_trans_free_restart_list(void)
struct iwl_trans_reprobe {
struct device *dev;
- struct work_struct work;
+ struct delayed_work work;
};
static void iwl_trans_reprobe_wk(struct work_struct *wk)
{
struct iwl_trans_reprobe *reprobe;
- reprobe = container_of(wk, typeof(*reprobe), work);
+ reprobe = container_of(wk, typeof(*reprobe), work.work);
if (device_reprobe(reprobe->dev))
dev_err(reprobe->dev, "reprobe failed!\n");
@@ -97,7 +98,32 @@ static void iwl_trans_reprobe_wk(struct work_struct *wk)
module_put(THIS_MODULE);
}
-#define IWL_TRANS_RESET_OK_TIME 180 /* seconds */
+static void iwl_trans_schedule_reprobe(struct iwl_trans *trans,
+ unsigned int delay_ms)
+{
+ struct iwl_trans_reprobe *reprobe;
+
+ /*
+ * get a module reference to avoid doing this while unloading
+ * anyway and to avoid scheduling a work with code that's
+ * being removed.
+ */
+ if (!try_module_get(THIS_MODULE)) {
+ IWL_ERR(trans, "Module is being unloaded - abort\n");
+ return;
+ }
+
+ reprobe = kzalloc(sizeof(*reprobe), GFP_KERNEL);
+ if (!reprobe) {
+ module_put(THIS_MODULE);
+ return;
+ }
+ reprobe->dev = get_device(trans->dev);
+ INIT_DELAYED_WORK(&reprobe->work, iwl_trans_reprobe_wk);
+ schedule_delayed_work(&reprobe->work, msecs_to_jiffies(delay_ms));
+}
+
+#define IWL_TRANS_RESET_OK_TIME 7 /* seconds */
static enum iwl_reset_mode
iwl_trans_determine_restart_mode(struct iwl_trans *trans)
@@ -105,18 +131,46 @@ iwl_trans_determine_restart_mode(struct iwl_trans *trans)
struct iwl_trans_dev_restart_data *data;
enum iwl_reset_mode at_least = 0;
unsigned int index;
- static const enum iwl_reset_mode escalation_list[] = {
+ static const enum iwl_reset_mode escalation_list_old[] = {
IWL_RESET_MODE_SW_RESET,
IWL_RESET_MODE_REPROBE,
IWL_RESET_MODE_REPROBE,
IWL_RESET_MODE_FUNC_RESET,
- /* FIXME: add TOP reset */
IWL_RESET_MODE_PROD_RESET,
- /* FIXME: add TOP reset */
+ };
+ static const enum iwl_reset_mode escalation_list_sc[] = {
+ IWL_RESET_MODE_SW_RESET,
+ IWL_RESET_MODE_REPROBE,
+ IWL_RESET_MODE_REPROBE,
+ IWL_RESET_MODE_FUNC_RESET,
+ IWL_RESET_MODE_TOP_RESET,
+ IWL_RESET_MODE_PROD_RESET,
+ IWL_RESET_MODE_TOP_RESET,
IWL_RESET_MODE_PROD_RESET,
- /* FIXME: add TOP reset */
+ IWL_RESET_MODE_TOP_RESET,
IWL_RESET_MODE_PROD_RESET,
};
+ const enum iwl_reset_mode *escalation_list;
+ size_t escalation_list_size;
+
+ /* used by TOP fatal error/TOP reset */
+ if (trans->restart.mode.type == IWL_ERR_TYPE_TOP_RESET_FAILED)
+ return IWL_RESET_MODE_PROD_RESET;
+
+ if (trans->request_top_reset) {
+ trans->request_top_reset = 0;
+ if (trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_SC)
+ return IWL_RESET_MODE_TOP_RESET;
+ return IWL_RESET_MODE_PROD_RESET;
+ }
+
+ if (trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_SC) {
+ escalation_list = escalation_list_sc;
+ escalation_list_size = ARRAY_SIZE(escalation_list_sc);
+ } else {
+ escalation_list = escalation_list_old;
+ escalation_list_size = ARRAY_SIZE(escalation_list_old);
+ }
if (trans->restart.during_reset)
at_least = IWL_RESET_MODE_REPROBE;
@@ -125,25 +179,39 @@ iwl_trans_determine_restart_mode(struct iwl_trans *trans)
if (!data)
return at_least;
- if (ktime_get_boottime_seconds() - data->last_error >=
+ if (!data->backoff &&
+ ktime_get_boottime_seconds() - data->last_error >=
IWL_TRANS_RESET_OK_TIME)
data->restart_count = 0;
index = data->restart_count;
- if (index >= ARRAY_SIZE(escalation_list))
- index = ARRAY_SIZE(escalation_list) - 1;
+ if (index >= escalation_list_size) {
+ index = escalation_list_size - 1;
+ if (!data->backoff) {
+ data->backoff = true;
+ return IWL_RESET_MODE_BACKOFF;
+ }
+ data->backoff = false;
+ }
return max(at_least, escalation_list[index]);
}
+#define IWL_TRANS_TOP_FOLLOWER_WAIT 180 /* ms */
+
#define IWL_TRANS_RESET_DELAY (HZ * 60)
static void iwl_trans_restart_wk(struct work_struct *wk)
{
- struct iwl_trans *trans = container_of(wk, typeof(*trans), restart.wk);
- struct iwl_trans_reprobe *reprobe;
+ struct iwl_trans *trans = container_of(wk, typeof(*trans),
+ restart.wk.work);
enum iwl_reset_mode mode;
+ if (trans->restart.mode.type == IWL_ERR_TYPE_TOP_RESET_BY_BT) {
+ iwl_trans_schedule_reprobe(trans, IWL_TRANS_TOP_FOLLOWER_WAIT);
+ return;
+ }
+
if (!trans->op_mode)
return;
@@ -168,35 +236,29 @@ static void iwl_trans_restart_wk(struct work_struct *wk)
return;
mode = iwl_trans_determine_restart_mode(trans);
+ if (mode == IWL_RESET_MODE_BACKOFF) {
+ IWL_ERR(trans, "Too many device errors - delay next reset\n");
+ queue_delayed_work(system_unbound_wq, &trans->restart.wk,
+ IWL_TRANS_RESET_DELAY);
+ return;
+ }
iwl_trans_inc_restart_count(trans->dev);
switch (mode) {
+ case IWL_RESET_MODE_TOP_RESET:
+ trans->do_top_reset = 1;
+ IWL_ERR(trans, "Device error - TOP reset\n");
+ fallthrough;
case IWL_RESET_MODE_SW_RESET:
- IWL_ERR(trans, "Device error - SW reset\n");
+ if (mode == IWL_RESET_MODE_SW_RESET)
+ IWL_ERR(trans, "Device error - SW reset\n");
iwl_trans_opmode_sw_reset(trans, trans->restart.mode.type);
break;
case IWL_RESET_MODE_REPROBE:
IWL_ERR(trans, "Device error - reprobe!\n");
- /*
- * get a module reference to avoid doing this while unloading
- * anyway and to avoid scheduling a work with code that's
- * being removed.
- */
- if (!try_module_get(THIS_MODULE)) {
- IWL_ERR(trans, "Module is being unloaded - abort\n");
- return;
- }
-
- reprobe = kzalloc(sizeof(*reprobe), GFP_KERNEL);
- if (!reprobe) {
- module_put(THIS_MODULE);
- return;
- }
- reprobe->dev = get_device(trans->dev);
- INIT_WORK(&reprobe->work, iwl_trans_reprobe_wk);
- schedule_work(&reprobe->work);
+ iwl_trans_schedule_reprobe(trans, 0);
break;
default:
iwl_trans_pcie_reset(trans, mode);
@@ -206,7 +268,7 @@ static void iwl_trans_restart_wk(struct work_struct *wk)
struct iwl_trans *iwl_trans_alloc(unsigned int priv_size,
struct device *dev,
- const struct iwl_cfg_trans_params *cfg_trans)
+ const struct iwl_mac_cfg *mac_cfg)
{
struct iwl_trans *trans;
#ifdef CONFIG_LOCKDEP
@@ -217,7 +279,7 @@ struct iwl_trans *iwl_trans_alloc(unsigned int priv_size,
if (!trans)
return NULL;
- trans->trans_cfg = cfg_trans;
+ trans->mac_cfg = mac_cfg;
#ifdef CONFIG_LOCKDEP
lockdep_init_map(&trans->sync_cmd_lockdep_map, "sync_cmd_lockdep_map",
@@ -225,9 +287,8 @@ struct iwl_trans *iwl_trans_alloc(unsigned int priv_size,
#endif
trans->dev = dev;
- trans->num_rx_queues = 1;
- INIT_WORK(&trans->restart.wk, iwl_trans_restart_wk);
+ INIT_DELAYED_WORK(&trans->restart.wk, iwl_trans_restart_wk);
return trans;
}
@@ -236,14 +297,18 @@ int iwl_trans_init(struct iwl_trans *trans)
{
int txcmd_size, txcmd_align;
- if (!trans->trans_cfg->gen2) {
- txcmd_size = sizeof(struct iwl_tx_cmd);
+ /* check if name/num_rx_queues were set as a proxy for info being set */
+ if (WARN_ON(!trans->info.name || !trans->info.num_rxqs))
+ return -EINVAL;
+
+ if (!trans->mac_cfg->gen2) {
+ txcmd_size = sizeof(struct iwl_tx_cmd_v6);
txcmd_align = sizeof(void *);
- } else if (trans->trans_cfg->device_family < IWL_DEVICE_FAMILY_AX210) {
- txcmd_size = sizeof(struct iwl_tx_cmd_gen2);
+ } else if (trans->mac_cfg->device_family < IWL_DEVICE_FAMILY_AX210) {
+ txcmd_size = sizeof(struct iwl_tx_cmd_v9);
txcmd_align = 64;
} else {
- txcmd_size = sizeof(struct iwl_tx_cmd_gen3);
+ txcmd_size = sizeof(struct iwl_tx_cmd);
txcmd_align = 128;
}
@@ -251,7 +316,7 @@ int iwl_trans_init(struct iwl_trans *trans)
txcmd_size += 36; /* biggest possible 802.11 header */
/* Ensure device TX cmd cannot reach/cross a page boundary in gen2 */
- if (WARN_ON(trans->trans_cfg->gen2 && txcmd_size >= txcmd_align))
+ if (WARN_ON(trans->mac_cfg->gen2 && txcmd_size >= txcmd_align))
return -EINVAL;
snprintf(trans->dev_cmd_pool_name, sizeof(trans->dev_cmd_pool_name),
@@ -263,15 +328,12 @@ int iwl_trans_init(struct iwl_trans *trans)
if (!trans->dev_cmd_pool)
return -ENOMEM;
- /* Initialize the wait queue for commands */
- init_waitqueue_head(&trans->wait_command_queue);
-
return 0;
}
void iwl_trans_free(struct iwl_trans *trans)
{
- cancel_work_sync(&trans->restart.wk);
+ cancel_delayed_work_sync(&trans->restart.wk);
kmem_cache_destroy(trans->dev_cmd_pool);
}
@@ -283,17 +345,7 @@ int iwl_trans_send_cmd(struct iwl_trans *trans, struct iwl_host_cmd *cmd)
test_bit(STATUS_RFKILL_OPMODE, &trans->status)))
return -ERFKILL;
- /*
- * We can't test IWL_MVM_STATUS_IN_D3 in mvm->status because this
- * bit is set early in the D3 flow, before we send all the commands
- * that configure the firmware for D3 operation (power, patterns, ...)
- * and we don't want to flag all those with CMD_SEND_IN_D3.
- * So use the system_pm_mode instead. The only command sent after
- * we set system_pm_mode is D3_CONFIG_CMD, which we now flag with
- * CMD_SEND_IN_D3.
- */
- if (unlikely(trans->system_pm_mode == IWL_PLAT_PM_MODE_D3 &&
- !(cmd->flags & CMD_SEND_IN_D3)))
+ if (unlikely(test_bit(STATUS_SUSPENDED, &trans->status)))
return -EHOSTDOWN;
if (unlikely(test_bit(STATUS_FW_ERROR, &trans->status)))
@@ -306,7 +358,7 @@ int iwl_trans_send_cmd(struct iwl_trans *trans, struct iwl_host_cmd *cmd)
if (!(cmd->flags & CMD_ASYNC))
lock_map_acquire_read(&trans->sync_cmd_lockdep_map);
- if (trans->wide_cmd_header && !iwl_cmd_groupid(cmd->id)) {
+ if (trans->conf.wide_cmd_header && !iwl_cmd_groupid(cmd->id)) {
if (cmd->id != REPLY_ERROR)
cmd->id = DEF_ID(cmd->id);
}
@@ -350,11 +402,12 @@ const char *iwl_get_cmd_string(struct iwl_trans *trans, u32 id)
grp = iwl_cmd_groupid(id);
cmd = iwl_cmd_opcode(id);
- if (!trans->command_groups || grp >= trans->command_groups_size ||
- !trans->command_groups[grp].arr)
+ if (!trans->conf.command_groups ||
+ grp >= trans->conf.command_groups_size ||
+ !trans->conf.command_groups[grp].arr)
return "UNKNOWN";
- arr = &trans->command_groups[grp];
+ arr = &trans->conf.command_groups[grp];
ret = bsearch(&cmd, arr->arr, arr->size, size, iwl_hcmd_names_cmp);
if (!ret)
return "UNKNOWN";
@@ -362,37 +415,29 @@ const char *iwl_get_cmd_string(struct iwl_trans *trans, u32 id)
}
IWL_EXPORT_SYMBOL(iwl_get_cmd_string);
-int iwl_cmd_groups_verify_sorted(const struct iwl_trans_config *trans)
+void iwl_trans_op_mode_enter(struct iwl_trans *trans,
+ struct iwl_op_mode *op_mode)
{
- int i, j;
- const struct iwl_hcmd_arr *arr;
+ trans->op_mode = op_mode;
- for (i = 0; i < trans->command_groups_size; i++) {
- arr = &trans->command_groups[i];
- if (!arr->arr)
- continue;
- for (j = 0; j < arr->size - 1; j++)
- if (arr->arr[j].cmd_id > arr->arr[j + 1].cmd_id)
- return -1;
- }
- return 0;
-}
-IWL_EXPORT_SYMBOL(iwl_cmd_groups_verify_sorted);
+ if (WARN_ON(trans->conf.n_no_reclaim_cmds > MAX_NO_RECLAIM_CMDS))
+ trans->conf.n_no_reclaim_cmds =
+ ARRAY_SIZE(trans->conf.no_reclaim_cmds);
-void iwl_trans_configure(struct iwl_trans *trans,
- const struct iwl_trans_config *trans_cfg)
-{
- trans->op_mode = trans_cfg->op_mode;
+ WARN_ON_ONCE(!trans->conf.rx_mpdu_cmd);
- iwl_trans_pcie_configure(trans, trans_cfg);
- WARN_ON(iwl_cmd_groups_verify_sorted(trans_cfg));
+ iwl_trans_pcie_op_mode_enter(trans);
}
-IWL_EXPORT_SYMBOL(iwl_trans_configure);
+IWL_EXPORT_SYMBOL(iwl_trans_op_mode_enter);
int iwl_trans_start_hw(struct iwl_trans *trans)
{
might_sleep();
+ clear_bit(STATUS_TRANS_RESET_IN_PROGRESS, &trans->status);
+ /* opmode may not resume if it detects errors */
+ clear_bit(STATUS_SUSPENDED, &trans->status);
+
return iwl_trans_pcie_start_hw(trans);
}
IWL_EXPORT_SYMBOL(iwl_trans_start_hw);
@@ -403,7 +448,10 @@ void iwl_trans_op_mode_leave(struct iwl_trans *trans)
iwl_trans_pcie_op_mode_leave(trans);
+ cancel_delayed_work_sync(&trans->restart.wk);
+
trans->op_mode = NULL;
+ memset(&trans->conf, 0, sizeof(trans->conf));
trans->state = IWL_TRANS_NO_FW;
}
@@ -480,18 +528,31 @@ IWL_EXPORT_SYMBOL(iwl_trans_dump_data);
int iwl_trans_d3_suspend(struct iwl_trans *trans, bool test, bool reset)
{
+ int err;
+
might_sleep();
- return iwl_trans_pcie_d3_suspend(trans, test, reset);
+ err = iwl_trans_pcie_d3_suspend(trans, test, reset);
+
+ if (!err)
+ set_bit(STATUS_SUSPENDED, &trans->status);
+
+ return err;
}
IWL_EXPORT_SYMBOL(iwl_trans_d3_suspend);
int iwl_trans_d3_resume(struct iwl_trans *trans, enum iwl_d3_status *status,
bool test, bool reset)
{
+ int err;
+
might_sleep();
- return iwl_trans_pcie_d3_resume(trans, status, test, reset);
+ err = iwl_trans_pcie_d3_resume(trans, status, test, reset);
+
+ clear_bit(STATUS_SUSPENDED, &trans->status);
+
+ return err;
}
IWL_EXPORT_SYMBOL(iwl_trans_d3_resume);
@@ -538,38 +599,42 @@ void __releases(nic_access)
iwl_trans_release_nic_access(struct iwl_trans *trans)
{
iwl_trans_pcie_release_nic_access(trans);
- __release(nic_access);
}
IWL_EXPORT_SYMBOL(iwl_trans_release_nic_access);
-void iwl_trans_fw_alive(struct iwl_trans *trans, u32 scd_addr)
+void iwl_trans_fw_alive(struct iwl_trans *trans)
{
might_sleep();
trans->state = IWL_TRANS_FW_ALIVE;
- if (trans->trans_cfg->gen2)
+ if (trans->mac_cfg->gen2)
iwl_trans_pcie_gen2_fw_alive(trans);
else
- iwl_trans_pcie_fw_alive(trans, scd_addr);
+ iwl_trans_pcie_fw_alive(trans);
}
IWL_EXPORT_SYMBOL(iwl_trans_fw_alive);
-int iwl_trans_start_fw(struct iwl_trans *trans, const struct fw_img *fw,
- bool run_in_rfkill)
+int iwl_trans_start_fw(struct iwl_trans *trans, const struct iwl_fw *fw,
+ enum iwl_ucode_type ucode_type, bool run_in_rfkill)
{
+ const struct fw_img *img;
int ret;
might_sleep();
- WARN_ON_ONCE(!trans->rx_mpdu_cmd);
+ img = iwl_get_ucode_image(fw, ucode_type);
+ if (!img)
+ return -EINVAL;
clear_bit(STATUS_FW_ERROR, &trans->status);
- if (trans->trans_cfg->gen2)
- ret = iwl_trans_pcie_gen2_start_fw(trans, fw, run_in_rfkill);
+ if (trans->mac_cfg->gen2)
+ ret = iwl_trans_pcie_gen2_start_fw(trans, fw, img,
+ run_in_rfkill);
else
- ret = iwl_trans_pcie_start_fw(trans, fw, run_in_rfkill);
+ ret = iwl_trans_pcie_start_fw(trans, fw, img,
+ run_in_rfkill);
if (ret == 0)
trans->state = IWL_TRANS_FW_STARTED;
@@ -610,7 +675,7 @@ void iwl_trans_stop_device(struct iwl_trans *trans)
iwl_op_mode_dump_error(trans->op_mode, &mode);
}
- if (trans->trans_cfg->gen2)
+ if (trans->mac_cfg->gen2)
iwl_trans_pcie_gen2_stop_device(trans);
else
iwl_trans_pcie_stop_device(trans);
@@ -629,7 +694,7 @@ int iwl_trans_tx(struct iwl_trans *trans, struct sk_buff *skb,
"bad state = %d\n", trans->state))
return -EIO;
- if (trans->trans_cfg->gen2)
+ if (trans->mac_cfg->gen2)
return iwl_txq_gen2_tx(trans, skb, dev_cmd, queue);
return iwl_trans_pcie_tx(trans, skb, dev_cmd, queue);
@@ -639,6 +704,9 @@ IWL_EXPORT_SYMBOL(iwl_trans_tx);
void iwl_trans_reclaim(struct iwl_trans *trans, int queue, int ssn,
struct sk_buff_head *skbs, bool is_flush)
{
+ if (unlikely(test_bit(STATUS_FW_ERROR, &trans->status)))
+ return;
+
if (WARN_ONCE(trans->state != IWL_TRANS_FW_ALIVE,
"bad state = %d\n", trans->state))
return;
@@ -671,6 +739,9 @@ IWL_EXPORT_SYMBOL(iwl_trans_txq_enable_cfg);
int iwl_trans_wait_txq_empty(struct iwl_trans *trans, int queue)
{
+ if (unlikely(test_bit(STATUS_FW_ERROR, &trans->status)))
+ return -EIO;
+
if (WARN_ONCE(trans->state != IWL_TRANS_FW_ALIVE,
"bad state = %d\n", trans->state))
return -EIO;
@@ -756,14 +827,14 @@ int iwl_trans_load_pnvm(struct iwl_trans *trans,
const struct iwl_pnvm_image *pnvm_data,
const struct iwl_ucode_capabilities *capa)
{
- return iwl_trans_pcie_ctx_info_gen3_load_pnvm(trans, pnvm_data, capa);
+ return iwl_trans_pcie_ctx_info_v2_load_pnvm(trans, pnvm_data, capa);
}
IWL_EXPORT_SYMBOL(iwl_trans_load_pnvm);
void iwl_trans_set_pnvm(struct iwl_trans *trans,
const struct iwl_ucode_capabilities *capa)
{
- iwl_trans_pcie_ctx_info_gen3_set_pnvm(trans, capa);
+ iwl_trans_pcie_ctx_info_v2_set_pnvm(trans, capa);
}
IWL_EXPORT_SYMBOL(iwl_trans_set_pnvm);
@@ -771,7 +842,7 @@ int iwl_trans_load_reduce_power(struct iwl_trans *trans,
const struct iwl_pnvm_image *payloads,
const struct iwl_ucode_capabilities *capa)
{
- return iwl_trans_pcie_ctx_info_gen3_load_reduce_power(trans, payloads,
+ return iwl_trans_pcie_ctx_info_v2_load_reduce_power(trans, payloads,
capa);
}
IWL_EXPORT_SYMBOL(iwl_trans_load_reduce_power);
@@ -779,6 +850,6 @@ IWL_EXPORT_SYMBOL(iwl_trans_load_reduce_power);
void iwl_trans_set_reduce_power(struct iwl_trans *trans,
const struct iwl_ucode_capabilities *capa)
{
- iwl_trans_pcie_ctx_info_gen3_set_reduce_power(trans, capa);
+ iwl_trans_pcie_ctx_info_v2_set_reduce_power(trans, capa);
}
IWL_EXPORT_SYMBOL(iwl_trans_set_reduce_power);
diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-trans.h b/drivers/net/wireless/intel/iwlwifi/iwl-trans.h
index f6234065dbdd..012b1e44bce3 100644
--- a/drivers/net/wireless/intel/iwlwifi/iwl-trans.h
+++ b/drivers/net/wireless/intel/iwlwifi/iwl-trans.h
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
/*
- * Copyright (C) 2005-2014, 2018-2023 Intel Corporation
+ * Copyright (C) 2005-2014, 2018-2025 Intel Corporation
* Copyright (C) 2013-2015 Intel Mobile Communications GmbH
* Copyright (C) 2016-2017 Intel Deutschland GmbH
*/
@@ -109,16 +109,12 @@ static inline u32 iwl_rx_packet_payload_len(const struct iwl_rx_packet *pkt)
* the response. The caller needs to call iwl_free_resp when done.
* @CMD_SEND_IN_RFKILL: Send the command even if the NIC is in RF-kill.
* @CMD_BLOCK_TXQS: Block TXQs while the comment is executing.
- * @CMD_SEND_IN_D3: Allow the command to be sent in D3 mode, relevant to
- * SUSPEND and RESUME commands. We are in D3 mode when we set
- * trans->system_pm_mode to IWL_PLAT_PM_MODE_D3.
*/
enum CMD_MODE {
CMD_ASYNC = BIT(0),
CMD_WANT_SKB = BIT(1),
CMD_SEND_IN_RFKILL = BIT(2),
CMD_BLOCK_TXQS = BIT(3),
- CMD_SEND_IN_D3 = BIT(4),
};
#define CMD_MODE_BITS 5
@@ -304,6 +300,10 @@ enum iwl_d3_status {
* via iwl_trans_finish_sw_reset()
* @STATUS_RESET_PENDING: reset worker was scheduled, but didn't dump
* the firmware state yet
+ * @STATUS_TRANS_RESET_IN_PROGRESS: reset is still in progress, don't
+ * attempt another reset yet
+ * @STATUS_SUSPENDED: device is suspended, don't send commands that
+ * aren't marked accordingly
*/
enum iwl_trans_status {
STATUS_SYNC_HCMD_ACTIVE,
@@ -317,6 +317,8 @@ enum iwl_trans_status {
STATUS_SUPPRESS_CMD_ERROR_ONCE,
STATUS_IN_SW_RESET,
STATUS_RESET_PENDING,
+ STATUS_TRANS_RESET_IN_PROGRESS,
+ STATUS_SUSPENDED,
};
static inline int
@@ -328,6 +330,7 @@ iwl_trans_get_rb_size_order(enum iwl_amsdu_size rb_size)
case IWL_AMSDU_4K:
return get_order(4 * 1024);
case IWL_AMSDU_8K:
+ return get_order(8 * 1024);
case IWL_AMSDU_12K:
return get_order(16 * 1024);
default:
@@ -387,7 +390,8 @@ struct iwl_dump_sanitize_ops {
/**
* struct iwl_trans_config - transport configuration
*
- * @op_mode: pointer to the upper layer.
+ * These values should be set before iwl_trans_op_mode_enter().
+ *
* @cmd_queue: the index of the command queue.
* Must be set before start_fw.
* @cmd_fifo: the fifo for host commands
@@ -398,8 +402,6 @@ struct iwl_dump_sanitize_ops {
* @n_no_reclaim_cmds: # of commands in list
* @rx_buf_size: RX buffer size needed for A-MSDUs
* if unset 4k will be the RX buffer size
- * @bc_table_dword: set to true if the BC table expects the byte count to be
- * in DWORD (as opposed to bytes)
* @scd_set_active: should the transport configure the SCD for HCMD queue
* @command_groups: array of command groups, each member is an array of the
* commands in the group; for debugging only
@@ -410,17 +412,24 @@ struct iwl_dump_sanitize_ops {
* @queue_alloc_cmd_ver: queue allocation command version, set to 0
* for using the older SCD_QUEUE_CFG, set to the version of
* SCD_QUEUE_CONFIG_CMD otherwise.
+ * @wide_cmd_header: true when ucode supports wide command header format
+ * @rx_mpdu_cmd: MPDU RX command ID, must be assigned by opmode before
+ * starting the firmware, used for tracing
+ * @rx_mpdu_cmd_hdr_size: used for tracing, amount of data before the
+ * start of the 802.11 header in the @rx_mpdu_cmd
+ * @dsbr_urm_fw_dependent: switch to URM based on fw settings
+ * @dsbr_urm_permanent: switch to URM permanently
+ * @mbx_addr_0_step: step address data 0
+ * @mbx_addr_1_step: step address data 1
+ * @ext_32khz_clock_valid: if true, the external 32 KHz clock can be used
*/
struct iwl_trans_config {
- struct iwl_op_mode *op_mode;
-
u8 cmd_queue;
u8 cmd_fifo;
- const u8 *no_reclaim_cmds;
- unsigned int n_no_reclaim_cmds;
+ u8 n_no_reclaim_cmds;
+ u8 no_reclaim_cmds[MAX_NO_RECLAIM_CMDS];
enum iwl_amsdu_size rx_buf_size;
- bool bc_table_dword;
bool scd_set_active;
const struct iwl_hcmd_arr *command_groups;
int command_groups_size;
@@ -428,6 +437,16 @@ struct iwl_trans_config {
u8 cb_data_offs;
bool fw_reset_handshake;
u8 queue_alloc_cmd_ver;
+
+ bool wide_cmd_header;
+ u8 rx_mpdu_cmd, rx_mpdu_cmd_hdr_size;
+
+ u8 dsbr_urm_fw_dependent:1,
+ dsbr_urm_permanent:1,
+ ext_32khz_clock_valid:1;
+
+ u32 mbx_addr_0_step;
+ u32 mbx_addr_1_step;
};
struct iwl_trans_dump_data {
@@ -513,23 +532,6 @@ enum iwl_trans_state {
*/
/**
- * enum iwl_plat_pm_mode - platform power management mode
- *
- * This enumeration describes the device's platform power management
- * behavior when in system-wide suspend (i.e WoWLAN).
- *
- * @IWL_PLAT_PM_MODE_DISABLED: power management is disabled for this
- * device. In system-wide suspend mode, it means that the all
- * connections will be closed automatically by mac80211 before
- * the platform is suspended.
- * @IWL_PLAT_PM_MODE_D3: the device goes into D3 mode (i.e. WoWLAN).
- */
-enum iwl_plat_pm_mode {
- IWL_PLAT_PM_MODE_DISABLED,
- IWL_PLAT_PM_MODE_D3,
-};
-
-/**
* enum iwl_ini_cfg_state
* @IWL_INI_CFG_STATE_NOT_LOADED: no debug cfg was given
* @IWL_INI_CFG_STATE_LOADED: debug cfg was found and loaded
@@ -819,103 +821,90 @@ struct iwl_txq {
};
/**
+ * struct iwl_trans_info - transport info for outside use
+ * @name: the device name
+ * @max_skb_frags: maximum number of fragments an SKB can have when transmitted.
+ * 0 indicates that frag SKBs (NETIF_F_SG) aren't supported.
+ * @hw_rev: the revision data of the HW
+ * @hw_rev_step: The mac step of the HW
+ * @hw_rf_id: the device RF ID
+ * @hw_cnv_id: the device CNV ID
+ * @hw_crf_id: the device CRF ID
+ * @hw_wfpm_id: the device wfpm ID
+ * @hw_id: the ID of the device / sub-device
+ * Bits 0:15 represent the sub-device ID
+ * Bits 16:31 represent the device ID.
+ * @pcie_link_speed: current PCIe link speed (%PCI_EXP_LNKSTA_CLS_*),
+ * only valid for discrete (not integrated) NICs
+ * @num_rxqs: number of RX queues allocated by the transport
+ */
+struct iwl_trans_info {
+ const char *name;
+ u32 max_skb_frags;
+ u32 hw_rev;
+ u32 hw_rev_step;
+ u32 hw_rf_id;
+ u32 hw_crf_id;
+ u32 hw_cnv_id;
+ u32 hw_wfpm_id;
+ u32 hw_id;
+ u8 pcie_link_speed;
+ u8 num_rxqs;
+};
+
+/**
* struct iwl_trans - transport common data
*
* @csme_own: true if we couldn't get ownership on the device
* @op_mode: pointer to the op_mode
- * @trans_cfg: the trans-specific configuration part
+ * @mac_cfg: the trans-specific configuration part
* @cfg: pointer to the configuration
* @drv: pointer to iwl_drv
+ * @conf: configuration set by the opmode before enter
* @state: current device state
* @status: a bit-mask of transport status flags
* @dev: pointer to struct device * that represents the device
- * @max_skb_frags: maximum number of fragments an SKB can have when transmitted.
- * 0 indicates that frag SKBs (NETIF_F_SG) aren't supported.
- * @hw_rf_id: a u32 with the device RF ID
- * @hw_cnv_id: a u32 with the device CNV ID
- * @hw_crf_id: a u32 with the device CRF ID
- * @hw_wfpm_id: a u32 with the device wfpm ID
- * @hw_id: a u32 with the ID of the device / sub-device.
- * Set during transport allocation.
- * @hw_id_str: a string with info about HW ID. Set during transport allocation.
- * @sku_id: the SKU identifier (for PNVM matching)
+ * @info: device information for use by other layers
* @pnvm_loaded: indicates PNVM was loaded
- * @hw_rev: the revision data of the HW
- * @hw_rev_step: The mac step of the HW
* @pm_support: set to true in start_hw if link pm is supported
* @ltr_enabled: set to true if the LTR is enabled
* @fail_to_parse_pnvm_image: set to true if pnvm parsing failed
* @reduce_power_loaded: indicates reduced power section was loaded
* @failed_to_load_reduce_power_image: set to true if pnvm loading failed
- * @command_groups: pointer to command group name list array
- * @command_groups_size: array size of @command_groups
- * @wide_cmd_header: true when ucode supports wide command header format
- * @wait_command_queue: wait queue for sync commands
- * @num_rx_queues: number of RX queues allocated by the transport;
- * the transport must set this before calling iwl_drv_start()
- * @iml_len: the length of the image loader
- * @iml: a pointer to the image loader itself
* @dev_cmd_pool: pool for Tx cmd allocation - for internal use only.
* The user should use iwl_trans_{alloc,free}_tx_cmd.
* @dev_cmd_pool_name: name for the TX command allocation pool
* @dbgfs_dir: iwlwifi debugfs base dir for this device
* @sync_cmd_lockdep_map: lockdep map for checking sync commands
- * @rx_mpdu_cmd: MPDU RX command ID, must be assigned by opmode before
- * starting the firmware, used for tracing
- * @rx_mpdu_cmd_hdr_size: used for tracing, amount of data before the
- * start of the 802.11 header in the @rx_mpdu_cmd
* @dbg: additional debug data, see &struct iwl_trans_debug
* @init_dram: FW initialization DMA data
- * @system_pm_mode: the system-wide power management mode in use.
- * This mode is set dynamically, depending on the WoWLAN values
- * configured from the userspace at runtime.
- * @name: the device name
- * @mbx_addr_0_step: step address data 0
- * @mbx_addr_1_step: step address data 1
- * @pcie_link_speed: current PCIe link speed (%PCI_EXP_LNKSTA_CLS_*),
- * only valid for discrete (not integrated) NICs
- * @invalid_tx_cmd: invalid TX command buffer
* @reduced_cap_sku: reduced capability supported SKU
- * @no_160: device not supporting 160 MHz
* @step_urm: STEP is in URM, no support for MCS>9 in 320 MHz
* @restart: restart worker data
* @restart.wk: restart worker
* @restart.mode: reset/restart error mode information
* @restart.during_reset: error occurred during previous software reset
- * @me_recheck_wk: worker to recheck WiAMT/CSME presence
- * @me_present: WiAMT/CSME is detected as present (1), not present (0)
- * or unknown (-1, so can still use it as a boolean safely)
* @trans_specific: data for the specific transport this is allocated for/with
- * @dsbr_urm_fw_dependent: switch to URM based on fw settings
- * @dsbr_urm_permanent: switch to URM permanently
+ * @request_top_reset: TOP reset was requested, used by the reset
+ * worker that should be scheduled (with appropriate reason)
+ * @do_top_reset: indication to the (PCIe) transport/context-info
+ * to do the TOP reset
*/
struct iwl_trans {
bool csme_own;
struct iwl_op_mode *op_mode;
- const struct iwl_cfg_trans_params *trans_cfg;
- const struct iwl_cfg *cfg;
+ const struct iwl_mac_cfg *mac_cfg;
+ const struct iwl_rf_cfg *cfg;
struct iwl_drv *drv;
+ struct iwl_trans_config conf;
enum iwl_trans_state state;
unsigned long status;
struct device *dev;
- u32 max_skb_frags;
- u32 hw_rev;
- u32 hw_rev_step;
- u32 hw_rf_id;
- u32 hw_crf_id;
- u32 hw_cnv_id;
- u32 hw_wfpm_id;
- u32 hw_id;
- char hw_id_str[52];
- u32 sku_id[3];
- bool reduced_cap_sku;
- u8 no_160:1, step_urm:1;
- u8 dsbr_urm_fw_dependent:1,
- dsbr_urm_permanent:1;
-
- u8 rx_mpdu_cmd, rx_mpdu_cmd_hdr_size;
+ const struct iwl_trans_info info;
+ bool reduced_cap_sku;
+ bool step_urm;
bool pm_support;
bool ltr_enabled;
@@ -924,16 +913,6 @@ struct iwl_trans {
u8 reduce_power_loaded:1;
u8 failed_to_load_reduce_power_image:1;
- const struct iwl_hcmd_arr *command_groups;
- int command_groups_size;
- bool wide_cmd_header;
-
- wait_queue_head_t wait_command_queue;
- u8 num_rx_queues;
-
- size_t iml_len;
- u8 *iml;
-
/* The following fields are internal only */
struct kmem_cache *dev_cmd_pool;
char dev_cmd_pool_name[50];
@@ -947,24 +926,14 @@ struct iwl_trans {
struct iwl_trans_debug dbg;
struct iwl_self_init_dram init_dram;
- enum iwl_plat_pm_mode system_pm_mode;
-
- const char *name;
- u32 mbx_addr_0_step;
- u32 mbx_addr_1_step;
-
- u8 pcie_link_speed;
-
- struct iwl_dma_ptr invalid_tx_cmd;
-
struct {
- struct work_struct wk;
+ struct delayed_work wk;
struct iwl_fw_error_dump_mode mode;
bool during_reset;
} restart;
- struct delayed_work me_recheck_wk;
- s8 me_present;
+ u8 request_top_reset:1,
+ do_top_reset:1;
/* pointer to trans specific struct */
/*Ensure that this pointer will always be aligned to sizeof pointer */
@@ -972,19 +941,18 @@ struct iwl_trans {
};
const char *iwl_get_cmd_string(struct iwl_trans *trans, u32 id);
-int iwl_cmd_groups_verify_sorted(const struct iwl_trans_config *trans);
-void iwl_trans_configure(struct iwl_trans *trans,
- const struct iwl_trans_config *trans_cfg);
+void iwl_trans_op_mode_enter(struct iwl_trans *trans,
+ struct iwl_op_mode *op_mode);
int iwl_trans_start_hw(struct iwl_trans *trans);
void iwl_trans_op_mode_leave(struct iwl_trans *trans);
-void iwl_trans_fw_alive(struct iwl_trans *trans, u32 scd_addr);
+void iwl_trans_fw_alive(struct iwl_trans *trans);
-int iwl_trans_start_fw(struct iwl_trans *trans, const struct fw_img *fw,
- bool run_in_rfkill);
+int iwl_trans_start_fw(struct iwl_trans *trans, const struct iwl_fw *fw,
+ enum iwl_ucode_type ucode_type, bool run_in_rfkill);
void iwl_trans_stop_device(struct iwl_trans *trans);
@@ -1147,6 +1115,9 @@ static inline void iwl_trans_schedule_reset(struct iwl_trans *trans,
{
if (test_bit(STATUS_TRANS_DEAD, &trans->status))
return;
+ /* clear this on device init, not cleared on any unbind/reprobe */
+ if (test_and_set_bit(STATUS_TRANS_RESET_IN_PROGRESS, &trans->status))
+ return;
trans->restart.mode.type = type;
trans->restart.mode.context = IWL_ERR_CONTEXT_WORKER;
@@ -1159,7 +1130,7 @@ static inline void iwl_trans_schedule_reset(struct iwl_trans *trans,
*/
trans->restart.during_reset = test_bit(STATUS_IN_SW_RESET,
&trans->status);
- queue_work(system_unbound_wq, &trans->restart.wk);
+ queue_delayed_work(system_unbound_wq, &trans->restart.wk, 0);
}
static inline void iwl_trans_fw_error(struct iwl_trans *trans,
@@ -1184,6 +1155,9 @@ static inline void iwl_trans_opmode_sw_reset(struct iwl_trans *trans,
set_bit(STATUS_IN_SW_RESET, &trans->status);
+ if (WARN_ON(type == IWL_ERR_TYPE_TOP_RESET_BY_BT))
+ return;
+
if (!trans->op_mode->ops->sw_reset ||
!trans->op_mode->ops->sw_reset(trans->op_mode, type))
clear_bit(STATUS_IN_SW_RESET, &trans->status);
@@ -1231,7 +1205,7 @@ static inline void iwl_trans_finish_sw_reset(struct iwl_trans *trans)
*****************************************************/
struct iwl_trans *iwl_trans_alloc(unsigned int priv_size,
struct device *dev,
- const struct iwl_cfg_trans_params *cfg_trans);
+ const struct iwl_mac_cfg *cfg_trans);
int iwl_trans_init(struct iwl_trans *trans);
void iwl_trans_free(struct iwl_trans *trans);
@@ -1242,6 +1216,19 @@ static inline bool iwl_trans_is_hw_error_value(u32 val)
void iwl_trans_free_restart_list(void);
+static inline u16 iwl_trans_get_num_rbds(struct iwl_trans *trans)
+{
+ u16 result = trans->cfg->num_rbds;
+
+ /*
+ * Since AX210 family (So/Ty) the device cannot put mutliple
+ * frames into the same buffer, so double the value for them.
+ */
+ if (trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_AX210)
+ return 2 * result;
+ return result;
+}
+
/*****************************************************
* PCIe handling
*****************************************************/
@@ -1253,16 +1240,37 @@ enum iwl_reset_mode {
/* upper level modes: */
IWL_RESET_MODE_SW_RESET,
IWL_RESET_MODE_REPROBE,
+ /* TOP reset doesn't require PCIe remove */
+ IWL_RESET_MODE_TOP_RESET,
/* PCIE level modes: */
IWL_RESET_MODE_REMOVE_ONLY,
IWL_RESET_MODE_RESCAN,
IWL_RESET_MODE_FUNC_RESET,
IWL_RESET_MODE_PROD_RESET,
+
+ /* keep last - special backoff value */
+ IWL_RESET_MODE_BACKOFF,
};
void iwl_trans_pcie_reset(struct iwl_trans *trans, enum iwl_reset_mode mode);
+void iwl_trans_pcie_fw_reset_handshake(struct iwl_trans *trans);
int iwl_trans_pcie_send_hcmd(struct iwl_trans *trans,
struct iwl_host_cmd *cmd);
+/* Internal helper */
+static inline void iwl_trans_set_info(struct iwl_trans *trans,
+ struct iwl_trans_info *info)
+{
+ struct iwl_trans_info *write;
+
+ write = (void *)(uintptr_t)&trans->info;
+ *write = *info;
+}
+
+static inline u16 iwl_trans_get_device_id(struct iwl_trans *trans)
+{
+ return u32_get_bits(trans->info.hw_id, GENMASK(31, 16));
+}
+
#endif /* __iwl_trans_h__ */
diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-utils.c b/drivers/net/wireless/intel/iwlwifi/iwl-utils.c
index b14ec98e28b6..c5b49851e4b9 100644
--- a/drivers/net/wireless/intel/iwlwifi/iwl-utils.c
+++ b/drivers/net/wireless/intel/iwlwifi/iwl-utils.c
@@ -4,7 +4,6 @@
*/
#include <net/gso.h>
#include <linux/ieee80211.h>
-#include <net/gso.h>
#include <net/ip.h>
#include "iwl-drv.h"
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/Makefile b/drivers/net/wireless/intel/iwlwifi/mld/Makefile
new file mode 100644
index 000000000000..ece66e7a9be4
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/mld/Makefile
@@ -0,0 +1,16 @@
+# SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
+obj-$(CONFIG_IWLMLD) += iwlmld.o
+obj-$(CONFIG_IWLWIFI_KUNIT_TESTS) += tests/
+
+iwlmld-y += mld.o notif.o mac80211.o fw.o power.o iface.o link.o rx.o mcc.o session-protect.o phy.o
+iwlmld-y += scan.o sta.o tx.o coex.o tlc.o agg.o key.o regulatory.o ap.o thermal.o roc.o stats.o
+iwlmld-y += low_latency.o mlo.o ptp.o time_sync.o ftm-initiator.o
+iwlmld-$(CONFIG_IWLWIFI_DEBUGFS) += debugfs.o
+iwlmld-$(CONFIG_IWLWIFI_LEDS) += led.o
+iwlmld-$(CONFIG_PM_SLEEP) += d3.o
+
+# non-upstream things
+iwlmld-$(CONFIG_IWL_VENDOR_CMDS) += vendor-cmd.o
+iwlmld-$(CONFIG_IWLMVM_AX_SOFTAP_TESTMODE) += ax-softap-testmode.o
+
+subdir-ccflags-y += -I$(src)/../
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/agg.c b/drivers/net/wireless/intel/iwlwifi/mld/agg.c
new file mode 100644
index 000000000000..bda488ae9eec
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/mld/agg.c
@@ -0,0 +1,672 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
+/*
+ * Copyright (C) 2024-2025 Intel Corporation
+ */
+#include "agg.h"
+#include "sta.h"
+#include "hcmd.h"
+
+static void
+iwl_mld_reorder_release_frames(struct iwl_mld *mld, struct ieee80211_sta *sta,
+ struct napi_struct *napi,
+ struct iwl_mld_baid_data *baid_data,
+ struct iwl_mld_reorder_buffer *reorder_buf,
+ u16 nssn)
+{
+ struct iwl_mld_reorder_buf_entry *entries =
+ &baid_data->entries[reorder_buf->queue *
+ baid_data->entries_per_queue];
+ u16 ssn = reorder_buf->head_sn;
+
+ while (ieee80211_sn_less(ssn, nssn)) {
+ int index = ssn % baid_data->buf_size;
+ struct sk_buff_head *skb_list = &entries[index].frames;
+ struct sk_buff *skb;
+
+ ssn = ieee80211_sn_inc(ssn);
+
+ /* Empty the list. Will have more than one frame for A-MSDU.
+ * Empty list is valid as well since nssn indicates frames were
+ * received.
+ */
+ while ((skb = __skb_dequeue(skb_list))) {
+ iwl_mld_pass_packet_to_mac80211(mld, napi, skb,
+ reorder_buf->queue,
+ sta);
+ reorder_buf->num_stored--;
+ }
+ }
+ reorder_buf->head_sn = nssn;
+}
+
+static void iwl_mld_release_frames_from_notif(struct iwl_mld *mld,
+ struct napi_struct *napi,
+ u8 baid, u16 nssn, int queue)
+{
+ struct iwl_mld_reorder_buffer *reorder_buf;
+ struct iwl_mld_baid_data *ba_data;
+ struct ieee80211_link_sta *link_sta;
+ u32 sta_id;
+
+ IWL_DEBUG_HT(mld, "Frame release notification for BAID %u, NSSN %d\n",
+ baid, nssn);
+
+ if (WARN_ON_ONCE(baid == IWL_RX_REORDER_DATA_INVALID_BAID ||
+ baid >= ARRAY_SIZE(mld->fw_id_to_ba)))
+ return;
+
+ rcu_read_lock();
+
+ ba_data = rcu_dereference(mld->fw_id_to_ba[baid]);
+ if (!ba_data) {
+ IWL_DEBUG_HT(mld, "BAID %d not found in map\n", baid);
+ goto out_unlock;
+ }
+
+ /* pick any STA ID to find the pointer */
+ sta_id = ffs(ba_data->sta_mask) - 1;
+ link_sta = rcu_dereference(mld->fw_id_to_link_sta[sta_id]);
+ if (WARN_ON_ONCE(IS_ERR_OR_NULL(link_sta) || !link_sta->sta))
+ goto out_unlock;
+
+ reorder_buf = &ba_data->reorder_buf[queue];
+
+ iwl_mld_reorder_release_frames(mld, link_sta->sta, napi, ba_data,
+ reorder_buf, nssn);
+out_unlock:
+ rcu_read_unlock();
+}
+
+void iwl_mld_handle_frame_release_notif(struct iwl_mld *mld,
+ struct napi_struct *napi,
+ struct iwl_rx_packet *pkt, int queue)
+{
+ struct iwl_frame_release *release = (void *)pkt->data;
+ u32 pkt_len = iwl_rx_packet_payload_len(pkt);
+
+ if (IWL_FW_CHECK(mld, pkt_len < sizeof(*release),
+ "Unexpected frame release notif size %u (expected %zu)\n",
+ pkt_len, sizeof(*release)))
+ return;
+
+ iwl_mld_release_frames_from_notif(mld, napi, release->baid,
+ le16_to_cpu(release->nssn),
+ queue);
+}
+
+void iwl_mld_handle_bar_frame_release_notif(struct iwl_mld *mld,
+ struct napi_struct *napi,
+ struct iwl_rx_packet *pkt,
+ int queue)
+{
+ struct iwl_bar_frame_release *release = (void *)pkt->data;
+ struct iwl_mld_baid_data *baid_data;
+ unsigned int baid, nssn, sta_id, tid;
+ u32 pkt_len = iwl_rx_packet_payload_len(pkt);
+
+ if (IWL_FW_CHECK(mld, pkt_len < sizeof(*release),
+ "Unexpected frame release notif size %u (expected %zu)\n",
+ pkt_len, sizeof(*release)))
+ return;
+
+ baid = le32_get_bits(release->ba_info,
+ IWL_BAR_FRAME_RELEASE_BAID_MASK);
+ nssn = le32_get_bits(release->ba_info,
+ IWL_BAR_FRAME_RELEASE_NSSN_MASK);
+ sta_id = le32_get_bits(release->sta_tid,
+ IWL_BAR_FRAME_RELEASE_STA_MASK);
+ tid = le32_get_bits(release->sta_tid,
+ IWL_BAR_FRAME_RELEASE_TID_MASK);
+
+ if (IWL_FW_CHECK(mld, baid >= ARRAY_SIZE(mld->fw_id_to_ba),
+ "BAR release: invalid BAID (%x)\n", baid))
+ return;
+
+ rcu_read_lock();
+ baid_data = rcu_dereference(mld->fw_id_to_ba[baid]);
+ if (!baid_data) {
+ IWL_DEBUG_HT(mld,
+ "Got valid BAID %d but not allocated\n",
+ baid);
+ goto out_unlock;
+ }
+
+ if (IWL_FW_CHECK(mld, tid != baid_data->tid ||
+ sta_id > mld->fw->ucode_capa.num_stations ||
+ !(baid_data->sta_mask & BIT(sta_id)),
+ "BAID 0x%x is mapped to sta_mask:0x%x tid:%d, but BAR release received for sta:%d tid:%d\n",
+ baid, baid_data->sta_mask, baid_data->tid, sta_id,
+ tid))
+ goto out_unlock;
+
+ IWL_DEBUG_DROP(mld, "Received a BAR, expect packet loss: nssn %d\n",
+ nssn);
+
+ iwl_mld_release_frames_from_notif(mld, napi, baid, nssn, queue);
+out_unlock:
+ rcu_read_unlock();
+}
+
+void iwl_mld_del_ba(struct iwl_mld *mld, int queue,
+ struct iwl_mld_delba_data *data)
+{
+ struct iwl_mld_baid_data *ba_data;
+ struct iwl_mld_reorder_buffer *reorder_buf;
+ struct ieee80211_link_sta *link_sta;
+ u8 baid = data->baid;
+ u32 sta_id;
+
+ if (WARN_ONCE(baid >= IWL_MAX_BAID, "invalid BAID: %x\n", baid))
+ return;
+
+ rcu_read_lock();
+
+ ba_data = rcu_dereference(mld->fw_id_to_ba[baid]);
+ if (WARN_ON_ONCE(!ba_data))
+ goto out_unlock;
+
+ /* pick any STA ID to find the pointer */
+ sta_id = ffs(ba_data->sta_mask) - 1;
+ link_sta = rcu_dereference(mld->fw_id_to_link_sta[sta_id]);
+ if (WARN_ON_ONCE(IS_ERR_OR_NULL(link_sta) || !link_sta->sta))
+ goto out_unlock;
+
+ reorder_buf = &ba_data->reorder_buf[queue];
+
+ /* release all frames that are in the reorder buffer to the stack */
+ iwl_mld_reorder_release_frames(mld, link_sta->sta, NULL,
+ ba_data, reorder_buf,
+ ieee80211_sn_add(reorder_buf->head_sn,
+ ba_data->buf_size));
+out_unlock:
+ rcu_read_unlock();
+}
+
+/* Returns true if the MPDU was buffered\dropped, false if it should be passed
+ * to upper layer.
+ */
+enum iwl_mld_reorder_result
+iwl_mld_reorder(struct iwl_mld *mld, struct napi_struct *napi,
+ int queue, struct ieee80211_sta *sta,
+ struct sk_buff *skb, struct iwl_rx_mpdu_desc *desc)
+{
+ struct ieee80211_hdr *hdr = (void *)skb_mac_header(skb);
+ struct iwl_mld_baid_data *baid_data;
+ struct iwl_mld_reorder_buffer *buffer;
+ struct iwl_mld_reorder_buf_entry *entries;
+ struct iwl_mld_sta *mld_sta = iwl_mld_sta_from_mac80211(sta);
+ struct iwl_mld_link_sta *mld_link_sta;
+ u32 reorder = le32_to_cpu(desc->reorder_data);
+ bool amsdu, last_subframe, is_old_sn, is_dup;
+ u8 tid = ieee80211_get_tid(hdr);
+ u8 baid;
+ u16 nssn, sn;
+ u32 sta_mask = 0;
+ int index;
+ u8 link_id;
+
+ baid = u32_get_bits(reorder, IWL_RX_MPDU_REORDER_BAID_MASK);
+
+ /* This also covers the case of receiving a Block Ack Request
+ * outside a BA session; we'll pass it to mac80211 and that
+ * then sends a delBA action frame.
+ * This also covers pure monitor mode, in which case we won't
+ * have any BA sessions.
+ */
+ if (baid == IWL_RX_REORDER_DATA_INVALID_BAID)
+ return IWL_MLD_PASS_SKB;
+
+ /* no sta yet */
+ if (WARN_ONCE(!sta,
+ "Got valid BAID without a valid station assigned\n"))
+ return IWL_MLD_PASS_SKB;
+
+ /* not a data packet */
+ if (!ieee80211_is_data_qos(hdr->frame_control) ||
+ is_multicast_ether_addr(hdr->addr1))
+ return IWL_MLD_PASS_SKB;
+
+ if (unlikely(!ieee80211_is_data_present(hdr->frame_control)))
+ return IWL_MLD_PASS_SKB;
+
+ baid_data = rcu_dereference(mld->fw_id_to_ba[baid]);
+ if (!baid_data) {
+ IWL_DEBUG_HT(mld,
+ "Got valid BAID but no baid allocated, bypass re-ordering (BAID=%d reorder=0x%x)\n",
+ baid, reorder);
+ return IWL_MLD_PASS_SKB;
+ }
+
+ for_each_mld_link_sta(mld_sta, mld_link_sta, link_id)
+ sta_mask |= BIT(mld_link_sta->fw_id);
+
+ /* verify the BAID is correctly mapped to the sta and tid */
+ if (IWL_FW_CHECK(mld,
+ tid != baid_data->tid ||
+ !(sta_mask & baid_data->sta_mask),
+ "BAID 0x%x is mapped to sta_mask:0x%x tid:%d, but was received for sta_mask:0x%x tid:%d\n",
+ baid, baid_data->sta_mask, baid_data->tid,
+ sta_mask, tid))
+ return IWL_MLD_PASS_SKB;
+
+ buffer = &baid_data->reorder_buf[queue];
+ entries = &baid_data->entries[queue * baid_data->entries_per_queue];
+
+ is_old_sn = !!(reorder & IWL_RX_MPDU_REORDER_BA_OLD_SN);
+
+ if (!buffer->valid && is_old_sn)
+ return IWL_MLD_PASS_SKB;
+
+ buffer->valid = true;
+
+ is_dup = !!(desc->status & cpu_to_le32(IWL_RX_MPDU_STATUS_DUPLICATE));
+
+ /* drop any duplicated or outdated packets */
+ if (is_dup || is_old_sn)
+ return IWL_MLD_DROP_SKB;
+
+ sn = u32_get_bits(reorder, IWL_RX_MPDU_REORDER_SN_MASK);
+ nssn = u32_get_bits(reorder, IWL_RX_MPDU_REORDER_NSSN_MASK);
+ amsdu = desc->mac_flags2 & IWL_RX_MPDU_MFLG2_AMSDU;
+ last_subframe = desc->amsdu_info & IWL_RX_MPDU_AMSDU_LAST_SUBFRAME;
+
+ /* release immediately if allowed by nssn and no stored frames */
+ if (!buffer->num_stored && ieee80211_sn_less(sn, nssn)) {
+ if (!amsdu || last_subframe)
+ buffer->head_sn = nssn;
+ return IWL_MLD_PASS_SKB;
+ }
+
+ /* release immediately if there are no stored frames, and the sn is
+ * equal to the head.
+ * This can happen due to reorder timer, where NSSN is behind head_sn.
+ * When we released everything, and we got the next frame in the
+ * sequence, according to the NSSN we can't release immediately,
+ * while technically there is no hole and we can move forward.
+ */
+ if (!buffer->num_stored && sn == buffer->head_sn) {
+ if (!amsdu || last_subframe)
+ buffer->head_sn = ieee80211_sn_inc(buffer->head_sn);
+ return IWL_MLD_PASS_SKB;
+ }
+
+ /* put in reorder buffer */
+ index = sn % baid_data->buf_size;
+ __skb_queue_tail(&entries[index].frames, skb);
+ buffer->num_stored++;
+
+ /* We cannot trust NSSN for AMSDU sub-frames that are not the last. The
+ * reason is that NSSN advances on the first sub-frame, and may cause
+ * the reorder buffer to advance before all the sub-frames arrive.
+ *
+ * Example: reorder buffer contains SN 0 & 2, and we receive AMSDU with
+ * SN 1. NSSN for first sub frame will be 3 with the result of driver
+ * releasing SN 0,1, 2. When sub-frame 1 arrives - reorder buffer is
+ * already ahead and it will be dropped.
+ * If the last sub-frame is not on this queue - we will get frame
+ * release notification with up to date NSSN.
+ */
+ if (!amsdu || last_subframe)
+ iwl_mld_reorder_release_frames(mld, sta, napi, baid_data,
+ buffer, nssn);
+
+ return IWL_MLD_BUFFERED_SKB;
+}
+EXPORT_SYMBOL_IF_IWLWIFI_KUNIT(iwl_mld_reorder);
+
+static void iwl_mld_rx_agg_session_expired(struct timer_list *t)
+{
+ struct iwl_mld_baid_data *data =
+ from_timer(data, t, session_timer);
+ struct iwl_mld_baid_data __rcu **rcu_ptr = data->rcu_ptr;
+ struct iwl_mld_baid_data *ba_data;
+ struct ieee80211_link_sta *link_sta;
+ struct iwl_mld_sta *mld_sta;
+ unsigned long timeout;
+ unsigned int sta_id;
+
+ rcu_read_lock();
+
+ ba_data = rcu_dereference(*rcu_ptr);
+ if (WARN_ON(!ba_data))
+ goto unlock;
+
+ if (WARN_ON(!ba_data->timeout))
+ goto unlock;
+
+ timeout = ba_data->last_rx_timestamp +
+ TU_TO_JIFFIES(ba_data->timeout * 2);
+ if (time_is_after_jiffies(timeout)) {
+ mod_timer(&ba_data->session_timer, timeout);
+ goto unlock;
+ }
+
+ /* timer expired, pick any STA ID to find the pointer */
+ sta_id = ffs(ba_data->sta_mask) - 1;
+ link_sta = rcu_dereference(ba_data->mld->fw_id_to_link_sta[sta_id]);
+
+ /* sta should be valid unless the following happens:
+ * The firmware asserts which triggers a reconfig flow, but
+ * the reconfig fails before we set the pointer to sta into
+ * the fw_id_to_link_sta pointer table. mac80211 can't stop
+ * A-MPDU and hence the timer continues to run. Then, the
+ * timer expires and sta is NULL.
+ */
+ if (IS_ERR_OR_NULL(link_sta) || WARN_ON(!link_sta->sta))
+ goto unlock;
+
+ mld_sta = iwl_mld_sta_from_mac80211(link_sta->sta);
+ ieee80211_rx_ba_timer_expired(mld_sta->vif, link_sta->sta->addr,
+ ba_data->tid);
+unlock:
+ rcu_read_unlock();
+}
+
+static int
+iwl_mld_stop_ba_in_fw(struct iwl_mld *mld, struct ieee80211_sta *sta, int tid)
+{
+ struct iwl_rx_baid_cfg_cmd cmd = {
+ .action = cpu_to_le32(IWL_RX_BAID_ACTION_REMOVE),
+ .remove.sta_id_mask =
+ cpu_to_le32(iwl_mld_fw_sta_id_mask(mld, sta)),
+ .remove.tid = cpu_to_le32(tid),
+
+ };
+ int ret;
+
+ ret = iwl_mld_send_cmd_pdu(mld,
+ WIDE_ID(DATA_PATH_GROUP,
+ RX_BAID_ALLOCATION_CONFIG_CMD),
+ &cmd);
+ if (ret)
+ return ret;
+
+ IWL_DEBUG_HT(mld, "RX BA Session stopped in fw\n");
+
+ return ret;
+}
+
+static int
+iwl_mld_start_ba_in_fw(struct iwl_mld *mld, struct ieee80211_sta *sta,
+ int tid, u16 ssn, u16 buf_size)
+{
+ struct iwl_rx_baid_cfg_cmd cmd = {
+ .action = cpu_to_le32(IWL_RX_BAID_ACTION_ADD),
+ .alloc.sta_id_mask =
+ cpu_to_le32(iwl_mld_fw_sta_id_mask(mld, sta)),
+ .alloc.tid = tid,
+ .alloc.ssn = cpu_to_le16(ssn),
+ .alloc.win_size = cpu_to_le16(buf_size),
+ };
+ struct iwl_host_cmd hcmd = {
+ .id = WIDE_ID(DATA_PATH_GROUP, RX_BAID_ALLOCATION_CONFIG_CMD),
+ .flags = CMD_WANT_SKB,
+ .len[0] = sizeof(cmd),
+ .data[0] = &cmd,
+ };
+ struct iwl_rx_baid_cfg_resp *resp;
+ struct iwl_rx_packet *pkt;
+ u32 resp_len;
+ int ret, baid;
+
+ BUILD_BUG_ON(sizeof(*resp) != sizeof(baid));
+
+ ret = iwl_mld_send_cmd(mld, &hcmd);
+ if (ret)
+ return ret;
+
+ pkt = hcmd.resp_pkt;
+
+ resp_len = iwl_rx_packet_payload_len(pkt);
+ if (IWL_FW_CHECK(mld, resp_len != sizeof(*resp),
+ "BAID_ALLOC_CMD: unexpected response length %d\n",
+ resp_len)) {
+ ret = -EIO;
+ goto out;
+ }
+
+ IWL_DEBUG_HT(mld, "RX BA Session started in fw\n");
+
+ resp = (void *)pkt->data;
+ baid = le32_to_cpu(resp->baid);
+
+ if (IWL_FW_CHECK(mld, baid < 0 || baid >= ARRAY_SIZE(mld->fw_id_to_ba),
+ "BAID_ALLOC_CMD: invalid BAID response %d\n", baid)) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ ret = baid;
+out:
+ iwl_free_resp(&hcmd);
+ return ret;
+}
+
+static void iwl_mld_init_reorder_buffer(struct iwl_mld *mld,
+ struct iwl_mld_baid_data *data,
+ u16 ssn)
+{
+ for (int i = 0; i < mld->trans->info.num_rxqs; i++) {
+ struct iwl_mld_reorder_buffer *reorder_buf =
+ &data->reorder_buf[i];
+ struct iwl_mld_reorder_buf_entry *entries =
+ &data->entries[i * data->entries_per_queue];
+
+ reorder_buf->head_sn = ssn;
+ reorder_buf->queue = i;
+
+ for (int j = 0; j < data->buf_size; j++)
+ __skb_queue_head_init(&entries[j].frames);
+ }
+}
+
+static void iwl_mld_free_reorder_buffer(struct iwl_mld *mld,
+ struct iwl_mld_baid_data *data)
+{
+ struct iwl_mld_delba_data delba_data = {
+ .baid = data->baid,
+ };
+
+ iwl_mld_sync_rx_queues(mld, IWL_MLD_RXQ_NOTIF_DEL_BA,
+ &delba_data, sizeof(delba_data));
+
+ for (int i = 0; i < mld->trans->info.num_rxqs; i++) {
+ struct iwl_mld_reorder_buffer *reorder_buf =
+ &data->reorder_buf[i];
+ struct iwl_mld_reorder_buf_entry *entries =
+ &data->entries[i * data->entries_per_queue];
+
+ if (likely(!reorder_buf->num_stored))
+ continue;
+
+ /* This shouldn't happen in regular DELBA since the RX queues
+ * sync internal DELBA notification should trigger a release
+ * of all frames in the reorder buffer.
+ */
+ WARN_ON(1);
+
+ for (int j = 0; j < data->buf_size; j++)
+ __skb_queue_purge(&entries[j].frames);
+ }
+}
+
+int iwl_mld_ampdu_rx_start(struct iwl_mld *mld, struct ieee80211_sta *sta,
+ int tid, u16 ssn, u16 buf_size, u16 timeout)
+{
+ struct iwl_mld_sta *mld_sta = iwl_mld_sta_from_mac80211(sta);
+ struct iwl_mld_baid_data *baid_data = NULL;
+ u32 reorder_buf_size = buf_size * sizeof(baid_data->entries[0]);
+ int ret, baid;
+ u32 sta_mask;
+
+ lockdep_assert_wiphy(mld->wiphy);
+
+ if (mld->num_rx_ba_sessions >= IWL_MAX_BAID) {
+ IWL_DEBUG_HT(mld,
+ "Max num of RX BA sessions reached; blocking new session\n");
+ return -ENOSPC;
+ }
+
+ sta_mask = iwl_mld_fw_sta_id_mask(mld, sta);
+ if (WARN_ON(!sta_mask))
+ return -EINVAL;
+
+ /* sparse doesn't like the __align() so don't check */
+#ifndef __CHECKER__
+ /* The division below will be OK if either the cache line size
+ * can be divided by the entry size (ALIGN will round up) or if
+ * the entry size can be divided by the cache line size, in which
+ * case the ALIGN() will do nothing.
+ */
+ BUILD_BUG_ON(SMP_CACHE_BYTES % sizeof(baid_data->entries[0]) &&
+ sizeof(baid_data->entries[0]) % SMP_CACHE_BYTES);
+#endif
+
+ /* Upward align the reorder buffer size to fill an entire cache
+ * line for each queue, to avoid sharing cache lines between
+ * different queues.
+ */
+ reorder_buf_size = ALIGN(reorder_buf_size, SMP_CACHE_BYTES);
+
+ /* Allocate here so if allocation fails we can bail out early
+ * before starting the BA session in the firmware
+ */
+ baid_data = kzalloc(sizeof(*baid_data) +
+ mld->trans->info.num_rxqs * reorder_buf_size,
+ GFP_KERNEL);
+ if (!baid_data)
+ return -ENOMEM;
+
+ /* This division is why we need the above BUILD_BUG_ON(),
+ * if that doesn't hold then this will not be right.
+ */
+ baid_data->entries_per_queue =
+ reorder_buf_size / sizeof(baid_data->entries[0]);
+
+ baid = iwl_mld_start_ba_in_fw(mld, sta, tid, ssn, buf_size);
+ if (baid < 0) {
+ ret = baid;
+ goto out_free;
+ }
+
+ mld->num_rx_ba_sessions++;
+ mld_sta->tid_to_baid[tid] = baid;
+
+ baid_data->baid = baid;
+ baid_data->mld = mld;
+ baid_data->tid = tid;
+ baid_data->buf_size = buf_size;
+ baid_data->sta_mask = sta_mask;
+ baid_data->timeout = timeout;
+ baid_data->last_rx_timestamp = jiffies;
+ baid_data->rcu_ptr = &mld->fw_id_to_ba[baid];
+
+ iwl_mld_init_reorder_buffer(mld, baid_data, ssn);
+
+ timer_setup(&baid_data->session_timer, iwl_mld_rx_agg_session_expired,
+ 0);
+ if (timeout)
+ mod_timer(&baid_data->session_timer,
+ TU_TO_EXP_TIME(timeout * 2));
+
+ IWL_DEBUG_HT(mld, "STA mask=0x%x (tid=%d) is assigned to BAID %d\n",
+ baid_data->sta_mask, tid, baid);
+
+ /* protect the BA data with RCU to cover a case where our
+ * internal RX sync mechanism will timeout (not that it's
+ * supposed to happen) and we will free the session data while
+ * RX is being processed in parallel
+ */
+ WARN_ON(rcu_access_pointer(mld->fw_id_to_ba[baid]));
+ rcu_assign_pointer(mld->fw_id_to_ba[baid], baid_data);
+
+ return 0;
+
+out_free:
+ kfree(baid_data);
+ return ret;
+}
+
+int iwl_mld_ampdu_rx_stop(struct iwl_mld *mld, struct ieee80211_sta *sta,
+ int tid)
+{
+ struct iwl_mld_sta *mld_sta = iwl_mld_sta_from_mac80211(sta);
+ int baid = mld_sta->tid_to_baid[tid];
+ struct iwl_mld_baid_data *baid_data;
+ int ret;
+
+ lockdep_assert_wiphy(mld->wiphy);
+
+ /* during firmware restart, do not send the command as the firmware no
+ * longer recognizes the session. instead, only clear the driver BA
+ * session data.
+ */
+ if (!mld->fw_status.in_hw_restart) {
+ ret = iwl_mld_stop_ba_in_fw(mld, sta, tid);
+ if (ret)
+ return ret;
+ }
+
+ if (!WARN_ON(mld->num_rx_ba_sessions == 0))
+ mld->num_rx_ba_sessions--;
+
+ baid_data = wiphy_dereference(mld->wiphy, mld->fw_id_to_ba[baid]);
+ if (WARN_ON(!baid_data))
+ return -EINVAL;
+
+ if (timer_pending(&baid_data->session_timer))
+ timer_shutdown_sync(&baid_data->session_timer);
+
+ iwl_mld_free_reorder_buffer(mld, baid_data);
+
+ RCU_INIT_POINTER(mld->fw_id_to_ba[baid], NULL);
+ kfree_rcu(baid_data, rcu_head);
+
+ IWL_DEBUG_HT(mld, "BAID %d is free\n", baid);
+
+ return 0;
+}
+
+int iwl_mld_update_sta_baids(struct iwl_mld *mld,
+ u32 old_sta_mask,
+ u32 new_sta_mask)
+{
+ struct iwl_rx_baid_cfg_cmd cmd = {
+ .action = cpu_to_le32(IWL_RX_BAID_ACTION_MODIFY),
+ .modify.old_sta_id_mask = cpu_to_le32(old_sta_mask),
+ .modify.new_sta_id_mask = cpu_to_le32(new_sta_mask),
+ };
+ u32 cmd_id = WIDE_ID(DATA_PATH_GROUP, RX_BAID_ALLOCATION_CONFIG_CMD);
+ int baid;
+
+ /* mac80211 will remove sessions later, but we ignore all that */
+ if (mld->fw_status.in_hw_restart)
+ return 0;
+
+ BUILD_BUG_ON(sizeof(struct iwl_rx_baid_cfg_resp) != sizeof(baid));
+
+ for (baid = 0; baid < ARRAY_SIZE(mld->fw_id_to_ba); baid++) {
+ struct iwl_mld_baid_data *data;
+ int ret;
+
+ data = wiphy_dereference(mld->wiphy, mld->fw_id_to_ba[baid]);
+ if (!data)
+ continue;
+
+ if (!(data->sta_mask & old_sta_mask))
+ continue;
+
+ WARN_ONCE(data->sta_mask != old_sta_mask,
+ "BAID data for %d corrupted - expected 0x%x found 0x%x\n",
+ baid, old_sta_mask, data->sta_mask);
+
+ cmd.modify.tid = cpu_to_le32(data->tid);
+
+ ret = iwl_mld_send_cmd_pdu(mld, cmd_id, &cmd);
+ if (ret)
+ return ret;
+ data->sta_mask = new_sta_mask;
+ }
+
+ return 0;
+}
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/agg.h b/drivers/net/wireless/intel/iwlwifi/mld/agg.h
new file mode 100644
index 000000000000..651c80d1c7cd
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/mld/agg.h
@@ -0,0 +1,127 @@
+/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
+/*
+ * Copyright (C) 2024 Intel Corporation
+ */
+#ifndef __iwl_agg_h__
+#define __iwl_agg_h__
+
+#include "mld.h"
+#include "fw/api/rx.h"
+
+/**
+ * struct iwl_mld_reorder_buffer - per ra/tid/queue reorder buffer
+ * @head_sn: reorder window head sequence number
+ * @num_stored: number of MPDUs stored in the buffer
+ * @queue: queue of this reorder buffer
+ * @valid: true if reordering is valid for this queue
+ */
+struct iwl_mld_reorder_buffer {
+ u16 head_sn;
+ u16 num_stored;
+ int queue;
+ bool valid;
+} ____cacheline_aligned_in_smp;
+
+/**
+ * struct iwl_mld_reorder_buf_entry - reorder buffer entry per-queue/per-seqno
+ * @frames: list of skbs stored. a list is necessary because in an A-MSDU,
+ * all sub-frames share the same sequence number, so they are stored
+ * together in the same list.
+ */
+struct iwl_mld_reorder_buf_entry {
+ struct sk_buff_head frames;
+}
+#ifndef __CHECKER__
+/* sparse doesn't like this construct: "bad integer constant expression" */
+__aligned(roundup_pow_of_two(sizeof(struct sk_buff_head)))
+#endif
+;
+
+/**
+ * struct iwl_mld_baid_data - Block Ack session data
+ * @rcu_head: RCU head for freeing this data
+ * @sta_mask: station mask for the BAID
+ * @tid: tid of the session
+ * @baid: baid of the session
+ * @buf_size: the reorder buffer size as set by the last ADDBA request
+ * @entries_per_queue: number of buffers per queue, this actually gets
+ * aligned up to avoid cache line sharing between queues
+ * @timeout: the timeout value specified in the ADDBA request.
+ * @last_rx_timestamp: timestamp of the last received packet (in jiffies). This
+ * value is updated only when the configured @timeout has passed since
+ * the last update to minimize cache bouncing between RX queues.
+ * @session_timer: timer is set to expire after 2 * @timeout (since we want
+ * to minimize the cache bouncing by updating @last_rx_timestamp only once
+ * after @timeout has passed). If no packets are received within this
+ * period, it informs mac80211 to initiate delBA flow, terminating the
+ * BA session.
+ * @rcu_ptr: BA data RCU protected access
+ * @mld: mld pointer, needed for timer context
+ * @reorder_buf: reorder buffer, allocated per queue
+ * @entries: data
+ */
+struct iwl_mld_baid_data {
+ struct rcu_head rcu_head;
+ u32 sta_mask;
+ u8 tid;
+ u8 baid;
+ u16 buf_size;
+ u16 entries_per_queue;
+ u16 timeout;
+ struct timer_list session_timer;
+ unsigned long last_rx_timestamp;
+ struct iwl_mld_baid_data __rcu **rcu_ptr;
+ struct iwl_mld *mld;
+ struct iwl_mld_reorder_buffer reorder_buf[IWL_MAX_RX_HW_QUEUES];
+ struct iwl_mld_reorder_buf_entry entries[] ____cacheline_aligned_in_smp;
+};
+
+/**
+ * struct iwl_mld_delba_data - RX queue sync data for %IWL_MLD_RXQ_NOTIF_DEL_BA
+ *
+ * @baid: Block Ack id, used to identify the BA session to be removed
+ */
+struct iwl_mld_delba_data {
+ u32 baid;
+} __packed;
+
+/**
+ * enum iwl_mld_reorder_result - Possible return values for iwl_mld_reorder()
+ * indicating how the caller should handle the skb based on the result.
+ *
+ * @IWL_MLD_PASS_SKB: skb should be passed to upper layer.
+ * @IWL_MLD_BUFFERED_SKB: skb has been buffered, don't pass it to upper layer.
+ * @IWL_MLD_DROP_SKB: skb should be dropped and freed by the caller.
+ */
+enum iwl_mld_reorder_result {
+ IWL_MLD_PASS_SKB,
+ IWL_MLD_BUFFERED_SKB,
+ IWL_MLD_DROP_SKB
+};
+
+int iwl_mld_ampdu_rx_start(struct iwl_mld *mld, struct ieee80211_sta *sta,
+ int tid, u16 ssn, u16 buf_size, u16 timeout);
+int iwl_mld_ampdu_rx_stop(struct iwl_mld *mld, struct ieee80211_sta *sta,
+ int tid);
+
+enum iwl_mld_reorder_result
+iwl_mld_reorder(struct iwl_mld *mld, struct napi_struct *napi,
+ int queue, struct ieee80211_sta *sta,
+ struct sk_buff *skb, struct iwl_rx_mpdu_desc *desc);
+
+void iwl_mld_handle_frame_release_notif(struct iwl_mld *mld,
+ struct napi_struct *napi,
+ struct iwl_rx_packet *pkt, int queue);
+void iwl_mld_handle_bar_frame_release_notif(struct iwl_mld *mld,
+ struct napi_struct *napi,
+ struct iwl_rx_packet *pkt,
+ int queue);
+
+void iwl_mld_del_ba(struct iwl_mld *mld, int queue,
+ struct iwl_mld_delba_data *data);
+
+int iwl_mld_update_sta_baids(struct iwl_mld *mld,
+ u32 old_sta_mask,
+ u32 new_sta_mask);
+
+#endif /* __iwl_agg_h__ */
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/ap.c b/drivers/net/wireless/intel/iwlwifi/mld/ap.c
new file mode 100644
index 000000000000..26511b49d89a
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/mld/ap.c
@@ -0,0 +1,353 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
+/*
+ * Copyright (C) 2024 Intel Corporation
+ */
+#include <linux/crc32.h>
+
+#include <net/mac80211.h>
+
+#include "ap.h"
+#include "hcmd.h"
+#include "tx.h"
+#include "power.h"
+#include "key.h"
+#include "phy.h"
+#include "iwl-utils.h"
+
+#include "fw/api/sta.h"
+
+void iwl_mld_set_tim_idx(struct iwl_mld *mld, __le32 *tim_index,
+ u8 *beacon, u32 frame_size)
+{
+ u32 tim_idx;
+ struct ieee80211_mgmt *mgmt = (void *)beacon;
+
+ /* The index is relative to frame start but we start looking at the
+ * variable-length part of the beacon.
+ */
+ tim_idx = mgmt->u.beacon.variable - beacon;
+
+ /* Parse variable-length elements of beacon to find WLAN_EID_TIM */
+ while ((tim_idx < (frame_size - 2)) &&
+ (beacon[tim_idx] != WLAN_EID_TIM))
+ tim_idx += beacon[tim_idx + 1] + 2;
+
+ /* If TIM field was found, set variables */
+ if ((tim_idx < (frame_size - 1)) && beacon[tim_idx] == WLAN_EID_TIM)
+ *tim_index = cpu_to_le32(tim_idx);
+ else
+ IWL_WARN(mld, "Unable to find TIM Element in beacon\n");
+}
+
+u8 iwl_mld_get_rate_flags(struct iwl_mld *mld,
+ struct ieee80211_tx_info *info,
+ struct ieee80211_vif *vif,
+ struct ieee80211_bss_conf *link,
+ enum nl80211_band band)
+{
+ u32 legacy = link->beacon_tx_rate.control[band].legacy;
+ u32 rate_idx, rate_flags = 0, fw_rate;
+
+ /* if beacon rate was configured try using it */
+ if (hweight32(legacy) == 1) {
+ u32 rate = ffs(legacy) - 1;
+ struct ieee80211_supported_band *sband =
+ mld->hw->wiphy->bands[band];
+
+ rate_idx = sband->bitrates[rate].hw_value;
+ } else {
+ rate_idx = iwl_mld_get_lowest_rate(mld, info, vif);
+ }
+
+ if (rate_idx <= IWL_LAST_CCK_RATE)
+ rate_flags = IWL_MAC_BEACON_CCK;
+
+ /* Legacy rates are indexed as follows:
+ * 0 - 3 for CCK and 0 - 7 for OFDM.
+ */
+ fw_rate = (rate_idx >= IWL_FIRST_OFDM_RATE ?
+ rate_idx - IWL_FIRST_OFDM_RATE : rate_idx);
+
+ return fw_rate | rate_flags;
+}
+
+int iwl_mld_send_beacon_template_cmd(struct iwl_mld *mld,
+ struct sk_buff *beacon,
+ struct iwl_mac_beacon_cmd *cmd)
+{
+ struct iwl_host_cmd hcmd = {
+ .id = BEACON_TEMPLATE_CMD,
+ };
+
+ hcmd.len[0] = sizeof(*cmd);
+ hcmd.data[0] = cmd;
+
+ hcmd.len[1] = beacon->len;
+ hcmd.data[1] = beacon->data;
+ hcmd.dataflags[1] = IWL_HCMD_DFL_DUP;
+
+ return iwl_mld_send_cmd(mld, &hcmd);
+}
+
+static int iwl_mld_fill_beacon_template_cmd(struct iwl_mld *mld,
+ struct ieee80211_vif *vif,
+ struct sk_buff *beacon,
+ struct iwl_mac_beacon_cmd *cmd,
+ struct ieee80211_bss_conf *link)
+{
+ struct iwl_mld_link *mld_link = iwl_mld_link_from_mac80211(link);
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(beacon);
+ struct ieee80211_chanctx_conf *ctx;
+ bool enable_fils;
+ u16 flags = 0;
+
+ lockdep_assert_wiphy(mld->wiphy);
+
+ if (WARN_ON(!mld_link))
+ return -EINVAL;
+
+ cmd->link_id = cpu_to_le32(mld_link->fw_id);
+
+ ctx = wiphy_dereference(mld->wiphy, link->chanctx_conf);
+ if (WARN_ON(!ctx || !ctx->def.chan))
+ return -EINVAL;
+
+ enable_fils = cfg80211_channel_is_psc(ctx->def.chan) ||
+ (ctx->def.chan->band == NL80211_BAND_6GHZ &&
+ ctx->def.width >= NL80211_CHAN_WIDTH_80);
+
+ if (enable_fils) {
+ flags |= IWL_MAC_BEACON_FILS;
+ cmd->short_ssid = cpu_to_le32(~crc32_le(~0, vif->cfg.ssid,
+ vif->cfg.ssid_len));
+ }
+
+ cmd->byte_cnt = cpu_to_le16((u16)beacon->len);
+
+ flags |= iwl_mld_get_rate_flags(mld, info, vif, link,
+ ctx->def.chan->band);
+
+ cmd->flags = cpu_to_le16(flags);
+
+ if (vif->type == NL80211_IFTYPE_AP) {
+ iwl_mld_set_tim_idx(mld, &cmd->tim_idx,
+ beacon->data, beacon->len);
+
+ cmd->btwt_offset =
+ cpu_to_le32(iwl_find_ie_offset(beacon->data,
+ WLAN_EID_S1G_TWT,
+ beacon->len));
+ }
+
+ cmd->csa_offset =
+ cpu_to_le32(iwl_find_ie_offset(beacon->data,
+ WLAN_EID_CHANNEL_SWITCH,
+ beacon->len));
+ cmd->ecsa_offset =
+ cpu_to_le32(iwl_find_ie_offset(beacon->data,
+ WLAN_EID_EXT_CHANSWITCH_ANN,
+ beacon->len));
+
+ return 0;
+}
+
+/* The beacon template for the AP/GO/IBSS has changed and needs update */
+int iwl_mld_update_beacon_template(struct iwl_mld *mld,
+ struct ieee80211_vif *vif,
+ struct ieee80211_bss_conf *link_conf)
+{
+ struct iwl_mac_beacon_cmd cmd = {};
+ struct sk_buff *beacon;
+ int ret;
+#ifdef CONFIG_IWLWIFI_DEBUGFS
+ struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
+#endif
+
+ WARN_ON(vif->type != NL80211_IFTYPE_AP &&
+ vif->type != NL80211_IFTYPE_ADHOC);
+
+ if (IWL_MLD_NON_TRANSMITTING_AP)
+ return 0;
+
+#ifdef CONFIG_IWLWIFI_DEBUGFS
+ if (mld_vif->beacon_inject_active) {
+ IWL_DEBUG_INFO(mld,
+ "Can't update template, beacon injection's active\n");
+ return -EBUSY;
+ }
+
+#endif
+ beacon = ieee80211_beacon_get_template(mld->hw, vif, NULL,
+ link_conf->link_id);
+ if (!beacon)
+ return -ENOMEM;
+
+ ret = iwl_mld_fill_beacon_template_cmd(mld, vif, beacon, &cmd,
+ link_conf);
+
+ if (!ret)
+ ret = iwl_mld_send_beacon_template_cmd(mld, beacon, &cmd);
+
+ dev_kfree_skb(beacon);
+
+ return ret;
+}
+
+void iwl_mld_free_ap_early_key(struct iwl_mld *mld,
+ struct ieee80211_key_conf *key,
+ struct iwl_mld_vif *mld_vif)
+{
+ struct iwl_mld_link *link;
+
+ if (WARN_ON(key->link_id < 0))
+ return;
+
+ link = iwl_mld_link_dereference_check(mld_vif, key->link_id);
+ if (WARN_ON(!link))
+ return;
+
+ for (int i = 0; i < ARRAY_SIZE(link->ap_early_keys); i++) {
+ if (link->ap_early_keys[i] != key)
+ continue;
+ /* Those weren't sent to FW, so should be marked as INVALID */
+ if (WARN_ON(key->hw_key_idx != STA_KEY_IDX_INVALID))
+ key->hw_key_idx = STA_KEY_IDX_INVALID;
+ link->ap_early_keys[i] = NULL;
+ }
+}
+
+int iwl_mld_store_ap_early_key(struct iwl_mld *mld,
+ struct ieee80211_key_conf *key,
+ struct iwl_mld_vif *mld_vif)
+{
+ struct iwl_mld_link *link;
+
+ if (WARN_ON(key->link_id < 0))
+ return -EINVAL;
+
+ link = iwl_mld_link_dereference_check(mld_vif, key->link_id);
+ if (WARN_ON(!link))
+ return -EINVAL;
+
+ for (int i = 0; i < ARRAY_SIZE(link->ap_early_keys); i++) {
+ if (!link->ap_early_keys[i]) {
+ link->ap_early_keys[i] = key;
+ return 0;
+ }
+ }
+
+ return -ENOSPC;
+}
+
+static int iwl_mld_send_ap_early_keys(struct iwl_mld *mld,
+ struct ieee80211_vif *vif,
+ struct ieee80211_bss_conf *link)
+{
+ struct iwl_mld_link *mld_link = iwl_mld_link_from_mac80211(link);
+ int ret = 0;
+
+ if (WARN_ON(!link))
+ return -EINVAL;
+
+ for (int i = 0; i < ARRAY_SIZE(mld_link->ap_early_keys); i++) {
+ struct ieee80211_key_conf *key = mld_link->ap_early_keys[i];
+
+ if (!key)
+ continue;
+
+ mld_link->ap_early_keys[i] = NULL;
+
+ ret = iwl_mld_add_key(mld, vif, NULL, key);
+ if (ret)
+ break;
+ }
+ return ret;
+}
+
+int iwl_mld_start_ap_ibss(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_bss_conf *link)
+{
+ struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw);
+ struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
+ struct ieee80211_chanctx_conf *ctx;
+ int ret;
+
+ if (vif->type == NL80211_IFTYPE_AP)
+ iwl_mld_send_ap_tx_power_constraint_cmd(mld, vif, link);
+
+ ret = iwl_mld_update_beacon_template(mld, vif, link);
+ if (ret)
+ return ret;
+
+ /* the link should be already activated when assigning chan context,
+ * and LINK_CONTEXT_MODIFY_EHT_PARAMS is deprecated
+ */
+ ret = iwl_mld_change_link_in_fw(mld, link,
+ LINK_CONTEXT_MODIFY_ALL &
+ ~(LINK_CONTEXT_MODIFY_ACTIVE |
+ LINK_CONTEXT_MODIFY_EHT_PARAMS));
+ if (ret)
+ return ret;
+
+ ret = iwl_mld_add_mcast_sta(mld, vif, link);
+ if (ret)
+ return ret;
+
+ ret = iwl_mld_add_bcast_sta(mld, vif, link);
+ if (ret)
+ goto rm_mcast;
+
+ /* Those keys were configured by the upper layers before starting the
+ * AP. Now that it is started and the bcast and mcast sta were added to
+ * the FW, we can add the keys too.
+ */
+ ret = iwl_mld_send_ap_early_keys(mld, vif, link);
+ if (ret)
+ goto rm_bcast;
+
+ if (ieee80211_vif_type_p2p(vif) == NL80211_IFTYPE_AP)
+ iwl_mld_vif_update_low_latency(mld, vif, true,
+ LOW_LATENCY_VIF_TYPE);
+
+ mld_vif->ap_ibss_active = true;
+
+ if (vif->p2p && mld->p2p_device_vif)
+ return iwl_mld_mac_fw_action(mld, mld->p2p_device_vif,
+ FW_CTXT_ACTION_MODIFY);
+
+ /* When the channel context was added, the link is not yet active, so
+ * min_def is always used. Update the PHY again here in case def should
+ * actually be used.
+ */
+ ctx = wiphy_dereference(mld->wiphy, link->chanctx_conf);
+ iwl_mld_update_phy_chandef(mld, ctx);
+
+ return 0;
+rm_bcast:
+ iwl_mld_remove_bcast_sta(mld, vif, link);
+rm_mcast:
+ iwl_mld_remove_mcast_sta(mld, vif, link);
+ return ret;
+}
+
+void iwl_mld_stop_ap_ibss(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ struct ieee80211_bss_conf *link)
+{
+ struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw);
+ struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
+
+ mld_vif->ap_ibss_active = false;
+
+ if (vif->p2p && mld->p2p_device_vif)
+ iwl_mld_mac_fw_action(mld, mld->p2p_device_vif,
+ FW_CTXT_ACTION_MODIFY);
+
+ if (ieee80211_vif_type_p2p(vif) == NL80211_IFTYPE_AP)
+ iwl_mld_vif_update_low_latency(mld, vif, false,
+ LOW_LATENCY_VIF_TYPE);
+
+ iwl_mld_remove_bcast_sta(mld, vif, link);
+
+ iwl_mld_remove_mcast_sta(mld, vif, link);
+}
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/ap.h b/drivers/net/wireless/intel/iwlwifi/mld/ap.h
new file mode 100644
index 000000000000..4a6f52b9552d
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/mld/ap.h
@@ -0,0 +1,45 @@
+/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
+/*
+ * Copyright (C) 2024 Intel Corporation
+ */
+#ifndef __iwl_ap_h__
+#define __iwl_ap_h__
+
+#include "mld.h"
+#include "iface.h"
+
+#include "fw/api/tx.h"
+
+int iwl_mld_update_beacon_template(struct iwl_mld *mld,
+ struct ieee80211_vif *vif,
+ struct ieee80211_bss_conf *link_conf);
+
+int iwl_mld_start_ap_ibss(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_bss_conf *link);
+
+void iwl_mld_stop_ap_ibss(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ struct ieee80211_bss_conf *link);
+
+int iwl_mld_store_ap_early_key(struct iwl_mld *mld,
+ struct ieee80211_key_conf *key,
+ struct iwl_mld_vif *mld_vif);
+
+void iwl_mld_free_ap_early_key(struct iwl_mld *mld,
+ struct ieee80211_key_conf *key,
+ struct iwl_mld_vif *mld_vif);
+
+u8 iwl_mld_get_rate_flags(struct iwl_mld *mld,
+ struct ieee80211_tx_info *info,
+ struct ieee80211_vif *vif,
+ struct ieee80211_bss_conf *link,
+ enum nl80211_band band);
+
+void iwl_mld_set_tim_idx(struct iwl_mld *mld, __le32 *tim_index,
+ u8 *beacon, u32 frame_size);
+
+int iwl_mld_send_beacon_template_cmd(struct iwl_mld *mld,
+ struct sk_buff *beacon,
+ struct iwl_mac_beacon_cmd *cmd);
+
+#endif /* __iwl_ap_h__ */
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/coex.c b/drivers/net/wireless/intel/iwlwifi/mld/coex.c
new file mode 100644
index 000000000000..32c727b3b391
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/mld/coex.c
@@ -0,0 +1,36 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
+/*
+ * Copyright (C) 2024-2025 Intel Corporation
+ */
+
+#include "fw/api/coex.h"
+
+#include "coex.h"
+#include "mld.h"
+#include "hcmd.h"
+#include "mlo.h"
+
+int iwl_mld_send_bt_init_conf(struct iwl_mld *mld)
+{
+ struct iwl_bt_coex_cmd cmd = {
+ .mode = cpu_to_le32(BT_COEX_NW),
+ .enabled_modules = cpu_to_le32(BT_COEX_MPLUT_ENABLED |
+ BT_COEX_HIGH_BAND_RET),
+ };
+
+ return iwl_mld_send_cmd_pdu(mld, BT_CONFIG, &cmd);
+}
+
+void iwl_mld_handle_bt_coex_notif(struct iwl_mld *mld,
+ struct iwl_rx_packet *pkt)
+{
+ const struct iwl_bt_coex_profile_notif *notif = (const void *)pkt->data;
+ const struct iwl_bt_coex_profile_notif zero_notif = {};
+ /* zeroed structure means that BT is OFF */
+ bool bt_is_active = memcmp(notif, &zero_notif, sizeof(*notif));
+
+ mld->last_bt_notif = *notif;
+ IWL_DEBUG_INFO(mld, "BT was turned %s\n", bt_is_active ? "ON" : "OFF");
+
+ iwl_mld_emlsr_check_bt(mld);
+}
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/coex.h b/drivers/net/wireless/intel/iwlwifi/mld/coex.h
new file mode 100644
index 000000000000..a77c5dc9613c
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/mld/coex.h
@@ -0,0 +1,15 @@
+/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
+/*
+ * Copyright (C) 2024 Intel Corporation
+ */
+#ifndef __iwl_mld_coex_h__
+#define __iwl_mld_coex_h__
+
+#include "mld.h"
+
+int iwl_mld_send_bt_init_conf(struct iwl_mld *mld);
+
+void iwl_mld_handle_bt_coex_notif(struct iwl_mld *mld,
+ struct iwl_rx_packet *pkt);
+
+#endif /* __iwl_mld_coex_h__ */
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/constants.h b/drivers/net/wireless/intel/iwlwifi/mld/constants.h
new file mode 100644
index 000000000000..2a59b29b75cb
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/mld/constants.h
@@ -0,0 +1,88 @@
+/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
+/*
+ * Copyright (C) 2024-2025 Intel Corporation
+ */
+#ifndef __iwl_mld_constants_h__
+#define __iwl_mld_constants_h__
+
+#define IWL_MLD_MISSED_BEACONS_SINCE_RX_THOLD 4
+#define IWL_MLD_MISSED_BEACONS_THRESHOLD 8
+#define IWL_MLD_MISSED_BEACONS_THRESHOLD_LONG 19
+#define IWL_MLD_BCN_LOSS_EXIT_ESR_THRESH_2_LINKS 5
+#define IWL_MLD_BCN_LOSS_EXIT_ESR_THRESH 15
+#define IWL_MLD_BCN_LOSS_EXIT_ESR_THRESH_BSS_PARAM_CHANGED 11
+#define IWL_MLD_LOW_RSSI_MLO_SCAN_THRESH -72
+
+#define IWL_MLD_DEFAULT_PS_TX_DATA_TIMEOUT (100 * USEC_PER_MSEC)
+#define IWL_MLD_DEFAULT_PS_RX_DATA_TIMEOUT (100 * USEC_PER_MSEC)
+#define IWL_MLD_WOWLAN_PS_TX_DATA_TIMEOUT (10 * USEC_PER_MSEC)
+#define IWL_MLD_WOWLAN_PS_RX_DATA_TIMEOUT (10 * USEC_PER_MSEC)
+#define IWL_MLD_SHORT_PS_TX_DATA_TIMEOUT (2 * 1024) /* defined in TU */
+#define IWL_MLD_SHORT_PS_RX_DATA_TIMEOUT (40 * 1024) /* defined in TU */
+
+#define IWL_MLD_UAPSD_RX_DATA_TIMEOUT (50 * USEC_PER_MSEC)
+#define IWL_MLD_UAPSD_TX_DATA_TIMEOUT (50 * USEC_PER_MSEC)
+
+#define IWL_MLD_PS_SNOOZE_INTERVAL 25
+#define IWL_MLD_PS_SNOOZE_INTERVAL 25
+#define IWL_MLD_PS_SNOOZE_WINDOW 50
+
+#define IWL_MLD_PS_SNOOZE_HEAVY_TX_THLD_PACKETS 30
+#define IWL_MLD_PS_SNOOZE_HEAVY_RX_THLD_PACKETS 20
+
+#define IWL_MLD_PS_HEAVY_TX_THLD_PERCENT 50
+#define IWL_MLD_PS_HEAVY_RX_THLD_PERCENT 50
+#define IWL_MLD_PS_HEAVY_TX_THLD_PACKETS 20
+#define IWL_MLD_PS_HEAVY_RX_THLD_PACKETS 8
+
+#define IWL_MLD_TRIGGER_LINK_SEL_TIME_SEC 30
+#define IWL_MLD_SCAN_EXPIRE_TIME_SEC 20
+
+#define IWL_MLD_TPT_COUNT_WINDOW (5 * HZ)
+
+/* OMI reduced BW thresholds (channel load percentage) */
+#define IWL_MLD_OMI_ENTER_CHAN_LOAD 10
+#define IWL_MLD_OMI_EXIT_CHAN_LOAD_160 20
+#define IWL_MLD_OMI_EXIT_CHAN_LOAD_320 30
+/* time (in milliseconds) to let AP "settle" the OMI */
+#define IWL_MLD_OMI_AP_SETTLE_DELAY 27
+/* time (in milliseconds) to not enter OMI reduced BW after leaving */
+#define IWL_MLD_OMI_EXIT_PROTECTION 5000
+
+#define IWL_MLD_DIS_RANDOM_FW_ID false
+#define IWL_MLD_D3_DEBUG false
+#define IWL_MLD_NON_TRANSMITTING_AP false
+#define IWL_MLD_6GHZ_PASSIVE_SCAN_TIMEOUT 3000 /* in seconds */
+#define IWL_MLD_6GHZ_PASSIVE_SCAN_ASSOC_TIMEOUT 60 /* in seconds */
+#define IWL_MLD_CONN_LISTEN_INTERVAL 10
+#define IWL_MLD_ADAPTIVE_DWELL_NUM_APS_OVERRIDE 0
+#define IWL_MLD_AUTO_EML_ENABLE true
+
+#define IWL_MLD_HIGH_RSSI_THRESH_20MHZ -67
+#define IWL_MLD_LOW_RSSI_THRESH_20MHZ -72
+#define IWL_MLD_HIGH_RSSI_THRESH_40MHZ -64
+#define IWL_MLD_LOW_RSSI_THRESH_40MHZ -72
+#define IWL_MLD_HIGH_RSSI_THRESH_80MHZ -61
+#define IWL_MLD_LOW_RSSI_THRESH_80MHZ -72
+#define IWL_MLD_HIGH_RSSI_THRESH_160MHZ -58
+#define IWL_MLD_LOW_RSSI_THRESH_160MHZ -72
+
+#define IWL_MLD_ENTER_EMLSR_TPT_THRESH 400
+#define IWL_MLD_EXIT_EMLSR_CHAN_LOAD 2 /* in percentage */
+
+#define IWL_MLD_FTM_INITIATOR_ALGO IWL_TOF_ALGO_TYPE_MAX_LIKE
+#define IWL_MLD_FTM_INITIATOR_DYNACK true
+#define IWL_MLD_FTM_LMR_FEEDBACK_TERMINATE false
+#define IWL_MLD_FTM_TEST_INCORRECT_SAC false
+#define IWL_MLD_FTM_R2I_MAX_REP 7
+#define IWL_MLD_FTM_I2R_MAX_REP 7
+#define IWL_MLD_FTM_R2I_MAX_STS 1
+#define IWL_MLD_FTM_I2R_MAX_STS 1
+#define IWL_MLD_FTM_R2I_MAX_TOTAL_LTF 3
+#define IWL_MLD_FTM_I2R_MAX_TOTAL_LTF 3
+#define IWL_MLD_FTM_RESP_NDP_SUPPORT true
+#define IWL_MLD_FTM_RESP_LMR_FEEDBACK_SUPPORT true
+#define IWL_MLD_FTM_NON_TB_MIN_TIME_BETWEEN_MSR 7
+#define IWL_MLD_FTM_NON_TB_MAX_TIME_BETWEEN_MSR 1000
+
+#endif /* __iwl_mld_constants_h__ */
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/d3.c b/drivers/net/wireless/intel/iwlwifi/mld/d3.c
new file mode 100644
index 000000000000..339b148d6793
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/mld/d3.c
@@ -0,0 +1,1998 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
+/*
+ * Copyright (C) 2024-2025 Intel Corporation
+ */
+#include "mld.h"
+
+#include "d3.h"
+#include "power.h"
+#include "hcmd.h"
+#include "iface.h"
+#include "mcc.h"
+#include "sta.h"
+#include "mlo.h"
+
+#include "fw/api/d3.h"
+#include "fw/api/offload.h"
+#include "fw/api/sta.h"
+#include "fw/dbg.h"
+
+#include <net/ipv6.h>
+#include <net/addrconf.h>
+#include <linux/bitops.h>
+
+/**
+ * enum iwl_mld_d3_notif - d3 notifications
+ * @IWL_D3_NOTIF_WOWLAN_INFO: WOWLAN_INFO_NOTIF is expected/was received
+ * @IWL_D3_NOTIF_WOWLAN_WAKE_PKT: WOWLAN_WAKE_PKT_NOTIF is expected/was received
+ * @IWL_D3_NOTIF_PROT_OFFLOAD: PROT_OFFLOAD_NOTIF is expected/was received
+ * @IWL_D3_ND_MATCH_INFO: OFFLOAD_MATCH_INFO_NOTIF is expected/was received
+ * @IWL_D3_NOTIF_D3_END_NOTIF: D3_END_NOTIF is expected/was received
+ */
+enum iwl_mld_d3_notif {
+ IWL_D3_NOTIF_WOWLAN_INFO = BIT(0),
+ IWL_D3_NOTIF_WOWLAN_WAKE_PKT = BIT(1),
+ IWL_D3_NOTIF_PROT_OFFLOAD = BIT(2),
+ IWL_D3_ND_MATCH_INFO = BIT(3),
+ IWL_D3_NOTIF_D3_END_NOTIF = BIT(4)
+};
+
+struct iwl_mld_resume_key_iter_data {
+ struct iwl_mld *mld;
+ struct iwl_mld_wowlan_status *wowlan_status;
+ u32 num_keys, gtk_cipher, igtk_cipher, bigtk_cipher;
+ bool unhandled_cipher;
+};
+
+struct iwl_mld_suspend_key_iter_data {
+ struct iwl_wowlan_rsc_tsc_params_cmd *rsc;
+ bool have_rsc;
+ int gtks;
+ int found_gtk_idx[4];
+ __le32 gtk_cipher;
+ __le32 igtk_cipher;
+ __le32 bigtk_cipher;
+};
+
+struct iwl_mld_mcast_key_data {
+ u8 key[WOWLAN_KEY_MAX_SIZE];
+ u8 len;
+ u8 flags;
+ u8 id;
+ union {
+ struct {
+ struct ieee80211_key_seq aes_seq[IWL_MAX_TID_COUNT];
+ struct ieee80211_key_seq tkip_seq[IWL_MAX_TID_COUNT];
+ } gtk;
+ struct {
+ struct ieee80211_key_seq cmac_gmac_seq;
+ } igtk_bigtk;
+ };
+
+};
+
+/**
+ * struct iwl_mld_wowlan_status - contains wowlan status data from
+ * all wowlan notifications
+ * @wakeup_reasons: wakeup reasons, see &enum iwl_wowlan_wakeup_reason
+ * @replay_ctr: GTK rekey replay counter
+ * @pattern_number: number of the matched patterns on packets
+ * @last_qos_seq: QoS sequence counter of offloaded tid
+ * @num_of_gtk_rekeys: number of GTK rekeys during D3
+ * @tid_offloaded_tx: tid used by the firmware to transmit data packets
+ * while in wowlan
+ * @wake_packet: wakeup packet received
+ * @wake_packet_length: wake packet length
+ * @wake_packet_bufsize: wake packet bufsize
+ * @gtk: data of the last two used gtk's by the FW upon resume
+ * @igtk: data of the last used igtk by the FW upon resume
+ * @bigtk: data of the last two used gtk's by the FW upon resume
+ * @ptk: last seq numbers per tid passed by the FW,
+ * holds both in tkip and aes formats
+ */
+struct iwl_mld_wowlan_status {
+ u32 wakeup_reasons;
+ u64 replay_ctr;
+ u16 pattern_number;
+ u16 last_qos_seq;
+ u32 num_of_gtk_rekeys;
+ u8 tid_offloaded_tx;
+ u8 *wake_packet;
+ u32 wake_packet_length;
+ u32 wake_packet_bufsize;
+ struct iwl_mld_mcast_key_data gtk[WOWLAN_GTK_KEYS_NUM];
+ struct iwl_mld_mcast_key_data igtk;
+ struct iwl_mld_mcast_key_data bigtk[WOWLAN_BIGTK_KEYS_NUM];
+ struct {
+ struct ieee80211_key_seq aes_seq[IWL_MAX_TID_COUNT];
+ struct ieee80211_key_seq tkip_seq[IWL_MAX_TID_COUNT];
+
+ } ptk;
+};
+
+#define NETDETECT_QUERY_BUF_LEN \
+ (sizeof(struct iwl_scan_offload_profile_match) * \
+ IWL_SCAN_MAX_PROFILES_V2)
+
+/**
+ * struct iwl_mld_netdetect_res - contains netdetect results from
+ * match_info_notif
+ * @matched_profiles: bitmap of matched profiles, referencing the
+ * matches passed in the scan offload request
+ * @matches: array of match information, one for each match
+ */
+struct iwl_mld_netdetect_res {
+ u32 matched_profiles;
+ u8 matches[NETDETECT_QUERY_BUF_LEN];
+};
+
+/**
+ * struct iwl_mld_resume_data - d3 resume flow data
+ * @notifs_expected: bitmap of expected notifications from fw,
+ * see &enum iwl_mld_d3_notif
+ * @notifs_received: bitmap of received notifications from fw,
+ * see &enum iwl_mld_d3_notif
+ * @d3_end_flags: bitmap of flags from d3_end_notif
+ * @notif_handling_err: error handling one of the resume notifications
+ * @wowlan_status: wowlan status data from all wowlan notifications
+ * @netdetect_res: contains netdetect results from match_info_notif
+ */
+struct iwl_mld_resume_data {
+ u32 notifs_expected;
+ u32 notifs_received;
+ u32 d3_end_flags;
+ bool notif_handling_err;
+ struct iwl_mld_wowlan_status *wowlan_status;
+ struct iwl_mld_netdetect_res *netdetect_res;
+};
+
+#define IWL_WOWLAN_WAKEUP_REASON_HAS_WAKEUP_PKT \
+ (IWL_WOWLAN_WAKEUP_BY_MAGIC_PACKET | \
+ IWL_WOWLAN_WAKEUP_BY_PATTERN | \
+ IWL_WAKEUP_BY_PATTERN_IPV4_TCP_SYN |\
+ IWL_WAKEUP_BY_PATTERN_IPV4_TCP_SYN_WILDCARD |\
+ IWL_WAKEUP_BY_PATTERN_IPV6_TCP_SYN |\
+ IWL_WAKEUP_BY_PATTERN_IPV6_TCP_SYN_WILDCARD)
+
+#define IWL_WOWLAN_OFFLOAD_TID 0
+
+void iwl_mld_set_rekey_data(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct cfg80211_gtk_rekey_data *data)
+{
+ struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw);
+ struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
+ struct iwl_mld_wowlan_data *wowlan_data = &mld_vif->wowlan_data;
+
+ lockdep_assert_wiphy(mld->wiphy);
+
+ wowlan_data->rekey_data.kek_len = data->kek_len;
+ wowlan_data->rekey_data.kck_len = data->kck_len;
+ memcpy(wowlan_data->rekey_data.kek, data->kek, data->kek_len);
+ memcpy(wowlan_data->rekey_data.kck, data->kck, data->kck_len);
+ wowlan_data->rekey_data.akm = data->akm & 0xFF;
+ wowlan_data->rekey_data.replay_ctr =
+ cpu_to_le64(be64_to_cpup((const __be64 *)data->replay_ctr));
+ wowlan_data->rekey_data.valid = true;
+}
+
+#if IS_ENABLED(CONFIG_IPV6)
+void iwl_mld_ipv6_addr_change(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct inet6_dev *idev)
+{
+ struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
+ struct iwl_mld_wowlan_data *wowlan_data = &mld_vif->wowlan_data;
+ struct inet6_ifaddr *ifa;
+ int idx = 0;
+
+ memset(wowlan_data->tentative_addrs, 0,
+ sizeof(wowlan_data->tentative_addrs));
+
+ read_lock_bh(&idev->lock);
+ list_for_each_entry(ifa, &idev->addr_list, if_list) {
+ wowlan_data->target_ipv6_addrs[idx] = ifa->addr;
+ if (ifa->flags & IFA_F_TENTATIVE)
+ __set_bit(idx, wowlan_data->tentative_addrs);
+ idx++;
+ if (idx >= IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_MAX)
+ break;
+ }
+ read_unlock_bh(&idev->lock);
+
+ wowlan_data->num_target_ipv6_addrs = idx;
+}
+#endif
+
+enum rt_status {
+ FW_ALIVE,
+ FW_NEEDS_RESET,
+ FW_ERROR,
+};
+
+static enum rt_status iwl_mld_check_err_tables(struct iwl_mld *mld,
+ struct ieee80211_vif *vif)
+{
+ u32 err_id;
+
+ /* check for lmac1 error */
+ if (iwl_fwrt_read_err_table(mld->trans,
+ mld->trans->dbg.lmac_error_event_table[0],
+ &err_id)) {
+ if (err_id == RF_KILL_INDICATOR_FOR_WOWLAN && vif) {
+ struct cfg80211_wowlan_wakeup wakeup = {
+ .rfkill_release = true,
+ };
+ ieee80211_report_wowlan_wakeup(vif, &wakeup,
+ GFP_KERNEL);
+
+ return FW_NEEDS_RESET;
+ }
+ return FW_ERROR;
+ }
+
+ /* check if we have lmac2 set and check for error */
+ if (iwl_fwrt_read_err_table(mld->trans,
+ mld->trans->dbg.lmac_error_event_table[1],
+ NULL))
+ return FW_ERROR;
+
+ /* check for umac error */
+ if (iwl_fwrt_read_err_table(mld->trans,
+ mld->trans->dbg.umac_error_event_table,
+ NULL))
+ return FW_ERROR;
+
+ return FW_ALIVE;
+}
+
+static bool iwl_mld_fw_needs_restart(struct iwl_mld *mld,
+ struct ieee80211_vif *vif)
+{
+ enum rt_status rt_status = iwl_mld_check_err_tables(mld, vif);
+
+ if (rt_status == FW_ALIVE)
+ return false;
+
+ if (rt_status == FW_ERROR) {
+ IWL_ERR(mld, "FW Error occurred during suspend\n");
+ iwl_fwrt_dump_error_logs(&mld->fwrt);
+ iwl_dbg_tlv_time_point(&mld->fwrt,
+ IWL_FW_INI_TIME_POINT_FW_ASSERT, NULL);
+ }
+
+ return true;
+}
+
+static int
+iwl_mld_netdetect_config(struct iwl_mld *mld,
+ struct ieee80211_vif *vif,
+ const struct cfg80211_wowlan *wowlan)
+{
+ int ret;
+ struct cfg80211_sched_scan_request *netdetect_cfg =
+ wowlan->nd_config;
+ struct ieee80211_scan_ies ies = {};
+
+ ret = iwl_mld_scan_stop(mld, IWL_MLD_SCAN_SCHED, true);
+ if (ret)
+ return ret;
+
+ ret = iwl_mld_sched_scan_start(mld, vif, netdetect_cfg, &ies,
+ IWL_MLD_SCAN_NETDETECT);
+ return ret;
+}
+
+static void
+iwl_mld_le64_to_tkip_seq(__le64 le_pn, struct ieee80211_key_seq *seq)
+{
+ u64 pn = le64_to_cpu(le_pn);
+
+ seq->tkip.iv16 = (u16)pn;
+ seq->tkip.iv32 = (u32)(pn >> 16);
+}
+
+static void
+iwl_mld_le64_to_aes_seq(__le64 le_pn, struct ieee80211_key_seq *seq)
+{
+ u64 pn = le64_to_cpu(le_pn);
+
+ seq->ccmp.pn[0] = pn >> 40;
+ seq->ccmp.pn[1] = pn >> 32;
+ seq->ccmp.pn[2] = pn >> 24;
+ seq->ccmp.pn[3] = pn >> 16;
+ seq->ccmp.pn[4] = pn >> 8;
+ seq->ccmp.pn[5] = pn;
+}
+
+static void
+iwl_mld_convert_gtk_resume_seq(struct iwl_mld_mcast_key_data *gtk_data,
+ const struct iwl_wowlan_all_rsc_tsc_v5 *sc,
+ int rsc_idx)
+{
+ struct ieee80211_key_seq *aes_seq = gtk_data->gtk.aes_seq;
+ struct ieee80211_key_seq *tkip_seq = gtk_data->gtk.tkip_seq;
+
+ if (rsc_idx >= ARRAY_SIZE(sc->mcast_rsc))
+ return;
+
+ /* We store both the TKIP and AES representations coming from the
+ * FW because we decode the data from there before we iterate
+ * the keys and know which type is used.
+ */
+ for (int tid = 0; tid < IWL_MAX_TID_COUNT; tid++) {
+ iwl_mld_le64_to_tkip_seq(sc->mcast_rsc[rsc_idx][tid],
+ &tkip_seq[tid]);
+ iwl_mld_le64_to_aes_seq(sc->mcast_rsc[rsc_idx][tid],
+ &aes_seq[tid]);
+ }
+}
+
+static void
+iwl_mld_convert_gtk_resume_data(struct iwl_mld *mld,
+ struct iwl_mld_wowlan_status *wowlan_status,
+ const struct iwl_wowlan_gtk_status_v3 *gtk_data,
+ const struct iwl_wowlan_all_rsc_tsc_v5 *sc)
+{
+ int status_idx = 0;
+
+ BUILD_BUG_ON(sizeof(wowlan_status->gtk[0].key) <
+ sizeof(gtk_data[0].key));
+ BUILD_BUG_ON(ARRAY_SIZE(wowlan_status->gtk) < WOWLAN_GTK_KEYS_NUM);
+
+ for (int notif_idx = 0; notif_idx < ARRAY_SIZE(wowlan_status->gtk);
+ notif_idx++) {
+ int rsc_idx;
+
+ if (!(gtk_data[notif_idx].key_len))
+ continue;
+
+ wowlan_status->gtk[status_idx].len =
+ gtk_data[notif_idx].key_len;
+ wowlan_status->gtk[status_idx].flags =
+ gtk_data[notif_idx].key_flags;
+ wowlan_status->gtk[status_idx].id =
+ wowlan_status->gtk[status_idx].flags &
+ IWL_WOWLAN_GTK_IDX_MASK;
+ memcpy(wowlan_status->gtk[status_idx].key,
+ gtk_data[notif_idx].key,
+ sizeof(gtk_data[notif_idx].key));
+
+ /* The rsc for both gtk keys are stored in gtk[0]->sc->mcast_rsc
+ * The gtk ids can be any two numbers between 0 and 3,
+ * the id_map maps between the key id and the index in sc->mcast
+ */
+ rsc_idx =
+ sc->mcast_key_id_map[wowlan_status->gtk[status_idx].id];
+ iwl_mld_convert_gtk_resume_seq(&wowlan_status->gtk[status_idx],
+ sc, rsc_idx);
+
+ /* if it's as long as the TKIP encryption key, copy MIC key */
+ if (wowlan_status->gtk[status_idx].len ==
+ NL80211_TKIP_DATA_OFFSET_TX_MIC_KEY)
+ memcpy(wowlan_status->gtk[status_idx].key +
+ NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY,
+ gtk_data[notif_idx].tkip_mic_key,
+ sizeof(gtk_data[notif_idx].tkip_mic_key));
+ status_idx++;
+ }
+}
+
+static void
+iwl_mld_convert_ptk_resume_seq(struct iwl_mld *mld,
+ struct iwl_mld_wowlan_status *wowlan_status,
+ const struct iwl_wowlan_all_rsc_tsc_v5 *sc)
+{
+ struct ieee80211_key_seq *aes_seq = wowlan_status->ptk.aes_seq;
+ struct ieee80211_key_seq *tkip_seq = wowlan_status->ptk.tkip_seq;
+
+ BUILD_BUG_ON(ARRAY_SIZE(sc->ucast_rsc) != IWL_MAX_TID_COUNT);
+
+ for (int tid = 0; tid < IWL_MAX_TID_COUNT; tid++) {
+ iwl_mld_le64_to_aes_seq(sc->ucast_rsc[tid], &aes_seq[tid]);
+ iwl_mld_le64_to_tkip_seq(sc->ucast_rsc[tid], &tkip_seq[tid]);
+ }
+}
+
+static void
+iwl_mld_convert_mcast_ipn(struct iwl_mld_mcast_key_data *key_status,
+ const struct iwl_wowlan_igtk_status *key)
+{
+ struct ieee80211_key_seq *seq =
+ &key_status->igtk_bigtk.cmac_gmac_seq;
+ u8 ipn_len = ARRAY_SIZE(key->ipn);
+
+ BUILD_BUG_ON(ipn_len != ARRAY_SIZE(seq->aes_gmac.pn));
+ BUILD_BUG_ON(ipn_len != ARRAY_SIZE(seq->aes_cmac.pn));
+ BUILD_BUG_ON(offsetof(struct ieee80211_key_seq, aes_gmac) !=
+ offsetof(struct ieee80211_key_seq, aes_cmac));
+
+ /* mac80211 expects big endian for memcmp() to work, convert.
+ * We don't have the key cipher yet so copy to both to cmac and gmac
+ */
+ for (int i = 0; i < ipn_len; i++) {
+ seq->aes_gmac.pn[i] = key->ipn[ipn_len - i - 1];
+ seq->aes_cmac.pn[i] = key->ipn[ipn_len - i - 1];
+ }
+}
+
+static void
+iwl_mld_convert_igtk_resume_data(struct iwl_mld_wowlan_status *wowlan_status,
+ const struct iwl_wowlan_igtk_status *igtk)
+{
+ BUILD_BUG_ON(sizeof(wowlan_status->igtk.key) < sizeof(igtk->key));
+
+ if (!igtk->key_len)
+ return;
+
+ wowlan_status->igtk.len = igtk->key_len;
+ wowlan_status->igtk.flags = igtk->key_flags;
+ wowlan_status->igtk.id =
+ u32_get_bits(igtk->key_flags,
+ IWL_WOWLAN_IGTK_BIGTK_IDX_MASK) +
+ WOWLAN_IGTK_MIN_INDEX;
+
+ memcpy(wowlan_status->igtk.key, igtk->key, sizeof(igtk->key));
+ iwl_mld_convert_mcast_ipn(&wowlan_status->igtk, igtk);
+}
+
+static void
+iwl_mld_convert_bigtk_resume_data(struct iwl_mld_wowlan_status *wowlan_status,
+ const struct iwl_wowlan_igtk_status *bigtk)
+{
+ int status_idx = 0;
+
+ BUILD_BUG_ON(ARRAY_SIZE(wowlan_status->bigtk) < WOWLAN_BIGTK_KEYS_NUM);
+
+ for (int notif_idx = 0; notif_idx < WOWLAN_BIGTK_KEYS_NUM;
+ notif_idx++) {
+ if (!bigtk[notif_idx].key_len)
+ continue;
+
+ wowlan_status->bigtk[status_idx].len = bigtk[notif_idx].key_len;
+ wowlan_status->bigtk[status_idx].flags =
+ bigtk[notif_idx].key_flags;
+ wowlan_status->bigtk[status_idx].id =
+ u32_get_bits(bigtk[notif_idx].key_flags,
+ IWL_WOWLAN_IGTK_BIGTK_IDX_MASK)
+ + WOWLAN_BIGTK_MIN_INDEX;
+
+ BUILD_BUG_ON(sizeof(wowlan_status->bigtk[status_idx].key) <
+ sizeof(bigtk[notif_idx].key));
+ memcpy(wowlan_status->bigtk[status_idx].key,
+ bigtk[notif_idx].key, sizeof(bigtk[notif_idx].key));
+ iwl_mld_convert_mcast_ipn(&wowlan_status->bigtk[status_idx],
+ &bigtk[notif_idx]);
+ status_idx++;
+ }
+}
+
+static bool
+iwl_mld_handle_wowlan_info_notif(struct iwl_mld *mld,
+ struct iwl_mld_wowlan_status *wowlan_status,
+ struct iwl_rx_packet *pkt)
+{
+ const struct iwl_wowlan_info_notif *notif = (void *)pkt->data;
+ u32 expected_len, len = iwl_rx_packet_payload_len(pkt);
+
+ expected_len = sizeof(*notif);
+
+ if (IWL_FW_CHECK(mld, len < expected_len,
+ "Invalid wowlan_info_notif (expected=%ud got=%ud)\n",
+ expected_len, len))
+ return true;
+
+ if (IWL_FW_CHECK(mld, notif->tid_offloaded_tx != IWL_WOWLAN_OFFLOAD_TID,
+ "Invalid tid_offloaded_tx %d\n",
+ wowlan_status->tid_offloaded_tx))
+ return true;
+
+ iwl_mld_convert_gtk_resume_data(mld, wowlan_status, notif->gtk,
+ &notif->gtk[0].sc);
+ iwl_mld_convert_ptk_resume_seq(mld, wowlan_status, &notif->gtk[0].sc);
+ /* only one igtk is passed by FW */
+ iwl_mld_convert_igtk_resume_data(wowlan_status, &notif->igtk[0]);
+ iwl_mld_convert_bigtk_resume_data(wowlan_status, notif->bigtk);
+
+ wowlan_status->replay_ctr = le64_to_cpu(notif->replay_ctr);
+ wowlan_status->pattern_number = le16_to_cpu(notif->pattern_number);
+
+ wowlan_status->tid_offloaded_tx = notif->tid_offloaded_tx;
+ wowlan_status->last_qos_seq = le16_to_cpu(notif->qos_seq_ctr);
+ wowlan_status->num_of_gtk_rekeys =
+ le32_to_cpu(notif->num_of_gtk_rekeys);
+ wowlan_status->wakeup_reasons = le32_to_cpu(notif->wakeup_reasons);
+ return false;
+ /* TODO: mlo_links (task=MLO)*/
+}
+
+static bool
+iwl_mld_handle_wake_pkt_notif(struct iwl_mld *mld,
+ struct iwl_mld_wowlan_status *wowlan_status,
+ struct iwl_rx_packet *pkt)
+{
+ const struct iwl_wowlan_wake_pkt_notif *notif = (void *)pkt->data;
+ u32 actual_size, len = iwl_rx_packet_payload_len(pkt);
+ u32 expected_size = le32_to_cpu(notif->wake_packet_length);
+
+ if (IWL_FW_CHECK(mld, len < sizeof(*notif),
+ "Invalid WoWLAN wake packet notification (expected size=%zu got=%u)\n",
+ sizeof(*notif), len))
+ return true;
+
+ if (IWL_FW_CHECK(mld, !(wowlan_status->wakeup_reasons &
+ IWL_WOWLAN_WAKEUP_REASON_HAS_WAKEUP_PKT),
+ "Got wake packet but wakeup reason is %x\n",
+ wowlan_status->wakeup_reasons))
+ return true;
+
+ actual_size = len - offsetof(struct iwl_wowlan_wake_pkt_notif,
+ wake_packet);
+
+ /* actual_size got the padding from the notification, remove it. */
+ if (expected_size < actual_size)
+ actual_size = expected_size;
+ wowlan_status->wake_packet = kmemdup(notif->wake_packet, actual_size,
+ GFP_ATOMIC);
+ if (!wowlan_status->wake_packet)
+ return true;
+
+ wowlan_status->wake_packet_length = expected_size;
+ wowlan_status->wake_packet_bufsize = actual_size;
+
+ return false;
+}
+
+static void
+iwl_mld_set_wake_packet(struct iwl_mld *mld,
+ struct ieee80211_vif *vif,
+ const struct iwl_mld_wowlan_status *wowlan_status,
+ struct cfg80211_wowlan_wakeup *wakeup,
+ struct sk_buff **_pkt)
+{
+ int pkt_bufsize = wowlan_status->wake_packet_bufsize;
+ int expected_pktlen = wowlan_status->wake_packet_length;
+ const u8 *pktdata = wowlan_status->wake_packet;
+ const struct ieee80211_hdr *hdr = (const void *)pktdata;
+ int truncated = expected_pktlen - pkt_bufsize;
+
+ if (ieee80211_is_data(hdr->frame_control)) {
+ int hdrlen = ieee80211_hdrlen(hdr->frame_control);
+ int ivlen = 0, icvlen = 4; /* also FCS */
+
+ struct sk_buff *pkt = alloc_skb(pkt_bufsize, GFP_KERNEL);
+ *_pkt = pkt;
+ if (!pkt)
+ return;
+
+ skb_put_data(pkt, pktdata, hdrlen);
+ pktdata += hdrlen;
+ pkt_bufsize -= hdrlen;
+
+ /* if truncated, FCS/ICV is (partially) gone */
+ if (truncated >= icvlen) {
+ truncated -= icvlen;
+ icvlen = 0;
+ } else {
+ icvlen -= truncated;
+ truncated = 0;
+ }
+
+ pkt_bufsize -= ivlen + icvlen;
+ pktdata += ivlen;
+
+ skb_put_data(pkt, pktdata, pkt_bufsize);
+
+ if (ieee80211_data_to_8023(pkt, vif->addr, vif->type))
+ return;
+ wakeup->packet = pkt->data;
+ wakeup->packet_present_len = pkt->len;
+ wakeup->packet_len = pkt->len - truncated;
+ wakeup->packet_80211 = false;
+ } else {
+ int fcslen = 4;
+
+ if (truncated >= 4) {
+ truncated -= 4;
+ fcslen = 0;
+ } else {
+ fcslen -= truncated;
+ truncated = 0;
+ }
+ pkt_bufsize -= fcslen;
+ wakeup->packet = wowlan_status->wake_packet;
+ wakeup->packet_present_len = pkt_bufsize;
+ wakeup->packet_len = expected_pktlen - truncated;
+ wakeup->packet_80211 = true;
+ }
+}
+
+static void
+iwl_mld_report_wowlan_wakeup(struct iwl_mld *mld,
+ struct ieee80211_vif *vif,
+ struct iwl_mld_wowlan_status *wowlan_status)
+{
+ struct sk_buff *pkt = NULL;
+ struct cfg80211_wowlan_wakeup wakeup = {
+ .pattern_idx = -1,
+ };
+ u32 reasons = wowlan_status->wakeup_reasons;
+
+ if (reasons == IWL_WOWLAN_WAKEUP_BY_NON_WIRELESS) {
+ ieee80211_report_wowlan_wakeup(vif, NULL, GFP_KERNEL);
+ return;
+ }
+
+ pm_wakeup_event(mld->dev, 0);
+
+ if (reasons & IWL_WOWLAN_WAKEUP_BY_MAGIC_PACKET)
+ wakeup.magic_pkt = true;
+
+ if (reasons & IWL_WOWLAN_WAKEUP_BY_PATTERN)
+ wakeup.pattern_idx =
+ wowlan_status->pattern_number;
+
+ if (reasons & (IWL_WOWLAN_WAKEUP_BY_DISCONNECTION_ON_MISSED_BEACON |
+ IWL_WOWLAN_WAKEUP_BY_DISCONNECTION_ON_DEAUTH |
+ IWL_WOWLAN_WAKEUP_BY_GTK_REKEY_FAILURE))
+ wakeup.disconnect = true;
+
+ if (reasons & IWL_WOWLAN_WAKEUP_BY_GTK_REKEY_FAILURE)
+ wakeup.gtk_rekey_failure = true;
+
+ if (reasons & IWL_WOWLAN_WAKEUP_BY_RFKILL_DEASSERTED)
+ wakeup.rfkill_release = true;
+
+ if (reasons & IWL_WOWLAN_WAKEUP_BY_EAPOL_REQUEST)
+ wakeup.eap_identity_req = true;
+
+ if (reasons & IWL_WOWLAN_WAKEUP_BY_FOUR_WAY_HANDSHAKE)
+ wakeup.four_way_handshake = true;
+
+ if (reasons & IWL_WOWLAN_WAKEUP_BY_REM_WAKE_LINK_LOSS)
+ wakeup.tcp_connlost = true;
+
+ if (reasons & IWL_WOWLAN_WAKEUP_BY_REM_WAKE_SIGNATURE_TABLE)
+ wakeup.tcp_nomoretokens = true;
+
+ if (reasons & IWL_WOWLAN_WAKEUP_BY_REM_WAKE_WAKEUP_PACKET)
+ wakeup.tcp_match = true;
+
+ if (reasons & IWL_WAKEUP_BY_11W_UNPROTECTED_DEAUTH_OR_DISASSOC)
+ wakeup.unprot_deauth_disassoc = true;
+
+ if (wowlan_status->wake_packet)
+ iwl_mld_set_wake_packet(mld, vif, wowlan_status, &wakeup, &pkt);
+
+ ieee80211_report_wowlan_wakeup(vif, &wakeup, GFP_KERNEL);
+ kfree_skb(pkt);
+}
+
+static void
+iwl_mld_set_key_rx_seq_tids(struct ieee80211_key_conf *key,
+ struct ieee80211_key_seq *seq)
+{
+ int tid;
+
+ for (tid = 0; tid < IWL_MAX_TID_COUNT; tid++)
+ ieee80211_set_key_rx_seq(key, tid, &seq[tid]);
+}
+
+static void
+iwl_mld_set_key_rx_seq(struct ieee80211_key_conf *key,
+ struct iwl_mld_mcast_key_data *key_data)
+{
+ switch (key->cipher) {
+ case WLAN_CIPHER_SUITE_CCMP:
+ case WLAN_CIPHER_SUITE_GCMP:
+ case WLAN_CIPHER_SUITE_GCMP_256:
+ iwl_mld_set_key_rx_seq_tids(key,
+ key_data->gtk.aes_seq);
+ break;
+ case WLAN_CIPHER_SUITE_TKIP:
+ iwl_mld_set_key_rx_seq_tids(key,
+ key_data->gtk.tkip_seq);
+ break;
+ case WLAN_CIPHER_SUITE_BIP_GMAC_128:
+ case WLAN_CIPHER_SUITE_BIP_GMAC_256:
+ case WLAN_CIPHER_SUITE_BIP_CMAC_256:
+ case WLAN_CIPHER_SUITE_AES_CMAC:
+ /* igtk/bigtk ciphers*/
+ ieee80211_set_key_rx_seq(key, 0,
+ &key_data->igtk_bigtk.cmac_gmac_seq);
+ break;
+ default:
+ WARN_ON(1);
+ }
+}
+
+static void
+iwl_mld_d3_update_mcast_key(struct iwl_mld *mld,
+ struct ieee80211_vif *vif,
+ struct iwl_mld_wowlan_status *wowlan_status,
+ struct ieee80211_key_conf *key,
+ struct iwl_mld_mcast_key_data *key_data)
+{
+ if (key->keyidx != key_data->id &&
+ (key->keyidx < 4 || key->keyidx > 5)) {
+ IWL_ERR(mld,
+ "Unexpected keyId mismatch. Old keyId:%d, New keyId:%d\n",
+ key->keyidx, key_data->id);
+ return;
+ }
+
+ /* All installed keys are sent by the FW, even weren't
+ * rekeyed during D3.
+ * We remove an existing key if it has the same index as
+ * a new key and a rekey has occurred during d3
+ */
+ if (wowlan_status->num_of_gtk_rekeys && key_data->len) {
+ if (key->keyidx == 4 || key->keyidx == 5) {
+ struct iwl_mld_vif *mld_vif =
+ iwl_mld_vif_from_mac80211(vif);
+ struct iwl_mld_link *mld_link;
+ int link_id = vif->active_links ?
+ __ffs(vif->active_links) : 0;
+
+ mld_link = iwl_mld_link_dereference_check(mld_vif,
+ link_id);
+ if (WARN_ON(!mld_link))
+ return;
+
+ if (mld_link->igtk == key)
+ mld_link->igtk = NULL;
+ mld->num_igtks--;
+ }
+
+ ieee80211_remove_key(key);
+ return;
+ }
+
+ iwl_mld_set_key_rx_seq(key, key_data);
+}
+
+static void
+iwl_mld_update_ptk_rx_seq(struct iwl_mld *mld,
+ struct iwl_mld_wowlan_status *wowlan_status,
+ struct ieee80211_sta *sta,
+ struct ieee80211_key_conf *key,
+ bool is_tkip)
+{
+ struct iwl_mld_sta *mld_sta =
+ iwl_mld_sta_from_mac80211(sta);
+ struct iwl_mld_ptk_pn *mld_ptk_pn =
+ wiphy_dereference(mld->wiphy,
+ mld_sta->ptk_pn[key->keyidx]);
+
+ iwl_mld_set_key_rx_seq_tids(key, is_tkip ?
+ wowlan_status->ptk.tkip_seq :
+ wowlan_status->ptk.aes_seq);
+ if (is_tkip)
+ return;
+
+ if (WARN_ON(!mld_ptk_pn))
+ return;
+
+ for (int tid = 0; tid < IWL_MAX_TID_COUNT; tid++) {
+ for (int i = 1; i < mld->trans->info.num_rxqs; i++)
+ memcpy(mld_ptk_pn->q[i].pn[tid],
+ wowlan_status->ptk.aes_seq[tid].ccmp.pn,
+ IEEE80211_CCMP_PN_LEN);
+ }
+}
+
+static void
+iwl_mld_resume_keys_iter(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta,
+ struct ieee80211_key_conf *key,
+ void *_data)
+{
+ struct iwl_mld_resume_key_iter_data *data = _data;
+ struct iwl_mld_wowlan_status *wowlan_status = data->wowlan_status;
+ u8 status_idx;
+
+ /* TODO: check key link id (task=MLO) */
+ if (data->unhandled_cipher)
+ return;
+
+ switch (key->cipher) {
+ case WLAN_CIPHER_SUITE_WEP40:
+ case WLAN_CIPHER_SUITE_WEP104:
+ /* ignore WEP completely, nothing to do */
+ return;
+ case WLAN_CIPHER_SUITE_CCMP:
+ case WLAN_CIPHER_SUITE_GCMP:
+ case WLAN_CIPHER_SUITE_GCMP_256:
+ case WLAN_CIPHER_SUITE_TKIP:
+ if (sta) {
+ iwl_mld_update_ptk_rx_seq(data->mld, wowlan_status,
+ sta, key,
+ key->cipher ==
+ WLAN_CIPHER_SUITE_TKIP);
+ return;
+ }
+
+ if (WARN_ON(data->gtk_cipher &&
+ data->gtk_cipher != key->cipher))
+ return;
+
+ data->gtk_cipher = key->cipher;
+ status_idx = key->keyidx == wowlan_status->gtk[1].id;
+ iwl_mld_d3_update_mcast_key(data->mld, vif, wowlan_status, key,
+ &wowlan_status->gtk[status_idx]);
+ break;
+ case WLAN_CIPHER_SUITE_BIP_GMAC_128:
+ case WLAN_CIPHER_SUITE_BIP_GMAC_256:
+ case WLAN_CIPHER_SUITE_BIP_CMAC_256:
+ case WLAN_CIPHER_SUITE_AES_CMAC:
+ if (key->keyidx == 4 || key->keyidx == 5) {
+ if (WARN_ON(data->igtk_cipher &&
+ data->igtk_cipher != key->cipher))
+ return;
+
+ data->igtk_cipher = key->cipher;
+ iwl_mld_d3_update_mcast_key(data->mld, vif,
+ wowlan_status,
+ key, &wowlan_status->igtk);
+ }
+ if (key->keyidx == 6 || key->keyidx == 7) {
+ if (WARN_ON(data->bigtk_cipher &&
+ data->bigtk_cipher != key->cipher))
+ return;
+
+ data->bigtk_cipher = key->cipher;
+ status_idx = key->keyidx == wowlan_status->bigtk[1].id;
+ iwl_mld_d3_update_mcast_key(data->mld, vif,
+ wowlan_status, key,
+ &wowlan_status->bigtk[status_idx]);
+ }
+ break;
+ default:
+ data->unhandled_cipher = true;
+ return;
+ }
+ data->num_keys++;
+}
+
+static bool
+iwl_mld_add_mcast_rekey(struct ieee80211_vif *vif,
+ struct iwl_mld *mld,
+ struct iwl_mld_mcast_key_data *key_data,
+ struct ieee80211_bss_conf *link_conf,
+ u32 cipher)
+{
+ struct ieee80211_key_conf *key_config;
+ struct {
+ struct ieee80211_key_conf conf;
+ u8 key[WOWLAN_KEY_MAX_SIZE];
+ } conf = {
+ .conf.cipher = cipher,
+ .conf.keyidx = key_data->id,
+ };
+ int link_id = vif->active_links ? __ffs(vif->active_links) : -1;
+
+ BUILD_BUG_ON(WLAN_KEY_LEN_CCMP != WLAN_KEY_LEN_GCMP);
+ BUILD_BUG_ON(sizeof(conf.key) < WLAN_KEY_LEN_CCMP);
+ BUILD_BUG_ON(sizeof(conf.key) < WLAN_KEY_LEN_GCMP_256);
+ BUILD_BUG_ON(sizeof(conf.key) < WLAN_KEY_LEN_TKIP);
+ BUILD_BUG_ON(sizeof(conf.key) < WLAN_KEY_LEN_BIP_GMAC_128);
+ BUILD_BUG_ON(sizeof(conf.key) < WLAN_KEY_LEN_BIP_GMAC_256);
+ BUILD_BUG_ON(sizeof(conf.key) < WLAN_KEY_LEN_AES_CMAC);
+ BUILD_BUG_ON(sizeof(conf.key) < sizeof(key_data->key));
+
+ if (!key_data->len)
+ return true;
+
+ switch (cipher) {
+ case WLAN_CIPHER_SUITE_CCMP:
+ case WLAN_CIPHER_SUITE_GCMP:
+ conf.conf.keylen = WLAN_KEY_LEN_CCMP;
+ break;
+ case WLAN_CIPHER_SUITE_GCMP_256:
+ conf.conf.keylen = WLAN_KEY_LEN_GCMP_256;
+ break;
+ case WLAN_CIPHER_SUITE_TKIP:
+ conf.conf.keylen = WLAN_KEY_LEN_TKIP;
+ break;
+ case WLAN_CIPHER_SUITE_BIP_GMAC_128:
+ conf.conf.keylen = WLAN_KEY_LEN_BIP_GMAC_128;
+ break;
+ case WLAN_CIPHER_SUITE_BIP_GMAC_256:
+ conf.conf.keylen = WLAN_KEY_LEN_BIP_GMAC_256;
+ break;
+ case WLAN_CIPHER_SUITE_AES_CMAC:
+ conf.conf.keylen = WLAN_KEY_LEN_AES_CMAC;
+ break;
+ case WLAN_CIPHER_SUITE_BIP_CMAC_256:
+ conf.conf.keylen = WLAN_KEY_LEN_BIP_CMAC_256;
+ break;
+ default:
+ WARN_ON(1);
+ }
+
+ memcpy(conf.conf.key, key_data->key, conf.conf.keylen);
+ key_config = ieee80211_gtk_rekey_add(vif, &conf.conf, link_id);
+ if (IS_ERR(key_config))
+ return false;
+
+ iwl_mld_set_key_rx_seq(key_config, key_data);
+
+ /* The FW holds only one igtk so we keep track of the valid one */
+ if (key_config->keyidx == 4 || key_config->keyidx == 5) {
+ struct iwl_mld_link *mld_link =
+ iwl_mld_link_from_mac80211(link_conf);
+ mld_link->igtk = key_config;
+ mld->num_igtks++;
+ }
+ return true;
+}
+
+static bool
+iwl_mld_add_all_rekeys(struct ieee80211_vif *vif,
+ struct iwl_mld_wowlan_status *wowlan_status,
+ struct iwl_mld_resume_key_iter_data *key_iter_data,
+ struct ieee80211_bss_conf *link_conf)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(wowlan_status->gtk); i++)
+ if (!iwl_mld_add_mcast_rekey(vif, key_iter_data->mld,
+ &wowlan_status->gtk[i],
+ link_conf,
+ key_iter_data->gtk_cipher))
+ return false;
+
+ if (!iwl_mld_add_mcast_rekey(vif, key_iter_data->mld,
+ &wowlan_status->igtk,
+ link_conf, key_iter_data->igtk_cipher))
+ return false;
+
+ for (i = 0; i < ARRAY_SIZE(wowlan_status->bigtk); i++)
+ if (!iwl_mld_add_mcast_rekey(vif, key_iter_data->mld,
+ &wowlan_status->bigtk[i],
+ link_conf,
+ key_iter_data->bigtk_cipher))
+ return false;
+
+ return true;
+}
+
+static bool
+iwl_mld_update_sec_keys(struct iwl_mld *mld,
+ struct ieee80211_vif *vif,
+ struct iwl_mld_wowlan_status *wowlan_status)
+{
+ int link_id = vif->active_links ? __ffs(vif->active_links) : 0;
+ struct ieee80211_bss_conf *link_conf =
+ link_conf_dereference_protected(vif, link_id);
+ __be64 replay_ctr = cpu_to_be64(wowlan_status->replay_ctr);
+ struct iwl_mld_resume_key_iter_data key_iter_data = {
+ .mld = mld,
+ .wowlan_status = wowlan_status,
+ };
+
+ if (WARN_ON(!link_conf))
+ return false;
+
+ ieee80211_iter_keys(mld->hw, vif, iwl_mld_resume_keys_iter,
+ &key_iter_data);
+
+ if (key_iter_data.unhandled_cipher)
+ return false;
+
+ IWL_DEBUG_WOWLAN(mld,
+ "Number of installed keys: %d, Number of rekeys: %d\n",
+ key_iter_data.num_keys,
+ wowlan_status->num_of_gtk_rekeys);
+
+ if (!key_iter_data.num_keys || !wowlan_status->num_of_gtk_rekeys)
+ return true;
+
+ iwl_mld_add_all_rekeys(vif, wowlan_status, &key_iter_data,
+ link_conf);
+
+ ieee80211_gtk_rekey_notify(vif, link_conf->bssid,
+ (void *)&replay_ctr, GFP_KERNEL);
+ /* TODO: MLO rekey (task=MLO) */
+ return true;
+}
+
+static bool
+iwl_mld_process_wowlan_status(struct iwl_mld *mld,
+ struct ieee80211_vif *vif,
+ struct iwl_mld_wowlan_status *wowlan_status)
+{
+ struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
+ struct ieee80211_sta *ap_sta = mld_vif->ap_sta;
+ struct iwl_mld_txq *mld_txq;
+
+ iwl_mld_report_wowlan_wakeup(mld, vif, wowlan_status);
+
+ if (WARN_ON(!ap_sta))
+ return false;
+
+ mld_txq =
+ iwl_mld_txq_from_mac80211(ap_sta->txq[wowlan_status->tid_offloaded_tx]);
+
+ /* Update the pointers of the Tx queue that may have moved during
+ * suspend if the firmware sent frames.
+ * The firmware stores last-used value, we store next value.
+ */
+ WARN_ON(!mld_txq->status.allocated);
+ iwl_trans_set_q_ptrs(mld->trans, mld_txq->fw_id,
+ (wowlan_status->last_qos_seq +
+ 0x10) >> 4);
+
+ if (!iwl_mld_update_sec_keys(mld, vif, wowlan_status))
+ return false;
+
+ if (wowlan_status->wakeup_reasons &
+ (IWL_WOWLAN_WAKEUP_BY_DISCONNECTION_ON_MISSED_BEACON |
+ IWL_WOWLAN_WAKEUP_BY_DISCONNECTION_ON_DEAUTH |
+ IWL_WOWLAN_WAKEUP_BY_GTK_REKEY_FAILURE))
+ return false;
+
+ return true;
+}
+
+static bool
+iwl_mld_netdetect_match_info_handler(struct iwl_mld *mld,
+ struct iwl_mld_resume_data *resume_data,
+ struct iwl_rx_packet *pkt)
+{
+ struct iwl_mld_netdetect_res *results = resume_data->netdetect_res;
+ const struct iwl_scan_offload_match_info *notif = (void *)pkt->data;
+ u32 len = iwl_rx_packet_payload_len(pkt);
+
+ if (IWL_FW_CHECK(mld, !mld->netdetect,
+ "Got scan match info notif when mld->netdetect==%d\n",
+ mld->netdetect))
+ return true;
+
+ if (IWL_FW_CHECK(mld, len < sizeof(*notif),
+ "Invalid scan offload match notif of length: %d\n",
+ len))
+ return true;
+
+ if (IWL_FW_CHECK(mld, resume_data->wowlan_status->wakeup_reasons !=
+ IWL_WOWLAN_WAKEUP_BY_NON_WIRELESS,
+ "Ignore scan match info: unexpected wakeup reason (expected=0x%x got=0x%x)\n",
+ IWL_WOWLAN_WAKEUP_BY_NON_WIRELESS,
+ resume_data->wowlan_status->wakeup_reasons))
+ return true;
+
+ results->matched_profiles = le32_to_cpu(notif->matched_profiles);
+ IWL_DEBUG_WOWLAN(mld, "number of matched profiles=%u\n",
+ results->matched_profiles);
+
+ if (results->matched_profiles)
+ memcpy(results->matches, notif->matches,
+ NETDETECT_QUERY_BUF_LEN);
+
+ /* No scan should be active at this point */
+ mld->scan.status = 0;
+ memset(mld->scan.uid_status, 0, sizeof(mld->scan.uid_status));
+ return false;
+}
+
+static void
+iwl_mld_set_netdetect_info(struct iwl_mld *mld,
+ const struct cfg80211_sched_scan_request *netdetect_cfg,
+ struct cfg80211_wowlan_nd_info *netdetect_info,
+ struct iwl_mld_netdetect_res *netdetect_res,
+ unsigned long matched_profiles)
+{
+ int i;
+
+ for_each_set_bit(i, &matched_profiles, netdetect_cfg->n_match_sets) {
+ struct cfg80211_wowlan_nd_match *match;
+ int idx, j, n_channels = 0;
+ struct iwl_scan_offload_profile_match *matches =
+ (void *)netdetect_res->matches;
+
+ for (int k = 0; k < SCAN_OFFLOAD_MATCHING_CHANNELS_LEN; k++)
+ n_channels +=
+ hweight8(matches[i].matching_channels[k]);
+ match = kzalloc(struct_size(match, channels, n_channels),
+ GFP_KERNEL);
+ if (!match)
+ return;
+
+ netdetect_info->matches[netdetect_info->n_matches] = match;
+ netdetect_info->n_matches++;
+
+ /* We inverted the order of the SSIDs in the scan
+ * request, so invert the index here.
+ */
+ idx = netdetect_cfg->n_match_sets - i - 1;
+ match->ssid.ssid_len =
+ netdetect_cfg->match_sets[idx].ssid.ssid_len;
+ memcpy(match->ssid.ssid,
+ netdetect_cfg->match_sets[idx].ssid.ssid,
+ match->ssid.ssid_len);
+
+ if (netdetect_cfg->n_channels < n_channels)
+ continue;
+
+ for_each_set_bit(j,
+ (unsigned long *)&matches[i].matching_channels[0],
+ sizeof(matches[i].matching_channels)) {
+ match->channels[match->n_channels] =
+ netdetect_cfg->channels[j]->center_freq;
+ match->n_channels++;
+ }
+ }
+}
+
+static void
+iwl_mld_process_netdetect_res(struct iwl_mld *mld,
+ struct ieee80211_vif *vif,
+ struct iwl_mld_resume_data *resume_data)
+{
+ struct cfg80211_wowlan_nd_info *netdetect_info = NULL;
+ const struct cfg80211_sched_scan_request *netdetect_cfg;
+ struct cfg80211_wowlan_wakeup wakeup = {
+ .pattern_idx = -1,
+ };
+ struct cfg80211_wowlan_wakeup *wakeup_report = &wakeup;
+ unsigned long matched_profiles;
+ u32 wakeup_reasons;
+ int n_matches;
+
+ lockdep_assert_wiphy(mld->wiphy);
+
+ if (WARN_ON(!mld->wiphy->wowlan_config ||
+ !mld->wiphy->wowlan_config->nd_config)) {
+ IWL_DEBUG_WOWLAN(mld,
+ "Netdetect isn't configured on resume flow\n");
+ goto out;
+ }
+
+ netdetect_cfg = mld->wiphy->wowlan_config->nd_config;
+ wakeup_reasons = resume_data->wowlan_status->wakeup_reasons;
+
+ if (wakeup_reasons & IWL_WOWLAN_WAKEUP_BY_RFKILL_DEASSERTED)
+ wakeup.rfkill_release = true;
+
+ if (wakeup_reasons != IWL_WOWLAN_WAKEUP_BY_NON_WIRELESS)
+ goto out;
+
+ if (!resume_data->netdetect_res->matched_profiles) {
+ IWL_DEBUG_WOWLAN(mld,
+ "Netdetect results aren't valid\n");
+ wakeup_report = NULL;
+ goto out;
+ }
+
+ matched_profiles = resume_data->netdetect_res->matched_profiles;
+ if (!netdetect_cfg->n_match_sets) {
+ IWL_DEBUG_WOWLAN(mld,
+ "No netdetect match sets are configured\n");
+ goto out;
+ }
+ n_matches = hweight_long(matched_profiles);
+ netdetect_info = kzalloc(struct_size(netdetect_info, matches,
+ n_matches), GFP_KERNEL);
+ if (netdetect_info)
+ iwl_mld_set_netdetect_info(mld, netdetect_cfg, netdetect_info,
+ resume_data->netdetect_res,
+ matched_profiles);
+
+ wakeup.net_detect = netdetect_info;
+ out:
+ ieee80211_report_wowlan_wakeup(vif, wakeup_report, GFP_KERNEL);
+ if (netdetect_info) {
+ for (int i = 0; i < netdetect_info->n_matches; i++)
+ kfree(netdetect_info->matches[i]);
+ kfree(netdetect_info);
+ }
+}
+
+static bool iwl_mld_handle_d3_notif(struct iwl_notif_wait_data *notif_wait,
+ struct iwl_rx_packet *pkt, void *data)
+{
+ struct iwl_mld_resume_data *resume_data = data;
+ struct iwl_mld *mld =
+ container_of(notif_wait, struct iwl_mld, notif_wait);
+
+ switch (WIDE_ID(pkt->hdr.group_id, pkt->hdr.cmd)) {
+ case WIDE_ID(PROT_OFFLOAD_GROUP, WOWLAN_INFO_NOTIFICATION): {
+ if (resume_data->notifs_received & IWL_D3_NOTIF_WOWLAN_INFO) {
+ IWL_DEBUG_WOWLAN(mld,
+ "got additional wowlan_info notif\n");
+ break;
+ }
+ resume_data->notif_handling_err =
+ iwl_mld_handle_wowlan_info_notif(mld,
+ resume_data->wowlan_status,
+ pkt);
+ resume_data->notifs_received |= IWL_D3_NOTIF_WOWLAN_INFO;
+
+ if (resume_data->wowlan_status->wakeup_reasons &
+ IWL_WOWLAN_WAKEUP_REASON_HAS_WAKEUP_PKT)
+ resume_data->notifs_expected |=
+ IWL_D3_NOTIF_WOWLAN_WAKE_PKT;
+ break;
+ }
+ case WIDE_ID(PROT_OFFLOAD_GROUP, WOWLAN_WAKE_PKT_NOTIFICATION): {
+ if (resume_data->notifs_received &
+ IWL_D3_NOTIF_WOWLAN_WAKE_PKT) {
+ /* We shouldn't get two wake packet notifications */
+ IWL_DEBUG_WOWLAN(mld,
+ "Got additional wowlan wake packet notification\n");
+ break;
+ }
+ resume_data->notif_handling_err =
+ iwl_mld_handle_wake_pkt_notif(mld,
+ resume_data->wowlan_status,
+ pkt);
+ resume_data->notifs_received |= IWL_D3_NOTIF_WOWLAN_WAKE_PKT;
+ break;
+ }
+ case WIDE_ID(SCAN_GROUP, OFFLOAD_MATCH_INFO_NOTIF): {
+ if (resume_data->notifs_received & IWL_D3_ND_MATCH_INFO) {
+ IWL_ERR(mld,
+ "Got additional netdetect match info\n");
+ break;
+ }
+
+ resume_data->notif_handling_err =
+ iwl_mld_netdetect_match_info_handler(mld, resume_data,
+ pkt);
+ resume_data->notifs_received |= IWL_D3_ND_MATCH_INFO;
+ break;
+ }
+ case WIDE_ID(PROT_OFFLOAD_GROUP, D3_END_NOTIFICATION): {
+ struct iwl_d3_end_notif *notif = (void *)pkt->data;
+
+ resume_data->d3_end_flags = le32_to_cpu(notif->flags);
+ resume_data->notifs_received |= IWL_D3_NOTIF_D3_END_NOTIF;
+ break;
+ }
+ default:
+ WARN_ON(1);
+ }
+
+ return resume_data->notifs_received == resume_data->notifs_expected;
+}
+
+#define IWL_MLD_D3_NOTIF_TIMEOUT (HZ / 3)
+
+static int iwl_mld_wait_d3_notif(struct iwl_mld *mld,
+ struct iwl_mld_resume_data *resume_data,
+ bool with_wowlan)
+{
+ static const u16 wowlan_resume_notif[] = {
+ WIDE_ID(PROT_OFFLOAD_GROUP, WOWLAN_INFO_NOTIFICATION),
+ WIDE_ID(PROT_OFFLOAD_GROUP, WOWLAN_WAKE_PKT_NOTIFICATION),
+ WIDE_ID(SCAN_GROUP, OFFLOAD_MATCH_INFO_NOTIF),
+ WIDE_ID(PROT_OFFLOAD_GROUP, D3_END_NOTIFICATION)
+ };
+ static const u16 d3_resume_notif[] = {
+ WIDE_ID(PROT_OFFLOAD_GROUP, D3_END_NOTIFICATION)
+ };
+ struct iwl_notification_wait wait_d3_notif;
+ enum iwl_d3_status d3_status;
+ int ret;
+
+ if (with_wowlan)
+ iwl_init_notification_wait(&mld->notif_wait, &wait_d3_notif,
+ wowlan_resume_notif,
+ ARRAY_SIZE(wowlan_resume_notif),
+ iwl_mld_handle_d3_notif,
+ resume_data);
+ else
+ iwl_init_notification_wait(&mld->notif_wait, &wait_d3_notif,
+ d3_resume_notif,
+ ARRAY_SIZE(d3_resume_notif),
+ iwl_mld_handle_d3_notif,
+ resume_data);
+
+ ret = iwl_trans_d3_resume(mld->trans, &d3_status, false, false);
+ if (ret || d3_status != IWL_D3_STATUS_ALIVE) {
+ if (d3_status != IWL_D3_STATUS_ALIVE) {
+ IWL_INFO(mld, "Device was reset during suspend\n");
+ ret = -ENOENT;
+ } else {
+ IWL_ERR(mld, "Transport resume failed\n");
+ }
+ iwl_remove_notification(&mld->notif_wait, &wait_d3_notif);
+ return ret;
+ }
+
+ ret = iwl_wait_notification(&mld->notif_wait, &wait_d3_notif,
+ IWL_MLD_D3_NOTIF_TIMEOUT);
+ if (ret)
+ IWL_ERR(mld, "Couldn't get the d3 notif %d\n", ret);
+
+ if (resume_data->notif_handling_err)
+ ret = -EIO;
+
+ return ret;
+}
+
+int iwl_mld_no_wowlan_suspend(struct iwl_mld *mld)
+{
+ struct iwl_d3_manager_config d3_cfg_cmd_data = {};
+ int ret;
+
+ lockdep_assert_wiphy(mld->wiphy);
+
+ IWL_DEBUG_WOWLAN(mld, "Starting the no wowlan suspend flow\n");
+
+ iwl_mld_low_latency_stop(mld);
+
+ /* This will happen if iwl_mld_supsend failed with FW error */
+ if (mld->trans->state == IWL_TRANS_NO_FW &&
+ test_bit(STATUS_FW_ERROR, &mld->trans->status))
+ return -ENODEV;
+
+ ret = iwl_mld_update_device_power(mld, true);
+ if (ret) {
+ IWL_ERR(mld,
+ "d3 suspend: couldn't send power_device %d\n", ret);
+ goto out;
+ }
+
+ ret = iwl_mld_send_cmd_pdu(mld, D3_CONFIG_CMD,
+ &d3_cfg_cmd_data);
+ if (ret) {
+ IWL_ERR(mld,
+ "d3 suspend: couldn't send D3_CONFIG_CMD %d\n", ret);
+ goto out;
+ }
+
+ ret = iwl_trans_d3_suspend(mld->trans, false, false);
+ if (ret) {
+ IWL_ERR(mld, "d3 suspend: trans_d3_suspend failed %d\n", ret);
+ } else {
+ /* Async notification might send hcmds, which is not allowed in suspend */
+ iwl_mld_cancel_async_notifications(mld);
+ mld->fw_status.in_d3 = true;
+ }
+
+ out:
+ if (ret) {
+ mld->trans->state = IWL_TRANS_NO_FW;
+ set_bit(STATUS_FW_ERROR, &mld->trans->status);
+ }
+
+ return ret;
+}
+
+int iwl_mld_no_wowlan_resume(struct iwl_mld *mld)
+{
+ struct iwl_mld_resume_data resume_data = {
+ .notifs_expected =
+ IWL_D3_NOTIF_D3_END_NOTIF,
+ };
+ int ret;
+
+ lockdep_assert_wiphy(mld->wiphy);
+
+ IWL_DEBUG_WOWLAN(mld, "Starting the no wowlan resume flow\n");
+
+ mld->fw_status.in_d3 = false;
+ iwl_fw_dbg_read_d3_debug_data(&mld->fwrt);
+
+ if (iwl_mld_fw_needs_restart(mld, NULL))
+ ret = -ENODEV;
+ else
+ ret = iwl_mld_wait_d3_notif(mld, &resume_data, false);
+
+ if (!ret && (resume_data.d3_end_flags & IWL_D0I3_RESET_REQUIRE))
+ return -ENODEV;
+
+ if (ret) {
+ mld->trans->state = IWL_TRANS_NO_FW;
+ set_bit(STATUS_FW_ERROR, &mld->trans->status);
+ return ret;
+ }
+ iwl_mld_low_latency_restart(mld);
+
+ return iwl_mld_update_device_power(mld, false);
+}
+
+static void
+iwl_mld_aes_seq_to_le64_pn(struct ieee80211_key_conf *key,
+ __le64 *key_rsc)
+{
+ for (int i = 0; i < IWL_MAX_TID_COUNT; i++) {
+ struct ieee80211_key_seq seq;
+ u8 *pn = key->cipher == WLAN_CIPHER_SUITE_CCMP ? seq.ccmp.pn :
+ seq.gcmp.pn;
+
+ ieee80211_get_key_rx_seq(key, i, &seq);
+ key_rsc[i] = cpu_to_le64((u64)pn[5] |
+ ((u64)pn[4] << 8) |
+ ((u64)pn[3] << 16) |
+ ((u64)pn[2] << 24) |
+ ((u64)pn[1] << 32) |
+ ((u64)pn[0] << 40));
+ }
+}
+
+static void
+iwl_mld_suspend_set_ucast_pn(struct iwl_mld *mld, struct ieee80211_sta *sta,
+ struct ieee80211_key_conf *key, __le64 *key_rsc)
+{
+ struct iwl_mld_sta *mld_sta =
+ iwl_mld_sta_from_mac80211(sta);
+ struct iwl_mld_ptk_pn *mld_ptk_pn;
+
+ if (WARN_ON(key->keyidx >= ARRAY_SIZE(mld_sta->ptk_pn)))
+ return;
+
+ mld_ptk_pn = wiphy_dereference(mld->wiphy,
+ mld_sta->ptk_pn[key->keyidx]);
+ if (WARN_ON(!mld_ptk_pn))
+ return;
+
+ for (int tid = 0; tid < IWL_MAX_TID_COUNT; tid++) {
+ struct ieee80211_key_seq seq;
+ u8 *max_pn = seq.ccmp.pn;
+
+ /* get the PN from mac80211, used on the default queue */
+ ieee80211_get_key_rx_seq(key, tid, &seq);
+
+ /* and use the internal data for all queues */
+ for (int que = 1; que < mld->trans->info.num_rxqs; que++) {
+ u8 *cur_pn = mld_ptk_pn->q[que].pn[tid];
+
+ if (memcmp(max_pn, cur_pn, IEEE80211_CCMP_PN_LEN) < 0)
+ max_pn = cur_pn;
+ }
+ key_rsc[tid] = cpu_to_le64((u64)max_pn[5] |
+ ((u64)max_pn[4] << 8) |
+ ((u64)max_pn[3] << 16) |
+ ((u64)max_pn[2] << 24) |
+ ((u64)max_pn[1] << 32) |
+ ((u64)max_pn[0] << 40));
+ }
+}
+
+static void
+iwl_mld_suspend_convert_tkip_ipn(struct ieee80211_key_conf *key,
+ __le64 *rsc)
+{
+ struct ieee80211_key_seq seq;
+
+ for (int i = 0; i < IWL_MAX_TID_COUNT; i++) {
+ ieee80211_get_key_rx_seq(key, i, &seq);
+ rsc[i] =
+ cpu_to_le64(((u64)seq.tkip.iv32 << 16) |
+ seq.tkip.iv16);
+ }
+}
+
+static void
+iwl_mld_suspend_key_data_iter(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta,
+ struct ieee80211_key_conf *key,
+ void *_data)
+{
+ struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw);
+ struct iwl_mld_suspend_key_iter_data *data = _data;
+ __le64 *key_rsc;
+ __le32 cipher = 0;
+
+ switch (key->cipher) {
+ case WLAN_CIPHER_SUITE_CCMP:
+ cipher = cpu_to_le32(STA_KEY_FLG_CCM);
+ fallthrough;
+ case WLAN_CIPHER_SUITE_GCMP:
+ case WLAN_CIPHER_SUITE_GCMP_256:
+ if (!cipher)
+ cipher = cpu_to_le32(STA_KEY_FLG_GCMP);
+ fallthrough;
+ case WLAN_CIPHER_SUITE_TKIP:
+ if (!cipher)
+ cipher = cpu_to_le32(STA_KEY_FLG_TKIP);
+ if (sta) {
+ key_rsc = data->rsc->ucast_rsc;
+ if (key->cipher == WLAN_CIPHER_SUITE_TKIP)
+ iwl_mld_suspend_convert_tkip_ipn(key, key_rsc);
+ else
+ iwl_mld_suspend_set_ucast_pn(mld, sta, key,
+ key_rsc);
+
+ data->have_rsc = true;
+ return;
+ }
+ /* We're iterating from old to new, there're 4 possible
+ * gtk ids, and only the last two keys matter
+ */
+ if (WARN_ON(data->gtks >=
+ ARRAY_SIZE(data->found_gtk_idx)))
+ return;
+
+ if (WARN_ON(key->keyidx >=
+ ARRAY_SIZE(data->rsc->mcast_key_id_map)))
+ return;
+ data->gtk_cipher = cipher;
+ data->found_gtk_idx[data->gtks] = key->keyidx;
+ key_rsc = data->rsc->mcast_rsc[data->gtks % 2];
+ data->rsc->mcast_key_id_map[key->keyidx] =
+ data->gtks % 2;
+
+ if (data->gtks >= 2) {
+ int prev = data->gtks % 2;
+ int prev_idx = data->found_gtk_idx[prev];
+
+ data->rsc->mcast_key_id_map[prev_idx] =
+ IWL_MCAST_KEY_MAP_INVALID;
+ }
+
+ if (key->cipher == WLAN_CIPHER_SUITE_TKIP)
+ iwl_mld_suspend_convert_tkip_ipn(key, key_rsc);
+ else
+ iwl_mld_aes_seq_to_le64_pn(key, key_rsc);
+
+ data->gtks++;
+ data->have_rsc = true;
+ break;
+ case WLAN_CIPHER_SUITE_BIP_GMAC_128:
+ case WLAN_CIPHER_SUITE_BIP_GMAC_256:
+ cipher = cpu_to_le32(STA_KEY_FLG_GCMP);
+ fallthrough;
+ case WLAN_CIPHER_SUITE_BIP_CMAC_256:
+ case WLAN_CIPHER_SUITE_AES_CMAC:
+ if (!cipher)
+ cipher = cpu_to_le32(STA_KEY_FLG_CCM);
+ if (key->keyidx == 4 || key->keyidx == 5)
+ data->igtk_cipher = cipher;
+
+ if (key->keyidx == 6 || key->keyidx == 7)
+ data->bigtk_cipher = cipher;
+
+ break;
+ }
+}
+
+static int
+iwl_mld_send_kek_kck_cmd(struct iwl_mld *mld,
+ struct iwl_mld_vif *mld_vif,
+ struct iwl_mld_suspend_key_iter_data data,
+ int ap_sta_id)
+{
+ struct iwl_wowlan_kek_kck_material_cmd_v4 kek_kck_cmd = {};
+ struct iwl_mld_rekey_data *rekey_data =
+ &mld_vif->wowlan_data.rekey_data;
+
+ memcpy(kek_kck_cmd.kck, rekey_data->kck,
+ rekey_data->kck_len);
+ kek_kck_cmd.kck_len = cpu_to_le16(rekey_data->kck_len);
+ memcpy(kek_kck_cmd.kek, rekey_data->kek,
+ rekey_data->kek_len);
+ kek_kck_cmd.kek_len = cpu_to_le16(rekey_data->kek_len);
+ kek_kck_cmd.replay_ctr = rekey_data->replay_ctr;
+ kek_kck_cmd.akm = cpu_to_le32(rekey_data->akm);
+ kek_kck_cmd.sta_id = cpu_to_le32(ap_sta_id);
+ kek_kck_cmd.gtk_cipher = data.gtk_cipher;
+ kek_kck_cmd.igtk_cipher = data.igtk_cipher;
+ kek_kck_cmd.bigtk_cipher = data.bigtk_cipher;
+
+ IWL_DEBUG_WOWLAN(mld, "setting akm %d\n",
+ rekey_data->akm);
+
+ return iwl_mld_send_cmd_pdu(mld, WOWLAN_KEK_KCK_MATERIAL,
+ &kek_kck_cmd);
+}
+
+static int
+iwl_mld_suspend_send_security_cmds(struct iwl_mld *mld,
+ struct ieee80211_vif *vif,
+ struct iwl_mld_vif *mld_vif,
+ int ap_sta_id)
+{
+ struct iwl_mld_suspend_key_iter_data data = {};
+ int ret;
+
+ data.rsc = kzalloc(sizeof(*data.rsc), GFP_KERNEL);
+ if (!data.rsc)
+ return -ENOMEM;
+
+ memset(data.rsc->mcast_key_id_map, IWL_MCAST_KEY_MAP_INVALID,
+ ARRAY_SIZE(data.rsc->mcast_key_id_map));
+
+ data.rsc->sta_id = cpu_to_le32(ap_sta_id);
+ ieee80211_iter_keys(mld->hw, vif,
+ iwl_mld_suspend_key_data_iter,
+ &data);
+
+ if (data.have_rsc)
+ ret = iwl_mld_send_cmd_pdu(mld, WOWLAN_TSC_RSC_PARAM,
+ data.rsc);
+ else
+ ret = 0;
+
+ if (!ret && mld_vif->wowlan_data.rekey_data.valid)
+ ret = iwl_mld_send_kek_kck_cmd(mld, mld_vif, data, ap_sta_id);
+
+ kfree(data.rsc);
+
+ return ret;
+}
+
+static void
+iwl_mld_set_wowlan_config_cmd(struct iwl_mld *mld,
+ struct cfg80211_wowlan *wowlan,
+ struct iwl_wowlan_config_cmd *wowlan_config_cmd,
+ struct ieee80211_sta *ap_sta)
+{
+ wowlan_config_cmd->is_11n_connection =
+ ap_sta->deflink.ht_cap.ht_supported;
+ wowlan_config_cmd->flags = ENABLE_L3_FILTERING |
+ ENABLE_NBNS_FILTERING | ENABLE_DHCP_FILTERING;
+
+ if (ap_sta->mfp)
+ wowlan_config_cmd->flags |= IS_11W_ASSOC;
+
+ if (wowlan->disconnect)
+ wowlan_config_cmd->wakeup_filter |=
+ cpu_to_le32(IWL_WOWLAN_WAKEUP_BEACON_MISS |
+ IWL_WOWLAN_WAKEUP_LINK_CHANGE);
+ if (wowlan->magic_pkt)
+ wowlan_config_cmd->wakeup_filter |=
+ cpu_to_le32(IWL_WOWLAN_WAKEUP_MAGIC_PACKET);
+ if (wowlan->gtk_rekey_failure)
+ wowlan_config_cmd->wakeup_filter |=
+ cpu_to_le32(IWL_WOWLAN_WAKEUP_GTK_REKEY_FAIL);
+ if (wowlan->eap_identity_req)
+ wowlan_config_cmd->wakeup_filter |=
+ cpu_to_le32(IWL_WOWLAN_WAKEUP_EAP_IDENT_REQ);
+ if (wowlan->four_way_handshake)
+ wowlan_config_cmd->wakeup_filter |=
+ cpu_to_le32(IWL_WOWLAN_WAKEUP_4WAY_HANDSHAKE);
+ if (wowlan->n_patterns)
+ wowlan_config_cmd->wakeup_filter |=
+ cpu_to_le32(IWL_WOWLAN_WAKEUP_PATTERN_MATCH);
+
+ if (wowlan->rfkill_release)
+ wowlan_config_cmd->wakeup_filter |=
+ cpu_to_le32(IWL_WOWLAN_WAKEUP_RF_KILL_DEASSERT);
+
+ if (wowlan->any) {
+ wowlan_config_cmd->wakeup_filter |=
+ cpu_to_le32(IWL_WOWLAN_WAKEUP_BEACON_MISS |
+ IWL_WOWLAN_WAKEUP_LINK_CHANGE |
+ IWL_WOWLAN_WAKEUP_RX_FRAME |
+ IWL_WOWLAN_WAKEUP_BCN_FILTERING);
+ }
+}
+
+static int iwl_mld_send_patterns(struct iwl_mld *mld,
+ struct cfg80211_wowlan *wowlan,
+ int ap_sta_id)
+{
+ struct iwl_wowlan_patterns_cmd *pattern_cmd;
+ struct iwl_host_cmd cmd = {
+ .id = WOWLAN_PATTERNS,
+ .dataflags[0] = IWL_HCMD_DFL_NOCOPY,
+ };
+ int ret;
+
+ if (!wowlan->n_patterns)
+ return 0;
+
+ cmd.len[0] = struct_size(pattern_cmd, patterns, wowlan->n_patterns);
+
+ pattern_cmd = kzalloc(cmd.len[0], GFP_KERNEL);
+ if (!pattern_cmd)
+ return -ENOMEM;
+
+ pattern_cmd->n_patterns = wowlan->n_patterns;
+ pattern_cmd->sta_id = ap_sta_id;
+
+ for (int i = 0; i < wowlan->n_patterns; i++) {
+ int mask_len = DIV_ROUND_UP(wowlan->patterns[i].pattern_len, 8);
+
+ pattern_cmd->patterns[i].pattern_type =
+ WOWLAN_PATTERN_TYPE_BITMASK;
+
+ memcpy(&pattern_cmd->patterns[i].u.bitmask.mask,
+ wowlan->patterns[i].mask, mask_len);
+ memcpy(&pattern_cmd->patterns[i].u.bitmask.pattern,
+ wowlan->patterns[i].pattern,
+ wowlan->patterns[i].pattern_len);
+ pattern_cmd->patterns[i].u.bitmask.mask_size = mask_len;
+ pattern_cmd->patterns[i].u.bitmask.pattern_size =
+ wowlan->patterns[i].pattern_len;
+ }
+
+ cmd.data[0] = pattern_cmd;
+ ret = iwl_mld_send_cmd(mld, &cmd);
+ kfree(pattern_cmd);
+ return ret;
+}
+
+static int
+iwl_mld_send_proto_offload(struct iwl_mld *mld,
+ struct ieee80211_vif *vif,
+ u8 ap_sta_id)
+{
+ struct iwl_proto_offload_cmd_v4 *cmd __free(kfree);
+ struct iwl_host_cmd hcmd = {
+ .id = PROT_OFFLOAD_CONFIG_CMD,
+ .dataflags[0] = IWL_HCMD_DFL_NOCOPY,
+ .len[0] = sizeof(*cmd),
+ };
+ u32 enabled = 0;
+
+ cmd = kzalloc(hcmd.len[0], GFP_KERNEL);
+
+#if IS_ENABLED(CONFIG_IPV6)
+ struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
+ struct iwl_mld_wowlan_data *wowlan_data = &mld_vif->wowlan_data;
+ struct iwl_ns_config *nsc;
+ struct iwl_targ_addr *addrs;
+ int n_nsc, n_addrs;
+ int i, c;
+ int num_skipped = 0;
+
+ nsc = cmd->ns_config;
+ n_nsc = IWL_PROTO_OFFLOAD_NUM_NS_CONFIG_V3L;
+ addrs = cmd->targ_addrs;
+ n_addrs = IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V3L;
+
+ /* For each address we have (and that will fit) fill a target
+ * address struct and combine for NS offload structs with the
+ * solicited node addresses.
+ */
+ for (i = 0, c = 0;
+ i < wowlan_data->num_target_ipv6_addrs &&
+ i < n_addrs && c < n_nsc; i++) {
+ int j;
+ struct in6_addr solicited_addr;
+
+ /* Because ns is offloaded skip tentative address to avoid
+ * violating RFC4862.
+ */
+ if (test_bit(i, wowlan_data->tentative_addrs)) {
+ num_skipped++;
+ continue;
+ }
+
+ addrconf_addr_solict_mult(&wowlan_data->target_ipv6_addrs[i],
+ &solicited_addr);
+ for (j = 0; j < c; j++)
+ if (ipv6_addr_cmp(&nsc[j].dest_ipv6_addr,
+ &solicited_addr) == 0)
+ break;
+ if (j == c)
+ c++;
+ addrs[i].addr = wowlan_data->target_ipv6_addrs[i];
+ addrs[i].config_num = cpu_to_le32(j);
+ nsc[j].dest_ipv6_addr = solicited_addr;
+ memcpy(nsc[j].target_mac_addr, vif->addr, ETH_ALEN);
+ }
+
+ if (wowlan_data->num_target_ipv6_addrs - num_skipped)
+ enabled |= IWL_D3_PROTO_IPV6_VALID;
+
+ cmd->num_valid_ipv6_addrs = cpu_to_le32(i - num_skipped);
+ if (enabled & IWL_D3_PROTO_IPV6_VALID)
+ enabled |= IWL_D3_PROTO_OFFLOAD_NS;
+#endif
+
+ if (vif->cfg.arp_addr_cnt) {
+ enabled |= IWL_D3_PROTO_OFFLOAD_ARP | IWL_D3_PROTO_IPV4_VALID;
+ cmd->common.host_ipv4_addr = vif->cfg.arp_addr_list[0];
+ ether_addr_copy(cmd->common.arp_mac_addr, vif->addr);
+ }
+
+ enabled |= IWL_D3_PROTO_OFFLOAD_BTM;
+ cmd->common.enabled = cpu_to_le32(enabled);
+ cmd->sta_id = cpu_to_le32(ap_sta_id);
+ hcmd.data[0] = cmd;
+ return iwl_mld_send_cmd(mld, &hcmd);
+}
+
+static int
+iwl_mld_wowlan_config(struct iwl_mld *mld, struct ieee80211_vif *bss_vif,
+ struct cfg80211_wowlan *wowlan)
+{
+ struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(bss_vif);
+ struct ieee80211_sta *ap_sta = mld_vif->ap_sta;
+ struct iwl_wowlan_config_cmd wowlan_config_cmd = {
+ .offloading_tid = IWL_WOWLAN_OFFLOAD_TID,
+ };
+ u32 sta_id_mask;
+ int ap_sta_id, ret;
+ int link_id = iwl_mld_get_primary_link(bss_vif);
+ struct ieee80211_bss_conf *link_conf;
+
+ ret = iwl_mld_block_emlsr_sync(mld, bss_vif,
+ IWL_MLD_EMLSR_BLOCKED_WOWLAN, link_id);
+ if (ret)
+ return ret;
+
+ link_conf = link_conf_dereference_protected(bss_vif, link_id);
+
+ if (WARN_ON(!ap_sta || !link_conf))
+ return -EINVAL;
+
+ sta_id_mask = iwl_mld_fw_sta_id_mask(mld, ap_sta);
+ if (WARN_ON(hweight32(sta_id_mask) != 1))
+ return -EINVAL;
+
+ ap_sta_id = __ffs(sta_id_mask);
+ wowlan_config_cmd.sta_id = ap_sta_id;
+
+ ret = iwl_mld_ensure_queue(mld,
+ ap_sta->txq[wowlan_config_cmd.offloading_tid]);
+ if (ret)
+ return ret;
+
+ iwl_mld_set_wowlan_config_cmd(mld, wowlan,
+ &wowlan_config_cmd, ap_sta);
+ ret = iwl_mld_send_cmd_pdu(mld, WOWLAN_CONFIGURATION,
+ &wowlan_config_cmd);
+ if (ret)
+ return ret;
+
+ ret = iwl_mld_suspend_send_security_cmds(mld, bss_vif, mld_vif,
+ ap_sta_id);
+ if (ret)
+ return ret;
+
+ ret = iwl_mld_send_patterns(mld, wowlan, ap_sta_id);
+ if (ret)
+ return ret;
+
+ ret = iwl_mld_send_proto_offload(mld, bss_vif, ap_sta_id);
+ if (ret)
+ return ret;
+
+ iwl_mld_enable_beacon_filter(mld, link_conf, true);
+ return iwl_mld_update_mac_power(mld, bss_vif, true);
+}
+
+int iwl_mld_wowlan_suspend(struct iwl_mld *mld, struct cfg80211_wowlan *wowlan)
+{
+ struct ieee80211_vif *bss_vif;
+
+ lockdep_assert_wiphy(mld->wiphy);
+
+ if (WARN_ON(!wowlan))
+ return 1;
+
+ IWL_DEBUG_WOWLAN(mld, "Starting the wowlan suspend flow\n");
+
+ bss_vif = iwl_mld_get_bss_vif(mld);
+ if (WARN_ON(!bss_vif))
+ return 1;
+
+ if (!bss_vif->cfg.assoc) {
+ int ret;
+ /* If we're not associated, this must be netdetect */
+ if (WARN_ON(!wowlan->nd_config))
+ return 1;
+
+ ret = iwl_mld_netdetect_config(mld, bss_vif, wowlan);
+ if (!ret)
+ mld->netdetect = true;
+
+ return ret;
+ }
+
+ return iwl_mld_wowlan_config(mld, bss_vif, wowlan);
+}
+
+/* Returns 0 on success, 1 if an error occurred in firmware during d3,
+ * A negative value is expected only in unrecovreable cases.
+ */
+int iwl_mld_wowlan_resume(struct iwl_mld *mld)
+{
+ struct ieee80211_vif *bss_vif;
+ struct ieee80211_bss_conf *link_conf;
+ struct iwl_mld_netdetect_res netdetect_res;
+ struct iwl_mld_resume_data resume_data = {
+ .notifs_expected =
+ IWL_D3_NOTIF_WOWLAN_INFO |
+ IWL_D3_NOTIF_D3_END_NOTIF,
+ .netdetect_res = &netdetect_res,
+ };
+ int link_id;
+ int ret;
+ bool fw_err = false;
+
+ lockdep_assert_wiphy(mld->wiphy);
+
+ IWL_DEBUG_WOWLAN(mld, "Starting the wowlan resume flow\n");
+
+ if (!mld->fw_status.in_d3) {
+ IWL_DEBUG_WOWLAN(mld,
+ "Device_powered_off() was called during wowlan\n");
+ goto err;
+ }
+
+ mld->fw_status.in_d3 = false;
+ mld->scan.last_start_time_jiffies = jiffies;
+
+ bss_vif = iwl_mld_get_bss_vif(mld);
+ if (WARN_ON(!bss_vif))
+ goto err;
+
+ /* We can't have several links upon wowlan entry,
+ * this is enforced in the suspend flow.
+ */
+ WARN_ON(hweight16(bss_vif->active_links) > 1);
+ link_id = bss_vif->active_links ? __ffs(bss_vif->active_links) : 0;
+ link_conf = link_conf_dereference_protected(bss_vif, link_id);
+
+ if (WARN_ON(!link_conf))
+ goto err;
+
+ iwl_fw_dbg_read_d3_debug_data(&mld->fwrt);
+
+ if (iwl_mld_fw_needs_restart(mld, bss_vif)) {
+ fw_err = true;
+ goto err;
+ }
+
+ resume_data.wowlan_status = kzalloc(sizeof(*resume_data.wowlan_status),
+ GFP_KERNEL);
+ if (!resume_data.wowlan_status)
+ return -1;
+
+ if (mld->netdetect)
+ resume_data.notifs_expected |= IWL_D3_ND_MATCH_INFO;
+
+ ret = iwl_mld_wait_d3_notif(mld, &resume_data, true);
+ if (ret) {
+ IWL_ERR(mld, "Couldn't get the d3 notifs %d\n", ret);
+ fw_err = true;
+ goto err;
+ }
+
+ if (resume_data.d3_end_flags & IWL_D0I3_RESET_REQUIRE) {
+ mld->fw_status.in_hw_restart = true;
+ goto process_wakeup_results;
+ }
+
+ iwl_mld_update_changed_regdomain(mld);
+ iwl_mld_update_mac_power(mld, bss_vif, false);
+ iwl_mld_enable_beacon_filter(mld, link_conf, false);
+ iwl_mld_update_device_power(mld, false);
+
+ if (mld->netdetect)
+ ret = iwl_mld_scan_stop(mld, IWL_MLD_SCAN_NETDETECT, false);
+
+ process_wakeup_results:
+ if (mld->netdetect) {
+ iwl_mld_process_netdetect_res(mld, bss_vif, &resume_data);
+ mld->netdetect = false;
+ } else {
+ bool keep_connection =
+ iwl_mld_process_wowlan_status(mld, bss_vif,
+ resume_data.wowlan_status);
+
+ /* EMLSR state will be cleared if the connection is not kept */
+ if (keep_connection)
+ iwl_mld_unblock_emlsr(mld, bss_vif,
+ IWL_MLD_EMLSR_BLOCKED_WOWLAN);
+ else
+ ieee80211_resume_disconnect(bss_vif);
+ }
+
+ goto out;
+
+ err:
+ if (fw_err) {
+ mld->trans->state = IWL_TRANS_NO_FW;
+ set_bit(STATUS_FW_ERROR, &mld->trans->status);
+ }
+
+ mld->fw_status.in_hw_restart = true;
+ ret = 1;
+ out:
+ if (resume_data.wowlan_status) {
+ kfree(resume_data.wowlan_status->wake_packet);
+ kfree(resume_data.wowlan_status);
+ }
+
+ return ret;
+}
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/d3.h b/drivers/net/wireless/intel/iwlwifi/mld/d3.h
new file mode 100644
index 000000000000..618d6fb3c796
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/mld/d3.h
@@ -0,0 +1,51 @@
+/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
+/*
+ * Copyright (C) 2024 Intel Corporation
+ */
+#ifndef __iwl_mld_d3_h__
+#define __iwl_mld_d3_h__
+
+#include "fw/api/d3.h"
+
+struct iwl_mld_rekey_data {
+ bool valid;
+ u8 kck[NL80211_KCK_EXT_LEN];
+ u8 kek[NL80211_KEK_EXT_LEN];
+ size_t kck_len;
+ size_t kek_len;
+ __le64 replay_ctr;
+ u32 akm;
+};
+
+/**
+ * struct iwl_mld_wowlan_data - data used by the wowlan suspend flow
+ *
+ * @target_ipv6_addrs: IPv6 addresses on this interface for offload
+ * @tentative_addrs: bitmap of tentative IPv6 addresses in @target_ipv6_addrs
+ * @num_target_ipv6_addrs: number of @target_ipv6_addrs
+ * @rekey_data: security key data used for rekeying during D3
+ */
+struct iwl_mld_wowlan_data {
+#if IS_ENABLED(CONFIG_IPV6)
+ struct in6_addr target_ipv6_addrs[IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_MAX];
+ unsigned long tentative_addrs[BITS_TO_LONGS(IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_MAX)];
+ int num_target_ipv6_addrs;
+#endif
+ struct iwl_mld_rekey_data rekey_data;
+};
+
+int iwl_mld_no_wowlan_resume(struct iwl_mld *mld);
+int iwl_mld_no_wowlan_suspend(struct iwl_mld *mld);
+int iwl_mld_wowlan_suspend(struct iwl_mld *mld,
+ struct cfg80211_wowlan *wowlan);
+int iwl_mld_wowlan_resume(struct iwl_mld *mld);
+void iwl_mld_set_rekey_data(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct cfg80211_gtk_rekey_data *data);
+#if IS_ENABLED(CONFIG_IPV6)
+void iwl_mld_ipv6_addr_change(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct inet6_dev *idev);
+#endif
+
+#endif /* __iwl_mld_d3_h__ */
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/debugfs.c b/drivers/net/wireless/intel/iwlwifi/mld/debugfs.c
new file mode 100644
index 000000000000..352da8aa7898
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/mld/debugfs.c
@@ -0,0 +1,1104 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
+/*
+ * Copyright (C) 2024-2025 Intel Corporation
+ */
+
+#include "mld.h"
+#include "debugfs.h"
+#include "iwl-io.h"
+#include "hcmd.h"
+#include "iface.h"
+#include "sta.h"
+#include "tlc.h"
+#include "power.h"
+#include "notif.h"
+#include "ap.h"
+#include "iwl-utils.h"
+#include "scan.h"
+#ifdef CONFIG_THERMAL
+#include "thermal.h"
+#endif
+
+#include "fw/api/rs.h"
+#include "fw/api/dhc.h"
+#include "fw/api/rfi.h"
+#include "fw/dhc-utils.h"
+#include <linux/dmi.h>
+
+#define MLD_DEBUGFS_READ_FILE_OPS(name, bufsz) \
+ _MLD_DEBUGFS_READ_FILE_OPS(name, bufsz, struct iwl_mld)
+
+#define MLD_DEBUGFS_ADD_FILE_ALIAS(alias, name, parent, mode) \
+ debugfs_create_file(alias, mode, parent, mld, \
+ &iwl_dbgfs_##name##_ops)
+#define MLD_DEBUGFS_ADD_FILE(name, parent, mode) \
+ MLD_DEBUGFS_ADD_FILE_ALIAS(#name, name, parent, mode)
+
+static bool iwl_mld_dbgfs_fw_cmd_disabled(struct iwl_mld *mld)
+{
+#ifdef CONFIG_PM_SLEEP
+ return !mld->fw_status.running || mld->fw_status.in_d3;
+#else
+ return !mld->fw_status.running;
+#endif /* CONFIG_PM_SLEEP */
+}
+
+static ssize_t iwl_dbgfs_fw_dbg_clear_write(struct iwl_mld *mld,
+ char *buf, size_t count)
+{
+ /* If the firmware is not running, silently succeed since there is
+ * no data to clear.
+ */
+ if (iwl_mld_dbgfs_fw_cmd_disabled(mld))
+ return 0;
+
+ iwl_fw_dbg_clear_monitor_buf(&mld->fwrt);
+
+ return count;
+}
+
+static ssize_t iwl_dbgfs_fw_nmi_write(struct iwl_mld *mld, char *buf,
+ size_t count)
+{
+ if (iwl_mld_dbgfs_fw_cmd_disabled(mld))
+ return -EIO;
+
+ IWL_ERR(mld, "Triggering an NMI from debugfs\n");
+
+ if (count == 6 && !strcmp(buf, "nolog\n"))
+ mld->fw_status.do_not_dump_once = true;
+
+ iwl_force_nmi(mld->trans);
+
+ return count;
+}
+
+static ssize_t iwl_dbgfs_fw_restart_write(struct iwl_mld *mld, char *buf,
+ size_t count)
+{
+ int __maybe_unused ret;
+
+ if (!iwlwifi_mod_params.fw_restart)
+ return -EPERM;
+
+ if (iwl_mld_dbgfs_fw_cmd_disabled(mld))
+ return -EIO;
+
+ if (count == 6 && !strcmp(buf, "nolog\n")) {
+ mld->fw_status.do_not_dump_once = true;
+ set_bit(STATUS_SUPPRESS_CMD_ERROR_ONCE, &mld->trans->status);
+ }
+
+ /* take the return value to make compiler happy - it will
+ * fail anyway
+ */
+ ret = iwl_mld_send_cmd_empty(mld, WIDE_ID(LONG_GROUP, REPLY_ERROR));
+
+ return count;
+}
+
+static ssize_t iwl_dbgfs_send_echo_cmd_write(struct iwl_mld *mld, char *buf,
+ size_t count)
+{
+ if (iwl_mld_dbgfs_fw_cmd_disabled(mld))
+ return -EIO;
+
+ return iwl_mld_send_cmd_empty(mld, ECHO_CMD) ?: count;
+}
+
+struct iwl_mld_sniffer_apply {
+ struct iwl_mld *mld;
+ const u8 *bssid;
+ u16 aid;
+};
+
+static bool iwl_mld_sniffer_apply(struct iwl_notif_wait_data *notif_data,
+ struct iwl_rx_packet *pkt, void *data)
+{
+ struct iwl_mld_sniffer_apply *apply = data;
+
+ apply->mld->monitor.cur_aid = cpu_to_le16(apply->aid);
+ memcpy(apply->mld->monitor.cur_bssid, apply->bssid,
+ sizeof(apply->mld->monitor.cur_bssid));
+
+ return true;
+}
+
+static ssize_t
+iwl_dbgfs_he_sniffer_params_write(struct iwl_mld *mld, char *buf,
+ size_t count)
+{
+ struct iwl_notification_wait wait;
+ struct iwl_he_monitor_cmd he_mon_cmd = {};
+ struct iwl_mld_sniffer_apply apply = {
+ .mld = mld,
+ };
+ u16 wait_cmds[] = {
+ WIDE_ID(DATA_PATH_GROUP, HE_AIR_SNIFFER_CONFIG_CMD),
+ };
+ u32 aid;
+ int ret;
+
+ if (iwl_mld_dbgfs_fw_cmd_disabled(mld))
+ return -EIO;
+
+ if (!mld->monitor.on)
+ return -ENODEV;
+
+ ret = sscanf(buf, "%x %2hhx:%2hhx:%2hhx:%2hhx:%2hhx:%2hhx", &aid,
+ &he_mon_cmd.bssid[0], &he_mon_cmd.bssid[1],
+ &he_mon_cmd.bssid[2], &he_mon_cmd.bssid[3],
+ &he_mon_cmd.bssid[4], &he_mon_cmd.bssid[5]);
+ if (ret != 7)
+ return -EINVAL;
+
+ he_mon_cmd.aid = cpu_to_le16(aid);
+
+ apply.aid = aid;
+ apply.bssid = (void *)he_mon_cmd.bssid;
+
+ /* Use the notification waiter to get our function triggered
+ * in sequence with other RX. This ensures that frames we get
+ * on the RX queue _before_ the new configuration is applied
+ * still have mld->cur_aid pointing to the old AID, and that
+ * frames on the RX queue _after_ the firmware processed the
+ * new configuration (and sent the response, synchronously)
+ * get mld->cur_aid correctly set to the new AID.
+ */
+ iwl_init_notification_wait(&mld->notif_wait, &wait,
+ wait_cmds, ARRAY_SIZE(wait_cmds),
+ iwl_mld_sniffer_apply, &apply);
+
+ ret = iwl_mld_send_cmd_pdu(mld,
+ WIDE_ID(DATA_PATH_GROUP,
+ HE_AIR_SNIFFER_CONFIG_CMD),
+ &he_mon_cmd);
+
+ /* no need to really wait, we already did anyway */
+ iwl_remove_notification(&mld->notif_wait, &wait);
+
+ return ret ?: count;
+}
+
+static ssize_t
+iwl_dbgfs_he_sniffer_params_read(struct iwl_mld *mld, char *buf, size_t count)
+{
+ return scnprintf(buf, count,
+ "%d %02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx\n",
+ le16_to_cpu(mld->monitor.cur_aid),
+ mld->monitor.cur_bssid[0], mld->monitor.cur_bssid[1],
+ mld->monitor.cur_bssid[2], mld->monitor.cur_bssid[3],
+ mld->monitor.cur_bssid[4], mld->monitor.cur_bssid[5]);
+}
+
+/* The size computation is as follows:
+ * each number needs at most 3 characters, number of rows is the size of
+ * the table; So, need 5 chars for the "freq: " part and each tuple afterwards
+ * needs 6 characters for numbers and 5 for the punctuation around. 32 bytes
+ * for feature support message.
+ */
+#define IWL_RFI_DDR_BUF_SIZE (IWL_RFI_DDR_LUT_INSTALLED_SIZE *\
+ (5 + IWL_RFI_DDR_LUT_ENTRY_CHANNELS_NUM *\
+ (6 + 5)) + 32)
+#define IWL_RFI_DLVR_BUF_SIZE (IWL_RFI_DLVR_LUT_INSTALLED_SIZE *\
+ (5 + IWL_RFI_DLVR_LUT_ENTRY_CHANNELS_NUM *\
+ (6 + 5)) + 32)
+#define IWL_RFI_DESENSE_BUF_SIZE IWL_RFI_DDR_BUF_SIZE
+
+/* Extra 32 for "DDR and DLVR table" message */
+#define IWL_RFI_BUF_SIZE (IWL_RFI_DDR_BUF_SIZE + IWL_RFI_DLVR_BUF_SIZE +\
+ IWL_RFI_DESENSE_BUF_SIZE + 32)
+
+static size_t iwl_mld_dump_tas_resp(struct iwl_dhc_tas_status_resp *resp,
+ size_t count, u8 *buf)
+{
+ const char * const tas_dis_reason[TAS_DISABLED_REASON_MAX] = {
+ [TAS_DISABLED_DUE_TO_BIOS] =
+ "Due To BIOS",
+ [TAS_DISABLED_DUE_TO_SAR_6DBM] =
+ "Due To SAR Limit Less Than 6 dBm",
+ [TAS_DISABLED_REASON_INVALID] =
+ "N/A",
+ [TAS_DISABLED_DUE_TO_TABLE_SOURCE_INVALID] =
+ "Due to table source invalid"
+ };
+ const char * const tas_current_status[TAS_DYNA_STATUS_MAX] = {
+ [TAS_DYNA_INACTIVE] = "INACTIVE",
+ [TAS_DYNA_INACTIVE_MVM_MODE] =
+ "inactive due to mvm mode",
+ [TAS_DYNA_INACTIVE_TRIGGER_MODE] =
+ "inactive due to trigger mode",
+ [TAS_DYNA_INACTIVE_BLOCK_LISTED] =
+ "inactive due to block listed",
+ [TAS_DYNA_INACTIVE_UHB_NON_US] =
+ "inactive due to uhb non US",
+ [TAS_DYNA_ACTIVE] = "ACTIVE",
+ };
+ ssize_t pos = 0;
+
+ if (resp->header.version != 1) {
+ pos += scnprintf(buf + pos, count - pos,
+ "Unsupported TAS response version:%d",
+ resp->header.version);
+ return pos;
+ }
+
+ pos += scnprintf(buf + pos, count - pos, "TAS Report\n");
+ switch (resp->tas_config_info.table_source) {
+ case BIOS_SOURCE_NONE:
+ pos += scnprintf(buf + pos, count - pos,
+ "BIOS SOURCE NONE ");
+ break;
+ case BIOS_SOURCE_ACPI:
+ pos += scnprintf(buf + pos, count - pos,
+ "BIOS SOURCE ACPI ");
+ break;
+ case BIOS_SOURCE_UEFI:
+ pos += scnprintf(buf + pos, count - pos,
+ "BIOS SOURCE UEFI ");
+ break;
+ default:
+ pos += scnprintf(buf + pos, count - pos,
+ "BIOS SOURCE UNKNOWN (%d) ",
+ resp->tas_config_info.table_source);
+ break;
+ }
+
+ pos += scnprintf(buf + pos, count - pos,
+ "revision is: %d data is: 0x%08x\n",
+ resp->tas_config_info.table_revision,
+ resp->tas_config_info.value);
+ pos += scnprintf(buf + pos, count - pos, "Current MCC: 0x%x\n",
+ le16_to_cpu(resp->curr_mcc));
+
+ pos += scnprintf(buf + pos, count - pos, "Block list entries:");
+ for (int i = 0; i < ARRAY_SIZE(resp->mcc_block_list); i++)
+ pos += scnprintf(buf + pos, count - pos, " 0x%x",
+ le16_to_cpu(resp->mcc_block_list[i]));
+
+ pos += scnprintf(buf + pos, count - pos,
+ "\nDo TAS Support Dual Radio?: %s\n",
+ hweight8(resp->valid_radio_mask) > 1 ?
+ "TRUE" : "FALSE");
+
+ for (int i = 0; i < ARRAY_SIZE(resp->tas_status_radio); i++) {
+ int tmp;
+ unsigned long dynamic_status;
+
+ if (!(resp->valid_radio_mask & BIT(i)))
+ continue;
+
+ pos += scnprintf(buf + pos, count - pos,
+ "TAS report for radio:%d\n", i + 1);
+ pos += scnprintf(buf + pos, count - pos,
+ "Static status: %sabled\n",
+ resp->tas_status_radio[i].static_status ?
+ "En" : "Dis");
+ if (!resp->tas_status_radio[i].static_status) {
+ u8 static_disable_reason =
+ resp->tas_status_radio[i].static_disable_reason;
+
+ pos += scnprintf(buf + pos, count - pos,
+ "\tStatic Disabled Reason: ");
+ if (static_disable_reason >= TAS_DISABLED_REASON_MAX) {
+ pos += scnprintf(buf + pos, count - pos,
+ "unsupported value (%d)\n",
+ static_disable_reason);
+ continue;
+ }
+
+ pos += scnprintf(buf + pos, count - pos,
+ "%s (%d)\n",
+ tas_dis_reason[static_disable_reason],
+ static_disable_reason);
+ continue;
+ }
+
+ pos += scnprintf(buf + pos, count - pos, "\tANT A %s and ",
+ (resp->tas_status_radio[i].dynamic_status_ant_a
+ & BIT(TAS_DYNA_ACTIVE)) ? "ON" : "OFF");
+
+ pos += scnprintf(buf + pos, count - pos, "ANT B %s for ",
+ (resp->tas_status_radio[i].dynamic_status_ant_b
+ & BIT(TAS_DYNA_ACTIVE)) ? "ON" : "OFF");
+
+ switch (resp->tas_status_radio[i].band) {
+ case PHY_BAND_5:
+ pos += scnprintf(buf + pos, count - pos, "HB\n");
+ break;
+ case PHY_BAND_24:
+ pos += scnprintf(buf + pos, count - pos, "LB\n");
+ break;
+ case PHY_BAND_6:
+ pos += scnprintf(buf + pos, count - pos, "UHB\n");
+ break;
+ default:
+ pos += scnprintf(buf + pos, count - pos,
+ "Unsupported band (%d)\n",
+ resp->tas_status_radio[i].band);
+ break;
+ }
+
+ pos += scnprintf(buf + pos, count - pos,
+ "Is near disconnection?: %s\n",
+ resp->tas_status_radio[i].near_disconnection ?
+ "True" : "False");
+
+ pos += scnprintf(buf + pos, count - pos,
+ "Dynamic status antenna A:\n");
+ dynamic_status = resp->tas_status_radio[i].dynamic_status_ant_a;
+ for_each_set_bit(tmp, &dynamic_status, TAS_DYNA_STATUS_MAX) {
+ pos += scnprintf(buf + pos, count - pos, "\t%s (%d)\n",
+ tas_current_status[tmp], tmp);
+ }
+ pos += scnprintf(buf + pos, count - pos,
+ "\nDynamic status antenna B:\n");
+ dynamic_status = resp->tas_status_radio[i].dynamic_status_ant_b;
+ for_each_set_bit(tmp, &dynamic_status, TAS_DYNA_STATUS_MAX) {
+ pos += scnprintf(buf + pos, count - pos, "\t%s (%d)\n",
+ tas_current_status[tmp], tmp);
+ }
+
+ tmp = le16_to_cpu(resp->tas_status_radio[i].max_reg_pwr_limit_ant_a);
+ pos += scnprintf(buf + pos, count - pos,
+ "Max antenna A regulatory pwr limit (dBm): %d.%03d\n",
+ tmp / 8, 125 * (tmp % 8));
+ tmp = le16_to_cpu(resp->tas_status_radio[i].max_reg_pwr_limit_ant_b);
+ pos += scnprintf(buf + pos, count - pos,
+ "Max antenna B regulatory pwr limit (dBm): %d.%03d\n",
+ tmp / 8, 125 * (tmp % 8));
+
+ tmp = le16_to_cpu(resp->tas_status_radio[i].sar_limit_ant_a);
+ pos += scnprintf(buf + pos, count - pos,
+ "Antenna A SAR limit (dBm): %d.%03d\n",
+ tmp / 8, 125 * (tmp % 8));
+ tmp = le16_to_cpu(resp->tas_status_radio[i].sar_limit_ant_b);
+ pos += scnprintf(buf + pos, count - pos,
+ "Antenna B SAR limit (dBm): %d.%03d\n",
+ tmp / 8, 125 * (tmp % 8));
+ }
+
+ return pos;
+}
+
+static ssize_t iwl_dbgfs_tas_get_status_read(struct iwl_mld *mld, char *buf,
+ size_t count)
+{
+ struct iwl_dhc_cmd cmd = {
+ .index_and_mask = cpu_to_le32(DHC_TABLE_TOOLS |
+ DHC_TARGET_UMAC |
+ DHC_TOOLS_UMAC_GET_TAS_STATUS),
+ };
+ struct iwl_host_cmd hcmd = {
+ .id = WIDE_ID(LEGACY_GROUP, DEBUG_HOST_COMMAND),
+ .flags = CMD_WANT_SKB,
+ .len[0] = sizeof(cmd),
+ .data[0] = &cmd,
+ };
+ struct iwl_dhc_tas_status_resp *resp = NULL;
+ u32 resp_len = 0;
+ ssize_t pos = 0;
+ u32 status;
+ int ret;
+
+ if (iwl_mld_dbgfs_fw_cmd_disabled(mld))
+ return -EIO;
+
+ ret = iwl_mld_send_cmd(mld, &hcmd);
+ if (ret)
+ return ret;
+
+ pos += scnprintf(buf + pos, count - pos, "\nOEM name: %s\n",
+ dmi_get_system_info(DMI_SYS_VENDOR) ?: "<unknown>");
+ pos += scnprintf(buf + pos, count - pos,
+ "\tVendor In Approved List: %s\n",
+ iwl_is_tas_approved() ? "YES" : "NO");
+
+ status = iwl_dhc_resp_status(mld->fwrt.fw, hcmd.resp_pkt);
+ if (status != 1) {
+ pos += scnprintf(buf + pos, count - pos,
+ "response status is not success: %d\n",
+ status);
+ goto out;
+ }
+
+ resp = iwl_dhc_resp_data(mld->fwrt.fw, hcmd.resp_pkt, &resp_len);
+ if (IS_ERR(resp) || resp_len != sizeof(*resp)) {
+ pos += scnprintf(buf + pos, count - pos,
+ "Invalid size for TAS response (%u instead of %zd)\n",
+ resp_len, sizeof(*resp));
+ goto out;
+ }
+
+ pos += iwl_mld_dump_tas_resp(resp, count - pos, buf + pos);
+
+out:
+ iwl_free_resp(&hcmd);
+ return pos;
+}
+
+WIPHY_DEBUGFS_WRITE_FILE_OPS_MLD(fw_nmi, 10);
+WIPHY_DEBUGFS_WRITE_FILE_OPS_MLD(fw_restart, 10);
+WIPHY_DEBUGFS_READ_WRITE_FILE_OPS_MLD(he_sniffer_params, 32);
+WIPHY_DEBUGFS_WRITE_FILE_OPS_MLD(fw_dbg_clear, 10);
+WIPHY_DEBUGFS_WRITE_FILE_OPS_MLD(send_echo_cmd, 8);
+WIPHY_DEBUGFS_READ_FILE_OPS_MLD(tas_get_status, 2048);
+
+static ssize_t iwl_dbgfs_wifi_6e_enable_read(struct iwl_mld *mld,
+ size_t count, u8 *buf)
+{
+ int err;
+ u32 value;
+
+ err = iwl_bios_get_dsm(&mld->fwrt, DSM_FUNC_ENABLE_6E, &value);
+ if (err)
+ return err;
+
+ return scnprintf(buf, count, "0x%08x\n", value);
+}
+
+MLD_DEBUGFS_READ_FILE_OPS(wifi_6e_enable, 64);
+
+static ssize_t iwl_dbgfs_inject_packet_write(struct iwl_mld *mld,
+ char *buf, size_t count)
+{
+ struct iwl_op_mode *opmode = container_of((void *)mld,
+ struct iwl_op_mode,
+ op_mode_specific);
+ struct iwl_rx_cmd_buffer rxb = {};
+ struct iwl_rx_packet *pkt;
+ int n_bytes = count / 2;
+ int ret = -EINVAL;
+
+ if (iwl_mld_dbgfs_fw_cmd_disabled(mld))
+ return -EIO;
+
+ rxb._page = alloc_pages(GFP_KERNEL, 0);
+ if (!rxb._page)
+ return -ENOMEM;
+ pkt = rxb_addr(&rxb);
+
+ ret = hex2bin(page_address(rxb._page), buf, n_bytes);
+ if (ret)
+ goto out;
+
+ /* avoid invalid memory access and malformed packet */
+ if (n_bytes < sizeof(*pkt) ||
+ n_bytes != sizeof(*pkt) + iwl_rx_packet_payload_len(pkt))
+ goto out;
+
+ local_bh_disable();
+ iwl_mld_rx(opmode, NULL, &rxb);
+ local_bh_enable();
+ ret = 0;
+
+out:
+ iwl_free_rxb(&rxb);
+
+ return ret ?: count;
+}
+
+WIPHY_DEBUGFS_WRITE_FILE_OPS_MLD(inject_packet, 512);
+
+#ifdef CONFIG_THERMAL
+
+static ssize_t iwl_dbgfs_stop_ctdp_write(struct iwl_mld *mld,
+ char *buf, size_t count)
+{
+ if (iwl_mld_dbgfs_fw_cmd_disabled(mld))
+ return -EIO;
+
+ return iwl_mld_config_ctdp(mld, mld->cooling_dev.cur_state,
+ CTDP_CMD_OPERATION_STOP) ? : count;
+}
+
+WIPHY_DEBUGFS_WRITE_FILE_OPS_MLD(stop_ctdp, 8);
+
+static ssize_t iwl_dbgfs_start_ctdp_write(struct iwl_mld *mld,
+ char *buf, size_t count)
+{
+ if (iwl_mld_dbgfs_fw_cmd_disabled(mld))
+ return -EIO;
+
+ return iwl_mld_config_ctdp(mld, mld->cooling_dev.cur_state,
+ CTDP_CMD_OPERATION_START) ? : count;
+}
+
+WIPHY_DEBUGFS_WRITE_FILE_OPS_MLD(start_ctdp, 8);
+
+#endif /* CONFIG_THERMAL */
+
+void
+iwl_mld_add_debugfs_files(struct iwl_mld *mld, struct dentry *debugfs_dir)
+{
+ /* Add debugfs files here */
+
+ MLD_DEBUGFS_ADD_FILE(fw_nmi, debugfs_dir, 0200);
+ MLD_DEBUGFS_ADD_FILE(fw_restart, debugfs_dir, 0200);
+ MLD_DEBUGFS_ADD_FILE(wifi_6e_enable, debugfs_dir, 0400);
+ MLD_DEBUGFS_ADD_FILE(he_sniffer_params, debugfs_dir, 0600);
+ MLD_DEBUGFS_ADD_FILE(fw_dbg_clear, debugfs_dir, 0200);
+ MLD_DEBUGFS_ADD_FILE(send_echo_cmd, debugfs_dir, 0200);
+ MLD_DEBUGFS_ADD_FILE(tas_get_status, debugfs_dir, 0400);
+#ifdef CONFIG_THERMAL
+ MLD_DEBUGFS_ADD_FILE(start_ctdp, debugfs_dir, 0200);
+ MLD_DEBUGFS_ADD_FILE(stop_ctdp, debugfs_dir, 0200);
+#endif
+ MLD_DEBUGFS_ADD_FILE(inject_packet, debugfs_dir, 0200);
+
+ debugfs_create_bool("rx_ts_ptp", 0600, debugfs_dir,
+ &mld->monitor.ptp_time);
+
+ /* Create a symlink with mac80211. It will be removed when mac80211
+ * exits (before the opmode exits which removes the target.)
+ */
+ if (!IS_ERR(debugfs_dir)) {
+ char buf[100];
+
+ snprintf(buf, 100, "../../%pd2", debugfs_dir->d_parent);
+ debugfs_create_symlink("iwlwifi", mld->wiphy->debugfsdir,
+ buf);
+ }
+}
+
+#define VIF_DEBUGFS_WRITE_FILE_OPS(name, bufsz) \
+ WIPHY_DEBUGFS_WRITE_FILE_OPS(vif_##name, bufsz, vif)
+
+#define VIF_DEBUGFS_READ_WRITE_FILE_OPS(name, bufsz) \
+ IEEE80211_WIPHY_DEBUGFS_READ_WRITE_FILE_OPS(vif_##name, bufsz, vif) \
+
+#define VIF_DEBUGFS_ADD_FILE_ALIAS(alias, name, parent, mode) \
+ debugfs_create_file(alias, mode, parent, vif, \
+ &iwl_dbgfs_vif_##name##_ops)
+#define VIF_DEBUGFS_ADD_FILE(name, parent, mode) \
+ VIF_DEBUGFS_ADD_FILE_ALIAS(#name, name, parent, mode)
+
+static ssize_t iwl_dbgfs_vif_bf_params_write(struct iwl_mld *mld, char *buf,
+ size_t count, void *data)
+{
+ struct ieee80211_vif *vif = data;
+ struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
+ int link_id = vif->active_links ? __ffs(vif->active_links) : 0;
+ struct ieee80211_bss_conf *link_conf;
+ int val;
+
+ if (!strncmp("bf_enable_beacon_filter=", buf, 24)) {
+ if (sscanf(buf + 24, "%d", &val) != 1)
+ return -EINVAL;
+ } else {
+ return -EINVAL;
+ }
+
+ if (val != 0 && val != 1)
+ return -EINVAL;
+
+ link_conf = link_conf_dereference_protected(vif, link_id);
+ if (WARN_ON(!link_conf))
+ return -ENODEV;
+
+ if (iwl_mld_dbgfs_fw_cmd_disabled(mld))
+ return -EIO;
+
+ mld_vif->disable_bf = !val;
+
+ if (val)
+ return iwl_mld_enable_beacon_filter(mld, link_conf,
+ false) ?: count;
+ else
+ return iwl_mld_disable_beacon_filter(mld, vif) ?: count;
+}
+
+static ssize_t iwl_dbgfs_vif_pm_params_write(struct iwl_mld *mld,
+ char *buf,
+ size_t count, void *data)
+{
+ struct ieee80211_vif *vif = data;
+ struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
+ int val;
+
+ if (!strncmp("use_ps_poll=", buf, 12)) {
+ if (sscanf(buf + 12, "%d", &val) != 1)
+ return -EINVAL;
+ } else {
+ return -EINVAL;
+ }
+
+ if (iwl_mld_dbgfs_fw_cmd_disabled(mld))
+ return -EIO;
+
+ mld_vif->use_ps_poll = val;
+
+ return iwl_mld_update_mac_power(mld, vif, false) ?: count;
+}
+
+static ssize_t iwl_dbgfs_vif_low_latency_write(struct iwl_mld *mld,
+ char *buf, size_t count,
+ void *data)
+{
+ struct ieee80211_vif *vif = data;
+ u8 value;
+ int ret;
+
+ ret = kstrtou8(buf, 0, &value);
+ if (ret)
+ return ret;
+
+ if (value > 1)
+ return -EINVAL;
+
+ iwl_mld_vif_update_low_latency(mld, vif, value, LOW_LATENCY_DEBUGFS);
+
+ return count;
+}
+
+static ssize_t iwl_dbgfs_vif_low_latency_read(struct ieee80211_vif *vif,
+ size_t count, char *buf)
+{
+ struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
+ char format[] = "traffic=%d\ndbgfs=%d\nvif_type=%d\nactual=%d\n";
+ u8 ll_causes;
+
+ if (WARN_ON(count < sizeof(format)))
+ return -EINVAL;
+
+ ll_causes = READ_ONCE(mld_vif->low_latency_causes);
+
+ /* all values in format are boolean so the size of format is enough
+ * for holding the result string
+ */
+ return scnprintf(buf, count, format,
+ !!(ll_causes & LOW_LATENCY_TRAFFIC),
+ !!(ll_causes & LOW_LATENCY_DEBUGFS),
+ !!(ll_causes & LOW_LATENCY_VIF_TYPE),
+ !!(ll_causes));
+}
+
+VIF_DEBUGFS_WRITE_FILE_OPS(pm_params, 32);
+VIF_DEBUGFS_WRITE_FILE_OPS(bf_params, 32);
+VIF_DEBUGFS_READ_WRITE_FILE_OPS(low_latency, 45);
+
+static int
+_iwl_dbgfs_inject_beacon_ie(struct iwl_mld *mld, struct ieee80211_vif *vif,
+ char *bin, ssize_t len,
+ bool restore)
+{
+ struct iwl_mld_vif *mld_vif;
+ struct iwl_mld_link *mld_link;
+ struct iwl_mac_beacon_cmd beacon_cmd = {};
+ int n_bytes = len / 2;
+
+ /* Element len should be represented by u8 */
+ if (n_bytes >= U8_MAX)
+ return -EINVAL;
+
+ if (iwl_mld_dbgfs_fw_cmd_disabled(mld))
+ return -EIO;
+
+ if (!vif)
+ return -EINVAL;
+
+ mld_vif = iwl_mld_vif_from_mac80211(vif);
+ mld_vif->beacon_inject_active = true;
+ mld->hw->extra_beacon_tailroom = n_bytes;
+
+ for_each_mld_vif_valid_link(mld_vif, mld_link) {
+ u32 offset;
+ struct ieee80211_tx_info *info;
+ struct ieee80211_bss_conf *link_conf =
+ link_conf_dereference_protected(vif, link_id);
+ struct ieee80211_chanctx_conf *ctx =
+ wiphy_dereference(mld->wiphy, link_conf->chanctx_conf);
+ struct sk_buff *beacon =
+ ieee80211_beacon_get_template(mld->hw, vif,
+ NULL, link_id);
+
+ if (!beacon)
+ return -EINVAL;
+
+ if (!restore && (WARN_ON(!n_bytes || !bin) ||
+ hex2bin(skb_put_zero(beacon, n_bytes),
+ bin, n_bytes))) {
+ dev_kfree_skb(beacon);
+ return -EINVAL;
+ }
+
+ info = IEEE80211_SKB_CB(beacon);
+
+ beacon_cmd.flags =
+ cpu_to_le16(iwl_mld_get_rate_flags(mld, info, vif,
+ link_conf,
+ ctx->def.chan->band));
+ beacon_cmd.byte_cnt = cpu_to_le16((u16)beacon->len);
+ beacon_cmd.link_id =
+ cpu_to_le32(mld_link->fw_id);
+
+ iwl_mld_set_tim_idx(mld, &beacon_cmd.tim_idx,
+ beacon->data, beacon->len);
+
+ offset = iwl_find_ie_offset(beacon->data,
+ WLAN_EID_S1G_TWT,
+ beacon->len);
+
+ beacon_cmd.btwt_offset = cpu_to_le32(offset);
+
+ iwl_mld_send_beacon_template_cmd(mld, beacon, &beacon_cmd);
+ dev_kfree_skb(beacon);
+ }
+
+ if (restore)
+ mld_vif->beacon_inject_active = false;
+
+ return 0;
+}
+
+static ssize_t
+iwl_dbgfs_vif_inject_beacon_ie_write(struct iwl_mld *mld,
+ char *buf, size_t count,
+ void *data)
+{
+ struct ieee80211_vif *vif = data;
+ int ret = _iwl_dbgfs_inject_beacon_ie(mld, vif, buf,
+ count, false);
+
+ mld->hw->extra_beacon_tailroom = 0;
+ return ret ?: count;
+}
+
+VIF_DEBUGFS_WRITE_FILE_OPS(inject_beacon_ie, 512);
+
+static ssize_t
+iwl_dbgfs_vif_inject_beacon_ie_restore_write(struct iwl_mld *mld,
+ char *buf,
+ size_t count,
+ void *data)
+{
+ struct ieee80211_vif *vif = data;
+ int ret = _iwl_dbgfs_inject_beacon_ie(mld, vif, NULL,
+ 0, true);
+
+ mld->hw->extra_beacon_tailroom = 0;
+ return ret ?: count;
+}
+
+VIF_DEBUGFS_WRITE_FILE_OPS(inject_beacon_ie_restore, 512);
+
+static ssize_t
+iwl_dbgfs_vif_twt_setup_write(struct iwl_mld *mld, char *buf, size_t count,
+ void *data)
+{
+ struct iwl_host_cmd hcmd = {
+ .id = WIDE_ID(IWL_ALWAYS_LONG_GROUP, DEBUG_HOST_COMMAND),
+ };
+ struct ieee80211_vif *vif = data;
+ struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
+ struct iwl_dhc_cmd *cmd __free(kfree) = NULL;
+ struct iwl_dhc_twt_operation *dhc_twt_cmd;
+ u64 target_wake_time;
+ u32 twt_operation, interval_exp, interval_mantissa, min_wake_duration;
+ u8 trigger, flow_type, flow_id, protection, tenth_param;
+ u8 twt_request = 1, broadcast = 0;
+ int ret;
+
+ if (iwl_mld_dbgfs_fw_cmd_disabled(mld))
+ return -EIO;
+
+ ret = sscanf(buf, "%u %llu %u %u %u %hhu %hhu %hhu %hhu %hhu",
+ &twt_operation, &target_wake_time, &interval_exp,
+ &interval_mantissa, &min_wake_duration, &trigger,
+ &flow_type, &flow_id, &protection, &tenth_param);
+
+ /* the new twt_request parameter is optional for station */
+ if ((ret != 9 && ret != 10) ||
+ (ret == 10 && vif->type != NL80211_IFTYPE_STATION &&
+ tenth_param == 1))
+ return -EINVAL;
+
+ /* The 10th parameter:
+ * In STA mode - the TWT type (broadcast or individual)
+ * In AP mode - the role (0 responder, 2 unsolicited)
+ */
+ if (ret == 10) {
+ if (vif->type == NL80211_IFTYPE_STATION)
+ broadcast = tenth_param;
+ else
+ twt_request = tenth_param;
+ }
+
+ cmd = kzalloc(sizeof(*cmd) + sizeof(*dhc_twt_cmd), GFP_KERNEL);
+ if (!cmd)
+ return -ENOMEM;
+
+ dhc_twt_cmd = (void *)cmd->data;
+ dhc_twt_cmd->mac_id = cpu_to_le32(mld_vif->fw_id);
+ dhc_twt_cmd->twt_operation = cpu_to_le32(twt_operation);
+ dhc_twt_cmd->target_wake_time = cpu_to_le64(target_wake_time);
+ dhc_twt_cmd->interval_exp = cpu_to_le32(interval_exp);
+ dhc_twt_cmd->interval_mantissa = cpu_to_le32(interval_mantissa);
+ dhc_twt_cmd->min_wake_duration = cpu_to_le32(min_wake_duration);
+ dhc_twt_cmd->trigger = trigger;
+ dhc_twt_cmd->flow_type = flow_type;
+ dhc_twt_cmd->flow_id = flow_id;
+ dhc_twt_cmd->protection = protection;
+ dhc_twt_cmd->twt_request = twt_request;
+ dhc_twt_cmd->negotiation_type = broadcast ? 3 : 0;
+
+ cmd->length = cpu_to_le32(sizeof(*dhc_twt_cmd) >> 2);
+ cmd->index_and_mask =
+ cpu_to_le32(DHC_TABLE_INTEGRATION | DHC_TARGET_UMAC |
+ DHC_INT_UMAC_TWT_OPERATION);
+
+ hcmd.len[0] = sizeof(*cmd) + sizeof(*dhc_twt_cmd);
+ hcmd.data[0] = cmd;
+
+ ret = iwl_mld_send_cmd(mld, &hcmd);
+
+ return ret ?: count;
+}
+
+VIF_DEBUGFS_WRITE_FILE_OPS(twt_setup, 256);
+
+static ssize_t
+iwl_dbgfs_vif_twt_operation_write(struct iwl_mld *mld, char *buf, size_t count,
+ void *data)
+{
+ struct ieee80211_vif *vif = data;
+ struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
+ struct iwl_twt_operation_cmd twt_cmd = {};
+ int link_id = vif->active_links ? __ffs(vif->active_links) : 0;
+ struct iwl_mld_link *mld_link = iwl_mld_link_dereference_check(mld_vif,
+ link_id);
+ int ret;
+
+ if (WARN_ON(!mld_link))
+ return -ENODEV;
+
+ if (iwl_mld_dbgfs_fw_cmd_disabled(mld))
+ return -EIO;
+
+ if (hweight16(vif->active_links) > 1)
+ return -EOPNOTSUPP;
+
+ ret = sscanf(buf,
+ "%u %llu %u %u %u %hhu %hhu %hhu %hhu %hhu %hhu %hhu %hhu %hhu %hhu %hhu %hhu %hhu %hhu %hhu %hhu",
+ &twt_cmd.twt_operation, &twt_cmd.target_wake_time,
+ &twt_cmd.interval_exponent, &twt_cmd.interval_mantissa,
+ &twt_cmd.minimum_wake_duration, &twt_cmd.trigger,
+ &twt_cmd.flow_type, &twt_cmd.flow_id,
+ &twt_cmd.twt_protection, &twt_cmd.ndp_paging_indicator,
+ &twt_cmd.responder_pm_mode, &twt_cmd.negotiation_type,
+ &twt_cmd.twt_request, &twt_cmd.implicit,
+ &twt_cmd.twt_group_assignment, &twt_cmd.twt_channel,
+ &twt_cmd.restricted_info_present, &twt_cmd.dl_bitmap_valid,
+ &twt_cmd.ul_bitmap_valid, &twt_cmd.dl_tid_bitmap,
+ &twt_cmd.ul_tid_bitmap);
+
+ if (ret != 21)
+ return -EINVAL;
+
+ twt_cmd.link_id = cpu_to_le32(mld_link->fw_id);
+
+ ret = iwl_mld_send_cmd_pdu(mld,
+ WIDE_ID(MAC_CONF_GROUP, TWT_OPERATION_CMD),
+ &twt_cmd);
+ return ret ?: count;
+}
+
+VIF_DEBUGFS_WRITE_FILE_OPS(twt_operation, 256);
+
+static ssize_t iwl_dbgfs_vif_int_mlo_scan_write(struct iwl_mld *mld, char *buf,
+ size_t count, void *data)
+{
+ struct ieee80211_vif *vif = data;
+ u32 action;
+ int ret;
+
+ if (!vif->cfg.assoc || !ieee80211_vif_is_mld(vif))
+ return -EINVAL;
+
+ if (kstrtou32(buf, 0, &action))
+ return -EINVAL;
+
+ if (action == 0) {
+ ret = iwl_mld_scan_stop(mld, IWL_MLD_SCAN_INT_MLO, false);
+ } else if (action == 1) {
+ iwl_mld_int_mlo_scan(mld, vif);
+ ret = 0;
+ } else {
+ ret = -EINVAL;
+ }
+
+ return ret ?: count;
+}
+
+VIF_DEBUGFS_WRITE_FILE_OPS(int_mlo_scan, 32);
+
+void iwl_mld_add_vif_debugfs(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif)
+{
+ struct dentry *mld_vif_dbgfs =
+ debugfs_create_dir("iwlmld", vif->debugfs_dir);
+ struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
+ struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw);
+ char target[3 * 3 + 11 + (NL80211_WIPHY_NAME_MAXLEN + 1) +
+ (7 + IFNAMSIZ + 1) + 6 + 1];
+ char name[7 + IFNAMSIZ + 1];
+
+ /* Create symlink for convenience pointing to interface specific
+ * debugfs entries for the driver. For example, under
+ * /sys/kernel/debug/iwlwifi/0000\:02\:00.0/iwlmld/
+ * find
+ * netdev:wlan0 -> ../../../ieee80211/phy0/netdev:wlan0/iwlmld/
+ */
+ snprintf(name, sizeof(name), "%pd", vif->debugfs_dir);
+ snprintf(target, sizeof(target), "../../../%pd3/iwlmld",
+ vif->debugfs_dir);
+ if (!mld_vif->dbgfs_slink)
+ mld_vif->dbgfs_slink =
+ debugfs_create_symlink(name, mld->debugfs_dir, target);
+
+ if (iwlmld_mod_params.power_scheme != IWL_POWER_SCHEME_CAM &&
+ vif->type == NL80211_IFTYPE_STATION) {
+ VIF_DEBUGFS_ADD_FILE(pm_params, mld_vif_dbgfs, 0200);
+ VIF_DEBUGFS_ADD_FILE(bf_params, mld_vif_dbgfs, 0200);
+ }
+
+ if (vif->type == NL80211_IFTYPE_AP) {
+ VIF_DEBUGFS_ADD_FILE(inject_beacon_ie, mld_vif_dbgfs, 0200);
+ VIF_DEBUGFS_ADD_FILE(inject_beacon_ie_restore,
+ mld_vif_dbgfs, 0200);
+ }
+
+ VIF_DEBUGFS_ADD_FILE(low_latency, mld_vif_dbgfs, 0600);
+ VIF_DEBUGFS_ADD_FILE(twt_setup, mld_vif_dbgfs, 0200);
+ VIF_DEBUGFS_ADD_FILE(twt_operation, mld_vif_dbgfs, 0200);
+ VIF_DEBUGFS_ADD_FILE(int_mlo_scan, mld_vif_dbgfs, 0200);
+}
+#define LINK_DEBUGFS_WRITE_FILE_OPS(name, bufsz) \
+ WIPHY_DEBUGFS_WRITE_FILE_OPS(link_##name, bufsz, bss_conf)
+
+#define LINK_DEBUGFS_ADD_FILE_ALIAS(alias, name, parent, mode) \
+ debugfs_create_file(alias, mode, parent, link_conf, \
+ &iwl_dbgfs_link_##name##_ops)
+#define LINK_DEBUGFS_ADD_FILE(name, parent, mode) \
+ LINK_DEBUGFS_ADD_FILE_ALIAS(#name, name, parent, mode)
+
+void iwl_mld_add_link_debugfs(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_bss_conf *link_conf,
+ struct dentry *dir)
+{
+ struct dentry *mld_link_dir;
+
+ mld_link_dir = debugfs_lookup("iwlmld", dir);
+
+ /* For non-MLO vifs, the dir of deflink is the same as the vif's one.
+ * so if iwlmld dir already exists, this means that this is deflink.
+ * If not, this is a per-link dir of a MLO vif, add in it the iwlmld
+ * dir.
+ */
+ if (!mld_link_dir)
+ mld_link_dir = debugfs_create_dir("iwlmld", dir);
+}
+
+static ssize_t _iwl_dbgfs_fixed_rate_write(struct iwl_mld *mld, char *buf,
+ size_t count, void *data, bool v3)
+{
+ struct ieee80211_link_sta *link_sta = data;
+ struct iwl_mld_link_sta *mld_link_sta;
+ u32 rate;
+ u32 partial = false;
+ char pretty_rate[100];
+ int ret;
+ u8 fw_sta_id;
+
+ mld_link_sta = iwl_mld_link_sta_from_mac80211(link_sta);
+ if (WARN_ON(!mld_link_sta))
+ return -EINVAL;
+
+ fw_sta_id = mld_link_sta->fw_id;
+
+ if (sscanf(buf, "%i %i", &rate, &partial) == 0)
+ return -EINVAL;
+
+ if (iwl_mld_dbgfs_fw_cmd_disabled(mld))
+ return -EIO;
+
+ /* input is in FW format (v2 or v3) so convert to v3 */
+ rate = iwl_v3_rate_from_v2_v3(cpu_to_le32(rate), v3);
+ rate = le32_to_cpu(iwl_v3_rate_to_v2_v3(rate, mld->fw_rates_ver_3));
+
+ ret = iwl_mld_send_tlc_dhc(mld, fw_sta_id,
+ partial ? IWL_TLC_DEBUG_PARTIAL_FIXED_RATE :
+ IWL_TLC_DEBUG_FIXED_RATE,
+ rate);
+
+ rs_pretty_print_rate(pretty_rate, sizeof(pretty_rate), rate);
+
+ IWL_DEBUG_RATE(mld, "sta_id %d rate %s partial: %d, ret:%d\n",
+ fw_sta_id, pretty_rate, partial, ret);
+
+ return ret ? : count;
+}
+
+static ssize_t iwl_dbgfs_fixed_rate_write(struct iwl_mld *mld, char *buf,
+ size_t count, void *data)
+{
+ return _iwl_dbgfs_fixed_rate_write(mld, buf, count, data, false);
+}
+
+static ssize_t iwl_dbgfs_fixed_rate_v3_write(struct iwl_mld *mld, char *buf,
+ size_t count, void *data)
+{
+ return _iwl_dbgfs_fixed_rate_write(mld, buf, count, data, true);
+}
+
+static ssize_t iwl_dbgfs_tlc_dhc_write(struct iwl_mld *mld, char *buf,
+ size_t count, void *data)
+{
+ struct ieee80211_link_sta *link_sta = data;
+ struct iwl_mld_link_sta *mld_link_sta;
+ u32 type, value;
+ int ret;
+ u8 fw_sta_id;
+
+ mld_link_sta = iwl_mld_link_sta_from_mac80211(link_sta);
+ if (WARN_ON(!mld_link_sta))
+ return -EINVAL;
+
+ fw_sta_id = mld_link_sta->fw_id;
+
+ if (sscanf(buf, "%i %i", &type, &value) != 2) {
+ IWL_DEBUG_RATE(mld, "usage <type> <value>\n");
+ return -EINVAL;
+ }
+
+ if (iwl_mld_dbgfs_fw_cmd_disabled(mld))
+ return -EIO;
+
+ ret = iwl_mld_send_tlc_dhc(mld, fw_sta_id, type, value);
+
+ return ret ? : count;
+}
+
+#define LINK_STA_DEBUGFS_ADD_FILE_ALIAS(alias, name, parent, mode) \
+ debugfs_create_file(alias, mode, parent, link_sta, \
+ &iwl_dbgfs_##name##_ops)
+#define LINK_STA_DEBUGFS_ADD_FILE(name, parent, mode) \
+ LINK_STA_DEBUGFS_ADD_FILE_ALIAS(#name, name, parent, mode)
+
+#define LINK_STA_WIPHY_DEBUGFS_WRITE_OPS(name, bufsz) \
+ WIPHY_DEBUGFS_WRITE_FILE_OPS(name, bufsz, link_sta)
+
+LINK_STA_WIPHY_DEBUGFS_WRITE_OPS(tlc_dhc, 64);
+LINK_STA_WIPHY_DEBUGFS_WRITE_OPS(fixed_rate, 64);
+LINK_STA_WIPHY_DEBUGFS_WRITE_OPS(fixed_rate_v3, 64);
+
+void iwl_mld_add_link_sta_debugfs(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_link_sta *link_sta,
+ struct dentry *dir)
+{
+ LINK_STA_DEBUGFS_ADD_FILE(fixed_rate, dir, 0200);
+ LINK_STA_DEBUGFS_ADD_FILE(fixed_rate_v3, dir, 0200);
+ LINK_STA_DEBUGFS_ADD_FILE(tlc_dhc, dir, 0200);
+}
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/debugfs.h b/drivers/net/wireless/intel/iwlwifi/mld/debugfs.h
new file mode 100644
index 000000000000..eeba35342ba1
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/mld/debugfs.h
@@ -0,0 +1,244 @@
+/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
+/*
+ * Copyright (C) 2024-2025 Intel Corporation
+ */
+#include "iface.h"
+#include "sta.h"
+
+#define MLD_DEBUGFS_OPEN_WRAPPER(name, buflen, argtype) \
+struct dbgfs_##name##_data { \
+ argtype *arg; \
+ bool read_done; \
+ ssize_t rlen; \
+ char buf[buflen]; \
+}; \
+static int _iwl_dbgfs_##name##_open(struct inode *inode, \
+ struct file *file) \
+{ \
+ struct dbgfs_##name##_data *data; \
+ \
+ if ((file->f_flags & O_ACCMODE) == O_RDWR) \
+ return -EOPNOTSUPP; \
+ \
+ data = kzalloc(sizeof(*data), GFP_KERNEL); \
+ if (!data) \
+ return -ENOMEM; \
+ \
+ data->read_done = false; \
+ data->arg = inode->i_private; \
+ file->private_data = data; \
+ \
+ return 0; \
+}
+
+#define MLD_DEBUGFS_READ_WRAPPER(name) \
+static ssize_t _iwl_dbgfs_##name##_read(struct file *file, \
+ char __user *user_buf, \
+ size_t count, loff_t *ppos) \
+{ \
+ struct dbgfs_##name##_data *data = file->private_data; \
+ \
+ if (!data->read_done) { \
+ data->read_done = true; \
+ data->rlen = iwl_dbgfs_##name##_read(data->arg, \
+ sizeof(data->buf),\
+ data->buf); \
+ } \
+ \
+ if (data->rlen < 0) \
+ return data->rlen; \
+ return simple_read_from_buffer(user_buf, count, ppos, \
+ data->buf, data->rlen); \
+}
+
+static int _iwl_dbgfs_release(struct inode *inode, struct file *file)
+{
+ kfree(file->private_data);
+ return 0;
+}
+
+#define _MLD_DEBUGFS_READ_FILE_OPS(name, buflen, argtype) \
+MLD_DEBUGFS_OPEN_WRAPPER(name, buflen, argtype) \
+MLD_DEBUGFS_READ_WRAPPER(name) \
+static const struct file_operations iwl_dbgfs_##name##_ops = { \
+ .read = _iwl_dbgfs_##name##_read, \
+ .open = _iwl_dbgfs_##name##_open, \
+ .llseek = generic_file_llseek, \
+ .release = _iwl_dbgfs_release, \
+}
+
+#define WIPHY_DEBUGFS_WRITE_HANDLER_WRAPPER(name) \
+static ssize_t iwl_dbgfs_##name##_write_handler(struct wiphy *wiphy, \
+ struct file *file, char *buf, \
+ size_t count, void *data) \
+{ \
+ struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy); \
+ struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw); \
+ return iwl_dbgfs_##name##_write(mld, buf, count, data); \
+}
+
+static inline struct iwl_mld *
+iwl_mld_from_link_sta(struct ieee80211_link_sta *link_sta)
+{
+ struct ieee80211_vif *vif =
+ iwl_mld_sta_from_mac80211(link_sta->sta)->vif;
+ return iwl_mld_vif_from_mac80211(vif)->mld;
+}
+
+static inline struct iwl_mld *
+iwl_mld_from_bss_conf(struct ieee80211_bss_conf *link)
+{
+ return iwl_mld_vif_from_mac80211(link->vif)->mld;
+}
+
+static inline struct iwl_mld *iwl_mld_from_vif(struct ieee80211_vif *vif)
+{
+ return iwl_mld_vif_from_mac80211(vif)->mld;
+}
+
+#define WIPHY_DEBUGFS_WRITE_WRAPPER(name, bufsz, objtype) \
+WIPHY_DEBUGFS_WRITE_HANDLER_WRAPPER(name) \
+static ssize_t __iwl_dbgfs_##name##_write(struct file *file, \
+ const char __user *user_buf, \
+ size_t count, loff_t *ppos) \
+{ \
+ struct ieee80211_##objtype *arg = file->private_data; \
+ struct iwl_mld *mld = iwl_mld_from_##objtype(arg); \
+ char buf[bufsz] = {}; \
+ \
+ return wiphy_locked_debugfs_write(mld->wiphy, file, \
+ buf, sizeof(buf), \
+ user_buf, count, \
+ iwl_dbgfs_##name##_write_handler, \
+ arg); \
+}
+
+#define WIPHY_DEBUGFS_WRITE_FILE_OPS(name, bufsz, objtype) \
+ WIPHY_DEBUGFS_WRITE_WRAPPER(name, bufsz, objtype) \
+ static const struct file_operations iwl_dbgfs_##name##_ops = { \
+ .write = __iwl_dbgfs_##name##_write, \
+ .open = simple_open, \
+ .llseek = generic_file_llseek, \
+ }
+
+#define WIPHY_DEBUGFS_READ_HANDLER_WRAPPER_MLD(name) \
+static ssize_t iwl_dbgfs_##name##_read_handler(struct wiphy *wiphy, \
+ struct file *file, char *buf, \
+ size_t count, void *data) \
+{ \
+ struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy); \
+ struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw); \
+ return iwl_dbgfs_##name##_read(mld, buf, count); \
+}
+
+#define WIPHY_DEBUGFS_WRITE_HANDLER_WRAPPER_MLD(name) \
+static ssize_t iwl_dbgfs_##name##_write_handler(struct wiphy *wiphy, \
+ struct file *file, char *buf, \
+ size_t count, void *data) \
+{ \
+ struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy); \
+ struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw); \
+ return iwl_dbgfs_##name##_write(mld, buf, count); \
+}
+
+#define WIPHY_DEBUGFS_WRITE_WRAPPER_MLD(name) \
+WIPHY_DEBUGFS_WRITE_HANDLER_WRAPPER_MLD(name) \
+static ssize_t __iwl_dbgfs_##name##_write(struct file *file, \
+ const char __user *user_buf, \
+ size_t count, loff_t *ppos) \
+{ \
+ struct dbgfs_##name##_data *data = file->private_data; \
+ struct iwl_mld *mld = data->arg; \
+ \
+ return wiphy_locked_debugfs_write(mld->wiphy, file, \
+ data->buf, sizeof(data->buf), \
+ user_buf, count, \
+ iwl_dbgfs_##name##_write_handler, \
+ NULL); \
+}
+
+#define WIPHY_DEBUGFS_READ_WRAPPER_MLD(name) \
+WIPHY_DEBUGFS_READ_HANDLER_WRAPPER_MLD(name) \
+static ssize_t __iwl_dbgfs_##name##_read(struct file *file, \
+ char __user *user_buf, \
+ size_t count, loff_t *ppos) \
+{ \
+ struct dbgfs_##name##_data *data = file->private_data; \
+ struct iwl_mld *mld = data->arg; \
+ \
+ if (!data->read_done) { \
+ data->read_done = true; \
+ data->rlen = wiphy_locked_debugfs_read(mld->wiphy, \
+ file, data->buf, sizeof(data->buf), \
+ user_buf, count, ppos, \
+ iwl_dbgfs_##name##_read_handler, NULL); \
+ return data->rlen; \
+ } \
+ \
+ if (data->rlen < 0) \
+ return data->rlen; \
+ return simple_read_from_buffer(user_buf, count, ppos, \
+ data->buf, data->rlen); \
+}
+
+#define WIPHY_DEBUGFS_READ_FILE_OPS_MLD(name, bufsz) \
+ MLD_DEBUGFS_OPEN_WRAPPER(name, bufsz, struct iwl_mld) \
+ WIPHY_DEBUGFS_READ_WRAPPER_MLD(name) \
+ static const struct file_operations iwl_dbgfs_##name##_ops = { \
+ .read = __iwl_dbgfs_##name##_read, \
+ .open = _iwl_dbgfs_##name##_open, \
+ .llseek = generic_file_llseek, \
+ .release = _iwl_dbgfs_release, \
+ }
+
+#define WIPHY_DEBUGFS_WRITE_FILE_OPS_MLD(name, bufsz) \
+ MLD_DEBUGFS_OPEN_WRAPPER(name, bufsz, struct iwl_mld) \
+ WIPHY_DEBUGFS_WRITE_WRAPPER_MLD(name) \
+ static const struct file_operations iwl_dbgfs_##name##_ops = { \
+ .write = __iwl_dbgfs_##name##_write, \
+ .open = _iwl_dbgfs_##name##_open, \
+ .llseek = generic_file_llseek, \
+ .release = _iwl_dbgfs_release, \
+ }
+
+#define WIPHY_DEBUGFS_READ_WRITE_FILE_OPS_MLD(name, bufsz) \
+ MLD_DEBUGFS_OPEN_WRAPPER(name, bufsz, struct iwl_mld) \
+ WIPHY_DEBUGFS_WRITE_WRAPPER_MLD(name) \
+ WIPHY_DEBUGFS_READ_WRAPPER_MLD(name) \
+ static const struct file_operations iwl_dbgfs_##name##_ops = { \
+ .write = __iwl_dbgfs_##name##_write, \
+ .read = __iwl_dbgfs_##name##_read, \
+ .open = _iwl_dbgfs_##name##_open, \
+ .llseek = generic_file_llseek, \
+ .release = _iwl_dbgfs_release, \
+ }
+
+#define WIPHY_DEBUGFS_WRITE_WRAPPER_IEEE80211(name, bufsz, objtype) \
+WIPHY_DEBUGFS_WRITE_HANDLER_WRAPPER(name) \
+static ssize_t _iwl_dbgfs_##name##_write(struct file *file, \
+ const char __user *user_buf, \
+ size_t count, loff_t *ppos) \
+{ \
+ struct dbgfs_##name##_data *data = file->private_data; \
+ struct ieee80211_##objtype *arg = data->arg; \
+ struct iwl_mld *mld = iwl_mld_from_##objtype(arg); \
+ char buf[bufsz] = {}; \
+ \
+ return wiphy_locked_debugfs_write(mld->wiphy, file, \
+ buf, sizeof(buf), \
+ user_buf, count, \
+ iwl_dbgfs_##name##_write_handler, \
+ arg); \
+}
+
+#define IEEE80211_WIPHY_DEBUGFS_READ_WRITE_FILE_OPS(name, bufsz, objtype) \
+ MLD_DEBUGFS_OPEN_WRAPPER(name, bufsz, struct ieee80211_##objtype) \
+ WIPHY_DEBUGFS_WRITE_WRAPPER_IEEE80211(name, bufsz, objtype) \
+ MLD_DEBUGFS_READ_WRAPPER(name) \
+ static const struct file_operations iwl_dbgfs_##name##_ops = { \
+ .write = _iwl_dbgfs_##name##_write, \
+ .read = _iwl_dbgfs_##name##_read, \
+ .open = _iwl_dbgfs_##name##_open, \
+ .llseek = generic_file_llseek, \
+ .release = _iwl_dbgfs_release, \
+ }
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/ftm-initiator.c b/drivers/net/wireless/intel/iwlwifi/mld/ftm-initiator.c
new file mode 100644
index 000000000000..f77ba21a174d
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/mld/ftm-initiator.c
@@ -0,0 +1,451 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
+/*
+ * Copyright (C) 2025 Intel Corporation
+ */
+#include <linux/etherdevice.h>
+#include <linux/math64.h>
+#include <net/cfg80211.h>
+#include "mld.h"
+#include "iface.h"
+#include "phy.h"
+#include "iwl-io.h"
+#include "iwl-prph.h"
+#include "constants.h"
+#include "fw/api/location.h"
+#include "ftm-initiator.h"
+
+static void iwl_mld_ftm_cmd_common(struct iwl_mld *mld,
+ struct ieee80211_vif *vif,
+ struct iwl_tof_range_req_cmd *cmd,
+ struct cfg80211_pmsr_request *req)
+{
+ int i;
+
+ cmd->initiator_flags =
+ cpu_to_le32(IWL_TOF_INITIATOR_FLAGS_MACADDR_RANDOM |
+ IWL_TOF_INITIATOR_FLAGS_NON_ASAP_SUPPORT);
+ cmd->request_id = req->cookie;
+ cmd->num_of_ap = req->n_peers;
+
+ /* Use a large value for "no timeout". Don't use the maximum value
+ * because of fw limitations.
+ */
+ if (req->timeout)
+ cmd->req_timeout_ms = cpu_to_le32(min(req->timeout, 0xfffff));
+ else
+ cmd->req_timeout_ms = cpu_to_le32(0xfffff);
+
+ memcpy(cmd->macaddr_template, req->mac_addr, ETH_ALEN);
+ for (i = 0; i < ETH_ALEN; i++)
+ cmd->macaddr_mask[i] = ~req->mac_addr_mask[i];
+
+ if (vif->cfg.assoc) {
+ memcpy(cmd->range_req_bssid, vif->bss_conf.bssid, ETH_ALEN);
+
+ /* AP's TSF is only relevant if associated */
+ for (i = 0; i < req->n_peers; i++) {
+ if (req->peers[i].report_ap_tsf) {
+ struct iwl_mld_vif *mld_vif =
+ iwl_mld_vif_from_mac80211(vif);
+
+ cmd->tsf_mac_id = cpu_to_le32(mld_vif->fw_id);
+ return;
+ }
+ }
+ } else {
+ eth_broadcast_addr(cmd->range_req_bssid);
+ }
+
+ /* Don't report AP's TSF */
+ cmd->tsf_mac_id = cpu_to_le32(0xff);
+}
+
+static int
+iwl_mld_ftm_set_target_chandef(struct iwl_mld *mld,
+ struct cfg80211_pmsr_request_peer *peer,
+ struct iwl_tof_range_req_ap_entry *target)
+{
+ u32 freq = peer->chandef.chan->center_freq;
+
+ target->channel_num = ieee80211_frequency_to_channel(freq);
+
+ switch (peer->chandef.width) {
+ case NL80211_CHAN_WIDTH_20_NOHT:
+ target->format_bw = IWL_LOCATION_FRAME_FORMAT_LEGACY;
+ target->format_bw |= IWL_LOCATION_BW_20MHZ << LOCATION_BW_POS;
+ break;
+ case NL80211_CHAN_WIDTH_20:
+ target->format_bw = IWL_LOCATION_FRAME_FORMAT_HT;
+ target->format_bw |= IWL_LOCATION_BW_20MHZ << LOCATION_BW_POS;
+ break;
+ case NL80211_CHAN_WIDTH_40:
+ target->format_bw = IWL_LOCATION_FRAME_FORMAT_HT;
+ target->format_bw |= IWL_LOCATION_BW_40MHZ << LOCATION_BW_POS;
+ break;
+ case NL80211_CHAN_WIDTH_80:
+ target->format_bw = IWL_LOCATION_FRAME_FORMAT_VHT;
+ target->format_bw |= IWL_LOCATION_BW_80MHZ << LOCATION_BW_POS;
+ break;
+ case NL80211_CHAN_WIDTH_160:
+ target->format_bw = IWL_LOCATION_FRAME_FORMAT_HE;
+ target->format_bw |= IWL_LOCATION_BW_160MHZ << LOCATION_BW_POS;
+ break;
+ default:
+ IWL_ERR(mld, "Unsupported BW in FTM request (%d)\n",
+ peer->chandef.width);
+ return -EINVAL;
+}
+
+ /* non EDCA based measurement must use HE preamble */
+ if (peer->ftm.trigger_based || peer->ftm.non_trigger_based)
+ target->format_bw |= IWL_LOCATION_FRAME_FORMAT_HE;
+
+ target->ctrl_ch_position =
+ (peer->chandef.width > NL80211_CHAN_WIDTH_20) ?
+ iwl_mld_get_fw_ctrl_pos(&peer->chandef) : 0;
+
+ target->band = iwl_mld_nl80211_band_to_fw(peer->chandef.chan->band);
+ return 0;
+}
+
+#define FTM_SET_FLAG(flag) (target->initiator_ap_flags |= \
+ cpu_to_le32(IWL_INITIATOR_AP_FLAGS_##flag))
+
+static void
+iwl_mld_ftm_set_target_flags(struct iwl_mld *mld,
+ struct cfg80211_pmsr_request_peer *peer,
+ struct iwl_tof_range_req_ap_entry *target)
+{
+ target->initiator_ap_flags = cpu_to_le32(0);
+
+ if (peer->ftm.asap)
+ FTM_SET_FLAG(ASAP);
+
+ if (peer->ftm.request_lci)
+ FTM_SET_FLAG(LCI_REQUEST);
+
+ if (peer->ftm.request_civicloc)
+ FTM_SET_FLAG(CIVIC_REQUEST);
+
+ if (IWL_MLD_FTM_INITIATOR_DYNACK)
+ FTM_SET_FLAG(DYN_ACK);
+
+ if (IWL_MLD_FTM_INITIATOR_ALGO == IWL_TOF_ALGO_TYPE_LINEAR_REG)
+ FTM_SET_FLAG(ALGO_LR);
+ else if (IWL_MLD_FTM_INITIATOR_ALGO == IWL_TOF_ALGO_TYPE_FFT)
+ FTM_SET_FLAG(ALGO_FFT);
+
+ if (peer->ftm.trigger_based)
+ FTM_SET_FLAG(TB);
+ else if (peer->ftm.non_trigger_based)
+ FTM_SET_FLAG(NON_TB);
+
+ if ((peer->ftm.trigger_based || peer->ftm.non_trigger_based) &&
+ peer->ftm.lmr_feedback)
+ FTM_SET_FLAG(LMR_FEEDBACK);
+}
+
+static void iwl_mld_ftm_set_sta(struct iwl_mld *mld, struct ieee80211_vif *vif,
+ struct cfg80211_pmsr_request_peer *peer,
+ struct iwl_tof_range_req_ap_entry *target)
+{
+ struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
+ u32 sta_id_mask;
+
+ target->sta_id = IWL_INVALID_STA;
+
+ /* TODO: add ftm_unprotected debugfs support */
+
+ if (!vif->cfg.assoc || !mld_vif->ap_sta)
+ return;
+
+ sta_id_mask = iwl_mld_fw_sta_id_mask(mld, mld_vif->ap_sta);
+ if (WARN_ON(hweight32(sta_id_mask) != 1))
+ return;
+
+ target->sta_id = __ffs(sta_id_mask);
+
+ if (mld_vif->ap_sta->mfp &&
+ (peer->ftm.trigger_based || peer->ftm.non_trigger_based))
+ FTM_SET_FLAG(PMF);
+}
+
+static int
+iwl_mld_ftm_set_target(struct iwl_mld *mld, struct ieee80211_vif *vif,
+ struct cfg80211_pmsr_request_peer *peer,
+ struct iwl_tof_range_req_ap_entry *target)
+{
+ u32 i2r_max_sts;
+ int ret;
+
+ ret = iwl_mld_ftm_set_target_chandef(mld, peer, target);
+ if (ret)
+ return ret;
+
+ memcpy(target->bssid, peer->addr, ETH_ALEN);
+ target->burst_period = cpu_to_le16(peer->ftm.burst_period);
+ target->samples_per_burst = peer->ftm.ftms_per_burst;
+ target->num_of_bursts = peer->ftm.num_bursts_exp;
+ iwl_mld_ftm_set_target_flags(mld, peer, target);
+ iwl_mld_ftm_set_sta(mld, vif, peer, target);
+
+ /* TODO: add secured ranging support */
+
+ i2r_max_sts = IWL_MLD_FTM_I2R_MAX_STS > 1 ? 1 :
+ IWL_MLD_FTM_I2R_MAX_STS;
+
+ target->r2i_ndp_params = IWL_MLD_FTM_R2I_MAX_REP |
+ (IWL_MLD_FTM_R2I_MAX_STS << IWL_LOCATION_MAX_STS_POS) |
+ (IWL_MLD_FTM_R2I_MAX_TOTAL_LTF << IWL_LOCATION_TOTAL_LTF_POS);
+ target->i2r_ndp_params = IWL_MLD_FTM_I2R_MAX_REP |
+ (i2r_max_sts << IWL_LOCATION_MAX_STS_POS) |
+ (IWL_MLD_FTM_I2R_MAX_TOTAL_LTF << IWL_LOCATION_TOTAL_LTF_POS);
+
+ if (peer->ftm.non_trigger_based) {
+ target->min_time_between_msr =
+ cpu_to_le16(IWL_MLD_FTM_NON_TB_MIN_TIME_BETWEEN_MSR);
+ target->burst_period =
+ cpu_to_le16(IWL_MLD_FTM_NON_TB_MAX_TIME_BETWEEN_MSR);
+ } else {
+ target->min_time_between_msr = cpu_to_le16(0);
+ }
+
+ /* TODO: Beacon interval is currently unknown, so use the common value
+ * of 100 TUs.
+ */
+ target->beacon_interval = cpu_to_le16(100);
+
+ return 0;
+}
+
+int iwl_mld_ftm_start(struct iwl_mld *mld, struct ieee80211_vif *vif,
+ struct cfg80211_pmsr_request *req)
+{
+ struct iwl_tof_range_req_cmd cmd;
+ struct iwl_host_cmd hcmd = {
+ .id = WIDE_ID(LOCATION_GROUP, TOF_RANGE_REQ_CMD),
+ .dataflags[0] = IWL_HCMD_DFL_DUP,
+ .data[0] = &cmd,
+ .len[0] = sizeof(cmd),
+ };
+ u8 i;
+ int ret;
+
+ lockdep_assert_wiphy(mld->wiphy);
+
+ if (mld->ftm_initiator.req)
+ return -EBUSY;
+
+ if (req->n_peers > ARRAY_SIZE(cmd.ap))
+ return -EINVAL;
+
+ memset(&cmd, 0, sizeof(cmd));
+
+ iwl_mld_ftm_cmd_common(mld, vif, (void *)&cmd, req);
+
+ for (i = 0; i < cmd.num_of_ap; i++) {
+ struct cfg80211_pmsr_request_peer *peer = &req->peers[i];
+ struct iwl_tof_range_req_ap_entry *target = &cmd.ap[i];
+
+ ret = iwl_mld_ftm_set_target(mld, vif, peer, target);
+ if (ret)
+ return ret;
+ }
+
+ /* TODO: get the status from the response*/
+ ret = iwl_mld_send_cmd(mld, &hcmd);
+ if (!ret) {
+ mld->ftm_initiator.req = req;
+ mld->ftm_initiator.req_wdev = ieee80211_vif_to_wdev(vif);
+ }
+
+ return ret;
+}
+
+static void iwl_mld_ftm_reset(struct iwl_mld *mld)
+{
+ lockdep_assert_wiphy(mld->wiphy);
+
+ mld->ftm_initiator.req = NULL;
+ mld->ftm_initiator.req_wdev = NULL;
+ memset(mld->ftm_initiator.responses, 0,
+ sizeof(mld->ftm_initiator.responses));
+}
+
+static int iwl_mld_ftm_range_resp_valid(struct iwl_mld *mld, u8 request_id,
+ u8 num_of_aps)
+{
+ if (IWL_FW_CHECK(mld, request_id != (u8)mld->ftm_initiator.req->cookie,
+ "Request ID mismatch, got %u, active %u\n",
+ request_id, (u8)mld->ftm_initiator.req->cookie))
+ return -EINVAL;
+
+ if (IWL_FW_CHECK(mld, num_of_aps > mld->ftm_initiator.req->n_peers ||
+ num_of_aps > IWL_TOF_MAX_APS,
+ "FTM range response: invalid num of APs (%u)\n",
+ num_of_aps))
+ return -EINVAL;
+
+ return 0;
+}
+
+static int iwl_mld_ftm_find_peer(struct cfg80211_pmsr_request *req,
+ const u8 *addr)
+{
+ for (int i = 0; i < req->n_peers; i++) {
+ struct cfg80211_pmsr_request_peer *peer = &req->peers[i];
+
+ if (ether_addr_equal_unaligned(peer->addr, addr))
+ return i;
+ }
+
+ return -ENOENT;
+}
+
+static void iwl_mld_debug_range_resp(struct iwl_mld *mld, u8 index,
+ struct cfg80211_pmsr_result *res)
+{
+ s64 rtt_avg = div_s64(res->ftm.rtt_avg * 100, 6666);
+
+ IWL_DEBUG_INFO(mld, "entry %d\n", index);
+ IWL_DEBUG_INFO(mld, "\tstatus: %d\n", res->status);
+ IWL_DEBUG_INFO(mld, "\tBSSID: %pM\n", res->addr);
+ IWL_DEBUG_INFO(mld, "\thost time: %llu\n", res->host_time);
+ IWL_DEBUG_INFO(mld, "\tburst index: %d\n", res->ftm.burst_index);
+ IWL_DEBUG_INFO(mld, "\tsuccess num: %u\n", res->ftm.num_ftmr_successes);
+ IWL_DEBUG_INFO(mld, "\trssi: %d\n", res->ftm.rssi_avg);
+ IWL_DEBUG_INFO(mld, "\trssi spread: %d\n", res->ftm.rssi_spread);
+ IWL_DEBUG_INFO(mld, "\trtt: %lld\n", res->ftm.rtt_avg);
+ IWL_DEBUG_INFO(mld, "\trtt var: %llu\n", res->ftm.rtt_variance);
+ IWL_DEBUG_INFO(mld, "\trtt spread: %llu\n", res->ftm.rtt_spread);
+ IWL_DEBUG_INFO(mld, "\tdistance: %lld\n", rtt_avg);
+}
+
+void iwl_mld_handle_ftm_resp_notif(struct iwl_mld *mld,
+ struct iwl_rx_packet *pkt)
+{
+ struct iwl_tof_range_rsp_ntfy *fw_resp = (void *)pkt->data;
+ u8 num_of_aps, last_in_batch;
+
+ if (IWL_FW_CHECK(mld, !mld->ftm_initiator.req,
+ "FTM response without a pending request\n"))
+ return;
+
+ if (iwl_mld_ftm_range_resp_valid(mld, fw_resp->request_id,
+ fw_resp->num_of_aps))
+ return;
+
+ num_of_aps = fw_resp->num_of_aps;
+ last_in_batch = fw_resp->last_report;
+
+ IWL_DEBUG_INFO(mld, "Range response received\n");
+ IWL_DEBUG_INFO(mld, "request id: %llu, num of entries: %u\n",
+ mld->ftm_initiator.req->cookie, num_of_aps);
+
+ for (int i = 0; i < num_of_aps; i++) {
+ struct cfg80211_pmsr_result result = {};
+ struct iwl_tof_range_rsp_ap_entry_ntfy *fw_ap;
+ int peer_idx;
+
+ fw_ap = &fw_resp->ap[i];
+ result.final = fw_ap->last_burst;
+ result.ap_tsf = le32_to_cpu(fw_ap->start_tsf);
+ result.ap_tsf_valid = 1;
+
+ peer_idx = iwl_mld_ftm_find_peer(mld->ftm_initiator.req,
+ fw_ap->bssid);
+ if (peer_idx < 0) {
+ IWL_WARN(mld,
+ "Unknown address (%pM, target #%d) in FTM response\n",
+ fw_ap->bssid, i);
+ continue;
+ }
+
+ switch (fw_ap->measure_status) {
+ case IWL_TOF_ENTRY_SUCCESS:
+ result.status = NL80211_PMSR_STATUS_SUCCESS;
+ break;
+ case IWL_TOF_ENTRY_TIMING_MEASURE_TIMEOUT:
+ result.status = NL80211_PMSR_STATUS_TIMEOUT;
+ break;
+ case IWL_TOF_ENTRY_NO_RESPONSE:
+ result.status = NL80211_PMSR_STATUS_FAILURE;
+ result.ftm.failure_reason =
+ NL80211_PMSR_FTM_FAILURE_NO_RESPONSE;
+ break;
+ case IWL_TOF_ENTRY_REQUEST_REJECTED:
+ result.status = NL80211_PMSR_STATUS_FAILURE;
+ result.ftm.failure_reason =
+ NL80211_PMSR_FTM_FAILURE_PEER_BUSY;
+ result.ftm.busy_retry_time = fw_ap->refusal_period;
+ break;
+ default:
+ result.status = NL80211_PMSR_STATUS_FAILURE;
+ result.ftm.failure_reason =
+ NL80211_PMSR_FTM_FAILURE_UNSPECIFIED;
+ break;
+ }
+ memcpy(result.addr, fw_ap->bssid, ETH_ALEN);
+
+ /* TODO: convert the timestamp from the result to systime */
+ result.host_time = ktime_get_boottime_ns();
+
+ result.type = NL80211_PMSR_TYPE_FTM;
+ result.ftm.burst_index = mld->ftm_initiator.responses[peer_idx];
+ mld->ftm_initiator.responses[peer_idx]++;
+ result.ftm.rssi_avg = fw_ap->rssi;
+ result.ftm.rssi_avg_valid = 1;
+ result.ftm.rssi_spread = fw_ap->rssi_spread;
+ result.ftm.rssi_spread_valid = 1;
+ result.ftm.rtt_avg = (s32)le32_to_cpu(fw_ap->rtt);
+ result.ftm.rtt_avg_valid = 1;
+ result.ftm.rtt_variance = le32_to_cpu(fw_ap->rtt_variance);
+ result.ftm.rtt_variance_valid = 1;
+ result.ftm.rtt_spread = le32_to_cpu(fw_ap->rtt_spread);
+ result.ftm.rtt_spread_valid = 1;
+
+ cfg80211_pmsr_report(mld->ftm_initiator.req_wdev,
+ mld->ftm_initiator.req,
+ &result, GFP_KERNEL);
+
+ if (fw_has_api(&mld->fw->ucode_capa,
+ IWL_UCODE_TLV_API_FTM_RTT_ACCURACY))
+ IWL_DEBUG_INFO(mld, "RTT confidence: %u\n",
+ fw_ap->rttConfidence);
+
+ iwl_mld_debug_range_resp(mld, i, &result);
+ }
+
+ if (last_in_batch) {
+ cfg80211_pmsr_complete(mld->ftm_initiator.req_wdev,
+ mld->ftm_initiator.req,
+ GFP_KERNEL);
+ iwl_mld_ftm_reset(mld);
+ }
+}
+
+void iwl_mld_ftm_restart_cleanup(struct iwl_mld *mld)
+{
+ struct cfg80211_pmsr_result result = {
+ .status = NL80211_PMSR_STATUS_FAILURE,
+ .final = 1,
+ .host_time = ktime_get_boottime_ns(),
+ .type = NL80211_PMSR_TYPE_FTM,
+ };
+
+ if (!mld->ftm_initiator.req)
+ return;
+
+ for (int i = 0; i < mld->ftm_initiator.req->n_peers; i++) {
+ memcpy(result.addr, mld->ftm_initiator.req->peers[i].addr,
+ ETH_ALEN);
+
+ cfg80211_pmsr_report(mld->ftm_initiator.req_wdev,
+ mld->ftm_initiator.req,
+ &result, GFP_KERNEL);
+ }
+
+ cfg80211_pmsr_complete(mld->ftm_initiator.req_wdev,
+ mld->ftm_initiator.req, GFP_KERNEL);
+ iwl_mld_ftm_reset(mld);
+}
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/ftm-initiator.h b/drivers/net/wireless/intel/iwlwifi/mld/ftm-initiator.h
new file mode 100644
index 000000000000..3fab25a52508
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/mld/ftm-initiator.h
@@ -0,0 +1,29 @@
+/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
+/*
+ * Copyright (C) 2025 Intel Corporation
+ */
+#ifndef __iwl_mld_ftm_initiator_h__
+#define __iwl_mld_ftm_initiator_h__
+
+/**
+ * struct ftm_initiator_data - FTM initiator data
+ *
+ * @req: a pointer to cfg80211 FTM request
+ * @req_wdev: a pointer to the wdev that requested the current FTM request
+ * @responses: the number of responses received for the current FTM session.
+ * Used for tracking the burst index in a periodic request.
+ */
+struct ftm_initiator_data {
+ struct cfg80211_pmsr_request *req;
+ struct wireless_dev *req_wdev;
+ int responses[IWL_TOF_MAX_APS];
+};
+
+int iwl_mld_ftm_start(struct iwl_mld *mld, struct ieee80211_vif *vif,
+ struct cfg80211_pmsr_request *req);
+
+void iwl_mld_handle_ftm_resp_notif(struct iwl_mld *mld,
+ struct iwl_rx_packet *pkt);
+void iwl_mld_ftm_restart_cleanup(struct iwl_mld *mld);
+
+#endif /* __iwl_mld_ftm_initiator_h__ */
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/fw.c b/drivers/net/wireless/intel/iwlwifi/mld/fw.c
new file mode 100644
index 000000000000..73ed8d5cab43
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/mld/fw.c
@@ -0,0 +1,554 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
+/*
+ * Copyright (C) 2024-2025 Intel Corporation
+ */
+
+#include "mld.h"
+
+#include "fw/api/alive.h"
+#include "fw/api/scan.h"
+#include "fw/api/rx.h"
+#include "phy.h"
+#include "fw/dbg.h"
+#include "fw/pnvm.h"
+#include "hcmd.h"
+#include "power.h"
+#include "mcc.h"
+#include "led.h"
+#include "coex.h"
+#include "regulatory.h"
+#include "thermal.h"
+
+static int iwl_mld_send_tx_ant_cfg(struct iwl_mld *mld)
+{
+ struct iwl_tx_ant_cfg_cmd cmd;
+
+ lockdep_assert_wiphy(mld->wiphy);
+
+ cmd.valid = cpu_to_le32(iwl_mld_get_valid_tx_ant(mld));
+
+ IWL_DEBUG_FW(mld, "select valid tx ant: %u\n", cmd.valid);
+
+ return iwl_mld_send_cmd_pdu(mld, TX_ANT_CONFIGURATION_CMD, &cmd);
+}
+
+static int iwl_mld_send_rss_cfg_cmd(struct iwl_mld *mld)
+{
+ struct iwl_rss_config_cmd cmd = {
+ .flags = cpu_to_le32(IWL_RSS_ENABLE),
+ .hash_mask = BIT(IWL_RSS_HASH_TYPE_IPV4_TCP) |
+ BIT(IWL_RSS_HASH_TYPE_IPV4_UDP) |
+ BIT(IWL_RSS_HASH_TYPE_IPV4_PAYLOAD) |
+ BIT(IWL_RSS_HASH_TYPE_IPV6_TCP) |
+ BIT(IWL_RSS_HASH_TYPE_IPV6_UDP) |
+ BIT(IWL_RSS_HASH_TYPE_IPV6_PAYLOAD),
+ };
+
+ lockdep_assert_wiphy(mld->wiphy);
+
+ /* Do not direct RSS traffic to Q 0 which is our fallback queue */
+ for (int i = 0; i < ARRAY_SIZE(cmd.indirection_table); i++)
+ cmd.indirection_table[i] =
+ 1 + (i % (mld->trans->info.num_rxqs - 1));
+ netdev_rss_key_fill(cmd.secret_key, sizeof(cmd.secret_key));
+
+ return iwl_mld_send_cmd_pdu(mld, RSS_CONFIG_CMD, &cmd);
+}
+
+static int iwl_mld_config_scan(struct iwl_mld *mld)
+{
+ struct iwl_scan_config cmd = {
+ .tx_chains = cpu_to_le32(iwl_mld_get_valid_tx_ant(mld)),
+ .rx_chains = cpu_to_le32(iwl_mld_get_valid_rx_ant(mld))
+ };
+
+ return iwl_mld_send_cmd_pdu(mld, WIDE_ID(LONG_GROUP, SCAN_CFG_CMD),
+ &cmd);
+}
+
+static void iwl_mld_alive_imr_data(struct iwl_trans *trans,
+ const struct iwl_imr_alive_info *imr_info)
+{
+ struct iwl_imr_data *imr_data = &trans->dbg.imr_data;
+
+ imr_data->imr_enable = le32_to_cpu(imr_info->enabled);
+ imr_data->imr_size = le32_to_cpu(imr_info->size);
+ imr_data->imr2sram_remainbyte = imr_data->imr_size;
+ imr_data->imr_base_addr = imr_info->base_addr;
+ imr_data->imr_curr_addr = le64_to_cpu(imr_data->imr_base_addr);
+
+ if (imr_data->imr_enable)
+ return;
+
+ for (int i = 0; i < ARRAY_SIZE(trans->dbg.active_regions); i++) {
+ struct iwl_fw_ini_region_tlv *reg;
+
+ if (!trans->dbg.active_regions[i])
+ continue;
+
+ reg = (void *)trans->dbg.active_regions[i]->data;
+
+ /* We have only one DRAM IMR region, so we
+ * can break as soon as we find the first
+ * one.
+ */
+ if (reg->type == IWL_FW_INI_REGION_DRAM_IMR) {
+ trans->dbg.unsupported_region_msk |= BIT(i);
+ break;
+ }
+ }
+}
+
+struct iwl_mld_alive_data {
+ __le32 sku_id[3];
+ bool valid;
+};
+
+static bool iwl_alive_fn(struct iwl_notif_wait_data *notif_wait,
+ struct iwl_rx_packet *pkt, void *data)
+{
+ unsigned int pkt_len = iwl_rx_packet_payload_len(pkt);
+ unsigned int expected_sz;
+ struct iwl_mld *mld =
+ container_of(notif_wait, struct iwl_mld, notif_wait);
+ struct iwl_trans *trans = mld->trans;
+ u32 version = iwl_fw_lookup_notif_ver(mld->fw, LEGACY_GROUP,
+ UCODE_ALIVE_NTFY, 0);
+ struct iwl_mld_alive_data *alive_data = data;
+ struct iwl_alive_ntf *palive;
+ struct iwl_umac_alive *umac;
+ struct iwl_lmac_alive *lmac1;
+ struct iwl_lmac_alive *lmac2 = NULL;
+ u32 lmac_error_event_table;
+ u32 umac_error_table;
+ u16 status;
+
+ switch (version) {
+ case 6:
+ case 7:
+ expected_sz = sizeof(struct iwl_alive_ntf_v6);
+ break;
+ case 8:
+ expected_sz = sizeof(struct iwl_alive_ntf);
+ break;
+ default:
+ return false;
+ }
+
+ if (pkt_len != expected_sz)
+ return false;
+
+ palive = (void *)pkt->data;
+
+ iwl_mld_alive_imr_data(trans, &palive->imr);
+
+ umac = &palive->umac_data;
+ lmac1 = &palive->lmac_data[0];
+ lmac2 = &palive->lmac_data[1];
+ status = le16_to_cpu(palive->status);
+
+ BUILD_BUG_ON(sizeof(alive_data->sku_id) !=
+ sizeof(palive->sku_id.data));
+ memcpy(alive_data->sku_id, palive->sku_id.data,
+ sizeof(palive->sku_id.data));
+
+ IWL_DEBUG_FW(mld, "Got sku_id: 0x0%x 0x0%x 0x0%x\n",
+ le32_to_cpu(alive_data->sku_id[0]),
+ le32_to_cpu(alive_data->sku_id[1]),
+ le32_to_cpu(alive_data->sku_id[2]));
+
+ lmac_error_event_table =
+ le32_to_cpu(lmac1->dbg_ptrs.error_event_table_ptr);
+ iwl_fw_lmac1_set_alive_err_table(trans, lmac_error_event_table);
+
+ if (lmac2)
+ trans->dbg.lmac_error_event_table[1] =
+ le32_to_cpu(lmac2->dbg_ptrs.error_event_table_ptr);
+
+ umac_error_table = le32_to_cpu(umac->dbg_ptrs.error_info_addr) &
+ ~FW_ADDR_CACHE_CONTROL;
+
+ if (umac_error_table >= trans->mac_cfg->base->min_umac_error_event_table)
+ iwl_fw_umac_set_alive_err_table(trans, umac_error_table);
+ else
+ IWL_ERR(mld, "Not valid error log pointer 0x%08X\n",
+ umac_error_table);
+
+ alive_data->valid = status == IWL_ALIVE_STATUS_OK;
+
+ IWL_DEBUG_FW(mld,
+ "Alive ucode status 0x%04x revision 0x%01X 0x%01X\n",
+ status, lmac1->ver_type, lmac1->ver_subtype);
+
+ if (lmac2)
+ IWL_DEBUG_FW(mld, "Alive ucode CDB\n");
+
+ IWL_DEBUG_FW(mld,
+ "UMAC version: Major - 0x%x, Minor - 0x%x\n",
+ le32_to_cpu(umac->umac_major),
+ le32_to_cpu(umac->umac_minor));
+
+ if (version >= 7)
+ IWL_DEBUG_FW(mld, "FW alive flags 0x%x\n",
+ le16_to_cpu(palive->flags));
+
+ if (version >= 8)
+ IWL_DEBUG_FW(mld, "platform_id 0x%llx\n",
+ le64_to_cpu(palive->platform_id));
+
+ iwl_fwrt_update_fw_versions(&mld->fwrt, lmac1, umac);
+
+ return true;
+}
+
+#define MLD_ALIVE_TIMEOUT (2 * HZ)
+#define MLD_INIT_COMPLETE_TIMEOUT (2 * HZ)
+
+static void iwl_mld_print_alive_notif_timeout(struct iwl_mld *mld)
+{
+ struct iwl_trans *trans = mld->trans;
+ struct iwl_pc_data *pc_data;
+ u8 count;
+
+ IWL_ERR(mld,
+ "SecBoot CPU1 Status: 0x%x, CPU2 Status: 0x%x\n",
+ iwl_read_umac_prph(trans, UMAG_SB_CPU_1_STATUS),
+ iwl_read_umac_prph(trans,
+ UMAG_SB_CPU_2_STATUS));
+#define IWL_FW_PRINT_REG_INFO(reg_name) \
+ IWL_ERR(mld, #reg_name ": 0x%x\n", iwl_read_umac_prph(trans, reg_name))
+
+ IWL_FW_PRINT_REG_INFO(WFPM_LMAC1_PD_NOTIFICATION);
+
+ IWL_FW_PRINT_REG_INFO(HPM_SECONDARY_DEVICE_STATE);
+
+ /* print OTP info */
+ IWL_FW_PRINT_REG_INFO(WFPM_MAC_OTP_CFG7_ADDR);
+ IWL_FW_PRINT_REG_INFO(WFPM_MAC_OTP_CFG7_DATA);
+#undef IWL_FW_PRINT_REG_INFO
+
+ pc_data = trans->dbg.pc_data;
+ for (count = 0; count < trans->dbg.num_pc; count++, pc_data++)
+ IWL_ERR(mld, "%s: 0x%x\n", pc_data->pc_name,
+ pc_data->pc_address);
+}
+
+static int iwl_mld_load_fw_wait_alive(struct iwl_mld *mld,
+ struct iwl_mld_alive_data *alive_data)
+{
+ static const u16 alive_cmd[] = { UCODE_ALIVE_NTFY };
+ struct iwl_notification_wait alive_wait;
+ int ret;
+
+ lockdep_assert_wiphy(mld->wiphy);
+
+ iwl_init_notification_wait(&mld->notif_wait, &alive_wait,
+ alive_cmd, ARRAY_SIZE(alive_cmd),
+ iwl_alive_fn, alive_data);
+
+ iwl_dbg_tlv_time_point(&mld->fwrt, IWL_FW_INI_TIME_POINT_EARLY, NULL);
+
+ ret = iwl_trans_start_fw(mld->trans, mld->fw, IWL_UCODE_REGULAR, true);
+ if (ret) {
+ iwl_remove_notification(&mld->notif_wait, &alive_wait);
+ return ret;
+ }
+
+ ret = iwl_wait_notification(&mld->notif_wait, &alive_wait,
+ MLD_ALIVE_TIMEOUT);
+
+ if (ret) {
+ if (ret == -ETIMEDOUT)
+ iwl_fw_dbg_error_collect(&mld->fwrt,
+ FW_DBG_TRIGGER_ALIVE_TIMEOUT);
+ iwl_mld_print_alive_notif_timeout(mld);
+ return ret;
+ }
+
+ if (!alive_data->valid) {
+ IWL_ERR(mld, "Loaded firmware is not valid!\n");
+ return -EIO;
+ }
+
+ iwl_trans_fw_alive(mld->trans);
+
+ return 0;
+}
+
+static int iwl_mld_run_fw_init_sequence(struct iwl_mld *mld)
+{
+ struct iwl_notification_wait init_wait;
+ struct iwl_init_extended_cfg_cmd init_cfg = {
+ .init_flags = cpu_to_le32(BIT(IWL_INIT_PHY)),
+ };
+ struct iwl_mld_alive_data alive_data = {};
+ static const u16 init_complete[] = {
+ INIT_COMPLETE_NOTIF,
+ };
+ int ret;
+
+ lockdep_assert_wiphy(mld->wiphy);
+
+ ret = iwl_mld_load_fw_wait_alive(mld, &alive_data);
+ if (ret)
+ return ret;
+
+ ret = iwl_pnvm_load(mld->trans, &mld->notif_wait,
+ &mld->fw->ucode_capa, alive_data.sku_id);
+ if (ret) {
+ IWL_ERR(mld, "Timeout waiting for PNVM load %d\n", ret);
+ return ret;
+ }
+
+ iwl_dbg_tlv_time_point(&mld->fwrt, IWL_FW_INI_TIME_POINT_AFTER_ALIVE,
+ NULL);
+
+ iwl_init_notification_wait(&mld->notif_wait,
+ &init_wait,
+ init_complete,
+ ARRAY_SIZE(init_complete),
+ NULL, NULL);
+
+ ret = iwl_mld_send_cmd_pdu(mld,
+ WIDE_ID(SYSTEM_GROUP, INIT_EXTENDED_CFG_CMD),
+ &init_cfg);
+ if (ret) {
+ IWL_ERR(mld, "Failed to send init config command: %d\n", ret);
+ iwl_remove_notification(&mld->notif_wait, &init_wait);
+ return ret;
+ }
+
+ ret = iwl_mld_send_phy_cfg_cmd(mld);
+ if (ret) {
+ IWL_ERR(mld, "Failed to send PHY config command: %d\n", ret);
+ iwl_remove_notification(&mld->notif_wait, &init_wait);
+ return ret;
+ }
+
+ ret = iwl_wait_notification(&mld->notif_wait, &init_wait,
+ MLD_INIT_COMPLETE_TIMEOUT);
+ if (ret) {
+ IWL_ERR(mld, "Failed to get INIT_COMPLETE %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+int iwl_mld_load_fw(struct iwl_mld *mld)
+{
+ int ret;
+
+ lockdep_assert_wiphy(mld->wiphy);
+
+ ret = iwl_trans_start_hw(mld->trans);
+ if (ret)
+ return ret;
+
+ ret = iwl_mld_run_fw_init_sequence(mld);
+ if (ret)
+ goto err;
+
+ ret = iwl_mld_init_mcc(mld);
+ if (ret)
+ goto err;
+
+ mld->fw_status.running = true;
+
+ return 0;
+err:
+ iwl_mld_stop_fw(mld);
+ return ret;
+}
+
+void iwl_mld_stop_fw(struct iwl_mld *mld)
+{
+ lockdep_assert_wiphy(mld->wiphy);
+
+ iwl_abort_notification_waits(&mld->notif_wait);
+
+ iwl_fw_dbg_stop_sync(&mld->fwrt);
+
+ iwl_trans_stop_device(mld->trans);
+
+ /* HW is stopped, no more coming RX. Cancel all notifications in
+ * case they were sent just before stopping the HW.
+ */
+ iwl_mld_cancel_async_notifications(mld);
+
+ mld->fw_status.running = false;
+}
+
+static void iwl_mld_restart_disconnect_iter(void *data, u8 *mac,
+ struct ieee80211_vif *vif)
+{
+ if (vif->type == NL80211_IFTYPE_STATION)
+ ieee80211_hw_restart_disconnect(vif);
+}
+
+void iwl_mld_send_recovery_cmd(struct iwl_mld *mld, u32 flags)
+{
+ u32 error_log_size = mld->fw->ucode_capa.error_log_size;
+ struct iwl_fw_error_recovery_cmd recovery_cmd = {
+ .flags = cpu_to_le32(flags),
+ };
+ struct iwl_host_cmd cmd = {
+ .id = WIDE_ID(SYSTEM_GROUP, FW_ERROR_RECOVERY_CMD),
+ .flags = CMD_WANT_SKB,
+ .data = {&recovery_cmd, },
+ .len = {sizeof(recovery_cmd), },
+ };
+ int ret;
+
+ /* no error log was defined in TLV */
+ if (!error_log_size)
+ return;
+
+ if (flags & ERROR_RECOVERY_UPDATE_DB) {
+ /* no buf was allocated upon NIC error */
+ if (!mld->error_recovery_buf)
+ return;
+
+ cmd.data[1] = mld->error_recovery_buf;
+ cmd.len[1] = error_log_size;
+ cmd.dataflags[1] = IWL_HCMD_DFL_NOCOPY;
+ recovery_cmd.buf_size = cpu_to_le32(error_log_size);
+ }
+
+ ret = iwl_mld_send_cmd(mld, &cmd);
+
+ /* we no longer need the recovery buffer */
+ kfree(mld->error_recovery_buf);
+ mld->error_recovery_buf = NULL;
+
+ if (ret) {
+ IWL_ERR(mld, "Failed to send recovery cmd %d\n", ret);
+ return;
+ }
+
+ if (flags & ERROR_RECOVERY_UPDATE_DB) {
+ struct iwl_rx_packet *pkt = cmd.resp_pkt;
+ u32 pkt_len = iwl_rx_packet_payload_len(pkt);
+ u32 resp;
+
+ if (IWL_FW_CHECK(mld, pkt_len != sizeof(resp),
+ "Unexpected recovery cmd response size %u (expected %zu)\n",
+ pkt_len, sizeof(resp)))
+ goto out;
+
+ resp = le32_to_cpup((__le32 *)cmd.resp_pkt->data);
+ if (!resp)
+ goto out;
+
+ IWL_ERR(mld,
+ "Failed to send recovery cmd blob was invalid %d\n",
+ resp);
+
+ ieee80211_iterate_interfaces(mld->hw, 0,
+ iwl_mld_restart_disconnect_iter,
+ NULL);
+ }
+
+out:
+ iwl_free_resp(&cmd);
+}
+
+static int iwl_mld_config_fw(struct iwl_mld *mld)
+{
+ int ret;
+
+ lockdep_assert_wiphy(mld->wiphy);
+
+ iwl_fw_disable_dbg_asserts(&mld->fwrt);
+ iwl_get_shared_mem_conf(&mld->fwrt);
+
+ ret = iwl_mld_send_tx_ant_cfg(mld);
+ if (ret)
+ return ret;
+
+ ret = iwl_mld_send_bt_init_conf(mld);
+ if (ret)
+ return ret;
+
+ ret = iwl_set_soc_latency(&mld->fwrt);
+ if (ret)
+ return ret;
+
+ iwl_mld_configure_lari(mld);
+
+ ret = iwl_mld_config_temp_report_ths(mld);
+ if (ret)
+ return ret;
+
+#ifdef CONFIG_THERMAL
+ ret = iwl_mld_config_ctdp(mld, mld->cooling_dev.cur_state,
+ CTDP_CMD_OPERATION_START);
+ if (ret)
+ return ret;
+#endif
+
+ ret = iwl_configure_rxq(&mld->fwrt);
+ if (ret)
+ return ret;
+
+ ret = iwl_mld_send_rss_cfg_cmd(mld);
+ if (ret)
+ return ret;
+
+ ret = iwl_mld_config_scan(mld);
+ if (ret)
+ return ret;
+
+ ret = iwl_mld_update_device_power(mld, false);
+ if (ret)
+ return ret;
+
+ if (mld->fw_status.in_hw_restart) {
+ iwl_mld_send_recovery_cmd(mld, ERROR_RECOVERY_UPDATE_DB);
+ iwl_mld_time_sync_fw_config(mld);
+ }
+
+ iwl_mld_led_config_fw(mld);
+
+ ret = iwl_mld_init_ppag(mld);
+ if (ret)
+ return ret;
+
+ ret = iwl_mld_init_sar(mld);
+ if (ret)
+ return ret;
+
+ ret = iwl_mld_init_sgom(mld);
+ if (ret)
+ return ret;
+
+ iwl_mld_init_tas(mld);
+ iwl_mld_init_uats(mld);
+
+ return 0;
+}
+
+int iwl_mld_start_fw(struct iwl_mld *mld)
+{
+ int ret;
+
+ lockdep_assert_wiphy(mld->wiphy);
+
+ ret = iwl_mld_load_fw(mld);
+ if (IWL_FW_CHECK(mld, ret, "Failed to start firmware %d\n", ret)) {
+ iwl_fw_dbg_error_collect(&mld->fwrt, FW_DBG_TRIGGER_DRIVER);
+ return ret;
+ }
+
+ IWL_DEBUG_INFO(mld, "uCode started.\n");
+
+ ret = iwl_mld_config_fw(mld);
+ if (ret)
+ goto error;
+
+ return 0;
+
+error:
+ iwl_mld_stop_fw(mld);
+ return ret;
+}
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/hcmd.h b/drivers/net/wireless/intel/iwlwifi/mld/hcmd.h
new file mode 100644
index 000000000000..64a8d4248324
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/mld/hcmd.h
@@ -0,0 +1,56 @@
+/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
+/*
+ * Copyright (C) 2024-2025 Intel Corporation
+ */
+#ifndef __iwl_mld_hcmd_h__
+#define __iwl_mld_hcmd_h__
+
+static inline int iwl_mld_send_cmd(struct iwl_mld *mld, struct iwl_host_cmd *cmd)
+{
+ /* No commands, including the d3 related commands, should be sent
+ * after entering d3
+ */
+#ifdef CONFIG_PM_SLEEP
+ if (WARN_ON(mld->fw_status.in_d3))
+ return -EIO;
+#endif
+
+ if (!(cmd->flags & CMD_ASYNC))
+ lockdep_assert_wiphy(mld->wiphy);
+
+ /* Devices that need to shutdown immediately on rfkill are not
+ * supported, so we can send all the cmds in rfkill
+ */
+ cmd->flags |= CMD_SEND_IN_RFKILL;
+
+ return iwl_trans_send_cmd(mld->trans, cmd);
+}
+
+static inline int
+__iwl_mld_send_cmd_with_flags_pdu(struct iwl_mld *mld, u32 id,
+ u32 flags, const void *data, u16 len)
+{
+ struct iwl_host_cmd cmd = {
+ .id = id,
+ .len = { data ? len : 0, },
+ .data = { data, },
+ .flags = flags,
+ };
+
+ return iwl_mld_send_cmd(mld, &cmd);
+}
+
+#define _iwl_mld_send_cmd_with_flags_pdu(mld, id, flags, data, len, \
+ ignored...) \
+ __iwl_mld_send_cmd_with_flags_pdu(mld, id, flags, data, len)
+#define iwl_mld_send_cmd_with_flags_pdu(mld, id, flags, data, len...) \
+ _iwl_mld_send_cmd_with_flags_pdu(mld, id, flags, data, ##len, \
+ sizeof(*(data)))
+
+#define iwl_mld_send_cmd_pdu(mld, id, ...) \
+ iwl_mld_send_cmd_with_flags_pdu(mld, id, 0, __VA_ARGS__)
+
+#define iwl_mld_send_cmd_empty(mld, id) \
+ iwl_mld_send_cmd_with_flags_pdu(mld, id, 0, NULL, 0)
+
+#endif /* __iwl_mld_hcmd_h__ */
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/iface.c b/drivers/net/wireless/intel/iwlwifi/mld/iface.c
new file mode 100644
index 000000000000..235b55e0fe59
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/mld/iface.c
@@ -0,0 +1,692 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
+/*
+ * Copyright (C) 2024-2025 Intel Corporation
+ */
+#include <net/cfg80211.h>
+
+#include "iface.h"
+#include "hcmd.h"
+#include "key.h"
+#include "mlo.h"
+#include "mac80211.h"
+
+#include "fw/api/context.h"
+#include "fw/api/mac.h"
+#include "fw/api/time-event.h"
+#include "fw/api/datapath.h"
+
+/* Cleanup function for struct iwl_mld_vif, will be called in restart */
+void iwl_mld_cleanup_vif(void *data, u8 *mac, struct ieee80211_vif *vif)
+{
+ struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
+ struct iwl_mld *mld = mld_vif->mld;
+ struct iwl_mld_link *link;
+
+ mld_vif->emlsr.blocked_reasons &= ~IWL_MLD_EMLSR_BLOCKED_ROC;
+
+ if (mld_vif->aux_sta.sta_id != IWL_INVALID_STA)
+ iwl_mld_free_internal_sta(mld, &mld_vif->aux_sta);
+
+ /* EMLSR is turned back on during recovery */
+ vif->driver_flags &= ~IEEE80211_VIF_EML_ACTIVE;
+
+ if (mld_vif->roc_activity != ROC_NUM_ACTIVITIES)
+ ieee80211_remain_on_channel_expired(mld->hw);
+
+ mld_vif->roc_activity = ROC_NUM_ACTIVITIES;
+
+ for_each_mld_vif_valid_link(mld_vif, link) {
+ iwl_mld_cleanup_link(mld_vif->mld, link);
+
+ /* Correctly allocated primary link in non-MLO mode */
+ if (!ieee80211_vif_is_mld(vif) &&
+ link_id == 0 && link == &mld_vif->deflink)
+ continue;
+
+ if (vif->active_links & BIT(link_id))
+ continue;
+
+ /* Should not happen as link removal should always succeed */
+ WARN_ON(1);
+ if (link != &mld_vif->deflink)
+ kfree_rcu(link, rcu_head);
+ RCU_INIT_POINTER(mld_vif->link[link_id], NULL);
+ }
+
+ ieee80211_iter_keys(mld->hw, vif, iwl_mld_cleanup_keys_iter, NULL);
+
+ CLEANUP_STRUCT(mld_vif);
+}
+
+static int iwl_mld_send_mac_cmd(struct iwl_mld *mld,
+ struct iwl_mac_config_cmd *cmd)
+{
+ int ret;
+
+ lockdep_assert_wiphy(mld->wiphy);
+
+ ret = iwl_mld_send_cmd_pdu(mld,
+ WIDE_ID(MAC_CONF_GROUP, MAC_CONFIG_CMD),
+ cmd);
+ if (ret)
+ IWL_ERR(mld, "Failed to send MAC_CONFIG_CMD ret = %d\n", ret);
+
+ return ret;
+}
+
+int iwl_mld_mac80211_iftype_to_fw(const struct ieee80211_vif *vif)
+{
+ switch (vif->type) {
+ case NL80211_IFTYPE_STATION:
+ return vif->p2p ? FW_MAC_TYPE_P2P_STA : FW_MAC_TYPE_BSS_STA;
+ case NL80211_IFTYPE_AP:
+ return FW_MAC_TYPE_GO;
+ case NL80211_IFTYPE_MONITOR:
+ return FW_MAC_TYPE_LISTENER;
+ case NL80211_IFTYPE_P2P_DEVICE:
+ return FW_MAC_TYPE_P2P_DEVICE;
+ case NL80211_IFTYPE_ADHOC:
+ return FW_MAC_TYPE_IBSS;
+ default:
+ WARN_ON_ONCE(1);
+ }
+ return FW_MAC_TYPE_BSS_STA;
+}
+
+static bool iwl_mld_is_nic_ack_enabled(struct iwl_mld *mld,
+ struct ieee80211_vif *vif)
+{
+ const struct ieee80211_supported_band *sband;
+ const struct ieee80211_sta_he_cap *own_he_cap;
+
+ lockdep_assert_wiphy(mld->wiphy);
+
+ /* This capability is the same for all bands,
+ * so take it from one of them.
+ */
+ sband = mld->hw->wiphy->bands[NL80211_BAND_2GHZ];
+ own_he_cap = ieee80211_get_he_iftype_cap_vif(sband, vif);
+
+ return own_he_cap && (own_he_cap->he_cap_elem.mac_cap_info[2] &
+ IEEE80211_HE_MAC_CAP2_ACK_EN);
+}
+
+static void iwl_mld_set_he_support(struct iwl_mld *mld,
+ struct ieee80211_vif *vif,
+ struct iwl_mac_config_cmd *cmd,
+ int cmd_ver)
+{
+ if (vif->type == NL80211_IFTYPE_AP) {
+ if (cmd_ver == 2)
+ cmd->wifi_gen_v2.he_ap_support = cpu_to_le16(1);
+ else
+ cmd->wifi_gen.he_ap_support = 1;
+ } else {
+ if (cmd_ver == 2)
+ cmd->wifi_gen_v2.he_support = cpu_to_le16(1);
+ else
+ cmd->wifi_gen.he_support = 1;
+ }
+}
+
+/* fill the common part for all interface types */
+static void iwl_mld_mac_cmd_fill_common(struct iwl_mld *mld,
+ struct ieee80211_vif *vif,
+ struct iwl_mac_config_cmd *cmd,
+ u32 action)
+{
+ struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
+ struct ieee80211_bss_conf *link_conf;
+ unsigned int link_id;
+ int cmd_ver = iwl_fw_lookup_cmd_ver(mld->fw,
+ WIDE_ID(MAC_CONF_GROUP,
+ MAC_CONFIG_CMD), 0);
+
+ lockdep_assert_wiphy(mld->wiphy);
+
+ cmd->id_and_color = cpu_to_le32(mld_vif->fw_id);
+ cmd->action = cpu_to_le32(action);
+
+ cmd->mac_type =
+ cpu_to_le32(iwl_mld_mac80211_iftype_to_fw(vif));
+
+ memcpy(cmd->local_mld_addr, vif->addr, ETH_ALEN);
+
+ if (iwlwifi_mod_params.disable_11ax)
+ return;
+
+ cmd->nic_not_ack_enabled =
+ cpu_to_le32(!iwl_mld_is_nic_ack_enabled(mld, vif));
+
+ /* If we have MLO enabled, then the firmware needs to enable
+ * address translation for the station(s) we add. That depends
+ * on having EHT enabled in firmware, which in turn depends on
+ * mac80211 in the code below.
+ * However, mac80211 doesn't enable HE/EHT until it has parsed
+ * the association response successfully, so just skip all that
+ * and enable both when we have MLO.
+ */
+ if (ieee80211_vif_is_mld(vif)) {
+ iwl_mld_set_he_support(mld, vif, cmd, cmd_ver);
+ if (cmd_ver == 2)
+ cmd->wifi_gen_v2.eht_support = cpu_to_le32(1);
+ else
+ cmd->wifi_gen.eht_support = 1;
+ return;
+ }
+
+ for_each_vif_active_link(vif, link_conf, link_id) {
+ if (!link_conf->he_support)
+ continue;
+
+ iwl_mld_set_he_support(mld, vif, cmd, cmd_ver);
+
+ /* EHT, if supported, was already set above */
+ break;
+ }
+}
+
+static void iwl_mld_fill_mac_cmd_sta(struct iwl_mld *mld,
+ struct ieee80211_vif *vif, u32 action,
+ struct iwl_mac_config_cmd *cmd)
+{
+ struct ieee80211_bss_conf *link;
+ u32 twt_policy = 0;
+ int link_id;
+
+ lockdep_assert_wiphy(mld->wiphy);
+
+ WARN_ON(vif->type != NL80211_IFTYPE_STATION);
+
+ /* We always want to hear MCAST frames, if we're not authorized yet,
+ * we'll drop them.
+ */
+ cmd->filter_flags |= cpu_to_le32(MAC_CFG_FILTER_ACCEPT_GRP);
+
+ /* Adding a MAC ctxt with is_assoc set is not allowed in fw
+ * (and shouldn't happen)
+ */
+ if (vif->cfg.assoc && action != FW_CTXT_ACTION_ADD) {
+ cmd->client.is_assoc = 1;
+
+ if (!iwl_mld_vif_from_mac80211(vif)->authorized)
+ cmd->client.data_policy |=
+ cpu_to_le16(COEX_HIGH_PRIORITY_ENABLE);
+ } else {
+ /* Allow beacons to pass through as long as we are not
+ * associated
+ */
+ cmd->filter_flags |= cpu_to_le32(MAC_CFG_FILTER_ACCEPT_BEACON);
+ }
+
+ cmd->client.assoc_id = cpu_to_le16(vif->cfg.aid);
+
+ if (ieee80211_vif_is_mld(vif)) {
+ u16 esr_transition_timeout =
+ u16_get_bits(vif->cfg.eml_cap,
+ IEEE80211_EML_CAP_TRANSITION_TIMEOUT);
+
+ cmd->client.esr_transition_timeout =
+ min_t(u16, IEEE80211_EML_CAP_TRANSITION_TIMEOUT_128TU,
+ esr_transition_timeout);
+ cmd->client.medium_sync_delay =
+ cpu_to_le16(vif->cfg.eml_med_sync_delay);
+ }
+
+ for_each_vif_active_link(vif, link, link_id) {
+ if (!link->he_support)
+ continue;
+
+ if (link->twt_requester)
+ twt_policy |= TWT_SUPPORTED;
+ if (link->twt_protected)
+ twt_policy |= PROTECTED_TWT_SUPPORTED;
+ if (link->twt_broadcast)
+ twt_policy |= BROADCAST_TWT_SUPPORTED;
+ }
+
+ if (!iwlwifi_mod_params.disable_11ax)
+ cmd->client.data_policy |= cpu_to_le16(twt_policy);
+
+ if (vif->probe_req_reg && vif->cfg.assoc && vif->p2p)
+ cmd->filter_flags |=
+ cpu_to_le32(MAC_CFG_FILTER_ACCEPT_PROBE_REQ);
+}
+
+static void iwl_mld_fill_mac_cmd_ap(struct iwl_mld *mld,
+ struct ieee80211_vif *vif,
+ struct iwl_mac_config_cmd *cmd)
+{
+ struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
+
+ lockdep_assert_wiphy(mld->wiphy);
+
+ WARN_ON(vif->type != NL80211_IFTYPE_AP);
+
+ cmd->filter_flags |= cpu_to_le32(MAC_CFG_FILTER_ACCEPT_PROBE_REQ);
+
+ /* in AP mode, pass beacons from other APs (needed for ht protection).
+ * When there're no any associated station, which means that we are not
+ * TXing anyway, don't ask FW to pass beacons to prevent unnecessary
+ * wake-ups.
+ */
+ if (mld_vif->num_associated_stas)
+ cmd->filter_flags |= cpu_to_le32(MAC_CFG_FILTER_ACCEPT_BEACON);
+}
+
+static void iwl_mld_go_iterator(void *_data, u8 *mac, struct ieee80211_vif *vif)
+{
+ bool *go_active = _data;
+
+ if (ieee80211_vif_type_p2p(vif) == NL80211_IFTYPE_P2P_GO &&
+ iwl_mld_vif_from_mac80211(vif)->ap_ibss_active)
+ *go_active = true;
+}
+
+static bool iwl_mld_p2p_dev_has_extended_disc(struct iwl_mld *mld)
+{
+ bool go_active = false;
+
+ /* This flag should be set to true when the P2P Device is
+ * discoverable and there is at least a P2P GO. Setting
+ * this flag will allow the P2P Device to be discoverable on other
+ * channels in addition to its listen channel.
+ * Note that this flag should not be set in other cases as it opens the
+ * Rx filters on all MAC and increases the number of interrupts.
+ */
+ ieee80211_iterate_active_interfaces(mld->hw,
+ IEEE80211_IFACE_ITER_RESUME_ALL,
+ iwl_mld_go_iterator, &go_active);
+
+ return go_active;
+}
+
+static void iwl_mld_fill_mac_cmd_p2p_dev(struct iwl_mld *mld,
+ struct ieee80211_vif *vif,
+ struct iwl_mac_config_cmd *cmd)
+{
+ bool ext_disc = iwl_mld_p2p_dev_has_extended_disc(mld);
+
+ lockdep_assert_wiphy(mld->wiphy);
+
+ /* Override the filter flags to accept all management frames. This is
+ * needed to support both P2P device discovery using probe requests and
+ * P2P service discovery using action frames
+ */
+ cmd->filter_flags = cpu_to_le32(MAC_CFG_FILTER_ACCEPT_CONTROL_AND_MGMT);
+
+ if (ext_disc)
+ cmd->p2p_dev.is_disc_extended = cpu_to_le32(1);
+}
+
+static void iwl_mld_fill_mac_cmd_ibss(struct iwl_mld *mld,
+ struct ieee80211_vif *vif,
+ struct iwl_mac_config_cmd *cmd)
+{
+ lockdep_assert_wiphy(mld->wiphy);
+
+ WARN_ON(vif->type != NL80211_IFTYPE_ADHOC);
+
+ cmd->filter_flags |= cpu_to_le32(MAC_CFG_FILTER_ACCEPT_BEACON |
+ MAC_CFG_FILTER_ACCEPT_PROBE_REQ |
+ MAC_CFG_FILTER_ACCEPT_GRP);
+}
+
+static int
+iwl_mld_rm_mac_from_fw(struct iwl_mld *mld, struct ieee80211_vif *vif)
+{
+ struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
+ struct iwl_mac_config_cmd cmd = {
+ .action = cpu_to_le32(FW_CTXT_ACTION_REMOVE),
+ .id_and_color = cpu_to_le32(mld_vif->fw_id),
+ };
+
+ return iwl_mld_send_mac_cmd(mld, &cmd);
+}
+
+int iwl_mld_mac_fw_action(struct iwl_mld *mld, struct ieee80211_vif *vif,
+ u32 action)
+{
+ struct iwl_mac_config_cmd cmd = {};
+
+ lockdep_assert_wiphy(mld->wiphy);
+
+ if (action == FW_CTXT_ACTION_REMOVE)
+ return iwl_mld_rm_mac_from_fw(mld, vif);
+
+ iwl_mld_mac_cmd_fill_common(mld, vif, &cmd, action);
+
+ switch (vif->type) {
+ case NL80211_IFTYPE_STATION:
+ iwl_mld_fill_mac_cmd_sta(mld, vif, action, &cmd);
+ break;
+ case NL80211_IFTYPE_AP:
+ iwl_mld_fill_mac_cmd_ap(mld, vif, &cmd);
+ break;
+ case NL80211_IFTYPE_MONITOR:
+ cmd.filter_flags =
+ cpu_to_le32(MAC_CFG_FILTER_PROMISC |
+ MAC_CFG_FILTER_ACCEPT_CONTROL_AND_MGMT |
+ MAC_CFG_FILTER_ACCEPT_BEACON |
+ MAC_CFG_FILTER_ACCEPT_PROBE_REQ |
+ MAC_CFG_FILTER_ACCEPT_GRP);
+ break;
+ case NL80211_IFTYPE_P2P_DEVICE:
+ iwl_mld_fill_mac_cmd_p2p_dev(mld, vif, &cmd);
+ break;
+ case NL80211_IFTYPE_ADHOC:
+ iwl_mld_fill_mac_cmd_ibss(mld, vif, &cmd);
+ break;
+ default:
+ WARN(1, "not supported yet\n");
+ return -EOPNOTSUPP;
+ }
+
+ return iwl_mld_send_mac_cmd(mld, &cmd);
+}
+
+IWL_MLD_ALLOC_FN(vif, vif)
+
+/* Constructor function for struct iwl_mld_vif */
+static int
+iwl_mld_init_vif(struct iwl_mld *mld, struct ieee80211_vif *vif)
+{
+ struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
+ int ret;
+
+ lockdep_assert_wiphy(mld->wiphy);
+
+ mld_vif->mld = mld;
+ mld_vif->roc_activity = ROC_NUM_ACTIVITIES;
+
+ ret = iwl_mld_allocate_vif_fw_id(mld, &mld_vif->fw_id, vif);
+ if (ret)
+ return ret;
+
+ if (!mld->fw_status.in_hw_restart) {
+ wiphy_work_init(&mld_vif->emlsr.unblock_tpt_wk,
+ iwl_mld_emlsr_unblock_tpt_wk);
+ wiphy_delayed_work_init(&mld_vif->emlsr.check_tpt_wk,
+ iwl_mld_emlsr_check_tpt);
+ wiphy_delayed_work_init(&mld_vif->emlsr.prevent_done_wk,
+ iwl_mld_emlsr_prevent_done_wk);
+ wiphy_delayed_work_init(&mld_vif->emlsr.tmp_non_bss_done_wk,
+ iwl_mld_emlsr_tmp_non_bss_done_wk);
+ }
+ iwl_mld_init_internal_sta(&mld_vif->aux_sta);
+
+ return 0;
+}
+
+int iwl_mld_add_vif(struct iwl_mld *mld, struct ieee80211_vif *vif)
+{
+ struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
+ int ret;
+
+ lockdep_assert_wiphy(mld->wiphy);
+
+ ret = iwl_mld_init_vif(mld, vif);
+ if (ret)
+ return ret;
+
+ ret = iwl_mld_mac_fw_action(mld, vif, FW_CTXT_ACTION_ADD);
+ if (ret)
+ RCU_INIT_POINTER(mld->fw_id_to_vif[mld_vif->fw_id], NULL);
+
+ return ret;
+}
+
+int iwl_mld_rm_vif(struct iwl_mld *mld, struct ieee80211_vif *vif)
+{
+ struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
+ int ret;
+
+ lockdep_assert_wiphy(mld->wiphy);
+
+ ret = iwl_mld_mac_fw_action(mld, vif, FW_CTXT_ACTION_REMOVE);
+
+ if (WARN_ON(mld_vif->fw_id >= ARRAY_SIZE(mld->fw_id_to_vif)))
+ return -EINVAL;
+
+ RCU_INIT_POINTER(mld->fw_id_to_vif[mld_vif->fw_id], NULL);
+
+ iwl_mld_cancel_notifications_of_object(mld, IWL_MLD_OBJECT_TYPE_VIF,
+ mld_vif->fw_id);
+
+ return ret;
+}
+
+void iwl_mld_set_vif_associated(struct iwl_mld *mld,
+ struct ieee80211_vif *vif)
+{
+ struct ieee80211_bss_conf *link;
+ unsigned int link_id;
+
+ for_each_vif_active_link(vif, link, link_id) {
+ if (iwl_mld_link_set_associated(mld, vif, link))
+ IWL_ERR(mld, "failed to update link %d\n", link_id);
+ }
+
+ iwl_mld_recalc_multicast_filter(mld);
+}
+
+static void iwl_mld_get_fw_id_bss_bitmap_iter(void *_data, u8 *mac,
+ struct ieee80211_vif *vif)
+{
+ u8 *fw_id_bitmap = _data;
+ struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
+
+ if (ieee80211_vif_type_p2p(vif) != NL80211_IFTYPE_STATION)
+ return;
+
+ *fw_id_bitmap |= BIT(mld_vif->fw_id);
+}
+
+u8 iwl_mld_get_fw_bss_vifs_ids(struct iwl_mld *mld)
+{
+ u8 fw_id_bitmap = 0;
+
+ ieee80211_iterate_active_interfaces_mtx(mld->hw,
+ IEEE80211_IFACE_SKIP_SDATA_NOT_IN_DRIVER,
+ iwl_mld_get_fw_id_bss_bitmap_iter,
+ &fw_id_bitmap);
+
+ return fw_id_bitmap;
+}
+
+void iwl_mld_handle_probe_resp_data_notif(struct iwl_mld *mld,
+ struct iwl_rx_packet *pkt)
+{
+ const struct iwl_probe_resp_data_notif *notif = (void *)pkt->data;
+ struct iwl_probe_resp_data *old_data, *new_data;
+ struct ieee80211_vif *vif;
+ struct iwl_mld_link *mld_link;
+
+ IWL_DEBUG_INFO(mld, "Probe response data notif: noa %d, csa %d\n",
+ notif->noa_active, notif->csa_counter);
+
+ if (IWL_FW_CHECK(mld, le32_to_cpu(notif->mac_id) >=
+ ARRAY_SIZE(mld->fw_id_to_vif),
+ "mac id is invalid: %d\n",
+ le32_to_cpu(notif->mac_id)))
+ return;
+
+ vif = wiphy_dereference(mld->wiphy,
+ mld->fw_id_to_vif[le32_to_cpu(notif->mac_id)]);
+
+ /* the firmware gives us the mac_id (and not the link_id), mac80211
+ * gets a vif and not a link, bottom line, this flow is not MLD ready
+ * yet.
+ */
+ if (WARN_ON(!vif) || ieee80211_vif_is_mld(vif))
+ return;
+
+ if (notif->csa_counter != IWL_PROBE_RESP_DATA_NO_CSA &&
+ notif->csa_counter >= 1)
+ ieee80211_beacon_set_cntdwn(vif, notif->csa_counter);
+
+ if (!vif->p2p)
+ return;
+
+ mld_link = &iwl_mld_vif_from_mac80211(vif)->deflink;
+
+ new_data = kzalloc(sizeof(*new_data), GFP_KERNEL);
+ if (!new_data)
+ return;
+
+ memcpy(&new_data->notif, notif, sizeof(new_data->notif));
+
+ /* noa_attr contains 1 reserved byte, need to substruct it */
+ new_data->noa_len = sizeof(struct ieee80211_vendor_ie) +
+ sizeof(new_data->notif.noa_attr) - 1;
+
+ /*
+ * If it's a one time NoA, only one descriptor is needed,
+ * adjust the length according to len_low.
+ */
+ if (new_data->notif.noa_attr.len_low ==
+ sizeof(struct ieee80211_p2p_noa_desc) + 2)
+ new_data->noa_len -= sizeof(struct ieee80211_p2p_noa_desc);
+
+ old_data = wiphy_dereference(mld->wiphy, mld_link->probe_resp_data);
+ rcu_assign_pointer(mld_link->probe_resp_data, new_data);
+
+ if (old_data)
+ kfree_rcu(old_data, rcu_head);
+}
+
+void iwl_mld_handle_uapsd_misbehaving_ap_notif(struct iwl_mld *mld,
+ struct iwl_rx_packet *pkt)
+{
+ struct iwl_uapsd_misbehaving_ap_notif *notif = (void *)pkt->data;
+ struct ieee80211_vif *vif;
+
+ if (IWL_FW_CHECK(mld, notif->mac_id >= ARRAY_SIZE(mld->fw_id_to_vif),
+ "mac id is invalid: %d\n", notif->mac_id))
+ return;
+
+ vif = wiphy_dereference(mld->wiphy, mld->fw_id_to_vif[notif->mac_id]);
+
+ if (WARN_ON(!vif) || ieee80211_vif_is_mld(vif))
+ return;
+
+ IWL_WARN(mld, "uapsd misbehaving AP: %pM\n", vif->bss_conf.bssid);
+}
+
+void iwl_mld_handle_datapath_monitor_notif(struct iwl_mld *mld,
+ struct iwl_rx_packet *pkt)
+{
+ struct iwl_datapath_monitor_notif *notif = (void *)pkt->data;
+ struct ieee80211_bss_conf *link;
+ struct ieee80211_supported_band *sband;
+ const struct ieee80211_sta_he_cap *he_cap;
+ struct ieee80211_vif *vif;
+ struct iwl_mld_vif *mld_vif;
+
+ if (notif->type != cpu_to_le32(IWL_DP_MON_NOTIF_TYPE_EXT_CCA))
+ return;
+
+ link = iwl_mld_fw_id_to_link_conf(mld, notif->link_id);
+ if (WARN_ON(!link))
+ return;
+
+ vif = link->vif;
+ if (WARN_ON(!vif) || vif->type != NL80211_IFTYPE_STATION ||
+ !vif->cfg.assoc)
+ return;
+
+ if (!link->chanreq.oper.chan ||
+ link->chanreq.oper.chan->band != NL80211_BAND_2GHZ ||
+ link->chanreq.oper.width < NL80211_CHAN_WIDTH_40)
+ return;
+
+ mld_vif = iwl_mld_vif_from_mac80211(vif);
+
+ /* this shouldn't happen *again*, ignore it */
+ if (mld_vif->cca_40mhz_workaround != CCA_40_MHZ_WA_NONE)
+ return;
+
+ mld_vif->cca_40mhz_workaround = CCA_40_MHZ_WA_RECONNECT;
+
+ /*
+ * This capability manipulation isn't really ideal, but it's the
+ * easiest choice - otherwise we'd have to do some major changes
+ * in mac80211 to support this, which isn't worth it. This does
+ * mean that userspace may have outdated information, but that's
+ * actually not an issue at all.
+ */
+ sband = mld->wiphy->bands[NL80211_BAND_2GHZ];
+
+ WARN_ON(!sband->ht_cap.ht_supported);
+ WARN_ON(!(sband->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40));
+ sband->ht_cap.cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40;
+
+ he_cap = ieee80211_get_he_iftype_cap_vif(sband, vif);
+
+ if (he_cap) {
+ /* we know that ours is writable */
+ struct ieee80211_sta_he_cap *he = (void *)(uintptr_t)he_cap;
+
+ WARN_ON(!he->has_he);
+ WARN_ON(!(he->he_cap_elem.phy_cap_info[0] &
+ IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_IN_2G));
+ he->he_cap_elem.phy_cap_info[0] &=
+ ~IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_IN_2G;
+ }
+
+ ieee80211_disconnect(vif, true);
+}
+
+void iwl_mld_reset_cca_40mhz_workaround(struct iwl_mld *mld,
+ struct ieee80211_vif *vif)
+{
+ struct ieee80211_supported_band *sband;
+ const struct ieee80211_sta_he_cap *he_cap;
+ struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
+
+ if (vif->type != NL80211_IFTYPE_STATION)
+ return;
+
+ if (mld_vif->cca_40mhz_workaround == CCA_40_MHZ_WA_NONE)
+ return;
+
+ /* Now we are just reconnecting with the new capabilities,
+ * but remember to reset the capabilities when we disconnect for real
+ */
+ if (mld_vif->cca_40mhz_workaround == CCA_40_MHZ_WA_RECONNECT) {
+ mld_vif->cca_40mhz_workaround = CCA_40_MHZ_WA_RESET;
+ return;
+ }
+
+ /* Now cca_40mhz_workaround == CCA_40_MHZ_WA_RESET */
+
+ sband = mld->wiphy->bands[NL80211_BAND_2GHZ];
+
+ sband->ht_cap.cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40;
+
+ he_cap = ieee80211_get_he_iftype_cap_vif(sband, vif);
+
+ if (he_cap) {
+ /* we know that ours is writable */
+ struct ieee80211_sta_he_cap *he = (void *)(uintptr_t)he_cap;
+
+ he->he_cap_elem.phy_cap_info[0] |=
+ IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_IN_2G;
+ }
+
+ mld_vif->cca_40mhz_workaround = CCA_40_MHZ_WA_NONE;
+}
+
+struct ieee80211_vif *iwl_mld_get_bss_vif(struct iwl_mld *mld)
+{
+ unsigned long fw_id_bitmap = iwl_mld_get_fw_bss_vifs_ids(mld);
+ int fw_id;
+
+ if (hweight8(fw_id_bitmap) != 1)
+ return NULL;
+
+ fw_id = __ffs(fw_id_bitmap);
+
+ return wiphy_dereference(mld->wiphy,
+ mld->fw_id_to_vif[fw_id]);
+}
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/iface.h b/drivers/net/wireless/intel/iwlwifi/mld/iface.h
new file mode 100644
index 000000000000..49e2ce65557d
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/mld/iface.h
@@ -0,0 +1,238 @@
+/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
+/*
+ * Copyright (C) 2024-2025 Intel Corporation
+ */
+#ifndef __iwl_mld_iface_h__
+#define __iwl_mld_iface_h__
+
+#include <net/mac80211.h>
+
+#include "link.h"
+#include "session-protect.h"
+#include "d3.h"
+#include "fw/api/time-event.h"
+
+enum iwl_mld_cca_40mhz_wa_status {
+ CCA_40_MHZ_WA_NONE,
+ CCA_40_MHZ_WA_RESET,
+ CCA_40_MHZ_WA_RECONNECT,
+};
+
+/**
+ * enum iwl_mld_emlsr_blocked - defines reasons for which EMLSR is blocked
+ *
+ * These blocks are applied/stored per-VIF.
+ *
+ * @IWL_MLD_EMLSR_BLOCKED_PREVENTION: Prevent repeated EMLSR enter/exit
+ * @IWL_MLD_EMLSR_BLOCKED_WOWLAN: WOWLAN is preventing EMLSR
+ * @IWL_MLD_EMLSR_BLOCKED_ROC: remain-on-channel is preventing EMLSR
+ * @IWL_MLD_EMLSR_BLOCKED_NON_BSS: An active non-BSS interface's link is
+ * preventing EMLSR
+ * @IWL_MLD_EMLSR_BLOCKED_TMP_NON_BSS: An expected active non-BSS interface's
+ * link is preventing EMLSR. This is a temporary blocking that is set when
+ * there is an indication that a non-BSS interface is to be added.
+ * @IWL_MLD_EMLSR_BLOCKED_TPT: throughput is too low to make EMLSR worthwhile
+ */
+enum iwl_mld_emlsr_blocked {
+ IWL_MLD_EMLSR_BLOCKED_PREVENTION = 0x1,
+ IWL_MLD_EMLSR_BLOCKED_WOWLAN = 0x2,
+ IWL_MLD_EMLSR_BLOCKED_ROC = 0x4,
+ IWL_MLD_EMLSR_BLOCKED_NON_BSS = 0x8,
+ IWL_MLD_EMLSR_BLOCKED_TMP_NON_BSS = 0x10,
+ IWL_MLD_EMLSR_BLOCKED_TPT = 0x20,
+};
+
+/**
+ * enum iwl_mld_emlsr_exit - defines reasons for exiting EMLSR
+ *
+ * Reasons to exit EMLSR may be either link specific or even specific to a
+ * combination of links.
+ *
+ * @IWL_MLD_EMLSR_EXIT_BLOCK: Exit due to a block reason being set
+ * @IWL_MLD_EMLSR_EXIT_MISSED_BEACON: Exit due to missed beacons
+ * @IWL_MLD_EMLSR_EXIT_FAIL_ENTRY: FW failed to enter EMLSR
+ * @IWL_MLD_EMLSR_EXIT_CSA: EMLSR prevented due to channel switch on link
+ * @IWL_MLD_EMLSR_EXIT_EQUAL_BAND: EMLSR prevented as both links share the band
+ * @IWL_MLD_EMLSR_EXIT_LOW_RSSI: Link RSSI is unsuitable for EMLSR
+ * @IWL_MLD_EMLSR_EXIT_LINK_USAGE: Exit EMLSR due to low TPT on secondary link
+ * @IWL_MLD_EMLSR_EXIT_BT_COEX: Exit EMLSR due to BT coexistence
+ * @IWL_MLD_EMLSR_EXIT_CHAN_LOAD: Exit EMLSR because the primary channel is not
+ * loaded enough to justify EMLSR.
+ * @IWL_MLD_EMLSR_EXIT_RFI: Exit EMLSR due to RFI
+ * @IWL_MLD_EMLSR_EXIT_FW_REQUEST: Exit EMLSR because the FW requested it
+ * @IWL_MLD_EMLSR_EXIT_INVALID: internal exit reason due to invalid data
+ */
+enum iwl_mld_emlsr_exit {
+ IWL_MLD_EMLSR_EXIT_BLOCK = 0x1,
+ IWL_MLD_EMLSR_EXIT_MISSED_BEACON = 0x2,
+ IWL_MLD_EMLSR_EXIT_FAIL_ENTRY = 0x4,
+ IWL_MLD_EMLSR_EXIT_CSA = 0x8,
+ IWL_MLD_EMLSR_EXIT_EQUAL_BAND = 0x10,
+ IWL_MLD_EMLSR_EXIT_LOW_RSSI = 0x20,
+ IWL_MLD_EMLSR_EXIT_LINK_USAGE = 0x40,
+ IWL_MLD_EMLSR_EXIT_BT_COEX = 0x80,
+ IWL_MLD_EMLSR_EXIT_CHAN_LOAD = 0x100,
+ IWL_MLD_EMLSR_EXIT_RFI = 0x200,
+ IWL_MLD_EMLSR_EXIT_FW_REQUEST = 0x400,
+ IWL_MLD_EMLSR_EXIT_INVALID = 0x800,
+};
+
+/**
+ * struct iwl_mld_emlsr - per-VIF data about EMLSR operation
+ *
+ * @primary: The current primary link
+ * @selected_primary: Primary link as selected during the last link selection
+ * @selected_links: Links as selected during the last link selection
+ * @blocked_reasons: Reasons preventing EMLSR from being enabled
+ * @last_exit_reason: Reason for the last EMLSR exit
+ * @last_exit_ts: Time of the last EMLSR exit (if @last_exit_reason is non-zero)
+ * @exit_repeat_count: Number of times EMLSR was exited for the same reason
+ * @unblock_tpt_wk: Unblock EMLSR because the throughput limit was reached
+ * @check_tpt_wk: a worker to check if IWL_MLD_EMLSR_BLOCKED_TPT should be
+ * added, for example if there is no longer enough traffic.
+ * @prevent_done_wk: Worker to remove %IWL_MLD_EMLSR_BLOCKED_PREVENTION
+ * @tmp_non_bss_done_wk: Worker to remove %IWL_MLD_EMLSR_BLOCKED_TMP_NON_BSS
+ */
+struct iwl_mld_emlsr {
+ struct_group(zeroed_on_not_authorized,
+ u8 primary;
+
+ u8 selected_primary;
+ u16 selected_links;
+
+ enum iwl_mld_emlsr_blocked blocked_reasons;
+
+ enum iwl_mld_emlsr_exit last_exit_reason;
+ unsigned long last_exit_ts;
+ u8 exit_repeat_count;
+ );
+
+ struct wiphy_work unblock_tpt_wk;
+ struct wiphy_delayed_work check_tpt_wk;
+
+ struct wiphy_delayed_work prevent_done_wk;
+ struct wiphy_delayed_work tmp_non_bss_done_wk;
+};
+
+/**
+ * struct iwl_mld_vif - virtual interface (MAC context) configuration parameters
+ *
+ * @fw_id: fw id of the mac context.
+ * @session_protect: session protection parameters
+ * @ap_sta: pointer to AP sta, for easier access to it.
+ * Relevant only for STA vifs.
+ * @authorized: indicates the AP station was set to authorized
+ * @bigtks: BIGTKs of the AP, for beacon protection.
+ * Only valid for STA. (FIXME: needs to be per link)
+ * @num_associated_stas: number of associated STAs. Relevant only for AP mode.
+ * @ap_ibss_active: whether the AP/IBSS was started
+ * @cca_40mhz_workaround: When we are connected in 2.4 GHz and 40 MHz, and the
+ * environment is too loaded, we work around this by reconnecting to the
+ * same AP with 20 MHz. This manages the status of the workaround.
+ * @beacon_inject_active: indicates an active debugfs beacon ie injection
+ * @low_latency_causes: bit flags, indicating the causes for low-latency,
+ * see @iwl_mld_low_latency_cause.
+ * @ps_disabled: indicates that PS is disabled for this interface
+ * @mld: pointer to the mld structure.
+ * @deflink: default link data, for use in non-MLO,
+ * @link: reference to link data for each valid link, for use in MLO.
+ * @emlsr: information related to EMLSR
+ * @wowlan_data: data used by the wowlan suspend flow
+ * @use_ps_poll: use ps_poll frames
+ * @disable_bf: disable beacon filter
+ * @dbgfs_slink: debugfs symlink for this interface
+ * @roc_activity: the id of the roc_activity running. Relevant for STA and
+ * p2p device only. Set to %ROC_NUM_ACTIVITIES when not in use.
+ * @aux_sta: station used for remain on channel. Used in P2P device.
+ */
+struct iwl_mld_vif {
+ /* Add here fields that need clean up on restart */
+ struct_group(zeroed_on_hw_restart,
+ u8 fw_id;
+ struct iwl_mld_session_protect session_protect;
+ struct ieee80211_sta *ap_sta;
+ bool authorized;
+ struct ieee80211_key_conf __rcu *bigtks[2];
+ u8 num_associated_stas;
+ bool ap_ibss_active;
+ enum iwl_mld_cca_40mhz_wa_status cca_40mhz_workaround;
+#ifdef CONFIG_IWLWIFI_DEBUGFS
+ bool beacon_inject_active;
+#endif
+ u8 low_latency_causes;
+ bool ps_disabled;
+ );
+ /* And here fields that survive a fw restart */
+ struct iwl_mld *mld;
+ struct iwl_mld_link deflink;
+ struct iwl_mld_link __rcu *link[IEEE80211_MLD_MAX_NUM_LINKS];
+
+ struct iwl_mld_emlsr emlsr;
+
+#ifdef CONFIG_PM_SLEEP
+ struct iwl_mld_wowlan_data wowlan_data;
+#endif
+#ifdef CONFIG_IWLWIFI_DEBUGFS
+ bool use_ps_poll;
+ bool disable_bf;
+ struct dentry *dbgfs_slink;
+#endif
+ enum iwl_roc_activity roc_activity;
+ struct iwl_mld_int_sta aux_sta;
+};
+
+static inline struct iwl_mld_vif *
+iwl_mld_vif_from_mac80211(struct ieee80211_vif *vif)
+{
+ return (void *)vif->drv_priv;
+}
+
+#define iwl_mld_link_dereference_check(mld_vif, link_id) \
+ rcu_dereference_check((mld_vif)->link[link_id], \
+ lockdep_is_held(&mld_vif->mld->wiphy->mtx))
+
+#define for_each_mld_vif_valid_link(mld_vif, mld_link) \
+ for (int link_id = 0; link_id < ARRAY_SIZE((mld_vif)->link); \
+ link_id++) \
+ if ((mld_link = iwl_mld_link_dereference_check(mld_vif, link_id)))
+
+/* Retrieve pointer to mld link from mac80211 structures */
+static inline struct iwl_mld_link *
+iwl_mld_link_from_mac80211(struct ieee80211_bss_conf *bss_conf)
+{
+ struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(bss_conf->vif);
+
+ return iwl_mld_link_dereference_check(mld_vif, bss_conf->link_id);
+}
+
+int iwl_mld_mac80211_iftype_to_fw(const struct ieee80211_vif *vif);
+
+/* Cleanup function for struct iwl_mld_vif, will be called in restart */
+void iwl_mld_cleanup_vif(void *data, u8 *mac, struct ieee80211_vif *vif);
+int iwl_mld_mac_fw_action(struct iwl_mld *mld, struct ieee80211_vif *vif,
+ u32 action);
+int iwl_mld_add_vif(struct iwl_mld *mld, struct ieee80211_vif *vif);
+int iwl_mld_rm_vif(struct iwl_mld *mld, struct ieee80211_vif *vif);
+void iwl_mld_set_vif_associated(struct iwl_mld *mld,
+ struct ieee80211_vif *vif);
+u8 iwl_mld_get_fw_bss_vifs_ids(struct iwl_mld *mld);
+void iwl_mld_handle_probe_resp_data_notif(struct iwl_mld *mld,
+ struct iwl_rx_packet *pkt);
+
+void iwl_mld_handle_datapath_monitor_notif(struct iwl_mld *mld,
+ struct iwl_rx_packet *pkt);
+
+void iwl_mld_handle_uapsd_misbehaving_ap_notif(struct iwl_mld *mld,
+ struct iwl_rx_packet *pkt);
+
+void iwl_mld_reset_cca_40mhz_workaround(struct iwl_mld *mld,
+ struct ieee80211_vif *vif);
+
+static inline bool iwl_mld_vif_low_latency(const struct iwl_mld_vif *mld_vif)
+{
+ return !!mld_vif->low_latency_causes;
+}
+
+struct ieee80211_vif *iwl_mld_get_bss_vif(struct iwl_mld *mld);
+
+#endif /* __iwl_mld_iface_h__ */
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/key.c b/drivers/net/wireless/intel/iwlwifi/mld/key.c
new file mode 100644
index 000000000000..0eff13e5ffd5
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/mld/key.c
@@ -0,0 +1,358 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
+/*
+ * Copyright (C) 2024 Intel Corporation
+ */
+#include "key.h"
+#include "iface.h"
+#include "sta.h"
+#include "fw/api/datapath.h"
+
+static u32 iwl_mld_get_key_flags(struct iwl_mld *mld,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta,
+ struct ieee80211_key_conf *key)
+{
+ struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
+ bool pairwise = key->flags & IEEE80211_KEY_FLAG_PAIRWISE;
+ bool igtk = key->keyidx == 4 || key->keyidx == 5;
+ u32 flags = 0;
+
+ if (!pairwise)
+ flags |= IWL_SEC_KEY_FLAG_MCAST_KEY;
+
+ switch (key->cipher) {
+ case WLAN_CIPHER_SUITE_TKIP:
+ flags |= IWL_SEC_KEY_FLAG_CIPHER_TKIP;
+ break;
+ case WLAN_CIPHER_SUITE_AES_CMAC:
+ case WLAN_CIPHER_SUITE_CCMP:
+ flags |= IWL_SEC_KEY_FLAG_CIPHER_CCMP;
+ break;
+ case WLAN_CIPHER_SUITE_GCMP_256:
+ case WLAN_CIPHER_SUITE_BIP_GMAC_256:
+ flags |= IWL_SEC_KEY_FLAG_KEY_SIZE;
+ fallthrough;
+ case WLAN_CIPHER_SUITE_GCMP:
+ case WLAN_CIPHER_SUITE_BIP_GMAC_128:
+ flags |= IWL_SEC_KEY_FLAG_CIPHER_GCMP;
+ break;
+ }
+
+ if (!sta && vif->type == NL80211_IFTYPE_STATION)
+ sta = mld_vif->ap_sta;
+
+ /* If we are installing an iGTK (in AP or STA mode), we need to tell
+ * the firmware this key will en/decrypt MGMT frames.
+ * Same goes if we are installing a pairwise key for an MFP station.
+ * In case we're installing a groupwise key (which is not an iGTK),
+ * then, we will not use this key for MGMT frames.
+ */
+ if ((sta && sta->mfp && pairwise) || igtk)
+ flags |= IWL_SEC_KEY_FLAG_MFP;
+
+ if (key->flags & IEEE80211_KEY_FLAG_SPP_AMSDU)
+ flags |= IWL_SEC_KEY_FLAG_SPP_AMSDU;
+
+ return flags;
+}
+
+static u32 iwl_mld_get_key_sta_mask(struct iwl_mld *mld,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta,
+ struct ieee80211_key_conf *key)
+{
+ struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
+ struct ieee80211_link_sta *link_sta;
+ int sta_id;
+
+ lockdep_assert_wiphy(mld->wiphy);
+
+ /* AP group keys are per link and should be on the mcast/bcast STA */
+ if (vif->type == NL80211_IFTYPE_AP &&
+ !(key->flags & IEEE80211_KEY_FLAG_PAIRWISE)) {
+ struct iwl_mld_link *link = NULL;
+
+ if (key->link_id >= 0)
+ link = iwl_mld_link_dereference_check(mld_vif,
+ key->link_id);
+
+ if (WARN_ON(!link))
+ return 0;
+
+ /* In this stage we should have both the bcast and mcast STAs */
+ if (WARN_ON(link->bcast_sta.sta_id == IWL_INVALID_STA ||
+ link->mcast_sta.sta_id == IWL_INVALID_STA))
+ return 0;
+
+ /* IGTK/BIGTK to bcast STA */
+ if (key->keyidx >= 4)
+ return BIT(link->bcast_sta.sta_id);
+
+ /* GTK for data to mcast STA */
+ return BIT(link->mcast_sta.sta_id);
+ }
+
+ /* for client mode use the AP STA also for group keys */
+ if (!sta && vif->type == NL80211_IFTYPE_STATION)
+ sta = mld_vif->ap_sta;
+
+ /* STA should be non-NULL now */
+ if (WARN_ON(!sta))
+ return 0;
+
+ /* Key is not per-link, get the full sta mask */
+ if (key->link_id < 0)
+ return iwl_mld_fw_sta_id_mask(mld, sta);
+
+ /* The link_sta shouldn't be NULL now, but this is checked in
+ * iwl_mld_fw_sta_id_mask
+ */
+ link_sta = link_sta_dereference_check(sta, key->link_id);
+
+ sta_id = iwl_mld_fw_sta_id_from_link_sta(mld, link_sta);
+ if (sta_id < 0)
+ return 0;
+
+ return BIT(sta_id);
+}
+
+static int iwl_mld_add_key_to_fw(struct iwl_mld *mld, u32 sta_mask,
+ u32 key_flags, struct ieee80211_key_conf *key)
+{
+ struct iwl_sec_key_cmd cmd = {
+ .action = cpu_to_le32(FW_CTXT_ACTION_ADD),
+ .u.add.sta_mask = cpu_to_le32(sta_mask),
+ .u.add.key_id = cpu_to_le32(key->keyidx),
+ .u.add.key_flags = cpu_to_le32(key_flags),
+ .u.add.tx_seq = cpu_to_le64(atomic64_read(&key->tx_pn)),
+ };
+ bool tkip = key->cipher == WLAN_CIPHER_SUITE_TKIP;
+ int max_key_len = sizeof(cmd.u.add.key);
+
+ if (WARN_ON(!sta_mask))
+ return -EINVAL;
+
+ if (WARN_ON(key->keylen > max_key_len))
+ return -EINVAL;
+
+ memcpy(cmd.u.add.key, key->key, key->keylen);
+
+ if (tkip) {
+ memcpy(cmd.u.add.tkip_mic_rx_key,
+ key->key + NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY,
+ 8);
+ memcpy(cmd.u.add.tkip_mic_tx_key,
+ key->key + NL80211_TKIP_DATA_OFFSET_TX_MIC_KEY,
+ 8);
+ }
+
+ return iwl_mld_send_cmd_pdu(mld, WIDE_ID(DATA_PATH_GROUP, SEC_KEY_CMD),
+ &cmd);
+}
+
+static void iwl_mld_remove_key_from_fw(struct iwl_mld *mld, u32 sta_mask,
+ u32 key_flags, u32 keyidx)
+{
+ struct iwl_sec_key_cmd cmd = {
+ .action = cpu_to_le32(FW_CTXT_ACTION_REMOVE),
+ .u.remove.sta_mask = cpu_to_le32(sta_mask),
+ .u.remove.key_id = cpu_to_le32(keyidx),
+ .u.remove.key_flags = cpu_to_le32(key_flags),
+ };
+
+ if (WARN_ON(!sta_mask))
+ return;
+
+ iwl_mld_send_cmd_pdu(mld, WIDE_ID(DATA_PATH_GROUP, SEC_KEY_CMD), &cmd);
+}
+
+void iwl_mld_remove_key(struct iwl_mld *mld, struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta,
+ struct ieee80211_key_conf *key)
+{
+ u32 sta_mask = iwl_mld_get_key_sta_mask(mld, vif, sta, key);
+ u32 key_flags = iwl_mld_get_key_flags(mld, vif, sta, key);
+ struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
+
+ lockdep_assert_wiphy(mld->wiphy);
+
+ if (!sta_mask)
+ return;
+
+ if (key->keyidx == 4 || key->keyidx == 5) {
+ struct iwl_mld_link *mld_link;
+ unsigned int link_id = 0;
+
+ /* set to -1 for non-MLO right now */
+ if (key->link_id >= 0)
+ link_id = key->link_id;
+
+ mld_link = iwl_mld_link_dereference_check(mld_vif, link_id);
+ if (WARN_ON(!mld_link))
+ return;
+
+ if (mld_link->igtk == key)
+ mld_link->igtk = NULL;
+
+ mld->num_igtks--;
+ }
+
+ iwl_mld_remove_key_from_fw(mld, sta_mask, key_flags, key->keyidx);
+
+ /* no longer in HW */
+ key->hw_key_idx = STA_KEY_IDX_INVALID;
+}
+
+int iwl_mld_add_key(struct iwl_mld *mld,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta,
+ struct ieee80211_key_conf *key)
+{
+ u32 sta_mask = iwl_mld_get_key_sta_mask(mld, vif, sta, key);
+ u32 key_flags = iwl_mld_get_key_flags(mld, vif, sta, key);
+ struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
+ struct iwl_mld_link *mld_link = NULL;
+ bool igtk = key->keyidx == 4 || key->keyidx == 5;
+ int ret;
+
+ lockdep_assert_wiphy(mld->wiphy);
+
+ if (!sta_mask)
+ return -EINVAL;
+
+ if (igtk) {
+ if (mld->num_igtks == IWL_MAX_NUM_IGTKS)
+ return -EOPNOTSUPP;
+
+ u8 link_id = 0;
+
+ /* set to -1 for non-MLO right now */
+ if (key->link_id >= 0)
+ link_id = key->link_id;
+
+ mld_link = iwl_mld_link_dereference_check(mld_vif, link_id);
+
+ if (WARN_ON(!mld_link))
+ return -EINVAL;
+
+ if (mld_link->igtk) {
+ IWL_DEBUG_MAC80211(mld, "remove old IGTK %d\n",
+ mld_link->igtk->keyidx);
+ iwl_mld_remove_key(mld, vif, sta, mld_link->igtk);
+ }
+
+ WARN_ON(mld_link->igtk);
+ }
+
+ ret = iwl_mld_add_key_to_fw(mld, sta_mask, key_flags, key);
+ if (ret)
+ return ret;
+
+ if (mld_link) {
+ mld_link->igtk = key;
+ mld->num_igtks++;
+ }
+
+ /* We don't really need this, but need it to be not invalid,
+ * so we will know if the key is in fw.
+ */
+ key->hw_key_idx = 0;
+
+ return 0;
+}
+
+struct remove_ap_keys_iter_data {
+ u8 link_id;
+ struct ieee80211_sta *sta;
+};
+
+static void iwl_mld_remove_ap_keys_iter(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta,
+ struct ieee80211_key_conf *key,
+ void *_data)
+{
+ struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw);
+ struct remove_ap_keys_iter_data *data = _data;
+
+ if (key->hw_key_idx == STA_KEY_IDX_INVALID)
+ return;
+
+ /* All the pairwise keys should have been removed by now */
+ if (WARN_ON(sta))
+ return;
+
+ if (key->link_id >= 0 && key->link_id != data->link_id)
+ return;
+
+ iwl_mld_remove_key(mld, vif, data->sta, key);
+}
+
+void iwl_mld_remove_ap_keys(struct iwl_mld *mld, struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta, unsigned int link_id)
+{
+ struct remove_ap_keys_iter_data iter_data = {
+ .link_id = link_id,
+ .sta = sta,
+ };
+
+ if (WARN_ON_ONCE(vif->type != NL80211_IFTYPE_STATION))
+ return;
+
+ ieee80211_iter_keys(mld->hw, vif,
+ iwl_mld_remove_ap_keys_iter,
+ &iter_data);
+}
+
+struct iwl_mvm_sta_key_update_data {
+ struct ieee80211_sta *sta;
+ u32 old_sta_mask;
+ u32 new_sta_mask;
+ int err;
+};
+
+static void iwl_mld_update_sta_key_iter(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta,
+ struct ieee80211_key_conf *key,
+ void *_data)
+{
+ struct iwl_mvm_sta_key_update_data *data = _data;
+ struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw);
+ struct iwl_sec_key_cmd cmd = {
+ .action = cpu_to_le32(FW_CTXT_ACTION_MODIFY),
+ .u.modify.old_sta_mask = cpu_to_le32(data->old_sta_mask),
+ .u.modify.new_sta_mask = cpu_to_le32(data->new_sta_mask),
+ .u.modify.key_id = cpu_to_le32(key->keyidx),
+ .u.modify.key_flags =
+ cpu_to_le32(iwl_mld_get_key_flags(mld, vif, sta, key)),
+ };
+ int err;
+
+ /* only need to do this for pairwise keys (link_id == -1) */
+ if (sta != data->sta || key->link_id >= 0)
+ return;
+
+ err = iwl_mld_send_cmd_pdu(mld, WIDE_ID(DATA_PATH_GROUP, SEC_KEY_CMD),
+ &cmd);
+
+ if (err)
+ data->err = err;
+}
+
+int iwl_mld_update_sta_keys(struct iwl_mld *mld,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta,
+ u32 old_sta_mask,
+ u32 new_sta_mask)
+{
+ struct iwl_mvm_sta_key_update_data data = {
+ .sta = sta,
+ .old_sta_mask = old_sta_mask,
+ .new_sta_mask = new_sta_mask,
+ };
+
+ ieee80211_iter_keys(mld->hw, vif, iwl_mld_update_sta_key_iter,
+ &data);
+ return data.err;
+}
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/key.h b/drivers/net/wireless/intel/iwlwifi/mld/key.h
new file mode 100644
index 000000000000..a68ea48913be
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/mld/key.h
@@ -0,0 +1,39 @@
+/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
+/*
+ * Copyright (C) 2024 Intel Corporation
+ */
+#ifndef __iwl_mld_key_h__
+#define __iwl_mld_key_h__
+
+#include "mld.h"
+#include <net/mac80211.h>
+#include "fw/api/sta.h"
+
+void iwl_mld_remove_key(struct iwl_mld *mld,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta,
+ struct ieee80211_key_conf *key);
+int iwl_mld_add_key(struct iwl_mld *mld,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta,
+ struct ieee80211_key_conf *key);
+void iwl_mld_remove_ap_keys(struct iwl_mld *mld,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta,
+ unsigned int link_id);
+
+int iwl_mld_update_sta_keys(struct iwl_mld *mld,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta,
+ u32 old_sta_mask,
+ u32 new_sta_mask);
+
+static inline void
+iwl_mld_cleanup_keys_iter(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta,
+ struct ieee80211_key_conf *key, void *data)
+{
+ key->hw_key_idx = STA_KEY_IDX_INVALID;
+}
+
+#endif /* __iwl_mld_key_h__ */
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/led.c b/drivers/net/wireless/intel/iwlwifi/mld/led.c
new file mode 100644
index 000000000000..a37b32cbc6e6
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/mld/led.c
@@ -0,0 +1,100 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
+/*
+ * Copyright (C) 2024 Intel Corporation
+ */
+#include <linux/leds.h>
+#include <net/mac80211.h>
+
+#include "fw/api/led.h"
+#include "mld.h"
+#include "led.h"
+#include "hcmd.h"
+
+static void iwl_mld_send_led_fw_cmd(struct iwl_mld *mld, bool on)
+{
+ struct iwl_led_cmd led_cmd = {
+ .status = cpu_to_le32(on),
+ };
+ int err;
+
+ if (WARN_ON(!mld->fw_status.running))
+ return;
+
+ err = iwl_mld_send_cmd_with_flags_pdu(mld, WIDE_ID(LONG_GROUP,
+ LEDS_CMD),
+ CMD_ASYNC, &led_cmd);
+
+ if (err)
+ IWL_WARN(mld, "LED command failed: %d\n", err);
+}
+
+static void iwl_led_brightness_set(struct led_classdev *led_cdev,
+ enum led_brightness brightness)
+{
+ struct iwl_mld *mld = container_of(led_cdev, struct iwl_mld, led);
+
+ if (!mld->fw_status.running)
+ return;
+
+ iwl_mld_send_led_fw_cmd(mld, brightness > 0);
+}
+
+int iwl_mld_leds_init(struct iwl_mld *mld)
+{
+ int mode = iwlwifi_mod_params.led_mode;
+ int ret;
+
+ switch (mode) {
+ case IWL_LED_BLINK:
+ IWL_ERR(mld, "Blink led mode not supported, used default\n");
+ fallthrough;
+ case IWL_LED_DEFAULT:
+ case IWL_LED_RF_STATE:
+ mode = IWL_LED_RF_STATE;
+ break;
+ case IWL_LED_DISABLE:
+ IWL_INFO(mld, "Led disabled\n");
+ return 0;
+ default:
+ return -EINVAL;
+ }
+
+ mld->led.name = kasprintf(GFP_KERNEL, "%s-led",
+ wiphy_name(mld->hw->wiphy));
+ if (!mld->led.name)
+ return -ENOMEM;
+
+ mld->led.brightness_set = iwl_led_brightness_set;
+ mld->led.max_brightness = 1;
+
+ if (mode == IWL_LED_RF_STATE)
+ mld->led.default_trigger =
+ ieee80211_get_radio_led_name(mld->hw);
+
+ ret = led_classdev_register(mld->trans->dev, &mld->led);
+ if (ret) {
+ kfree(mld->led.name);
+ mld->led.name = NULL;
+ IWL_INFO(mld, "Failed to enable led\n");
+ }
+
+ return ret;
+}
+
+void iwl_mld_led_config_fw(struct iwl_mld *mld)
+{
+ if (!mld->led.name)
+ return;
+
+ iwl_mld_send_led_fw_cmd(mld, mld->led.brightness > 0);
+}
+
+void iwl_mld_leds_exit(struct iwl_mld *mld)
+{
+ if (!mld->led.name)
+ return;
+
+ led_classdev_unregister(&mld->led);
+ kfree(mld->led.name);
+ mld->led.name = NULL;
+}
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/led.h b/drivers/net/wireless/intel/iwlwifi/mld/led.h
new file mode 100644
index 000000000000..0954e214e73d
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/mld/led.h
@@ -0,0 +1,29 @@
+/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
+/*
+ * Copyright (C) 2024 Intel Corporation
+ */
+#ifndef __iwl_mld_led_h__
+#define __iwl_mld_led_h__
+
+#include "mld.h"
+
+#ifdef CONFIG_IWLWIFI_LEDS
+int iwl_mld_leds_init(struct iwl_mld *mld);
+void iwl_mld_leds_exit(struct iwl_mld *mld);
+void iwl_mld_led_config_fw(struct iwl_mld *mld);
+#else
+static inline int iwl_mld_leds_init(struct iwl_mld *mld)
+{
+ return 0;
+}
+
+static inline void iwl_mld_leds_exit(struct iwl_mld *mld)
+{
+}
+
+static inline void iwl_mld_led_config_fw(struct iwl_mld *mld)
+{
+}
+#endif
+
+#endif /* __iwl_mld_led_h__ */
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/link.c b/drivers/net/wireless/intel/iwlwifi/mld/link.c
new file mode 100644
index 000000000000..d0f56189ad3f
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/mld/link.c
@@ -0,0 +1,1214 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
+/*
+ * Copyright (C) 2024-2025 Intel Corporation
+ */
+
+#include "constants.h"
+#include "link.h"
+#include "iface.h"
+#include "mlo.h"
+#include "hcmd.h"
+#include "phy.h"
+#include "fw/api/rs.h"
+#include "fw/api/txq.h"
+#include "fw/api/mac.h"
+
+#include "fw/api/context.h"
+#include "fw/dbg.h"
+
+static int iwl_mld_send_link_cmd(struct iwl_mld *mld,
+ struct iwl_link_config_cmd *cmd,
+ enum iwl_ctxt_action action)
+{
+ int ret;
+
+ lockdep_assert_wiphy(mld->wiphy);
+
+ cmd->action = cpu_to_le32(action);
+ ret = iwl_mld_send_cmd_pdu(mld,
+ WIDE_ID(MAC_CONF_GROUP, LINK_CONFIG_CMD),
+ cmd);
+ if (ret)
+ IWL_ERR(mld, "Failed to send LINK_CONFIG_CMD (action:%d): %d\n",
+ action, ret);
+ return ret;
+}
+
+static int iwl_mld_add_link_to_fw(struct iwl_mld *mld,
+ struct ieee80211_bss_conf *link_conf)
+{
+ struct ieee80211_vif *vif = link_conf->vif;
+ struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
+ struct iwl_mld_link *link = iwl_mld_link_from_mac80211(link_conf);
+ struct iwl_link_config_cmd cmd = {};
+
+ lockdep_assert_wiphy(mld->wiphy);
+
+ if (WARN_ON(!link))
+ return -EINVAL;
+
+ cmd.link_id = cpu_to_le32(link->fw_id);
+ cmd.mac_id = cpu_to_le32(mld_vif->fw_id);
+ cmd.spec_link_id = link_conf->link_id;
+ cmd.phy_id = cpu_to_le32(FW_CTXT_ID_INVALID);
+
+ ether_addr_copy(cmd.local_link_addr, link_conf->addr);
+
+ if (vif->type == NL80211_IFTYPE_ADHOC && link_conf->bssid)
+ ether_addr_copy(cmd.ibss_bssid_addr, link_conf->bssid);
+
+ return iwl_mld_send_link_cmd(mld, &cmd, FW_CTXT_ACTION_ADD);
+}
+
+/* Get the basic rates of the used band and add the mandatory ones */
+static void iwl_mld_fill_rates(struct iwl_mld *mld,
+ struct ieee80211_bss_conf *link,
+ struct ieee80211_chanctx_conf *chan_ctx,
+ __le32 *cck_rates, __le32 *ofdm_rates)
+{
+ struct cfg80211_chan_def *chandef =
+ iwl_mld_get_chandef_from_chanctx(mld, chan_ctx);
+ struct ieee80211_supported_band *sband =
+ mld->hw->wiphy->bands[chandef->chan->band];
+ unsigned long basic = link->basic_rates;
+ int lowest_present_ofdm = 100;
+ int lowest_present_cck = 100;
+ u32 cck = 0;
+ u32 ofdm = 0;
+ int i;
+
+ for_each_set_bit(i, &basic, BITS_PER_LONG) {
+ int hw = sband->bitrates[i].hw_value;
+
+ if (hw >= IWL_FIRST_OFDM_RATE) {
+ ofdm |= BIT(hw - IWL_FIRST_OFDM_RATE);
+ if (lowest_present_ofdm > hw)
+ lowest_present_ofdm = hw;
+ } else {
+ BUILD_BUG_ON(IWL_FIRST_CCK_RATE != 0);
+
+ cck |= BIT(hw);
+ if (lowest_present_cck > hw)
+ lowest_present_cck = hw;
+ }
+ }
+
+ /* Now we've got the basic rates as bitmaps in the ofdm and cck
+ * variables. This isn't sufficient though, as there might not
+ * be all the right rates in the bitmap. E.g. if the only basic
+ * rates are 5.5 Mbps and 11 Mbps, we still need to add 1 Mbps
+ * and 6 Mbps because the 802.11-2007 standard says in 9.6:
+ *
+ * [...] a STA responding to a received frame shall transmit
+ * its Control Response frame [...] at the highest rate in the
+ * BSSBasicRateSet parameter that is less than or equal to the
+ * rate of the immediately previous frame in the frame exchange
+ * sequence ([...]) and that is of the same modulation class
+ * ([...]) as the received frame. If no rate contained in the
+ * BSSBasicRateSet parameter meets these conditions, then the
+ * control frame sent in response to a received frame shall be
+ * transmitted at the highest mandatory rate of the PHY that is
+ * less than or equal to the rate of the received frame, and
+ * that is of the same modulation class as the received frame.
+ *
+ * As a consequence, we need to add all mandatory rates that are
+ * lower than all of the basic rates to these bitmaps.
+ */
+
+ if (lowest_present_ofdm > IWL_RATE_24M_INDEX)
+ ofdm |= IWL_RATE_BIT_MSK(24) >> IWL_FIRST_OFDM_RATE;
+ if (lowest_present_ofdm > IWL_RATE_12M_INDEX)
+ ofdm |= IWL_RATE_BIT_MSK(12) >> IWL_FIRST_OFDM_RATE;
+ /* 6M already there or needed so always add */
+ ofdm |= IWL_RATE_BIT_MSK(6) >> IWL_FIRST_OFDM_RATE;
+
+ /* CCK is a bit more complex with DSSS vs. HR/DSSS vs. ERP.
+ * Note, however:
+ * - if no CCK rates are basic, it must be ERP since there must
+ * be some basic rates at all, so they're OFDM => ERP PHY
+ * (or we're in 5 GHz, and the cck bitmap will never be used)
+ * - if 11M is a basic rate, it must be ERP as well, so add 5.5M
+ * - if 5.5M is basic, 1M and 2M are mandatory
+ * - if 2M is basic, 1M is mandatory
+ * - if 1M is basic, that's the only valid ACK rate.
+ * As a consequence, it's not as complicated as it sounds, just add
+ * any lower rates to the ACK rate bitmap.
+ */
+ if (lowest_present_cck > IWL_RATE_11M_INDEX)
+ cck |= IWL_RATE_BIT_MSK(11) >> IWL_FIRST_CCK_RATE;
+ if (lowest_present_cck > IWL_RATE_5M_INDEX)
+ cck |= IWL_RATE_BIT_MSK(5) >> IWL_FIRST_CCK_RATE;
+ if (lowest_present_cck > IWL_RATE_2M_INDEX)
+ cck |= IWL_RATE_BIT_MSK(2) >> IWL_FIRST_CCK_RATE;
+ /* 1M already there or needed so always add */
+ cck |= IWL_RATE_BIT_MSK(1) >> IWL_FIRST_CCK_RATE;
+
+ *cck_rates = cpu_to_le32((u32)cck);
+ *ofdm_rates = cpu_to_le32((u32)ofdm);
+}
+
+static void iwl_mld_fill_protection_flags(struct iwl_mld *mld,
+ struct ieee80211_bss_conf *link,
+ __le32 *protection_flags)
+{
+ u8 protection_mode = link->ht_operation_mode &
+ IEEE80211_HT_OP_MODE_PROTECTION;
+ u8 ht_flag = LINK_PROT_FLG_HT_PROT | LINK_PROT_FLG_FAT_PROT;
+
+ IWL_DEBUG_RATE(mld, "HT protection mode: %d\n", protection_mode);
+
+ if (link->use_cts_prot)
+ *protection_flags |= cpu_to_le32(LINK_PROT_FLG_TGG_PROTECT);
+
+ /* See section 9.23.3.1 of IEEE 80211-2012.
+ * Nongreenfield HT STAs Present is not supported.
+ */
+ switch (protection_mode) {
+ case IEEE80211_HT_OP_MODE_PROTECTION_NONE:
+ break;
+ case IEEE80211_HT_OP_MODE_PROTECTION_NONMEMBER:
+ case IEEE80211_HT_OP_MODE_PROTECTION_NONHT_MIXED:
+ *protection_flags |= cpu_to_le32(ht_flag);
+ break;
+ case IEEE80211_HT_OP_MODE_PROTECTION_20MHZ:
+ /* Protect when channel wider than 20MHz */
+ if (link->chanreq.oper.width > NL80211_CHAN_WIDTH_20)
+ *protection_flags |= cpu_to_le32(ht_flag);
+ break;
+ }
+}
+
+static u8 iwl_mld_mac80211_ac_to_fw_ac(enum ieee80211_ac_numbers ac)
+{
+ static const u8 mac80211_ac_to_fw[] = {
+ AC_VO,
+ AC_VI,
+ AC_BE,
+ AC_BK
+ };
+
+ return mac80211_ac_to_fw[ac];
+}
+
+static void iwl_mld_fill_qos_params(struct ieee80211_bss_conf *link,
+ struct iwl_ac_qos *ac, __le32 *qos_flags)
+{
+ struct iwl_mld_link *mld_link = iwl_mld_link_from_mac80211(link);
+
+ /* no need to check mld_link since it is done in the caller */
+
+ for (int mac_ac = 0; mac_ac < IEEE80211_NUM_ACS; mac_ac++) {
+ u8 txf = iwl_mld_mac80211_ac_to_fw_tx_fifo(mac_ac);
+ u8 fw_ac = iwl_mld_mac80211_ac_to_fw_ac(mac_ac);
+
+ ac[fw_ac].cw_min =
+ cpu_to_le16(mld_link->queue_params[mac_ac].cw_min);
+ ac[fw_ac].cw_max =
+ cpu_to_le16(mld_link->queue_params[mac_ac].cw_max);
+ ac[fw_ac].edca_txop =
+ cpu_to_le16(mld_link->queue_params[mac_ac].txop * 32);
+ ac[fw_ac].aifsn = mld_link->queue_params[mac_ac].aifs;
+ ac[fw_ac].fifos_mask = BIT(txf);
+ }
+
+ if (link->qos)
+ *qos_flags |= cpu_to_le32(MAC_QOS_FLG_UPDATE_EDCA);
+
+ if (link->chanreq.oper.width != NL80211_CHAN_WIDTH_20_NOHT)
+ *qos_flags |= cpu_to_le32(MAC_QOS_FLG_TGN);
+}
+
+static bool iwl_mld_fill_mu_edca(struct iwl_mld *mld,
+ const struct iwl_mld_link *mld_link,
+ struct iwl_he_backoff_conf *trig_based_txf)
+{
+ for (int mac_ac = 0; mac_ac < IEEE80211_NUM_ACS; mac_ac++) {
+ const struct ieee80211_he_mu_edca_param_ac_rec *mu_edca =
+ &mld_link->queue_params[mac_ac].mu_edca_param_rec;
+ u8 fw_ac = iwl_mld_mac80211_ac_to_fw_ac(mac_ac);
+
+ if (!mld_link->queue_params[mac_ac].mu_edca)
+ return false;
+
+ trig_based_txf[fw_ac].cwmin =
+ cpu_to_le16(mu_edca->ecw_min_max & 0xf);
+ trig_based_txf[fw_ac].cwmax =
+ cpu_to_le16((mu_edca->ecw_min_max & 0xf0) >> 4);
+ trig_based_txf[fw_ac].aifsn =
+ cpu_to_le16(mu_edca->aifsn & 0xf);
+ trig_based_txf[fw_ac].mu_time =
+ cpu_to_le16(mu_edca->mu_edca_timer);
+ }
+ return true;
+}
+
+static u8 iwl_mld_sta_rx_bw_to_fw(enum ieee80211_sta_rx_bandwidth bw)
+{
+ switch (bw) {
+ default: /* potential future values not supported by this hw/driver */
+ case IEEE80211_STA_RX_BW_20:
+ return IWL_LINK_MODIFY_BW_20;
+ case IEEE80211_STA_RX_BW_40:
+ return IWL_LINK_MODIFY_BW_40;
+ case IEEE80211_STA_RX_BW_80:
+ return IWL_LINK_MODIFY_BW_80;
+ case IEEE80211_STA_RX_BW_160:
+ return IWL_LINK_MODIFY_BW_160;
+ case IEEE80211_STA_RX_BW_320:
+ return IWL_LINK_MODIFY_BW_320;
+ }
+}
+
+static int _iwl_mld_change_link_in_fw(struct iwl_mld *mld,
+ struct ieee80211_bss_conf *link,
+ enum ieee80211_sta_rx_bandwidth bw,
+ u32 changes)
+{
+ struct iwl_mld_link *mld_link = iwl_mld_link_from_mac80211(link);
+ struct ieee80211_vif *vif = link->vif;
+ struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
+ struct ieee80211_chanctx_conf *chan_ctx;
+ struct iwl_link_config_cmd cmd = {};
+ u32 flags = 0;
+
+ lockdep_assert_wiphy(mld->wiphy);
+
+ if (WARN_ON(!mld_link))
+ return -EINVAL;
+
+ cmd.link_id = cpu_to_le32(mld_link->fw_id);
+ cmd.spec_link_id = link->link_id;
+ cmd.mac_id = cpu_to_le32(mld_vif->fw_id);
+
+ chan_ctx = wiphy_dereference(mld->wiphy, mld_link->chan_ctx);
+
+ cmd.phy_id = cpu_to_le32(chan_ctx ?
+ iwl_mld_phy_from_mac80211(chan_ctx)->fw_id :
+ FW_CTXT_ID_INVALID);
+
+ ether_addr_copy(cmd.local_link_addr, link->addr);
+
+ cmd.active = cpu_to_le32(mld_link->active);
+
+ if ((changes & LINK_CONTEXT_MODIFY_ACTIVE) && !mld_link->active &&
+ mld_link->silent_deactivation) {
+ /* We are de-activating a link that is having CSA with
+ * immediate quiet in EMLSR. Tell the firmware not to send any
+ * frame.
+ */
+ cmd.block_tx = 1;
+ mld_link->silent_deactivation = false;
+ }
+
+ if (vif->type == NL80211_IFTYPE_ADHOC && link->bssid)
+ ether_addr_copy(cmd.ibss_bssid_addr, link->bssid);
+
+ /* Channel context is needed to get the rates */
+ if (chan_ctx)
+ iwl_mld_fill_rates(mld, link, chan_ctx, &cmd.cck_rates,
+ &cmd.ofdm_rates);
+
+ cmd.cck_short_preamble = cpu_to_le32(link->use_short_preamble);
+ cmd.short_slot = cpu_to_le32(link->use_short_slot);
+
+ iwl_mld_fill_protection_flags(mld, link, &cmd.protection_flags);
+
+ iwl_mld_fill_qos_params(link, cmd.ac, &cmd.qos_flags);
+
+ cmd.bi = cpu_to_le32(link->beacon_int);
+ cmd.dtim_interval = cpu_to_le32(link->beacon_int * link->dtim_period);
+
+ if (changes & LINK_CONTEXT_MODIFY_BANDWIDTH)
+ cmd.modify_bandwidth = iwl_mld_sta_rx_bw_to_fw(bw);
+
+ /* Configure HE parameters only if HE is supported, and only after
+ * the parameters are set in mac80211 (meaning after assoc)
+ */
+ if (!link->he_support || iwlwifi_mod_params.disable_11ax ||
+ (vif->type == NL80211_IFTYPE_STATION && !vif->cfg.assoc)) {
+ changes &= ~LINK_CONTEXT_MODIFY_HE_PARAMS;
+ goto send_cmd;
+ }
+
+ /* ap_sta may be NULL if we're disconnecting */
+ if (mld_vif->ap_sta) {
+ struct ieee80211_link_sta *link_sta =
+ link_sta_dereference_check(mld_vif->ap_sta,
+ link->link_id);
+
+ if (!WARN_ON(!link_sta) && link_sta->he_cap.has_he &&
+ link_sta->he_cap.he_cap_elem.mac_cap_info[5] &
+ IEEE80211_HE_MAC_CAP5_OM_CTRL_UL_MU_DATA_DIS_RX)
+ cmd.ul_mu_data_disable = 1;
+ }
+
+ cmd.htc_trig_based_pkt_ext = link->htc_trig_based_pkt_ext;
+
+ if (link->uora_exists) {
+ cmd.rand_alloc_ecwmin = link->uora_ocw_range & 0x7;
+ cmd.rand_alloc_ecwmax = (link->uora_ocw_range >> 3) & 0x7;
+ }
+
+ if (iwl_mld_fill_mu_edca(mld, mld_link, cmd.trig_based_txf))
+ flags |= LINK_FLG_MU_EDCA_CW;
+
+ cmd.bss_color = link->he_bss_color.color;
+
+ if (!link->he_bss_color.enabled)
+ flags |= LINK_FLG_BSS_COLOR_DIS;
+
+ cmd.frame_time_rts_th = cpu_to_le16(link->frame_time_rts_th);
+
+ /* Block 26-tone RU OFDMA transmissions */
+ if (mld_link->he_ru_2mhz_block)
+ flags |= LINK_FLG_RU_2MHZ_BLOCK;
+
+ if (link->nontransmitted) {
+ ether_addr_copy(cmd.ref_bssid_addr, link->transmitter_bssid);
+ cmd.bssid_index = link->bssid_index;
+ }
+
+ /* The only EHT parameter is puncturing, and starting from PHY cmd
+ * version 6 - it is sent there. For older versions of the PHY cmd,
+ * puncturing is not needed at all.
+ */
+ if (WARN_ON(changes & LINK_CONTEXT_MODIFY_EHT_PARAMS))
+ changes &= ~LINK_CONTEXT_MODIFY_EHT_PARAMS;
+
+send_cmd:
+ cmd.modify_mask = cpu_to_le32(changes);
+ cmd.flags = cpu_to_le32(flags);
+
+ return iwl_mld_send_link_cmd(mld, &cmd, FW_CTXT_ACTION_MODIFY);
+}
+
+int iwl_mld_change_link_in_fw(struct iwl_mld *mld,
+ struct ieee80211_bss_conf *link,
+ u32 changes)
+{
+ if (WARN_ON(changes & LINK_CONTEXT_MODIFY_BANDWIDTH))
+ changes &= ~LINK_CONTEXT_MODIFY_BANDWIDTH;
+
+ return _iwl_mld_change_link_in_fw(mld, link, 0, changes);
+}
+
+int iwl_mld_change_link_omi_bw(struct iwl_mld *mld,
+ struct ieee80211_bss_conf *link,
+ enum ieee80211_sta_rx_bandwidth bw)
+{
+ return _iwl_mld_change_link_in_fw(mld, link, bw,
+ LINK_CONTEXT_MODIFY_BANDWIDTH);
+}
+
+int iwl_mld_activate_link(struct iwl_mld *mld,
+ struct ieee80211_bss_conf *link)
+{
+ struct iwl_mld_link *mld_link = iwl_mld_link_from_mac80211(link);
+ int ret;
+
+ lockdep_assert_wiphy(mld->wiphy);
+
+ if (WARN_ON(!mld_link || mld_link->active))
+ return -EINVAL;
+
+ mld_link->rx_omi.exit_ts = jiffies;
+ mld_link->active = true;
+
+ ret = iwl_mld_change_link_in_fw(mld, link,
+ LINK_CONTEXT_MODIFY_ACTIVE);
+ if (ret)
+ mld_link->active = false;
+
+ return ret;
+}
+
+void iwl_mld_deactivate_link(struct iwl_mld *mld,
+ struct ieee80211_bss_conf *link)
+{
+ struct iwl_mld_link *mld_link = iwl_mld_link_from_mac80211(link);
+ struct iwl_probe_resp_data *probe_data;
+
+ lockdep_assert_wiphy(mld->wiphy);
+
+ if (WARN_ON(!mld_link || !mld_link->active))
+ return;
+
+ iwl_mld_cancel_session_protection(mld, link->vif, link->link_id);
+
+ /* If we deactivate the link, we will probably remove it, or switch
+ * channel. In both cases, the CSA or Notice of Absence information is
+ * now irrelevant. Remove the data here.
+ */
+ probe_data = wiphy_dereference(mld->wiphy, mld_link->probe_resp_data);
+ RCU_INIT_POINTER(mld_link->probe_resp_data, NULL);
+ if (probe_data)
+ kfree_rcu(probe_data, rcu_head);
+
+ mld_link->active = false;
+
+ iwl_mld_change_link_in_fw(mld, link, LINK_CONTEXT_MODIFY_ACTIVE);
+
+ /* Now that the link is not active in FW, we don't expect any new
+ * notifications for it. Cancel the ones that are already pending
+ */
+ iwl_mld_cancel_notifications_of_object(mld, IWL_MLD_OBJECT_TYPE_LINK,
+ mld_link->fw_id);
+}
+
+static void
+iwl_mld_rm_link_from_fw(struct iwl_mld *mld, struct ieee80211_bss_conf *link)
+{
+ struct iwl_mld_link *mld_link = iwl_mld_link_from_mac80211(link);
+ struct iwl_link_config_cmd cmd = {};
+
+ lockdep_assert_wiphy(mld->wiphy);
+
+ if (WARN_ON(!mld_link))
+ return;
+
+ cmd.link_id = cpu_to_le32(mld_link->fw_id);
+ cmd.spec_link_id = link->link_id;
+ cmd.phy_id = cpu_to_le32(FW_CTXT_ID_INVALID);
+
+ iwl_mld_send_link_cmd(mld, &cmd, FW_CTXT_ACTION_REMOVE);
+}
+
+static void iwl_mld_omi_bw_update(struct iwl_mld *mld,
+ struct ieee80211_bss_conf *link_conf,
+ struct iwl_mld_link *mld_link,
+ struct ieee80211_link_sta *link_sta,
+ enum ieee80211_sta_rx_bandwidth bw,
+ bool ap_update)
+{
+ enum ieee80211_sta_rx_bandwidth apply_bw;
+
+ mld_link->rx_omi.desired_bw = bw;
+
+ /* Can't update OMI while already in progress, desired_bw was
+ * set so on FW notification the worker will see the change
+ * and apply new the new desired bw.
+ */
+ if (mld_link->rx_omi.bw_in_progress)
+ return;
+
+ if (bw == IEEE80211_STA_RX_BW_MAX)
+ apply_bw = ieee80211_chan_width_to_rx_bw(link_conf->chanreq.oper.width);
+ else
+ apply_bw = bw;
+
+ if (!ap_update) {
+ /* The update isn't due to AP tracking after leaving OMI,
+ * where the AP could increase BW and then we must tell
+ * it that we can do the increased BW as well, if we did
+ * update the chandef.
+ * In this case, if we want MAX, then we will need to send
+ * a new OMI to the AP if it increases its own bandwidth as
+ * we can (due to internal and FW limitations, and being
+ * worried the AP might break) only send to what we're doing
+ * at the moment. In this case, set last_max_bw; otherwise
+ * if we really want to decrease our bandwidth set it to 0
+ * to indicate no updates are needed if the AP changes.
+ */
+ if (bw != IEEE80211_STA_RX_BW_MAX)
+ mld_link->rx_omi.last_max_bw = apply_bw;
+ else
+ mld_link->rx_omi.last_max_bw = 0;
+ } else {
+ /* Otherwise, if we're already trying to do maximum and
+ * the AP is changing, set last_max_bw to the new max the
+ * AP is using, we'll only get to this code path if the
+ * new bandwidth of the AP is bigger than what we sent it
+ * previously. This avoids repeatedly sending updates if
+ * it changes bandwidth, only doing it once on an increase.
+ */
+ mld_link->rx_omi.last_max_bw = apply_bw;
+ }
+
+ if (ieee80211_prepare_rx_omi_bw(link_sta, bw)) {
+ mld_link->rx_omi.bw_in_progress = apply_bw;
+ iwl_mld_change_link_omi_bw(mld, link_conf, apply_bw);
+ }
+}
+
+static void iwl_mld_omi_bw_finished_work(struct wiphy *wiphy,
+ struct wiphy_work *work)
+{
+ struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
+ struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw);
+ struct iwl_mld_link *mld_link =
+ container_of(work, typeof(*mld_link), rx_omi.finished_work.work);
+ enum ieee80211_sta_rx_bandwidth desired_bw, switched_to_bw;
+ struct ieee80211_vif *vif = mld_link->vif;
+ struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
+ struct ieee80211_bss_conf *link_conf;
+ struct ieee80211_link_sta *link_sta;
+
+ if (!mld_vif->ap_sta)
+ return;
+
+ link_sta = wiphy_dereference(mld->wiphy,
+ mld_vif->ap_sta->link[mld_link->link_id]);
+ if (WARN_ON_ONCE(!link_sta))
+ return;
+
+ link_conf = link_conf_dereference_protected(vif, link_sta->link_id);
+ if (WARN_ON_ONCE(!link_conf))
+ return;
+
+ if (WARN_ON(!mld_link->rx_omi.bw_in_progress))
+ return;
+
+ desired_bw = mld_link->rx_omi.desired_bw;
+ switched_to_bw = mld_link->rx_omi.bw_in_progress;
+
+ ieee80211_finalize_rx_omi_bw(link_sta);
+ mld_link->rx_omi.bw_in_progress = 0;
+
+ if (desired_bw != switched_to_bw)
+ iwl_mld_omi_bw_update(mld, link_conf, mld_link, link_sta,
+ desired_bw, false);
+}
+
+static struct ieee80211_vif *
+iwl_mld_get_omi_bw_reduction_pointers(struct iwl_mld *mld,
+ struct ieee80211_link_sta **link_sta,
+ struct iwl_mld_link **mld_link)
+{
+ struct iwl_mld_vif *mld_vif;
+ struct ieee80211_vif *vif;
+ int n_link_stas = 0;
+
+ *link_sta = NULL;
+
+ if (mld->trans->mac_cfg->device_family < IWL_DEVICE_FAMILY_SC)
+ return NULL;
+
+ vif = iwl_mld_get_bss_vif(mld);
+ if (!vif)
+ return NULL;
+
+ for (int i = 0; i < ARRAY_SIZE(mld->fw_id_to_link_sta); i++) {
+ struct ieee80211_link_sta *tmp;
+
+ tmp = wiphy_dereference(mld->wiphy, mld->fw_id_to_link_sta[i]);
+ if (IS_ERR_OR_NULL(tmp))
+ continue;
+
+ n_link_stas++;
+ *link_sta = tmp;
+ }
+
+ /* can't do anything if we have TDLS peers or EMLSR */
+ if (n_link_stas != 1)
+ return NULL;
+
+ mld_vif = iwl_mld_vif_from_mac80211(vif);
+ *mld_link = iwl_mld_link_dereference_check(mld_vif,
+ (*link_sta)->link_id);
+ if (WARN_ON(!*mld_link))
+ return NULL;
+
+ return vif;
+}
+
+void iwl_mld_omi_ap_changed_bw(struct iwl_mld *mld,
+ struct ieee80211_bss_conf *link_conf,
+ enum ieee80211_sta_rx_bandwidth bw)
+{
+ struct ieee80211_link_sta *link_sta;
+ struct iwl_mld_link *mld_link;
+ struct ieee80211_vif *vif;
+
+ vif = iwl_mld_get_omi_bw_reduction_pointers(mld, &link_sta, &mld_link);
+ if (!vif)
+ return;
+
+ if (WARN_ON(link_conf->vif != vif))
+ return;
+
+ /* This is 0 if we requested an OMI BW reduction and don't want to
+ * be sending an OMI when the AP's bandwidth changes.
+ */
+ if (!mld_link->rx_omi.last_max_bw)
+ return;
+
+ /* We only need to tell the AP if it increases BW over what we last
+ * told it we were using, if it reduces then our last OMI to it will
+ * not get used anyway (e.g. we said we want 160 but it's doing 80.)
+ */
+ if (bw < mld_link->rx_omi.last_max_bw)
+ return;
+
+ iwl_mld_omi_bw_update(mld, link_conf, mld_link, link_sta, bw, true);
+}
+
+void iwl_mld_handle_omi_status_notif(struct iwl_mld *mld,
+ struct iwl_rx_packet *pkt)
+{
+ struct ieee80211_link_sta *link_sta;
+ struct iwl_mld_link *mld_link;
+ struct ieee80211_vif *vif;
+
+ vif = iwl_mld_get_omi_bw_reduction_pointers(mld, &link_sta, &mld_link);
+ if (IWL_FW_CHECK(mld, !vif, "unexpected OMI notification\n"))
+ return;
+
+ if (IWL_FW_CHECK(mld, !mld_link->rx_omi.bw_in_progress,
+ "OMI notification when not requested\n"))
+ return;
+
+ wiphy_delayed_work_queue(mld->hw->wiphy,
+ &mld_link->rx_omi.finished_work,
+ msecs_to_jiffies(IWL_MLD_OMI_AP_SETTLE_DELAY));
+}
+
+void iwl_mld_leave_omi_bw_reduction(struct iwl_mld *mld)
+{
+ struct ieee80211_bss_conf *link_conf;
+ struct ieee80211_link_sta *link_sta;
+ struct iwl_mld_link *mld_link;
+ struct ieee80211_vif *vif;
+
+ vif = iwl_mld_get_omi_bw_reduction_pointers(mld, &link_sta, &mld_link);
+ if (!vif)
+ return;
+
+ link_conf = link_conf_dereference_protected(vif, link_sta->link_id);
+ if (WARN_ON_ONCE(!link_conf))
+ return;
+
+ if (!link_conf->he_support)
+ return;
+
+ mld_link->rx_omi.exit_ts = jiffies;
+
+ iwl_mld_omi_bw_update(mld, link_conf, mld_link, link_sta,
+ IEEE80211_STA_RX_BW_MAX, false);
+}
+
+void iwl_mld_check_omi_bw_reduction(struct iwl_mld *mld)
+{
+ enum ieee80211_sta_rx_bandwidth bw = IEEE80211_STA_RX_BW_MAX;
+ struct ieee80211_chanctx_conf *chanctx;
+ struct ieee80211_bss_conf *link_conf;
+ struct ieee80211_link_sta *link_sta;
+ struct cfg80211_chan_def chandef;
+ struct iwl_mld_link *mld_link;
+ struct iwl_mld_vif *mld_vif;
+ struct ieee80211_vif *vif;
+ struct iwl_mld_phy *phy;
+ u16 punctured;
+ int exit_thr;
+
+ /* not allowed in CAM mode */
+ if (iwlmld_mod_params.power_scheme == IWL_POWER_SCHEME_CAM)
+ return;
+
+ /* must have one BSS connection (no P2P), no TDLS, nor EMLSR */
+ vif = iwl_mld_get_omi_bw_reduction_pointers(mld, &link_sta, &mld_link);
+ if (!vif)
+ return;
+
+ link_conf = link_conf_dereference_protected(vif, link_sta->link_id);
+ if (WARN_ON_ONCE(!link_conf))
+ return;
+
+ if (!link_conf->he_support)
+ return;
+
+ chanctx = wiphy_dereference(mld->wiphy, mld_link->chan_ctx);
+ if (WARN_ON(!chanctx))
+ return;
+
+ mld_vif = iwl_mld_vif_from_mac80211(vif);
+ if (!mld_vif->authorized)
+ goto apply;
+
+ /* must not be in low-latency mode */
+ if (iwl_mld_vif_low_latency(mld_vif))
+ goto apply;
+
+ chandef = link_conf->chanreq.oper;
+
+ switch (chandef.width) {
+ case NL80211_CHAN_WIDTH_320:
+ exit_thr = IWL_MLD_OMI_EXIT_CHAN_LOAD_320;
+ break;
+ case NL80211_CHAN_WIDTH_160:
+ exit_thr = IWL_MLD_OMI_EXIT_CHAN_LOAD_160;
+ break;
+ default:
+ /* since we reduce to 80 MHz, must have more to start with */
+ goto apply;
+ }
+
+ /* not to be done if primary 80 MHz is punctured */
+ if (cfg80211_chandef_primary(&chandef, NL80211_CHAN_WIDTH_80,
+ &punctured) < 0 ||
+ punctured != 0)
+ goto apply;
+
+ phy = iwl_mld_phy_from_mac80211(chanctx);
+
+ if (phy->channel_load_by_us > exit_thr) {
+ /* send OMI for max bandwidth */
+ goto apply;
+ }
+
+ if (phy->channel_load_by_us > IWL_MLD_OMI_ENTER_CHAN_LOAD) {
+ /* no changes between enter/exit thresholds */
+ return;
+ }
+
+ if (time_is_after_jiffies(mld_link->rx_omi.exit_ts +
+ msecs_to_jiffies(IWL_MLD_OMI_EXIT_PROTECTION)))
+ return;
+
+ /* reduce bandwidth to 80 MHz to save power */
+ bw = IEEE80211_STA_RX_BW_80;
+apply:
+ iwl_mld_omi_bw_update(mld, link_conf, mld_link, link_sta, bw, false);
+}
+
+IWL_MLD_ALLOC_FN(link, bss_conf)
+
+/* Constructor function for struct iwl_mld_link */
+static int
+iwl_mld_init_link(struct iwl_mld *mld, struct ieee80211_bss_conf *link,
+ struct iwl_mld_link *mld_link)
+{
+ mld_link->vif = link->vif;
+ mld_link->link_id = link->link_id;
+
+ iwl_mld_init_internal_sta(&mld_link->bcast_sta);
+ iwl_mld_init_internal_sta(&mld_link->mcast_sta);
+ iwl_mld_init_internal_sta(&mld_link->mon_sta);
+
+ if (!mld->fw_status.in_hw_restart)
+ wiphy_delayed_work_init(&mld_link->rx_omi.finished_work,
+ iwl_mld_omi_bw_finished_work);
+
+ return iwl_mld_allocate_link_fw_id(mld, &mld_link->fw_id, link);
+}
+
+/* Initializes the link structure, maps fw id to the ieee80211_bss_conf, and
+ * adds a link to the fw
+ */
+int iwl_mld_add_link(struct iwl_mld *mld,
+ struct ieee80211_bss_conf *bss_conf)
+{
+ struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(bss_conf->vif);
+ struct iwl_mld_link *link = iwl_mld_link_from_mac80211(bss_conf);
+ bool is_deflink = bss_conf == &bss_conf->vif->bss_conf;
+ int ret;
+
+ if (!link) {
+ if (is_deflink)
+ link = &mld_vif->deflink;
+ else
+ link = kzalloc(sizeof(*link), GFP_KERNEL);
+ } else {
+ WARN_ON(!mld->fw_status.in_hw_restart);
+ }
+
+ ret = iwl_mld_init_link(mld, bss_conf, link);
+ if (ret)
+ goto free;
+
+ rcu_assign_pointer(mld_vif->link[bss_conf->link_id], link);
+
+ ret = iwl_mld_add_link_to_fw(mld, bss_conf);
+ if (ret) {
+ RCU_INIT_POINTER(mld->fw_id_to_bss_conf[link->fw_id], NULL);
+ RCU_INIT_POINTER(mld_vif->link[bss_conf->link_id], NULL);
+ goto free;
+ }
+
+ return ret;
+
+free:
+ if (!is_deflink)
+ kfree(link);
+ return ret;
+}
+
+/* Remove link from fw, unmap the bss_conf, and destroy the link structure */
+void iwl_mld_remove_link(struct iwl_mld *mld,
+ struct ieee80211_bss_conf *bss_conf)
+{
+ struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(bss_conf->vif);
+ struct iwl_mld_link *link = iwl_mld_link_from_mac80211(bss_conf);
+ bool is_deflink = link == &mld_vif->deflink;
+
+ if (WARN_ON(!link || link->active))
+ return;
+
+ iwl_mld_rm_link_from_fw(mld, bss_conf);
+ /* Continue cleanup on failure */
+
+ if (!is_deflink)
+ kfree_rcu(link, rcu_head);
+
+ RCU_INIT_POINTER(mld_vif->link[bss_conf->link_id], NULL);
+
+ wiphy_delayed_work_cancel(mld->wiphy, &link->rx_omi.finished_work);
+
+ if (WARN_ON(link->fw_id >= mld->fw->ucode_capa.num_links))
+ return;
+
+ RCU_INIT_POINTER(mld->fw_id_to_bss_conf[link->fw_id], NULL);
+}
+
+void iwl_mld_handle_missed_beacon_notif(struct iwl_mld *mld,
+ struct iwl_rx_packet *pkt)
+{
+ const struct iwl_missed_beacons_notif *notif = (const void *)pkt->data;
+ union iwl_dbg_tlv_tp_data tp_data = { .fw_pkt = pkt };
+ u32 link_id = le32_to_cpu(notif->link_id);
+ u32 missed_bcon = le32_to_cpu(notif->consec_missed_beacons);
+ u32 missed_bcon_since_rx =
+ le32_to_cpu(notif->consec_missed_beacons_since_last_rx);
+ u32 scnd_lnk_bcn_lost =
+ le32_to_cpu(notif->consec_missed_beacons_other_link);
+ struct ieee80211_bss_conf *link_conf =
+ iwl_mld_fw_id_to_link_conf(mld, link_id);
+ u32 bss_param_ch_cnt_link_id;
+ struct ieee80211_vif *vif;
+
+ if (WARN_ON(!link_conf))
+ return;
+
+ vif = link_conf->vif;
+ bss_param_ch_cnt_link_id = link_conf->bss_param_ch_cnt_link_id;
+
+ IWL_DEBUG_INFO(mld,
+ "missed bcn link_id=%u, %u consecutive=%u\n",
+ link_id, missed_bcon, missed_bcon_since_rx);
+
+ if (WARN_ON(!vif))
+ return;
+
+ mld->trans->dbg.dump_file_name_ext_valid = true;
+ snprintf(mld->trans->dbg.dump_file_name_ext, IWL_FW_INI_MAX_NAME,
+ "LinkId_%d_MacType_%d", link_id,
+ iwl_mld_mac80211_iftype_to_fw(vif));
+
+ iwl_dbg_tlv_time_point(&mld->fwrt,
+ IWL_FW_INI_TIME_POINT_MISSED_BEACONS, &tp_data);
+
+ if (missed_bcon >= IWL_MLD_MISSED_BEACONS_THRESHOLD_LONG) {
+ if (missed_bcon_since_rx >=
+ IWL_MLD_MISSED_BEACONS_SINCE_RX_THOLD) {
+ ieee80211_connection_loss(vif);
+ return;
+ }
+ IWL_WARN(mld,
+ "missed beacons exceeds threshold, but receiving data. Stay connected, Expect bugs.\n");
+ return;
+ }
+
+ if (missed_bcon_since_rx > IWL_MLD_MISSED_BEACONS_THRESHOLD) {
+ ieee80211_cqm_beacon_loss_notify(vif, GFP_ATOMIC);
+
+ /* try to switch links, no-op if we don't have MLO */
+ iwl_mld_int_mlo_scan(mld, vif);
+ }
+
+ /* no more logic if we're not in EMLSR */
+ if (hweight16(vif->active_links) <= 1)
+ return;
+
+ /* We are processing a notification before link activation */
+ if (le32_to_cpu(notif->other_link_id) == FW_CTXT_ID_INVALID)
+ return;
+
+ /* Exit EMLSR if we lost more than
+ * IWL_MLD_MISSED_BEACONS_EXIT_ESR_THRESH beacons on boths links
+ * OR more than IWL_MLD_BCN_LOSS_EXIT_ESR_THRESH on current link.
+ * OR more than IWL_MLD_BCN_LOSS_EXIT_ESR_THRESH_BSS_PARAM_CHANGED
+ * on current link and the link's bss_param_ch_count has changed on
+ * the other link's beacon.
+ */
+ if ((missed_bcon >= IWL_MLD_BCN_LOSS_EXIT_ESR_THRESH_2_LINKS &&
+ scnd_lnk_bcn_lost >= IWL_MLD_BCN_LOSS_EXIT_ESR_THRESH_2_LINKS) ||
+ missed_bcon >= IWL_MLD_BCN_LOSS_EXIT_ESR_THRESH ||
+ (bss_param_ch_cnt_link_id != link_id &&
+ missed_bcon >=
+ IWL_MLD_BCN_LOSS_EXIT_ESR_THRESH_BSS_PARAM_CHANGED)) {
+ iwl_mld_exit_emlsr(mld, vif, IWL_MLD_EMLSR_EXIT_MISSED_BEACON,
+ iwl_mld_get_primary_link(vif));
+ }
+}
+EXPORT_SYMBOL_IF_IWLWIFI_KUNIT(iwl_mld_handle_missed_beacon_notif);
+
+bool iwl_mld_cancel_missed_beacon_notif(struct iwl_mld *mld,
+ struct iwl_rx_packet *pkt,
+ u32 removed_link_id)
+{
+ struct iwl_missed_beacons_notif *notif = (void *)pkt->data;
+
+ if (le32_to_cpu(notif->other_link_id) == removed_link_id) {
+ /* Second link is being removed. Don't cancel the notification,
+ * but mark second link as invalid.
+ */
+ notif->other_link_id = cpu_to_le32(FW_CTXT_ID_INVALID);
+ }
+
+ /* If the primary link is removed, cancel the notification */
+ return le32_to_cpu(notif->link_id) == removed_link_id;
+}
+
+int iwl_mld_link_set_associated(struct iwl_mld *mld, struct ieee80211_vif *vif,
+ struct ieee80211_bss_conf *link)
+{
+ return iwl_mld_change_link_in_fw(mld, link, LINK_CONTEXT_MODIFY_ALL &
+ ~(LINK_CONTEXT_MODIFY_ACTIVE |
+ LINK_CONTEXT_MODIFY_EHT_PARAMS));
+}
+
+struct iwl_mld_rssi_to_grade {
+ s8 rssi[2];
+ u16 grade;
+};
+
+#define RSSI_TO_GRADE_LINE(_lb, _hb_uhb, _grade) \
+ { \
+ .rssi = {_lb, _hb_uhb}, \
+ .grade = _grade \
+ }
+
+/*
+ * This array must be sorted by increasing RSSI for proper functionality.
+ * The grades are actually estimated throughput, represented as fixed-point
+ * with a scale factor of 1/10.
+ */
+static const struct iwl_mld_rssi_to_grade rssi_to_grade_map[] = {
+ RSSI_TO_GRADE_LINE(-85, -89, 172),
+ RSSI_TO_GRADE_LINE(-83, -86, 344),
+ RSSI_TO_GRADE_LINE(-82, -85, 516),
+ RSSI_TO_GRADE_LINE(-80, -83, 688),
+ RSSI_TO_GRADE_LINE(-77, -79, 1032),
+ RSSI_TO_GRADE_LINE(-73, -76, 1376),
+ RSSI_TO_GRADE_LINE(-70, -74, 1548),
+ RSSI_TO_GRADE_LINE(-69, -72, 1720),
+ RSSI_TO_GRADE_LINE(-65, -68, 2064),
+ RSSI_TO_GRADE_LINE(-61, -66, 2294),
+ RSSI_TO_GRADE_LINE(-58, -61, 2580),
+ RSSI_TO_GRADE_LINE(-55, -58, 2868),
+ RSSI_TO_GRADE_LINE(-46, -55, 3098),
+ RSSI_TO_GRADE_LINE(-43, -54, 3442)
+};
+
+#define MAX_GRADE (rssi_to_grade_map[ARRAY_SIZE(rssi_to_grade_map) - 1].grade)
+
+#define DEFAULT_CHAN_LOAD_2GHZ 30
+#define DEFAULT_CHAN_LOAD_5GHZ 15
+#define DEFAULT_CHAN_LOAD_6GHZ 0
+
+/* Factors calculation is done with fixed-point with a scaling factor of 1/256 */
+#define SCALE_FACTOR 256
+#define MAX_CHAN_LOAD 256
+
+static unsigned int
+iwl_mld_get_n_subchannels(const struct ieee80211_bss_conf *link_conf)
+{
+ enum nl80211_chan_width chan_width =
+ link_conf->chanreq.oper.width;
+ int mhz = nl80211_chan_width_to_mhz(chan_width);
+ unsigned int n_subchannels;
+
+ if (WARN_ONCE(mhz < 20 || mhz > 320,
+ "Invalid channel width : (%d)\n", mhz))
+ return 1;
+
+ /* total number of subchannels */
+ n_subchannels = mhz / 20;
+
+ /* No puncturing if less than 80 MHz */
+ if (mhz >= 80)
+ n_subchannels -= hweight16(link_conf->chanreq.oper.punctured);
+
+ return n_subchannels;
+}
+
+static int
+iwl_mld_get_chan_load_from_element(struct iwl_mld *mld,
+ struct ieee80211_bss_conf *link_conf)
+{
+ struct ieee80211_vif *vif = link_conf->vif;
+ const struct cfg80211_bss_ies *ies;
+ const struct element *bss_load_elem = NULL;
+ const struct ieee80211_bss_load_elem *bss_load;
+
+ guard(rcu)();
+
+ if (ieee80211_vif_link_active(vif, link_conf->link_id))
+ ies = rcu_dereference(link_conf->bss->beacon_ies);
+ else
+ ies = rcu_dereference(link_conf->bss->ies);
+
+ if (ies)
+ bss_load_elem = cfg80211_find_elem(WLAN_EID_QBSS_LOAD,
+ ies->data, ies->len);
+
+ if (!bss_load_elem ||
+ bss_load_elem->datalen != sizeof(*bss_load))
+ return -EINVAL;
+
+ bss_load = (const void *)bss_load_elem->data;
+
+ return bss_load->channel_util;
+}
+
+static unsigned int
+iwl_mld_get_chan_load_by_us(struct iwl_mld *mld,
+ struct ieee80211_bss_conf *link_conf,
+ bool expect_active_link)
+{
+ struct iwl_mld_link *mld_link = iwl_mld_link_from_mac80211(link_conf);
+ struct ieee80211_chanctx_conf *chan_ctx;
+ struct iwl_mld_phy *phy;
+
+ if (!mld_link || !mld_link->active) {
+ WARN_ON(expect_active_link);
+ return 0;
+ }
+
+ if (WARN_ONCE(!rcu_access_pointer(mld_link->chan_ctx),
+ "Active link (%u) without channel ctxt assigned!\n",
+ link_conf->link_id))
+ return 0;
+
+ chan_ctx = wiphy_dereference(mld->wiphy, mld_link->chan_ctx);
+ phy = iwl_mld_phy_from_mac80211(chan_ctx);
+
+ return phy->channel_load_by_us;
+}
+
+/* Returns error if the channel utilization element is invalid/unavailable */
+int iwl_mld_get_chan_load_by_others(struct iwl_mld *mld,
+ struct ieee80211_bss_conf *link_conf,
+ bool expect_active_link)
+{
+ int chan_load;
+ unsigned int chan_load_by_us;
+
+ /* get overall load */
+ chan_load = iwl_mld_get_chan_load_from_element(mld, link_conf);
+ if (chan_load < 0)
+ return chan_load;
+
+ chan_load_by_us = iwl_mld_get_chan_load_by_us(mld, link_conf,
+ expect_active_link);
+
+ /* channel load by us is given in percentage */
+ chan_load_by_us =
+ NORMALIZE_PERCENT_TO_255(chan_load_by_us);
+
+ /* Use only values that firmware sends that can possibly be valid */
+ if (chan_load_by_us <= chan_load)
+ chan_load -= chan_load_by_us;
+
+ return chan_load;
+}
+
+static unsigned int
+iwl_mld_get_default_chan_load(struct ieee80211_bss_conf *link_conf)
+{
+ enum nl80211_band band = link_conf->chanreq.oper.chan->band;
+
+ switch (band) {
+ case NL80211_BAND_2GHZ:
+ return DEFAULT_CHAN_LOAD_2GHZ;
+ case NL80211_BAND_5GHZ:
+ return DEFAULT_CHAN_LOAD_5GHZ;
+ case NL80211_BAND_6GHZ:
+ return DEFAULT_CHAN_LOAD_6GHZ;
+ default:
+ WARN_ON(1);
+ return 0;
+ }
+}
+
+unsigned int iwl_mld_get_chan_load(struct iwl_mld *mld,
+ struct ieee80211_bss_conf *link_conf)
+{
+ int chan_load;
+
+ chan_load = iwl_mld_get_chan_load_by_others(mld, link_conf, false);
+ if (chan_load >= 0)
+ return chan_load;
+
+ /* No information from the element, take the defaults */
+ chan_load = iwl_mld_get_default_chan_load(link_conf);
+
+ /* The defaults are given in percentage */
+ return NORMALIZE_PERCENT_TO_255(chan_load);
+}
+
+static unsigned int
+iwl_mld_get_avail_chan_load(struct iwl_mld *mld,
+ struct ieee80211_bss_conf *link_conf)
+{
+ return MAX_CHAN_LOAD - iwl_mld_get_chan_load(mld, link_conf);
+}
+
+/* This function calculates the grade of a link. Returns 0 in error case */
+unsigned int iwl_mld_get_link_grade(struct iwl_mld *mld,
+ struct ieee80211_bss_conf *link_conf)
+{
+ enum nl80211_band band;
+ int rssi_idx;
+ s32 link_rssi;
+ unsigned int grade = MAX_GRADE;
+
+ if (WARN_ON_ONCE(!link_conf))
+ return 0;
+
+ band = link_conf->chanreq.oper.chan->band;
+ if (WARN_ONCE(band != NL80211_BAND_2GHZ &&
+ band != NL80211_BAND_5GHZ &&
+ band != NL80211_BAND_6GHZ,
+ "Invalid band (%u)\n", band))
+ return 0;
+
+ link_rssi = MBM_TO_DBM(link_conf->bss->signal);
+ /*
+ * For 6 GHz the RSSI of the beacons is lower than
+ * the RSSI of the data.
+ */
+ if (band == NL80211_BAND_6GHZ && link_rssi)
+ link_rssi += 4;
+
+ rssi_idx = band == NL80211_BAND_2GHZ ? 0 : 1;
+
+ /* No valid RSSI - take the lowest grade */
+ if (!link_rssi)
+ link_rssi = rssi_to_grade_map[0].rssi[rssi_idx];
+
+ IWL_DEBUG_EHT(mld,
+ "Calculating grade of link %d: band = %d, bandwidth = %d, punctured subchannels =0x%x RSSI = %d\n",
+ link_conf->link_id, band,
+ link_conf->chanreq.oper.width,
+ link_conf->chanreq.oper.punctured, link_rssi);
+
+ /* Get grade based on RSSI */
+ for (int i = 0; i < ARRAY_SIZE(rssi_to_grade_map); i++) {
+ const struct iwl_mld_rssi_to_grade *line =
+ &rssi_to_grade_map[i];
+
+ if (link_rssi > line->rssi[rssi_idx])
+ continue;
+ grade = line->grade;
+ break;
+ }
+
+ /* Apply the channel load and puncturing factors */
+ grade = grade * iwl_mld_get_avail_chan_load(mld, link_conf) / SCALE_FACTOR;
+ grade = grade * iwl_mld_get_n_subchannels(link_conf);
+
+ IWL_DEBUG_EHT(mld, "Link %d's grade: %d\n", link_conf->link_id, grade);
+
+ return grade;
+}
+EXPORT_SYMBOL_IF_IWLWIFI_KUNIT(iwl_mld_get_link_grade);
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/link.h b/drivers/net/wireless/intel/iwlwifi/mld/link.h
new file mode 100644
index 000000000000..39f04aae5579
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/mld/link.h
@@ -0,0 +1,153 @@
+/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
+/*
+ * Copyright (C) 2024-2025 Intel Corporation
+ */
+#ifndef __iwl_mld_link_h__
+#define __iwl_mld_link_h__
+
+#include <net/mac80211.h>
+
+#include "mld.h"
+#include "sta.h"
+
+/**
+ * struct iwl_probe_resp_data - data for NoA/CSA updates
+ * @rcu_head: used for freeing the data on update
+ * @notif: notification data
+ * @noa_len: length of NoA attribute, calculated from the notification
+ */
+struct iwl_probe_resp_data {
+ struct rcu_head rcu_head;
+ struct iwl_probe_resp_data_notif notif;
+ int noa_len;
+};
+
+/**
+ * struct iwl_mld_link - link configuration parameters
+ *
+ * @rcu_head: RCU head for freeing this data.
+ * @fw_id: the fw id of the link.
+ * @active: if the link is active or not.
+ * @queue_params: QoS data from mac80211. This is updated with a call to
+ * drv_conf_tx per each AC, and then notified once with BSS_CHANGED_QOS.
+ * So we store it here and then send one link cmd for all the ACs.
+ * @chan_ctx: pointer to the channel context assigned to the link. If a link
+ * has an assigned channel context it means that it is active.
+ * @he_ru_2mhz_block: 26-tone RU OFDMA transmissions should be blocked.
+ * @igtk: fw can only have one IGTK at a time, whereas mac80211 can have two.
+ * This tracks the one IGTK that currently exists in FW.
+ * @vif: the vif this link belongs to
+ * @bcast_sta: station used for broadcast packets. Used in AP, GO and IBSS.
+ * @mcast_sta: station used for multicast packets. Used in AP, GO and IBSS.
+ * @mon_sta: station used for TX injection in monitor interface.
+ * @link_id: over the air link ID
+ * @ap_early_keys: The firmware cannot install keys before bcast/mcast STAs,
+ * but higher layers work differently, so we store the keys here for
+ * later installation.
+ * @silent_deactivation: next deactivation needs to be silent.
+ * @probe_resp_data: data from FW notification to store NOA related data to be
+ * inserted into probe response.
+ * @rx_omi: data for BW reduction with OMI
+ * @rx_omi.bw_in_progress: update is in progress (indicates target BW)
+ * @rx_omi.exit_ts: timestamp of last exit
+ * @rx_omi.finished_work: work for the delayed reaction to the firmware saying
+ * the change was applied, and for then applying a new mode if it was
+ * updated while waiting for firmware/AP settle delay.
+ * @rx_omi.desired_bw: desired bandwidth
+ * @rx_omi.last_max_bw: last maximum BW used by firmware, for AP BW changes
+ */
+struct iwl_mld_link {
+ struct rcu_head rcu_head;
+
+ /* Add here fields that need clean up on restart */
+ struct_group(zeroed_on_hw_restart,
+ u8 fw_id;
+ bool active;
+ struct ieee80211_tx_queue_params queue_params[IEEE80211_NUM_ACS];
+ struct ieee80211_chanctx_conf __rcu *chan_ctx;
+ bool he_ru_2mhz_block;
+ struct ieee80211_key_conf *igtk;
+ );
+ /* And here fields that survive a fw restart */
+ struct ieee80211_vif *vif;
+ struct iwl_mld_int_sta bcast_sta;
+ struct iwl_mld_int_sta mcast_sta;
+ struct iwl_mld_int_sta mon_sta;
+ u8 link_id;
+
+ struct {
+ struct wiphy_delayed_work finished_work;
+ unsigned long exit_ts;
+ enum ieee80211_sta_rx_bandwidth bw_in_progress,
+ desired_bw,
+ last_max_bw;
+ } rx_omi;
+
+ /* we can only have 2 GTK + 2 IGTK + 2 BIGTK active at a time */
+ struct ieee80211_key_conf *ap_early_keys[6];
+ bool silent_deactivation;
+ struct iwl_probe_resp_data __rcu *probe_resp_data;
+};
+
+/* Cleanup function for struct iwl_mld_link, will be called in restart */
+static inline void
+iwl_mld_cleanup_link(struct iwl_mld *mld, struct iwl_mld_link *link)
+{
+ struct iwl_probe_resp_data *probe_data;
+
+ probe_data = wiphy_dereference(mld->wiphy, link->probe_resp_data);
+ RCU_INIT_POINTER(link->probe_resp_data, NULL);
+ if (probe_data)
+ kfree_rcu(probe_data, rcu_head);
+
+ CLEANUP_STRUCT(link);
+ if (link->bcast_sta.sta_id != IWL_INVALID_STA)
+ iwl_mld_free_internal_sta(mld, &link->bcast_sta);
+ if (link->mcast_sta.sta_id != IWL_INVALID_STA)
+ iwl_mld_free_internal_sta(mld, &link->mcast_sta);
+ if (link->mon_sta.sta_id != IWL_INVALID_STA)
+ iwl_mld_free_internal_sta(mld, &link->mon_sta);
+}
+
+/* Convert a percentage from [0,100] to [0,255] */
+#define NORMALIZE_PERCENT_TO_255(percentage) ((percentage) * 256 / 100)
+
+int iwl_mld_add_link(struct iwl_mld *mld,
+ struct ieee80211_bss_conf *bss_conf);
+void iwl_mld_remove_link(struct iwl_mld *mld,
+ struct ieee80211_bss_conf *bss_conf);
+int iwl_mld_activate_link(struct iwl_mld *mld,
+ struct ieee80211_bss_conf *link);
+void iwl_mld_deactivate_link(struct iwl_mld *mld,
+ struct ieee80211_bss_conf *link);
+int iwl_mld_change_link_omi_bw(struct iwl_mld *mld,
+ struct ieee80211_bss_conf *link,
+ enum ieee80211_sta_rx_bandwidth bw);
+int iwl_mld_change_link_in_fw(struct iwl_mld *mld,
+ struct ieee80211_bss_conf *link, u32 changes);
+void iwl_mld_handle_missed_beacon_notif(struct iwl_mld *mld,
+ struct iwl_rx_packet *pkt);
+bool iwl_mld_cancel_missed_beacon_notif(struct iwl_mld *mld,
+ struct iwl_rx_packet *pkt,
+ u32 removed_link_id);
+int iwl_mld_link_set_associated(struct iwl_mld *mld, struct ieee80211_vif *vif,
+ struct ieee80211_bss_conf *link);
+
+unsigned int iwl_mld_get_link_grade(struct iwl_mld *mld,
+ struct ieee80211_bss_conf *link_conf);
+
+unsigned int iwl_mld_get_chan_load(struct iwl_mld *mld,
+ struct ieee80211_bss_conf *link_conf);
+
+int iwl_mld_get_chan_load_by_others(struct iwl_mld *mld,
+ struct ieee80211_bss_conf *link_conf,
+ bool expect_active_link);
+void iwl_mld_handle_omi_status_notif(struct iwl_mld *mld,
+ struct iwl_rx_packet *pkt);
+void iwl_mld_leave_omi_bw_reduction(struct iwl_mld *mld);
+void iwl_mld_check_omi_bw_reduction(struct iwl_mld *mld);
+void iwl_mld_omi_ap_changed_bw(struct iwl_mld *mld,
+ struct ieee80211_bss_conf *link_conf,
+ enum ieee80211_sta_rx_bandwidth bw);
+
+#endif /* __iwl_mld_link_h__ */
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/low_latency.c b/drivers/net/wireless/intel/iwlwifi/mld/low_latency.c
new file mode 100644
index 000000000000..f7faa87b8ba6
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/mld/low_latency.c
@@ -0,0 +1,339 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
+/*
+ * Copyright (C) 2024-2025 Intel Corporation
+ */
+#include "mld.h"
+#include "iface.h"
+#include "low_latency.h"
+#include "hcmd.h"
+#include "power.h"
+#include "mlo.h"
+
+#define MLD_LL_WK_INTERVAL_MSEC 500
+#define MLD_LL_PERIOD (HZ * MLD_LL_WK_INTERVAL_MSEC / 1000)
+#define MLD_LL_ACTIVE_WK_PERIOD (HZ * 10)
+
+/* packets/MLD_LL_WK_PERIOD seconds */
+#define MLD_LL_ENABLE_THRESH 100
+
+static bool iwl_mld_calc_low_latency(struct iwl_mld *mld,
+ unsigned long timestamp)
+{
+ struct iwl_mld_low_latency *ll = &mld->low_latency;
+ bool global_low_latency = false;
+ u8 num_rx_q = mld->trans->info.num_rxqs;
+
+ for (int mac_id = 0; mac_id < NUM_MAC_INDEX_DRIVER; mac_id++) {
+ u32 total_vo_vi_pkts = 0;
+ bool ll_period_expired;
+
+ /* If it's not initialized yet, it means we have not yet
+ * received/transmitted any vo/vi packet on this MAC.
+ */
+ if (!ll->window_start[mac_id])
+ continue;
+
+ ll_period_expired =
+ time_after(timestamp, ll->window_start[mac_id] +
+ MLD_LL_ACTIVE_WK_PERIOD);
+
+ if (ll_period_expired)
+ ll->window_start[mac_id] = timestamp;
+
+ for (int q = 0; q < num_rx_q; q++) {
+ struct iwl_mld_low_latency_packets_counters *counters =
+ &mld->low_latency.pkts_counters[q];
+
+ spin_lock_bh(&counters->lock);
+
+ total_vo_vi_pkts += counters->vo_vi[mac_id];
+
+ if (ll_period_expired)
+ counters->vo_vi[mac_id] = 0;
+
+ spin_unlock_bh(&counters->lock);
+ }
+
+ /* enable immediately with enough packets but defer
+ * disabling only if the low-latency period expired and
+ * below threshold.
+ */
+ if (total_vo_vi_pkts > MLD_LL_ENABLE_THRESH)
+ mld->low_latency.result[mac_id] = true;
+ else if (ll_period_expired)
+ mld->low_latency.result[mac_id] = false;
+
+ global_low_latency |= mld->low_latency.result[mac_id];
+ }
+
+ return global_low_latency;
+}
+
+static void iwl_mld_low_latency_iter(void *_data, u8 *mac,
+ struct ieee80211_vif *vif)
+{
+ struct iwl_mld *mld = _data;
+ struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
+ bool prev = mld_vif->low_latency_causes & LOW_LATENCY_TRAFFIC;
+ bool low_latency;
+
+ if (WARN_ON(mld_vif->fw_id >= ARRAY_SIZE(mld->low_latency.result)))
+ return;
+
+ low_latency = mld->low_latency.result[mld_vif->fw_id];
+
+ if (prev != low_latency)
+ iwl_mld_vif_update_low_latency(mld, vif, low_latency,
+ LOW_LATENCY_TRAFFIC);
+}
+
+static void iwl_mld_low_latency_wk(struct wiphy *wiphy, struct wiphy_work *wk)
+{
+ struct iwl_mld *mld = container_of(wk, struct iwl_mld,
+ low_latency.work.work);
+ unsigned long timestamp = jiffies;
+ bool low_latency_active;
+
+ if (mld->fw_status.in_hw_restart)
+ return;
+
+ /* It is assumed that the work was scheduled only after checking
+ * at least MLD_LL_PERIOD has passed since the last update.
+ */
+
+ low_latency_active = iwl_mld_calc_low_latency(mld, timestamp);
+
+ /* Update the timestamp now after the low-latency calculation */
+ mld->low_latency.timestamp = timestamp;
+
+ /* If low-latency is active we need to force re-evaluation after
+ * 10 seconds, so that we can disable low-latency when
+ * the low-latency traffic ends.
+ *
+ * Otherwise, we don't need to run the work because there is nothing to
+ * disable.
+ *
+ * Note that this has no impact on the regular scheduling of the
+ * updates triggered by traffic - those happen whenever the
+ * MLD_LL_PERIOD timeout expire.
+ */
+ if (low_latency_active)
+ wiphy_delayed_work_queue(mld->wiphy, &mld->low_latency.work,
+ MLD_LL_ACTIVE_WK_PERIOD);
+
+ ieee80211_iterate_active_interfaces_mtx(mld->hw,
+ IEEE80211_IFACE_ITER_NORMAL,
+ iwl_mld_low_latency_iter, mld);
+}
+
+int iwl_mld_low_latency_init(struct iwl_mld *mld)
+{
+ struct iwl_mld_low_latency *ll = &mld->low_latency;
+ unsigned long ts = jiffies;
+
+ ll->pkts_counters = kcalloc(mld->trans->info.num_rxqs,
+ sizeof(*ll->pkts_counters), GFP_KERNEL);
+ if (!ll->pkts_counters)
+ return -ENOMEM;
+
+ for (int q = 0; q < mld->trans->info.num_rxqs; q++)
+ spin_lock_init(&ll->pkts_counters[q].lock);
+
+ wiphy_delayed_work_init(&ll->work, iwl_mld_low_latency_wk);
+
+ ll->timestamp = ts;
+
+ /* The low-latency window_start will be initialized per-MAC on
+ * the first vo/vi packet received/transmitted.
+ */
+
+ return 0;
+}
+
+void iwl_mld_low_latency_free(struct iwl_mld *mld)
+{
+ struct iwl_mld_low_latency *ll = &mld->low_latency;
+
+ kfree(ll->pkts_counters);
+ ll->pkts_counters = NULL;
+}
+
+void iwl_mld_low_latency_restart_cleanup(struct iwl_mld *mld)
+{
+ struct iwl_mld_low_latency *ll = &mld->low_latency;
+
+ ll->timestamp = jiffies;
+
+ memset(ll->window_start, 0, sizeof(ll->window_start));
+ memset(ll->result, 0, sizeof(ll->result));
+
+ for (int q = 0; q < mld->trans->info.num_rxqs; q++)
+ memset(ll->pkts_counters[q].vo_vi, 0,
+ sizeof(ll->pkts_counters[q].vo_vi));
+}
+
+static int iwl_mld_send_low_latency_cmd(struct iwl_mld *mld, bool low_latency,
+ u16 mac_id)
+{
+ struct iwl_mac_low_latency_cmd cmd = {
+ .mac_id = cpu_to_le32(mac_id)
+ };
+ u16 cmd_id = WIDE_ID(MAC_CONF_GROUP, LOW_LATENCY_CMD);
+ int ret;
+
+ if (low_latency) {
+ /* Currently we don't care about the direction */
+ cmd.low_latency_rx = 1;
+ cmd.low_latency_tx = 1;
+ }
+
+ ret = iwl_mld_send_cmd_pdu(mld, cmd_id, &cmd);
+ if (ret)
+ IWL_ERR(mld, "Failed to send low latency command\n");
+
+ return ret;
+}
+
+static void iwl_mld_vif_set_low_latency(struct iwl_mld_vif *mld_vif, bool set,
+ enum iwl_mld_low_latency_cause cause)
+{
+ if (set)
+ mld_vif->low_latency_causes |= cause;
+ else
+ mld_vif->low_latency_causes &= ~cause;
+}
+
+void iwl_mld_vif_update_low_latency(struct iwl_mld *mld,
+ struct ieee80211_vif *vif,
+ bool low_latency,
+ enum iwl_mld_low_latency_cause cause)
+{
+ struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
+ bool prev;
+
+ prev = iwl_mld_vif_low_latency(mld_vif);
+ iwl_mld_vif_set_low_latency(mld_vif, low_latency, cause);
+
+ low_latency = iwl_mld_vif_low_latency(mld_vif);
+ if (low_latency == prev)
+ return;
+
+ if (iwl_mld_send_low_latency_cmd(mld, low_latency, mld_vif->fw_id)) {
+ /* revert to previous low-latency state */
+ iwl_mld_vif_set_low_latency(mld_vif, prev, cause);
+ return;
+ }
+
+ if (low_latency)
+ iwl_mld_leave_omi_bw_reduction(mld);
+
+ if (ieee80211_vif_type_p2p(vif) != NL80211_IFTYPE_P2P_CLIENT)
+ return;
+
+ iwl_mld_update_mac_power(mld, vif, false);
+
+ if (low_latency)
+ iwl_mld_retry_emlsr(mld, vif);
+}
+
+static bool iwl_mld_is_vo_vi_pkt(struct ieee80211_hdr *hdr)
+{
+ u8 tid;
+ static const u8 tid_to_mac80211_ac[] = {
+ IEEE80211_AC_BE,
+ IEEE80211_AC_BK,
+ IEEE80211_AC_BK,
+ IEEE80211_AC_BE,
+ IEEE80211_AC_VI,
+ IEEE80211_AC_VI,
+ IEEE80211_AC_VO,
+ IEEE80211_AC_VO,
+ };
+
+ if (!hdr || !ieee80211_is_data_qos(hdr->frame_control))
+ return false;
+
+ tid = ieee80211_get_tid(hdr);
+ if (tid >= IWL_MAX_TID_COUNT)
+ return false;
+
+ return tid_to_mac80211_ac[tid] < IEEE80211_AC_VI;
+}
+
+void iwl_mld_low_latency_update_counters(struct iwl_mld *mld,
+ struct ieee80211_hdr *hdr,
+ struct ieee80211_sta *sta,
+ u8 queue)
+{
+ struct iwl_mld_sta *mld_sta = iwl_mld_sta_from_mac80211(sta);
+ struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(mld_sta->vif);
+ struct iwl_mld_low_latency_packets_counters *counters;
+ unsigned long ts = jiffies ? jiffies : 1;
+ u8 fw_id = mld_vif->fw_id;
+
+ /* we should have failed op mode init if NULL */
+ if (WARN_ON_ONCE(!mld->low_latency.pkts_counters))
+ return;
+
+ if (WARN_ON_ONCE(fw_id >= ARRAY_SIZE(counters->vo_vi) ||
+ queue >= mld->trans->info.num_rxqs))
+ return;
+
+ if (mld->low_latency.stopped)
+ return;
+
+ if (!iwl_mld_is_vo_vi_pkt(hdr))
+ return;
+
+ counters = &mld->low_latency.pkts_counters[queue];
+
+ spin_lock_bh(&counters->lock);
+ counters->vo_vi[fw_id]++;
+ spin_unlock_bh(&counters->lock);
+
+ /* Initialize the window_start on the first vo/vi packet */
+ if (!mld->low_latency.window_start[fw_id])
+ mld->low_latency.window_start[fw_id] = ts;
+
+ if (time_is_before_jiffies(mld->low_latency.timestamp + MLD_LL_PERIOD))
+ wiphy_delayed_work_queue(mld->wiphy, &mld->low_latency.work,
+ 0);
+}
+
+void iwl_mld_low_latency_stop(struct iwl_mld *mld)
+{
+ lockdep_assert_wiphy(mld->wiphy);
+
+ mld->low_latency.stopped = true;
+
+ wiphy_delayed_work_cancel(mld->wiphy, &mld->low_latency.work);
+}
+
+void iwl_mld_low_latency_restart(struct iwl_mld *mld)
+{
+ struct iwl_mld_low_latency *ll = &mld->low_latency;
+ bool low_latency = false;
+ unsigned long ts = jiffies;
+
+ lockdep_assert_wiphy(mld->wiphy);
+
+ ll->timestamp = ts;
+ mld->low_latency.stopped = false;
+
+ for (int mac = 0; mac < NUM_MAC_INDEX_DRIVER; mac++) {
+ ll->window_start[mac] = 0;
+ low_latency |= ll->result[mac];
+
+ for (int q = 0; q < mld->trans->info.num_rxqs; q++) {
+ spin_lock_bh(&ll->pkts_counters[q].lock);
+ ll->pkts_counters[q].vo_vi[mac] = 0;
+ spin_unlock_bh(&ll->pkts_counters[q].lock);
+ }
+ }
+
+ /* if low latency is active, force re-evaluation to cover the case of
+ * no traffic.
+ */
+ if (low_latency)
+ wiphy_delayed_work_queue(mld->wiphy, &ll->work, MLD_LL_PERIOD);
+}
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/low_latency.h b/drivers/net/wireless/intel/iwlwifi/mld/low_latency.h
new file mode 100644
index 000000000000..f59684d235af
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/mld/low_latency.h
@@ -0,0 +1,68 @@
+/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
+/*
+ * Copyright (C) 2024 Intel Corporation
+ */
+#ifndef __iwl_mld_low_latency_h__
+#define __iwl_mld_low_latency_h__
+
+/**
+ * struct iwl_mld_low_latency_packets_counters - Packets counters
+ * @lock: synchronize the counting in data path against the worker
+ * @vo_vi: per-mac, counts the number of TX and RX voice and video packets
+ */
+struct iwl_mld_low_latency_packets_counters {
+ spinlock_t lock;
+ u32 vo_vi[NUM_MAC_INDEX_DRIVER];
+} ____cacheline_aligned_in_smp;
+
+/**
+ * enum iwl_mld_low_latency_cause - low-latency set causes
+ *
+ * @LOW_LATENCY_TRAFFIC: indicates low-latency traffic was detected
+ * @LOW_LATENCY_DEBUGFS: low-latency mode set from debugfs
+ * @LOW_LATENCY_VIF_TYPE: low-latency mode set because of vif type (AP)
+ */
+enum iwl_mld_low_latency_cause {
+ LOW_LATENCY_TRAFFIC = BIT(0),
+ LOW_LATENCY_DEBUGFS = BIT(1),
+ LOW_LATENCY_VIF_TYPE = BIT(2),
+};
+
+/**
+ * struct iwl_mld_low_latency - Manage low-latency detection and activation.
+ * @work: this work is used to detect low-latency by monitoring the number of
+ * voice and video packets transmitted in a period of time. If the
+ * threshold is reached, low-latency is activated. When active,
+ * it is deactivated if the threshold is not reached within a
+ * 10-second period.
+ * @timestamp: timestamp of the last update.
+ * @window_start: per-mac, timestamp of the start of the current window. when
+ * the window is over, the counters are reset.
+ * @pkts_counters: per-queue array voice/video packet counters
+ * @result: per-mac latest low-latency result
+ * @stopped: if true, ignore the requests to update the counters
+ */
+struct iwl_mld_low_latency {
+ struct wiphy_delayed_work work;
+ unsigned long timestamp;
+ unsigned long window_start[NUM_MAC_INDEX_DRIVER];
+ struct iwl_mld_low_latency_packets_counters *pkts_counters;
+ bool result[NUM_MAC_INDEX_DRIVER];
+ bool stopped;
+};
+
+int iwl_mld_low_latency_init(struct iwl_mld *mld);
+void iwl_mld_low_latency_free(struct iwl_mld *mld);
+void iwl_mld_low_latency_restart_cleanup(struct iwl_mld *mld);
+void iwl_mld_vif_update_low_latency(struct iwl_mld *mld,
+ struct ieee80211_vif *vif,
+ bool low_latency,
+ enum iwl_mld_low_latency_cause cause);
+void iwl_mld_low_latency_update_counters(struct iwl_mld *mld,
+ struct ieee80211_hdr *hdr,
+ struct ieee80211_sta *sta,
+ u8 queue);
+void iwl_mld_low_latency_stop(struct iwl_mld *mld);
+void iwl_mld_low_latency_restart(struct iwl_mld *mld);
+
+#endif /* __iwl_mld_low_latency_h__ */
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c
new file mode 100644
index 000000000000..4ba050397632
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c
@@ -0,0 +1,2712 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
+/*
+ * Copyright (C) 2024-2025 Intel Corporation
+ */
+
+#include <net/mac80211.h>
+#include <linux/ip.h>
+
+#include "mld.h"
+#include "mac80211.h"
+#include "phy.h"
+#include "iface.h"
+#include "power.h"
+#include "sta.h"
+#include "agg.h"
+#include "scan.h"
+#include "d3.h"
+#include "tlc.h"
+#include "key.h"
+#include "ap.h"
+#include "tx.h"
+#include "roc.h"
+#include "mlo.h"
+#include "stats.h"
+#include "ftm-initiator.h"
+#include "low_latency.h"
+#include "fw/api/scan.h"
+#include "fw/api/context.h"
+#include "fw/api/filter.h"
+#include "fw/api/sta.h"
+#include "fw/api/tdls.h"
+#ifdef CONFIG_PM_SLEEP
+#include "fw/api/d3.h"
+#endif /* CONFIG_PM_SLEEP */
+#include "iwl-trans.h"
+
+#define IWL_MLD_LIMITS(ap) \
+ { \
+ .max = 2, \
+ .types = BIT(NL80211_IFTYPE_STATION), \
+ }, \
+ { \
+ .max = 1, \
+ .types = ap | \
+ BIT(NL80211_IFTYPE_P2P_CLIENT) | \
+ BIT(NL80211_IFTYPE_P2P_GO), \
+ }, \
+ { \
+ .max = 1, \
+ .types = BIT(NL80211_IFTYPE_P2P_DEVICE), \
+ }
+
+static const struct ieee80211_iface_limit iwl_mld_limits[] = {
+ IWL_MLD_LIMITS(0)
+};
+
+static const struct ieee80211_iface_limit iwl_mld_limits_ap[] = {
+ IWL_MLD_LIMITS(BIT(NL80211_IFTYPE_AP))
+};
+
+static const struct ieee80211_iface_combination
+iwl_mld_iface_combinations[] = {
+ {
+ .num_different_channels = 2,
+ .max_interfaces = 4,
+ .limits = iwl_mld_limits,
+ .n_limits = ARRAY_SIZE(iwl_mld_limits),
+ },
+ {
+ .num_different_channels = 1,
+ .max_interfaces = 4,
+ .limits = iwl_mld_limits_ap,
+ .n_limits = ARRAY_SIZE(iwl_mld_limits_ap),
+ },
+};
+
+static const u8 if_types_ext_capa_sta[] = {
+ [0] = WLAN_EXT_CAPA1_EXT_CHANNEL_SWITCHING,
+ [2] = WLAN_EXT_CAPA3_MULTI_BSSID_SUPPORT,
+ [7] = WLAN_EXT_CAPA8_OPMODE_NOTIF |
+ WLAN_EXT_CAPA8_MAX_MSDU_IN_AMSDU_LSB,
+ [8] = WLAN_EXT_CAPA9_MAX_MSDU_IN_AMSDU_MSB,
+ [9] = WLAN_EXT_CAPA10_TWT_REQUESTER_SUPPORT,
+};
+
+#define IWL_MLD_EMLSR_CAPA (IEEE80211_EML_CAP_EMLSR_SUPP | \
+ IEEE80211_EML_CAP_EMLSR_PADDING_DELAY_32US << \
+ __bf_shf(IEEE80211_EML_CAP_EMLSR_PADDING_DELAY) | \
+ IEEE80211_EML_CAP_EMLSR_TRANSITION_DELAY_64US << \
+ __bf_shf(IEEE80211_EML_CAP_EMLSR_TRANSITION_DELAY))
+#define IWL_MLD_CAPA_OPS (FIELD_PREP_CONST( \
+ IEEE80211_MLD_CAP_OP_TID_TO_LINK_MAP_NEG_SUPP, \
+ IEEE80211_MLD_CAP_OP_TID_TO_LINK_MAP_NEG_SUPP_SAME) | \
+ IEEE80211_MLD_CAP_OP_LINK_RECONF_SUPPORT)
+
+static const struct wiphy_iftype_ext_capab iftypes_ext_capa[] = {
+ {
+ .iftype = NL80211_IFTYPE_STATION,
+ .extended_capabilities = if_types_ext_capa_sta,
+ .extended_capabilities_mask = if_types_ext_capa_sta,
+ .extended_capabilities_len = sizeof(if_types_ext_capa_sta),
+ /* relevant only if EHT is supported */
+ .eml_capabilities = IWL_MLD_EMLSR_CAPA,
+ .mld_capa_and_ops = IWL_MLD_CAPA_OPS,
+ },
+};
+
+static void iwl_mld_hw_set_addresses(struct iwl_mld *mld)
+{
+ struct wiphy *wiphy = mld->wiphy;
+ int num_addrs = 1;
+
+ /* Extract MAC address */
+ memcpy(mld->addresses[0].addr, mld->nvm_data->hw_addr, ETH_ALEN);
+ wiphy->addresses = mld->addresses;
+ wiphy->n_addresses = 1;
+
+ /* Extract additional MAC addresses if available */
+ if (mld->nvm_data->n_hw_addrs > 1)
+ num_addrs = min(mld->nvm_data->n_hw_addrs,
+ IWL_MLD_MAX_ADDRESSES);
+
+ for (int i = 1; i < num_addrs; i++) {
+ memcpy(mld->addresses[i].addr,
+ mld->addresses[i - 1].addr,
+ ETH_ALEN);
+ mld->addresses[i].addr[ETH_ALEN - 1]++;
+ wiphy->n_addresses++;
+ }
+}
+
+static void iwl_mld_hw_set_channels(struct iwl_mld *mld)
+{
+ struct wiphy *wiphy = mld->wiphy;
+ struct ieee80211_supported_band *bands = mld->nvm_data->bands;
+
+ wiphy->bands[NL80211_BAND_2GHZ] = &bands[NL80211_BAND_2GHZ];
+ wiphy->bands[NL80211_BAND_5GHZ] = &bands[NL80211_BAND_5GHZ];
+
+ if (bands[NL80211_BAND_6GHZ].n_channels)
+ wiphy->bands[NL80211_BAND_6GHZ] = &bands[NL80211_BAND_6GHZ];
+}
+
+static void iwl_mld_hw_set_security(struct iwl_mld *mld)
+{
+ struct ieee80211_hw *hw = mld->hw;
+ static const u32 mld_ciphers[] = {
+ WLAN_CIPHER_SUITE_WEP40,
+ WLAN_CIPHER_SUITE_WEP104,
+ WLAN_CIPHER_SUITE_TKIP,
+ WLAN_CIPHER_SUITE_CCMP,
+ WLAN_CIPHER_SUITE_GCMP,
+ WLAN_CIPHER_SUITE_GCMP_256,
+ WLAN_CIPHER_SUITE_AES_CMAC,
+ WLAN_CIPHER_SUITE_BIP_GMAC_128,
+ WLAN_CIPHER_SUITE_BIP_GMAC_256
+ };
+
+ hw->wiphy->n_cipher_suites = ARRAY_SIZE(mld_ciphers);
+ hw->wiphy->cipher_suites = mld_ciphers;
+
+ ieee80211_hw_set(hw, MFP_CAPABLE);
+ wiphy_ext_feature_set(hw->wiphy,
+ NL80211_EXT_FEATURE_BEACON_PROTECTION);
+}
+
+static void iwl_mld_hw_set_antennas(struct iwl_mld *mld)
+{
+ struct wiphy *wiphy = mld->wiphy;
+
+ wiphy->available_antennas_tx = iwl_mld_get_valid_tx_ant(mld);
+ wiphy->available_antennas_rx = iwl_mld_get_valid_rx_ant(mld);
+}
+
+static void iwl_mld_hw_set_pm(struct iwl_mld *mld)
+{
+#ifdef CONFIG_PM_SLEEP
+ struct wiphy *wiphy = mld->wiphy;
+
+ if (!device_can_wakeup(mld->trans->dev))
+ return;
+
+ mld->wowlan.flags |= WIPHY_WOWLAN_MAGIC_PKT |
+ WIPHY_WOWLAN_DISCONNECT |
+ WIPHY_WOWLAN_EAP_IDENTITY_REQ |
+ WIPHY_WOWLAN_RFKILL_RELEASE |
+ WIPHY_WOWLAN_NET_DETECT |
+ WIPHY_WOWLAN_SUPPORTS_GTK_REKEY |
+ WIPHY_WOWLAN_GTK_REKEY_FAILURE |
+ WIPHY_WOWLAN_4WAY_HANDSHAKE;
+
+ mld->wowlan.n_patterns = IWL_WOWLAN_MAX_PATTERNS;
+ mld->wowlan.pattern_min_len = IWL_WOWLAN_MIN_PATTERN_LEN;
+ mld->wowlan.pattern_max_len = IWL_WOWLAN_MAX_PATTERN_LEN;
+ mld->wowlan.max_nd_match_sets = IWL_SCAN_MAX_PROFILES_V2;
+
+ wiphy->wowlan = &mld->wowlan;
+#endif /* CONFIG_PM_SLEEP */
+}
+
+static void iwl_mac_hw_set_radiotap(struct iwl_mld *mld)
+{
+ struct ieee80211_hw *hw = mld->hw;
+
+ hw->radiotap_mcs_details |= IEEE80211_RADIOTAP_MCS_HAVE_FEC |
+ IEEE80211_RADIOTAP_MCS_HAVE_STBC;
+
+ hw->radiotap_vht_details |= IEEE80211_RADIOTAP_VHT_KNOWN_STBC |
+ IEEE80211_RADIOTAP_VHT_KNOWN_BEAMFORMED;
+
+ hw->radiotap_timestamp.units_pos =
+ IEEE80211_RADIOTAP_TIMESTAMP_UNIT_US |
+ IEEE80211_RADIOTAP_TIMESTAMP_SPOS_PLCP_SIG_ACQ;
+
+ /* this is the case for CCK frames, it's better (only 8) for OFDM */
+ hw->radiotap_timestamp.accuracy = 22;
+}
+
+static void iwl_mac_hw_set_flags(struct iwl_mld *mld)
+{
+ struct ieee80211_hw *hw = mld->hw;
+
+ ieee80211_hw_set(hw, USES_RSS);
+ ieee80211_hw_set(hw, HANDLES_QUIET_CSA);
+ ieee80211_hw_set(hw, AP_LINK_PS);
+ ieee80211_hw_set(hw, SIGNAL_DBM);
+ ieee80211_hw_set(hw, SPECTRUM_MGMT);
+ ieee80211_hw_set(hw, REPORTS_TX_ACK_STATUS);
+ ieee80211_hw_set(hw, WANT_MONITOR_VIF);
+ ieee80211_hw_set(hw, SUPPORTS_PS);
+ ieee80211_hw_set(hw, SUPPORTS_DYNAMIC_PS);
+ ieee80211_hw_set(hw, AMPDU_AGGREGATION);
+ ieee80211_hw_set(hw, CONNECTION_MONITOR);
+ ieee80211_hw_set(hw, CHANCTX_STA_CSA);
+ ieee80211_hw_set(hw, SUPPORT_FAST_XMIT);
+ ieee80211_hw_set(hw, SUPPORTS_CLONED_SKBS);
+ ieee80211_hw_set(hw, NEEDS_UNIQUE_STA_ADDR);
+ ieee80211_hw_set(hw, SUPPORTS_VHT_EXT_NSS_BW);
+ ieee80211_hw_set(hw, BUFF_MMPDU_TXQ);
+ ieee80211_hw_set(hw, STA_MMPDU_TXQ);
+ ieee80211_hw_set(hw, TX_AMSDU);
+ ieee80211_hw_set(hw, TX_FRAG_LIST);
+ ieee80211_hw_set(hw, TX_AMPDU_SETUP_IN_HW);
+ ieee80211_hw_set(hw, HAS_RATE_CONTROL);
+ ieee80211_hw_set(hw, SUPPORTS_REORDERING_BUFFER);
+ ieee80211_hw_set(hw, SINGLE_SCAN_ON_ALL_BANDS);
+ ieee80211_hw_set(hw, SUPPORTS_AMSDU_IN_AMPDU);
+ ieee80211_hw_set(hw, TDLS_WIDER_BW);
+}
+
+static void iwl_mac_hw_set_wiphy(struct iwl_mld *mld)
+{
+ struct ieee80211_hw *hw = mld->hw;
+ struct wiphy *wiphy = hw->wiphy;
+ const struct iwl_ucode_capabilities *ucode_capa = &mld->fw->ucode_capa;
+
+ snprintf(wiphy->fw_version,
+ sizeof(wiphy->fw_version),
+ "%.31s", mld->fw->fw_version);
+
+ wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
+ BIT(NL80211_IFTYPE_P2P_CLIENT) |
+ BIT(NL80211_IFTYPE_AP) |
+ BIT(NL80211_IFTYPE_P2P_GO) |
+ BIT(NL80211_IFTYPE_P2P_DEVICE) |
+ BIT(NL80211_IFTYPE_ADHOC);
+
+ wiphy->features |= NL80211_FEATURE_SCHED_SCAN_RANDOM_MAC_ADDR |
+ NL80211_FEATURE_SCAN_RANDOM_MAC_ADDR |
+ NL80211_FEATURE_ND_RANDOM_MAC_ADDR |
+ NL80211_FEATURE_HT_IBSS |
+ NL80211_FEATURE_P2P_GO_CTWIN |
+ NL80211_FEATURE_LOW_PRIORITY_SCAN |
+ NL80211_FEATURE_P2P_GO_OPPPS |
+ NL80211_FEATURE_AP_MODE_CHAN_WIDTH_CHANGE |
+ NL80211_FEATURE_SUPPORTS_WMM_ADMISSION |
+ NL80211_FEATURE_TX_POWER_INSERTION |
+ NL80211_FEATURE_DS_PARAM_SET_IE_IN_PROBES;
+
+ wiphy->flags |= WIPHY_FLAG_IBSS_RSN |
+ WIPHY_FLAG_AP_UAPSD |
+ WIPHY_FLAG_HAS_CHANNEL_SWITCH |
+ WIPHY_FLAG_SPLIT_SCAN_6GHZ |
+ WIPHY_FLAG_SUPPORTS_TDLS |
+ WIPHY_FLAG_SUPPORTS_EXT_KEK_KCK;
+
+ if (mld->nvm_data->sku_cap_11be_enable &&
+ !iwlwifi_mod_params.disable_11ax &&
+ !iwlwifi_mod_params.disable_11be)
+ wiphy->flags |= WIPHY_FLAG_SUPPORTS_MLO;
+
+ /* the firmware uses u8 for num of iterations, but 0xff is saved for
+ * infinite loop, so the maximum number of iterations is actually 254.
+ */
+ wiphy->max_sched_scan_plan_iterations = 254;
+ wiphy->max_sched_scan_ie_len = iwl_mld_scan_max_template_size();
+ wiphy->max_scan_ie_len = iwl_mld_scan_max_template_size();
+ wiphy->max_sched_scan_ssids = PROBE_OPTION_MAX;
+ wiphy->max_scan_ssids = PROBE_OPTION_MAX;
+ wiphy->max_sched_scan_plans = IWL_MAX_SCHED_SCAN_PLANS;
+ wiphy->max_sched_scan_reqs = 1;
+ wiphy->max_sched_scan_plan_interval = U16_MAX;
+ wiphy->max_match_sets = IWL_SCAN_MAX_PROFILES_V2;
+
+ wiphy->max_remain_on_channel_duration = 10000;
+
+ wiphy->hw_version = mld->trans->info.hw_id;
+
+ wiphy->hw_timestamp_max_peers = 1;
+
+ wiphy->iface_combinations = iwl_mld_iface_combinations;
+ wiphy->n_iface_combinations = ARRAY_SIZE(iwl_mld_iface_combinations);
+
+ wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_VHT_IBSS);
+ wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_DFS_CONCURRENT);
+ wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_BEACON_RATE_LEGACY);
+ wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_SCAN_START_TIME);
+ wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_BSS_PARENT_TSF);
+ wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_SCAN_MIN_PREQ_CONTENT);
+ wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_ACCEPT_BCAST_PROBE_RESP);
+ wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_FILS_MAX_CHANNEL_TIME);
+ wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_OCE_PROBE_REQ_HIGH_TX_RATE);
+ wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_MU_MIMO_AIR_SNIFFER);
+ wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_SPP_AMSDU_SUPPORT);
+
+ if (fw_has_capa(ucode_capa, IWL_UCODE_TLV_CAPA_PROTECTED_TWT))
+ wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_PROTECTED_TWT);
+
+ wiphy->iftype_ext_capab = NULL;
+ wiphy->num_iftype_ext_capab = 0;
+
+ if (!iwlwifi_mod_params.disable_11ax) {
+ wiphy->iftype_ext_capab = iftypes_ext_capa;
+ wiphy->num_iftype_ext_capab = ARRAY_SIZE(iftypes_ext_capa);
+
+ ieee80211_hw_set(hw, SUPPORTS_MULTI_BSSID);
+ ieee80211_hw_set(hw, SUPPORTS_ONLY_HE_MULTI_BSSID);
+ }
+
+ if (iwlmld_mod_params.power_scheme != IWL_POWER_SCHEME_CAM)
+ wiphy->flags |= WIPHY_FLAG_PS_ON_BY_DEFAULT;
+ else
+ wiphy->flags &= ~WIPHY_FLAG_PS_ON_BY_DEFAULT;
+}
+
+static void iwl_mac_hw_set_misc(struct iwl_mld *mld)
+{
+ struct ieee80211_hw *hw = mld->hw;
+
+ hw->queues = IEEE80211_NUM_ACS;
+
+ hw->netdev_features = NETIF_F_HIGHDMA | NETIF_F_SG;
+ hw->netdev_features |= mld->trans->mac_cfg->base->features;
+
+ hw->max_tx_fragments = mld->trans->info.max_skb_frags;
+ hw->max_listen_interval = IWL_MLD_CONN_LISTEN_INTERVAL;
+
+ hw->uapsd_max_sp_len = IEEE80211_WMM_IE_STA_QOSINFO_SP_ALL;
+ hw->uapsd_queues = IEEE80211_WMM_IE_STA_QOSINFO_AC_VO |
+ IEEE80211_WMM_IE_STA_QOSINFO_AC_VI |
+ IEEE80211_WMM_IE_STA_QOSINFO_AC_BK |
+ IEEE80211_WMM_IE_STA_QOSINFO_AC_BE;
+
+ hw->chanctx_data_size = sizeof(struct iwl_mld_phy);
+ hw->vif_data_size = sizeof(struct iwl_mld_vif);
+ hw->sta_data_size = sizeof(struct iwl_mld_sta);
+ hw->txq_data_size = sizeof(struct iwl_mld_txq);
+
+ /* TODO: Remove this division when IEEE80211_MAX_AMPDU_BUF_EHT size
+ * is supported.
+ * Note: ensure that IWL_DEFAULT_QUEUE_SIZE_EHT is updated accordingly.
+ */
+ hw->max_rx_aggregation_subframes = IEEE80211_MAX_AMPDU_BUF_EHT / 2;
+}
+
+static int iwl_mld_hw_verify_preconditions(struct iwl_mld *mld)
+{
+ int ratecheck;
+
+ /* check for rates version 3 */
+ ratecheck =
+ (iwl_fw_lookup_cmd_ver(mld->fw, TX_CMD, 0) >= 11) +
+ (iwl_fw_lookup_notif_ver(mld->fw, DATA_PATH_GROUP,
+ TLC_MNG_UPDATE_NOTIF, 0) >= 4) +
+ (iwl_fw_lookup_notif_ver(mld->fw, LEGACY_GROUP,
+ REPLY_RX_MPDU_CMD, 0) >= 6) +
+ (iwl_fw_lookup_notif_ver(mld->fw, DATA_PATH_GROUP,
+ RX_NO_DATA_NOTIF, 0) >= 4) +
+ (iwl_fw_lookup_notif_ver(mld->fw, LONG_GROUP, TX_CMD, 0) >= 9);
+
+ if (ratecheck != 0 && ratecheck != 5) {
+ IWL_ERR(mld, "Firmware has inconsistent rates\n");
+ return -EINVAL;
+ }
+
+ /* 11ax is expected to be enabled for all supported devices */
+ if (WARN_ON(!mld->nvm_data->sku_cap_11ax_enable))
+ return -EINVAL;
+
+ /* LAR is expected to be enabled for all supported devices */
+ if (WARN_ON(!mld->nvm_data->lar_enabled))
+ return -EINVAL;
+
+ /* All supported devices are currently using version 3 of the cmd.
+ * Since version 3, IWL_SCAN_MAX_PROFILES_V2 shall be used where
+ * necessary.
+ */
+ if (WARN_ON(iwl_fw_lookup_cmd_ver(mld->fw,
+ SCAN_OFFLOAD_UPDATE_PROFILES_CMD,
+ IWL_FW_CMD_VER_UNKNOWN) != 3))
+ return -EINVAL;
+
+ return 0;
+}
+
+int iwl_mld_register_hw(struct iwl_mld *mld)
+{
+ /* verify once essential preconditions required for setting
+ * the hw capabilities
+ */
+ if (iwl_mld_hw_verify_preconditions(mld))
+ return -EINVAL;
+
+ iwl_mld_hw_set_addresses(mld);
+ iwl_mld_hw_set_channels(mld);
+ iwl_mld_hw_set_security(mld);
+ iwl_mld_hw_set_pm(mld);
+ iwl_mld_hw_set_antennas(mld);
+ iwl_mac_hw_set_radiotap(mld);
+ iwl_mac_hw_set_flags(mld);
+ iwl_mac_hw_set_wiphy(mld);
+ iwl_mac_hw_set_misc(mld);
+
+ SET_IEEE80211_DEV(mld->hw, mld->trans->dev);
+
+ return ieee80211_register_hw(mld->hw);
+}
+
+static void
+iwl_mld_mac80211_tx(struct ieee80211_hw *hw,
+ struct ieee80211_tx_control *control, struct sk_buff *skb)
+{
+ struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw);
+ struct ieee80211_sta *sta = control->sta;
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+ struct ieee80211_hdr *hdr = (void *)skb->data;
+ u32 link_id = u32_get_bits(info->control.flags,
+ IEEE80211_TX_CTRL_MLO_LINK);
+
+ /* In AP mode, mgmt frames are sent on the bcast station,
+ * so the FW can't translate the MLD addr to the link addr. Do it here
+ */
+ if (ieee80211_is_mgmt(hdr->frame_control) && sta &&
+ link_id != IEEE80211_LINK_UNSPECIFIED &&
+ !ieee80211_is_probe_resp(hdr->frame_control)) {
+ /* translate MLD addresses to LINK addresses */
+ struct ieee80211_link_sta *link_sta =
+ rcu_dereference(sta->link[link_id]);
+ struct ieee80211_bss_conf *link_conf =
+ rcu_dereference(info->control.vif->link_conf[link_id]);
+ struct ieee80211_mgmt *mgmt;
+
+ if (WARN_ON(!link_sta || !link_conf)) {
+ ieee80211_free_txskb(hw, skb);
+ return;
+ }
+
+ mgmt = (void *)hdr;
+ memcpy(mgmt->da, link_sta->addr, ETH_ALEN);
+ memcpy(mgmt->sa, link_conf->addr, ETH_ALEN);
+ memcpy(mgmt->bssid, link_conf->bssid, ETH_ALEN);
+ }
+
+ iwl_mld_tx_skb(mld, skb, NULL);
+}
+
+static void
+iwl_mld_restart_cleanup(struct iwl_mld *mld)
+{
+ iwl_cleanup_mld(mld);
+
+ ieee80211_iterate_interfaces(mld->hw, IEEE80211_IFACE_ITER_ACTIVE,
+ iwl_mld_cleanup_vif, NULL);
+
+ ieee80211_iterate_stations_atomic(mld->hw,
+ iwl_mld_cleanup_sta, NULL);
+
+ iwl_mld_ftm_restart_cleanup(mld);
+}
+
+static
+int iwl_mld_mac80211_start(struct ieee80211_hw *hw)
+{
+ struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw);
+ bool in_d3 = false;
+ int ret = 0;
+
+ lockdep_assert_wiphy(mld->wiphy);
+
+#ifdef CONFIG_PM_SLEEP
+ /* Unless the host goes into hibernate the FW always stays on and
+ * the d3_resume flow is used. When wowlan is configured, mac80211
+ * would call it's resume callback and the wowlan_resume flow
+ * would be used.
+ */
+
+ in_d3 = mld->fw_status.in_d3;
+ if (in_d3) {
+ /* mac80211 already cleaned up the state, no need for cleanup */
+ ret = iwl_mld_no_wowlan_resume(mld);
+ if (ret)
+ iwl_mld_stop_fw(mld);
+ }
+#endif /* CONFIG_PM_SLEEP */
+
+ if (mld->fw_status.in_hw_restart) {
+ iwl_mld_stop_fw(mld);
+ iwl_mld_restart_cleanup(mld);
+ }
+
+ if (!in_d3 || ret) {
+ ret = iwl_mld_start_fw(mld);
+ if (ret)
+ goto error;
+ }
+
+ mld->scan.last_start_time_jiffies = jiffies;
+
+ iwl_dbg_tlv_time_point(&mld->fwrt, IWL_FW_INI_TIME_POINT_POST_INIT,
+ NULL);
+ iwl_dbg_tlv_time_point(&mld->fwrt, IWL_FW_INI_TIME_POINT_PERIODIC,
+ NULL);
+
+ return 0;
+
+error:
+ /* If we failed to restart the hw, there is nothing useful
+ * we can do but indicate we are no longer in restart.
+ */
+ mld->fw_status.in_hw_restart = false;
+
+ return ret;
+}
+
+static
+void iwl_mld_mac80211_stop(struct ieee80211_hw *hw, bool suspend)
+{
+ struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw);
+
+ lockdep_assert_wiphy(mld->wiphy);
+
+ wiphy_work_cancel(mld->wiphy, &mld->add_txqs_wk);
+
+ /* if the suspend flow fails the fw is in error. Stop it here, and it
+ * will be started upon wakeup
+ */
+ if (!suspend ||
+ (IS_ENABLED(CONFIG_PM_SLEEP) && iwl_mld_no_wowlan_suspend(mld)))
+ iwl_mld_stop_fw(mld);
+
+ /* Clear in_hw_restart flag when stopping the hw, as mac80211 won't
+ * execute the restart.
+ */
+ mld->fw_status.in_hw_restart = false;
+
+ /* We shouldn't have any UIDs still set. Loop over all the UIDs to
+ * make sure there's nothing left there and warn if any is found.
+ */
+ for (int i = 0; i < ARRAY_SIZE(mld->scan.uid_status); i++)
+ if (WARN_ONCE(mld->scan.uid_status[i],
+ "UMAC scan UID %d status was not cleaned (0x%x 0x%x)\n",
+ i, mld->scan.uid_status[i], mld->scan.status))
+ mld->scan.uid_status[i] = 0;
+}
+
+static
+int iwl_mld_mac80211_config(struct ieee80211_hw *hw, u32 changed)
+{
+ return 0;
+}
+
+static
+int iwl_mld_mac80211_add_interface(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif)
+{
+ struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw);
+ int ret;
+
+ lockdep_assert_wiphy(mld->wiphy);
+
+ /* Construct mld_vif, add it to fw, and map its ID to ieee80211_vif */
+ ret = iwl_mld_add_vif(mld, vif);
+ if (ret)
+ return ret;
+
+ /*
+ * Add the default link, but not if this is an MLD vif as that implies
+ * the HW is restarting and it will be configured by change_vif_links.
+ */
+ if (!ieee80211_vif_is_mld(vif))
+ ret = iwl_mld_add_link(mld, &vif->bss_conf);
+ if (ret)
+ goto err;
+
+ if (vif->type == NL80211_IFTYPE_STATION) {
+ vif->driver_flags |= IEEE80211_VIF_REMOVE_AP_AFTER_DISASSOC;
+ if (!vif->p2p)
+ vif->driver_flags |= IEEE80211_VIF_BEACON_FILTER |
+ IEEE80211_VIF_SUPPORTS_CQM_RSSI;
+ }
+
+ if (vif->p2p || iwl_fw_lookup_cmd_ver(mld->fw, PHY_CONTEXT_CMD, 0) < 5)
+ vif->driver_flags |= IEEE80211_VIF_IGNORE_OFDMA_WIDER_BW;
+
+ /*
+ * For an MLD vif (in restart) we may not have a link; delay the call
+ * the initial change_vif_links.
+ */
+ if (vif->type == NL80211_IFTYPE_STATION &&
+ !ieee80211_vif_is_mld(vif))
+ iwl_mld_update_mac_power(mld, vif, false);
+
+ if (vif->type == NL80211_IFTYPE_MONITOR) {
+ mld->monitor.on = true;
+ ieee80211_hw_set(mld->hw, RX_INCLUDES_FCS);
+ }
+
+ if (vif->type == NL80211_IFTYPE_P2P_DEVICE)
+ mld->p2p_device_vif = vif;
+
+ return 0;
+
+err:
+ iwl_mld_rm_vif(mld, vif);
+ return ret;
+}
+
+static
+void iwl_mld_mac80211_remove_interface(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif)
+{
+ struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw);
+
+ lockdep_assert_wiphy(mld->wiphy);
+
+ if (ieee80211_vif_type_p2p(vif) == NL80211_IFTYPE_STATION)
+ vif->driver_flags &= ~(IEEE80211_VIF_BEACON_FILTER |
+ IEEE80211_VIF_SUPPORTS_CQM_RSSI);
+
+ if (vif->type == NL80211_IFTYPE_MONITOR) {
+ __clear_bit(IEEE80211_HW_RX_INCLUDES_FCS, mld->hw->flags);
+ mld->monitor.on = false;
+ }
+
+ if (vif->type == NL80211_IFTYPE_P2P_DEVICE)
+ mld->p2p_device_vif = NULL;
+
+ iwl_mld_remove_link(mld, &vif->bss_conf);
+
+#ifdef CONFIG_IWLWIFI_DEBUGFS
+ debugfs_remove(iwl_mld_vif_from_mac80211(vif)->dbgfs_slink);
+ iwl_mld_vif_from_mac80211(vif)->dbgfs_slink = NULL;
+#endif
+
+ iwl_mld_rm_vif(mld, vif);
+}
+
+struct iwl_mld_mc_iter_data {
+ struct iwl_mld *mld;
+ int port_id;
+};
+
+static void iwl_mld_mc_iface_iterator(void *data, u8 *mac,
+ struct ieee80211_vif *vif)
+{
+ struct iwl_mld_mc_iter_data *mc_data = data;
+ struct iwl_mld *mld = mc_data->mld;
+ struct iwl_mcast_filter_cmd *cmd = mld->mcast_filter_cmd;
+ struct iwl_host_cmd hcmd = {
+ .id = MCAST_FILTER_CMD,
+ .dataflags[0] = IWL_HCMD_DFL_NOCOPY,
+ };
+ int ret, len;
+
+ /* If we don't have free ports, mcast frames will be dropped */
+ if (WARN_ON_ONCE(mc_data->port_id >= MAX_PORT_ID_NUM))
+ return;
+
+ if (vif->type != NL80211_IFTYPE_STATION || !vif->cfg.assoc)
+ return;
+
+ cmd->port_id = mc_data->port_id++;
+ ether_addr_copy(cmd->bssid, vif->bss_conf.bssid);
+ len = roundup(sizeof(*cmd) + cmd->count * ETH_ALEN, 4);
+
+ hcmd.len[0] = len;
+ hcmd.data[0] = cmd;
+
+ ret = iwl_mld_send_cmd(mld, &hcmd);
+ if (ret)
+ IWL_ERR(mld, "mcast filter cmd error. ret=%d\n", ret);
+}
+
+void iwl_mld_recalc_multicast_filter(struct iwl_mld *mld)
+{
+ struct iwl_mld_mc_iter_data iter_data = {
+ .mld = mld,
+ };
+
+ if (WARN_ON_ONCE(!mld->mcast_filter_cmd))
+ return;
+
+ ieee80211_iterate_active_interfaces(mld->hw,
+ IEEE80211_IFACE_ITER_NORMAL,
+ iwl_mld_mc_iface_iterator,
+ &iter_data);
+}
+
+static u64
+iwl_mld_mac80211_prepare_multicast(struct ieee80211_hw *hw,
+ struct netdev_hw_addr_list *mc_list)
+{
+ struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw);
+ struct iwl_mcast_filter_cmd *cmd;
+ struct netdev_hw_addr *addr;
+ int addr_count = netdev_hw_addr_list_count(mc_list);
+ bool pass_all = addr_count > MAX_MCAST_FILTERING_ADDRESSES;
+ int len;
+
+ if (pass_all)
+ addr_count = 0;
+
+ /* len must be a multiple of 4 */
+ len = roundup(sizeof(*cmd) + addr_count * ETH_ALEN, 4);
+ cmd = kzalloc(len, GFP_ATOMIC);
+ if (!cmd)
+ return 0;
+
+ if (pass_all) {
+ cmd->pass_all = 1;
+ goto out;
+ }
+
+ netdev_hw_addr_list_for_each(addr, mc_list) {
+ IWL_DEBUG_MAC80211(mld, "mcast addr (%d): %pM\n",
+ cmd->count, addr->addr);
+ ether_addr_copy(&cmd->addr_list[cmd->count * ETH_ALEN],
+ addr->addr);
+ cmd->count++;
+ }
+
+out:
+ return (u64)(unsigned long)cmd;
+}
+
+static
+void iwl_mld_mac80211_configure_filter(struct ieee80211_hw *hw,
+ unsigned int changed_flags,
+ unsigned int *total_flags,
+ u64 multicast)
+{
+ struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw);
+ struct iwl_mcast_filter_cmd *cmd = (void *)(unsigned long)multicast;
+
+ /* Replace previous configuration */
+ kfree(mld->mcast_filter_cmd);
+ mld->mcast_filter_cmd = cmd;
+
+ if (!cmd)
+ goto out;
+
+ if (changed_flags & FIF_ALLMULTI)
+ cmd->pass_all = !!(*total_flags & FIF_ALLMULTI);
+
+ if (cmd->pass_all)
+ cmd->count = 0;
+
+ iwl_mld_recalc_multicast_filter(mld);
+out:
+ *total_flags = 0;
+}
+
+static
+void iwl_mld_mac80211_wake_tx_queue(struct ieee80211_hw *hw,
+ struct ieee80211_txq *txq)
+{
+ struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw);
+ struct iwl_mld_txq *mld_txq = iwl_mld_txq_from_mac80211(txq);
+
+ if (likely(mld_txq->status.allocated) || !txq->sta) {
+ iwl_mld_tx_from_txq(mld, txq);
+ return;
+ }
+
+ /* We don't support TSPEC tids. %IEEE80211_NUM_TIDS is for mgmt */
+ if (txq->tid != IEEE80211_NUM_TIDS && txq->tid >= IWL_MAX_TID_COUNT) {
+ IWL_DEBUG_MAC80211(mld, "TID %d is not supported\n", txq->tid);
+ return;
+ }
+
+ /* The worker will handle any packets we leave on the txq now */
+
+ spin_lock_bh(&mld->add_txqs_lock);
+ /* The list is being deleted only after the queue is fully allocated. */
+ if (list_empty(&mld_txq->list) &&
+ /* recheck under lock, otherwise it can be added twice */
+ !mld_txq->status.allocated) {
+ list_add_tail(&mld_txq->list, &mld->txqs_to_add);
+ wiphy_work_queue(mld->wiphy, &mld->add_txqs_wk);
+ }
+ spin_unlock_bh(&mld->add_txqs_lock);
+}
+
+static void iwl_mld_teardown_tdls_peers(struct iwl_mld *mld)
+{
+ lockdep_assert_wiphy(mld->wiphy);
+
+ for (int i = 0; i < mld->fw->ucode_capa.num_stations; i++) {
+ struct ieee80211_link_sta *link_sta;
+ struct iwl_mld_sta *mld_sta;
+
+ link_sta = wiphy_dereference(mld->wiphy,
+ mld->fw_id_to_link_sta[i]);
+ if (IS_ERR_OR_NULL(link_sta))
+ continue;
+
+ if (!link_sta->sta->tdls)
+ continue;
+
+ mld_sta = iwl_mld_sta_from_mac80211(link_sta->sta);
+
+ ieee80211_tdls_oper_request(mld_sta->vif, link_sta->addr,
+ NL80211_TDLS_TEARDOWN,
+ WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED,
+ GFP_KERNEL);
+ }
+}
+
+static
+int iwl_mld_add_chanctx(struct ieee80211_hw *hw,
+ struct ieee80211_chanctx_conf *ctx)
+{
+ struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw);
+ struct iwl_mld_phy *phy = iwl_mld_phy_from_mac80211(ctx);
+ int fw_id = iwl_mld_allocate_fw_phy_id(mld);
+ int ret;
+
+ if (fw_id < 0)
+ return fw_id;
+
+ phy->mld = mld;
+ phy->fw_id = fw_id;
+ phy->chandef = *iwl_mld_get_chandef_from_chanctx(mld, ctx);
+
+ ret = iwl_mld_phy_fw_action(mld, ctx, FW_CTXT_ACTION_ADD);
+ if (ret) {
+ mld->used_phy_ids &= ~BIT(phy->fw_id);
+ return ret;
+ }
+
+ if (hweight8(mld->used_phy_ids) > 1)
+ iwl_mld_teardown_tdls_peers(mld);
+
+ return 0;
+}
+
+static
+void iwl_mld_remove_chanctx(struct ieee80211_hw *hw,
+ struct ieee80211_chanctx_conf *ctx)
+{
+ struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw);
+ struct iwl_mld_phy *phy = iwl_mld_phy_from_mac80211(ctx);
+
+ iwl_mld_phy_fw_action(mld, ctx, FW_CTXT_ACTION_REMOVE);
+ mld->used_phy_ids &= ~BIT(phy->fw_id);
+}
+
+static
+void iwl_mld_change_chanctx(struct ieee80211_hw *hw,
+ struct ieee80211_chanctx_conf *ctx, u32 changed)
+{
+ struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw);
+ struct iwl_mld_phy *phy = iwl_mld_phy_from_mac80211(ctx);
+ struct cfg80211_chan_def *chandef =
+ iwl_mld_get_chandef_from_chanctx(mld, ctx);
+
+ /* We don't care about these */
+ if (!(changed & ~(IEEE80211_CHANCTX_CHANGE_RX_CHAINS |
+ IEEE80211_CHANCTX_CHANGE_RADAR |
+ IEEE80211_CHANCTX_CHANGE_CHANNEL)))
+ return;
+
+ /* Check if a FW update is required */
+
+ if (changed & IEEE80211_CHANCTX_CHANGE_AP)
+ goto update;
+
+ if (chandef->chan == phy->chandef.chan &&
+ chandef->center_freq1 == phy->chandef.center_freq1 &&
+ chandef->punctured == phy->chandef.punctured) {
+ /* Check if we are toggling between HT and non-HT, no-op */
+ if (phy->chandef.width == chandef->width ||
+ (phy->chandef.width <= NL80211_CHAN_WIDTH_20 &&
+ chandef->width <= NL80211_CHAN_WIDTH_20))
+ return;
+ }
+update:
+
+ iwl_mld_update_phy_chandef(mld, ctx);
+}
+
+static u8
+iwl_mld_chandef_get_primary_80(struct cfg80211_chan_def *chandef)
+{
+ int data_start;
+ int control_start;
+ int bw;
+
+ if (chandef->width == NL80211_CHAN_WIDTH_320)
+ bw = 320;
+ else if (chandef->width == NL80211_CHAN_WIDTH_160)
+ bw = 160;
+ else
+ return 0;
+
+ /* data is bw wide so the start is half the width */
+ data_start = chandef->center_freq1 - bw / 2;
+ /* control is 20Mhz width */
+ control_start = chandef->chan->center_freq - 10;
+
+ return (control_start - data_start) / 80;
+}
+
+static bool iwl_mld_can_activate_link(struct iwl_mld *mld,
+ struct ieee80211_vif *vif,
+ struct ieee80211_bss_conf *link)
+{
+ struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
+ struct iwl_mld_sta *mld_sta;
+ struct iwl_mld_link_sta *link_sta;
+
+ /* In association, we activate the assoc link before adding the STA. */
+ if (!mld_vif->ap_sta || !vif->cfg.assoc)
+ return true;
+
+ mld_sta = iwl_mld_sta_from_mac80211(mld_vif->ap_sta);
+
+ /* When switching links, we need to wait with the activation until the
+ * STA was added to the FW. It'll be activated in
+ * iwl_mld_update_link_stas
+ */
+ link_sta = wiphy_dereference(mld->wiphy, mld_sta->link[link->link_id]);
+
+ /* In restart we can have a link_sta that doesn't exist in FW yet */
+ return link_sta && link_sta->in_fw;
+}
+
+static
+int iwl_mld_assign_vif_chanctx(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_bss_conf *link,
+ struct ieee80211_chanctx_conf *ctx)
+{
+ struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw);
+ struct iwl_mld_link *mld_link = iwl_mld_link_from_mac80211(link);
+ unsigned int n_active = iwl_mld_count_active_links(mld, vif);
+ int ret;
+
+ lockdep_assert_wiphy(mld->wiphy);
+
+ if (WARN_ON(!mld_link))
+ return -EINVAL;
+
+ /* if the assigned one was not counted yet, count it now */
+ if (!rcu_access_pointer(mld_link->chan_ctx)) {
+ n_active++;
+
+ /* Track addition of non-BSS link */
+ if (ieee80211_vif_type_p2p(vif) != NL80211_IFTYPE_STATION) {
+ ret = iwl_mld_emlsr_check_non_bss_block(mld, 1);
+ if (ret)
+ return ret;
+ }
+ }
+
+ /* for AP, mac parameters such as HE support are updated at this stage. */
+ if (vif->type == NL80211_IFTYPE_AP) {
+ ret = iwl_mld_mac_fw_action(mld, vif, FW_CTXT_ACTION_MODIFY);
+
+ if (ret) {
+ IWL_ERR(mld, "failed to update MAC %pM\n", vif->addr);
+ return -EINVAL;
+ }
+ }
+
+ rcu_assign_pointer(mld_link->chan_ctx, ctx);
+
+ if (n_active > 1) {
+ struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
+
+ iwl_mld_leave_omi_bw_reduction(mld);
+
+ /* Indicate to mac80211 that EML is enabled */
+ vif->driver_flags |= IEEE80211_VIF_EML_ACTIVE;
+
+ if (vif->active_links & BIT(mld_vif->emlsr.selected_links))
+ mld_vif->emlsr.primary = mld_vif->emlsr.selected_primary;
+ else
+ mld_vif->emlsr.primary = __ffs(vif->active_links);
+
+ iwl_dbg_tlv_time_point(&mld->fwrt, IWL_FW_INI_TIME_ESR_LINK_UP,
+ NULL);
+ }
+
+ /* First send the link command with the phy context ID.
+ * Now that we have the phy, we know the band so also the rates
+ */
+ ret = iwl_mld_change_link_in_fw(mld, link,
+ LINK_CONTEXT_MODIFY_RATES_INFO);
+ if (ret)
+ goto err;
+
+ /* TODO: Initialize rate control for the AP station, since we might be
+ * doing a link switch here - we cannot initialize it before since
+ * this needs the phy context assigned (and in FW?), and we cannot
+ * do it later because it needs to be initialized as soon as we're
+ * able to TX on the link, i.e. when active. (task=link-switch)
+ */
+
+ /* Now activate the link */
+ if (iwl_mld_can_activate_link(mld, vif, link)) {
+ ret = iwl_mld_activate_link(mld, link);
+ if (ret)
+ goto err;
+ }
+
+ if (vif->type == NL80211_IFTYPE_STATION)
+ iwl_mld_send_ap_tx_power_constraint_cmd(mld, vif, link);
+
+ if (vif->type == NL80211_IFTYPE_MONITOR) {
+ ret = iwl_mld_add_mon_sta(mld, vif, link);
+ if (ret)
+ goto deactivate_link;
+
+ mld->monitor.p80 =
+ iwl_mld_chandef_get_primary_80(&vif->bss_conf.chanreq.oper);
+ }
+
+ return 0;
+
+deactivate_link:
+ if (mld_link->active)
+ iwl_mld_deactivate_link(mld, link);
+err:
+ RCU_INIT_POINTER(mld_link->chan_ctx, NULL);
+ return ret;
+}
+
+static
+void iwl_mld_unassign_vif_chanctx(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_bss_conf *link,
+ struct ieee80211_chanctx_conf *ctx)
+{
+ struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw);
+ struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
+ struct iwl_mld_link *mld_link = iwl_mld_link_from_mac80211(link);
+ unsigned int n_active = iwl_mld_count_active_links(mld, vif);
+
+ if (WARN_ON(!mld_link))
+ return;
+
+ /* Track removal of non-BSS link */
+ if (ieee80211_vif_type_p2p(vif) != NL80211_IFTYPE_STATION)
+ iwl_mld_emlsr_check_non_bss_block(mld, -1);
+
+ iwl_mld_deactivate_link(mld, link);
+
+ if (vif->type == NL80211_IFTYPE_MONITOR)
+ iwl_mld_remove_mon_sta(mld, vif, link);
+
+ if (n_active > 1) {
+ /* Indicate to mac80211 that EML is disabled */
+ vif->driver_flags &= ~IEEE80211_VIF_EML_ACTIVE;
+
+ iwl_dbg_tlv_time_point(&mld->fwrt,
+ IWL_FW_INI_TIME_ESR_LINK_DOWN,
+ NULL);
+ }
+
+ RCU_INIT_POINTER(mld_link->chan_ctx, NULL);
+
+ /* in the non-MLO case, remove/re-add the link to clean up FW state.
+ * In MLO, it'll be done in drv_change_vif_link
+ */
+ if (!ieee80211_vif_is_mld(vif) && !mld_vif->ap_sta &&
+ !WARN_ON_ONCE(vif->cfg.assoc) &&
+ vif->type != NL80211_IFTYPE_AP && !mld->fw_status.in_hw_restart) {
+ iwl_mld_remove_link(mld, link);
+ iwl_mld_add_link(mld, link);
+ }
+}
+
+static
+int iwl_mld_mac80211_set_rts_threshold(struct ieee80211_hw *hw, u32 value)
+{
+ return 0;
+}
+
+static void
+iwl_mld_link_info_changed_ap_ibss(struct iwl_mld *mld,
+ struct ieee80211_vif *vif,
+ struct ieee80211_bss_conf *link,
+ u64 changes)
+{
+ u32 link_changes = 0;
+
+ if (changes & BSS_CHANGED_ERP_SLOT)
+ link_changes |= LINK_CONTEXT_MODIFY_RATES_INFO;
+
+ if (changes & (BSS_CHANGED_ERP_CTS_PROT | BSS_CHANGED_HT))
+ link_changes |= LINK_CONTEXT_MODIFY_PROTECT_FLAGS;
+
+ if (changes & (BSS_CHANGED_QOS | BSS_CHANGED_BANDWIDTH))
+ link_changes |= LINK_CONTEXT_MODIFY_QOS_PARAMS;
+
+ if (changes & BSS_CHANGED_HE_BSS_COLOR)
+ link_changes |= LINK_CONTEXT_MODIFY_HE_PARAMS;
+
+ if (link_changes)
+ iwl_mld_change_link_in_fw(mld, link, link_changes);
+
+ if (changes & BSS_CHANGED_BEACON)
+ iwl_mld_update_beacon_template(mld, vif, link);
+}
+
+static
+u32 iwl_mld_link_changed_mapping(struct iwl_mld *mld,
+ struct ieee80211_vif *vif,
+ struct ieee80211_bss_conf *link_conf,
+ u64 changes)
+{
+ u32 link_changes = 0;
+ bool has_he, has_eht;
+
+ if (changes & BSS_CHANGED_QOS && vif->cfg.assoc && link_conf->qos)
+ link_changes |= LINK_CONTEXT_MODIFY_QOS_PARAMS;
+
+ if (changes & (BSS_CHANGED_ERP_PREAMBLE | BSS_CHANGED_BASIC_RATES |
+ BSS_CHANGED_ERP_SLOT))
+ link_changes |= LINK_CONTEXT_MODIFY_RATES_INFO;
+
+ if (changes & (BSS_CHANGED_HT | BSS_CHANGED_ERP_CTS_PROT))
+ link_changes |= LINK_CONTEXT_MODIFY_PROTECT_FLAGS;
+
+ /* TODO: task=MLO check mac80211's HE flags and if command is needed
+ * every time there's a link change. Currently used flags are
+ * BSS_CHANGED_HE_OBSS_PD and BSS_CHANGED_HE_BSS_COLOR.
+ */
+ has_he = link_conf->he_support && !iwlwifi_mod_params.disable_11ax;
+ has_eht = link_conf->eht_support && !iwlwifi_mod_params.disable_11be;
+
+ if (vif->cfg.assoc && (has_he || has_eht)) {
+ IWL_DEBUG_MAC80211(mld, "Associated in HE mode\n");
+ link_changes |= LINK_CONTEXT_MODIFY_HE_PARAMS;
+ }
+
+ return link_changes;
+}
+
+static void
+iwl_mld_mac80211_link_info_changed_sta(struct iwl_mld *mld,
+ struct ieee80211_vif *vif,
+ struct ieee80211_bss_conf *link_conf,
+ u64 changes)
+{
+ u32 link_changes = iwl_mld_link_changed_mapping(mld, vif, link_conf,
+ changes);
+
+ if (link_changes)
+ iwl_mld_change_link_in_fw(mld, link_conf, link_changes);
+
+ if (changes & BSS_CHANGED_TPE)
+ iwl_mld_send_ap_tx_power_constraint_cmd(mld, vif, link_conf);
+
+ if (changes & BSS_CHANGED_BEACON_INFO)
+ iwl_mld_update_mac_power(mld, vif, false);
+
+ /* The firmware will wait quite a while after association before it
+ * starts filtering the beacons. We can safely enable beacon filtering
+ * upon CQM configuration, even if we didn't get a beacon yet.
+ */
+ if (changes & (BSS_CHANGED_CQM | BSS_CHANGED_BEACON_INFO))
+ iwl_mld_enable_beacon_filter(mld, link_conf, false);
+
+ /* If we have used OMI before to reduce bandwidth to 80 MHz and then
+ * increased to 160 MHz again, and then the AP changes to 320 MHz, it
+ * will think that we're limited to 160 MHz right now. Update it by
+ * requesting a new OMI bandwidth.
+ */
+ if (changes & BSS_CHANGED_BANDWIDTH) {
+ enum ieee80211_sta_rx_bandwidth bw;
+
+ bw = ieee80211_chan_width_to_rx_bw(link_conf->chanreq.oper.width);
+
+ iwl_mld_omi_ap_changed_bw(mld, link_conf, bw);
+
+ }
+
+ if (changes & BSS_CHANGED_BANDWIDTH)
+ iwl_mld_retry_emlsr(mld, vif);
+}
+
+static int iwl_mld_update_mu_groups(struct iwl_mld *mld,
+ struct ieee80211_bss_conf *link_conf)
+{
+ struct iwl_mu_group_mgmt_cmd cmd = {};
+
+ BUILD_BUG_ON(sizeof(cmd.membership_status) !=
+ sizeof(link_conf->mu_group.membership));
+ BUILD_BUG_ON(sizeof(cmd.user_position) !=
+ sizeof(link_conf->mu_group.position));
+
+ memcpy(cmd.membership_status, link_conf->mu_group.membership,
+ WLAN_MEMBERSHIP_LEN);
+ memcpy(cmd.user_position, link_conf->mu_group.position,
+ WLAN_USER_POSITION_LEN);
+
+ return iwl_mld_send_cmd_pdu(mld,
+ WIDE_ID(DATA_PATH_GROUP,
+ UPDATE_MU_GROUPS_CMD),
+ &cmd);
+}
+
+static void
+iwl_mld_mac80211_link_info_changed(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_bss_conf *link_conf,
+ u64 changes)
+{
+ struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw);
+
+ switch (vif->type) {
+ case NL80211_IFTYPE_STATION:
+ iwl_mld_mac80211_link_info_changed_sta(mld, vif, link_conf,
+ changes);
+ break;
+ case NL80211_IFTYPE_AP:
+ case NL80211_IFTYPE_ADHOC:
+ iwl_mld_link_info_changed_ap_ibss(mld, vif, link_conf,
+ changes);
+ break;
+ case NL80211_IFTYPE_MONITOR:
+ /* The firmware tracks this on its own in STATION mode, but
+ * obviously not in sniffer mode.
+ */
+ if (changes & BSS_CHANGED_MU_GROUPS)
+ iwl_mld_update_mu_groups(mld, link_conf);
+ break;
+ default:
+ /* shouldn't happen */
+ WARN_ON_ONCE(1);
+ }
+
+ /* We now know our BSSID, we can configure the MAC context with
+ * eht_support if needed.
+ */
+ if (changes & BSS_CHANGED_BSSID)
+ iwl_mld_mac_fw_action(mld, vif, FW_CTXT_ACTION_MODIFY);
+
+ if (changes & BSS_CHANGED_TXPOWER)
+ iwl_mld_set_tx_power(mld, link_conf, link_conf->txpower);
+}
+
+static void
+iwl_mld_smps_workaround(struct iwl_mld *mld, struct ieee80211_vif *vif, bool enable)
+{
+ struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
+ bool workaround_required =
+ iwl_fw_lookup_cmd_ver(mld->fw, MAC_PM_POWER_TABLE, 0) < 2;
+
+ if (!workaround_required)
+ return;
+
+ /* Send the device-level power commands since the
+ * firmware checks the POWER_TABLE_CMD's POWER_SAVE_EN bit to
+ * determine SMPS mode.
+ */
+ if (mld_vif->ps_disabled == !enable)
+ return;
+
+ mld_vif->ps_disabled = !enable;
+
+ iwl_mld_update_device_power(mld, false);
+}
+
+static
+void iwl_mld_mac80211_vif_cfg_changed(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ u64 changes)
+{
+ struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw);
+ int ret;
+
+ lockdep_assert_wiphy(mld->wiphy);
+
+ if (vif->type != NL80211_IFTYPE_STATION)
+ return;
+
+ if (changes & BSS_CHANGED_ASSOC) {
+ ret = iwl_mld_mac_fw_action(mld, vif, FW_CTXT_ACTION_MODIFY);
+ if (ret)
+ IWL_ERR(mld, "failed to update context\n");
+
+ if (vif->cfg.assoc) {
+ /* Clear statistics to get clean beacon counter, and
+ * ask for periodic statistics, as they are needed for
+ * link selection and RX OMI decisions.
+ */
+ iwl_mld_clear_stats_in_fw(mld);
+ iwl_mld_request_periodic_fw_stats(mld, true);
+
+ iwl_mld_set_vif_associated(mld, vif);
+ } else {
+ iwl_mld_request_periodic_fw_stats(mld, false);
+ }
+ }
+
+ if (changes & BSS_CHANGED_PS) {
+ iwl_mld_smps_workaround(mld, vif, vif->cfg.ps);
+ iwl_mld_update_mac_power(mld, vif, false);
+ }
+
+ /* TODO: task=MLO BSS_CHANGED_MLD_VALID_LINKS/CHANGED_MLD_TTLM */
+}
+
+static int
+iwl_mld_mac80211_hw_scan(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_scan_request *hw_req)
+{
+ struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw);
+ int ret;
+
+ if (WARN_ON(!hw_req->req.n_channels ||
+ hw_req->req.n_channels >
+ mld->fw->ucode_capa.n_scan_channels))
+ return -EINVAL;
+
+ ret = iwl_mld_regular_scan_start(mld, vif, &hw_req->req, &hw_req->ies);
+ if (!ret) {
+ /* We will be busy with scanning, so the counters may not reflect the
+ * reality. Stop checking the counters until the scan ends
+ */
+ iwl_mld_start_ignoring_tpt_updates(mld);
+ }
+
+ return ret;
+}
+
+static void
+iwl_mld_mac80211_cancel_hw_scan(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif)
+{
+ struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw);
+
+ /* Due to a race condition, it's possible that mac80211 asks
+ * us to stop a hw_scan when it's already stopped. This can
+ * happen, for instance, if we stopped the scan ourselves,
+ * called ieee80211_scan_completed() and the userspace called
+ * cancel scan before ieee80211_scan_work() could run.
+ * To handle that, simply return if the scan is not running.
+ */
+ if (mld->scan.status & IWL_MLD_SCAN_REGULAR) {
+ iwl_mld_scan_stop(mld, IWL_MLD_SCAN_REGULAR, true);
+ /* Scan is over, we can check again the tpt counters */
+ iwl_mld_stop_ignoring_tpt_updates(mld);
+ }
+}
+
+static int
+iwl_mld_mac80211_sched_scan_start(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct cfg80211_sched_scan_request *req,
+ struct ieee80211_scan_ies *ies)
+{
+ struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw);
+
+ return iwl_mld_sched_scan_start(mld, vif, req, ies, IWL_MLD_SCAN_SCHED);
+}
+
+static int
+iwl_mld_mac80211_sched_scan_stop(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif)
+{
+ struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw);
+
+ /* Due to a race condition, it's possible that mac80211 asks
+ * us to stop a sched_scan when it's already stopped. This
+ * can happen, for instance, if we stopped the scan ourselves,
+ * called ieee80211_sched_scan_stopped() and the userspace called
+ * stop sched scan before ieee80211_sched_scan_stopped_work()
+ * could run. To handle this, simply return if the scan is
+ * not running.
+ */
+ if (!(mld->scan.status & IWL_MLD_SCAN_SCHED))
+ return 0;
+
+ return iwl_mld_scan_stop(mld, IWL_MLD_SCAN_SCHED, false);
+}
+
+static void
+iwl_mld_restart_complete_vif(void *data, u8 *mac, struct ieee80211_vif *vif)
+{
+ struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
+ struct ieee80211_bss_conf *link_conf;
+ struct iwl_mld *mld = data;
+ int link_id;
+
+ for_each_vif_active_link(vif, link_conf, link_id) {
+ enum ieee80211_sta_rx_bandwidth bw;
+ struct iwl_mld_link *mld_link;
+
+ mld_link = wiphy_dereference(mld->wiphy,
+ mld_vif->link[link_id]);
+
+ if (WARN_ON_ONCE(!mld_link))
+ continue;
+
+ bw = mld_link->rx_omi.bw_in_progress;
+ if (bw)
+ iwl_mld_change_link_omi_bw(mld, link_conf, bw);
+ }
+}
+
+static void
+iwl_mld_mac80211_reconfig_complete(struct ieee80211_hw *hw,
+ enum ieee80211_reconfig_type reconfig_type)
+{
+ struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw);
+
+ switch (reconfig_type) {
+ case IEEE80211_RECONFIG_TYPE_RESTART:
+ mld->fw_status.in_hw_restart = false;
+ iwl_mld_send_recovery_cmd(mld, ERROR_RECOVERY_END_OF_RECOVERY);
+
+ ieee80211_iterate_interfaces(mld->hw,
+ IEEE80211_IFACE_ITER_NORMAL,
+ iwl_mld_restart_complete_vif, mld);
+
+ iwl_trans_finish_sw_reset(mld->trans);
+ /* no need to lock, adding in parallel would schedule too */
+ if (!list_empty(&mld->txqs_to_add))
+ wiphy_work_queue(mld->wiphy, &mld->add_txqs_wk);
+
+ IWL_INFO(mld, "restart completed\n");
+ break;
+ case IEEE80211_RECONFIG_TYPE_SUSPEND:
+ break;
+ }
+}
+
+static
+void iwl_mld_mac80211_mgd_prepare_tx(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_prep_tx_info *info)
+{
+ struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw);
+ u32 duration = IWL_MLD_SESSION_PROTECTION_ASSOC_TIME_MS;
+
+ /* After a successful association the connection is etalibeshed
+ * and we can rely on the quota to send the disassociation frame.
+ */
+ if (info->was_assoc)
+ return;
+
+ if (info->duration > duration)
+ duration = info->duration;
+
+ iwl_mld_schedule_session_protection(mld, vif, duration,
+ IWL_MLD_SESSION_PROTECTION_MIN_TIME_MS,
+ info->link_id);
+}
+
+static
+void iwl_mld_mac_mgd_complete_tx(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_prep_tx_info *info)
+{
+ struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw);
+
+ /* Successful authentication is the only case that requires to let
+ * the session protection go. We'll need it for the upcoming
+ * association. For all the other cases, we need to cancel the session
+ * protection.
+ * After successful association the connection is established and
+ * further mgd tx can rely on the quota.
+ */
+ if (info->success && info->subtype == IEEE80211_STYPE_AUTH)
+ return;
+
+ /* The firmware will be on medium after we configure the vif as
+ * associated. Removing the session protection allows the firmware
+ * to stop being on medium. In order to ensure the continuity of our
+ * presence on medium, we need first to configure the vif as associated
+ * and only then, remove the session protection.
+ * Currently, mac80211 calls vif_cfg_changed() first and then,
+ * drv_mgd_complete_tx(). Ensure that this assumption stays true by
+ * a warning.
+ */
+ WARN_ON(info->success &&
+ (info->subtype == IEEE80211_STYPE_ASSOC_REQ ||
+ info->subtype == IEEE80211_STYPE_REASSOC_REQ) &&
+ !vif->cfg.assoc);
+
+ iwl_mld_cancel_session_protection(mld, vif, info->link_id);
+}
+
+static int
+iwl_mld_mac80211_conf_tx(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ unsigned int link_id, u16 ac,
+ const struct ieee80211_tx_queue_params *params)
+{
+ struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw);
+ struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
+ struct iwl_mld_link *link;
+
+ lockdep_assert_wiphy(mld->wiphy);
+
+ link = iwl_mld_link_dereference_check(mld_vif, link_id);
+ if (!link)
+ return -EINVAL;
+
+ link->queue_params[ac] = *params;
+
+ /* No need to update right away, we'll get BSS_CHANGED_QOS
+ * The exception is P2P_DEVICE interface which needs immediate update.
+ */
+ if (vif->type == NL80211_IFTYPE_P2P_DEVICE)
+ iwl_mld_change_link_in_fw(mld, &vif->bss_conf,
+ LINK_CONTEXT_MODIFY_QOS_PARAMS);
+
+ return 0;
+}
+
+static void iwl_mld_set_uapsd(struct iwl_mld *mld, struct ieee80211_vif *vif)
+{
+ vif->driver_flags &= ~IEEE80211_VIF_SUPPORTS_UAPSD;
+
+ if (vif->type != NL80211_IFTYPE_STATION)
+ return;
+
+ if (vif->p2p &&
+ !(iwlwifi_mod_params.uapsd_disable & IWL_DISABLE_UAPSD_P2P_CLIENT))
+ vif->driver_flags |= IEEE80211_VIF_SUPPORTS_UAPSD;
+
+ if (!vif->p2p &&
+ !(iwlwifi_mod_params.uapsd_disable & IWL_DISABLE_UAPSD_BSS))
+ vif->driver_flags |= IEEE80211_VIF_SUPPORTS_UAPSD;
+}
+
+int iwl_mld_tdls_sta_count(struct iwl_mld *mld)
+{
+ int count = 0;
+
+ lockdep_assert_wiphy(mld->wiphy);
+
+ for (int i = 0; i < mld->fw->ucode_capa.num_stations; i++) {
+ struct ieee80211_link_sta *link_sta;
+
+ link_sta = wiphy_dereference(mld->wiphy,
+ mld->fw_id_to_link_sta[i]);
+ if (IS_ERR_OR_NULL(link_sta))
+ continue;
+
+ if (!link_sta->sta->tdls)
+ continue;
+
+ count++;
+ }
+
+ return count;
+}
+
+static void iwl_mld_check_he_obss_narrow_bw_ru_iter(struct wiphy *wiphy,
+ struct cfg80211_bss *bss,
+ void *_data)
+{
+ bool *tolerated = _data;
+ const struct cfg80211_bss_ies *ies;
+ const struct element *elem;
+
+ rcu_read_lock();
+ ies = rcu_dereference(bss->ies);
+ elem = cfg80211_find_elem(WLAN_EID_EXT_CAPABILITY, ies->data,
+ ies->len);
+
+ if (!elem || elem->datalen < 10 ||
+ !(elem->data[10] &
+ WLAN_EXT_CAPA10_OBSS_NARROW_BW_RU_TOLERANCE_SUPPORT)) {
+ *tolerated = false;
+ }
+ rcu_read_unlock();
+}
+
+static void
+iwl_mld_check_he_obss_narrow_bw_ru(struct iwl_mld *mld,
+ struct iwl_mld_link *mld_link,
+ struct ieee80211_bss_conf *link_conf)
+{
+ bool tolerated = true;
+
+ if (WARN_ON_ONCE(!link_conf->chanreq.oper.chan))
+ return;
+
+ if (!(link_conf->chanreq.oper.chan->flags & IEEE80211_CHAN_RADAR)) {
+ mld_link->he_ru_2mhz_block = false;
+ return;
+ }
+
+ cfg80211_bss_iter(mld->wiphy, &link_conf->chanreq.oper,
+ iwl_mld_check_he_obss_narrow_bw_ru_iter, &tolerated);
+
+ /* If there is at least one AP on radar channel that cannot
+ * tolerate 26-tone RU UL OFDMA transmissions using HE TB PPDU.
+ */
+ mld_link->he_ru_2mhz_block = !tolerated;
+}
+
+static void iwl_mld_link_set_2mhz_block(struct iwl_mld *mld,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta)
+{
+ struct ieee80211_link_sta *link_sta;
+ unsigned int link_id;
+
+ for_each_sta_active_link(vif, sta, link_sta, link_id) {
+ struct ieee80211_bss_conf *link_conf =
+ link_conf_dereference_protected(vif, link_id);
+ struct iwl_mld_link *mld_link =
+ iwl_mld_link_from_mac80211(link_conf);
+
+ if (WARN_ON(!link_conf || !mld_link))
+ continue;
+
+ if (link_sta->he_cap.has_he)
+ iwl_mld_check_he_obss_narrow_bw_ru(mld, mld_link,
+ link_conf);
+ }
+}
+
+static int iwl_mld_move_sta_state_up(struct iwl_mld *mld,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta,
+ enum ieee80211_sta_state old_state,
+ enum ieee80211_sta_state new_state)
+{
+ struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
+ int tdls_count = 0;
+ int ret;
+
+ if (old_state == IEEE80211_STA_NOTEXIST &&
+ new_state == IEEE80211_STA_NONE) {
+ if (sta->tdls) {
+ if (vif->p2p || hweight8(mld->used_phy_ids) != 1)
+ return -EBUSY;
+
+ tdls_count = iwl_mld_tdls_sta_count(mld);
+ if (tdls_count >= IWL_TDLS_STA_COUNT)
+ return -EBUSY;
+ }
+
+ /*
+ * If this is the first STA (i.e. the AP) it won't do
+ * anything, otherwise must leave for any new STA on
+ * any other interface, or for TDLS, etc.
+ * Need to call this _before_ adding the STA so it can
+ * look up the one STA to use to ask mac80211 to leave
+ * OMI; in the unlikely event that adding the new STA
+ * then fails we'll just re-enter OMI later (via the
+ * statistics notification handling.)
+ */
+ iwl_mld_leave_omi_bw_reduction(mld);
+
+ ret = iwl_mld_add_sta(mld, sta, vif, STATION_TYPE_PEER);
+ if (ret)
+ return ret;
+
+ /* just added first TDLS STA, so disable PM */
+ if (sta->tdls && tdls_count == 0)
+ iwl_mld_update_mac_power(mld, vif, false);
+
+ if (vif->type == NL80211_IFTYPE_STATION && !sta->tdls)
+ mld_vif->ap_sta = sta;
+
+ /* Initialize TLC here already - this really tells
+ * the firmware only what the supported legacy rates are
+ * (may be) since it's initialized already from what the
+ * AP advertised in the beacon/probe response. This will
+ * allow the firmware to send auth/assoc frames with one
+ * of the supported rates already, rather than having to
+ * use a mandatory rate.
+ * If we're the AP, we'll just assume mandatory rates at
+ * this point, but we know nothing about the STA anyway.
+ */
+ iwl_mld_config_tlc(mld, vif, sta);
+
+ return ret;
+ } else if (old_state == IEEE80211_STA_NONE &&
+ new_state == IEEE80211_STA_AUTH) {
+ iwl_mld_set_uapsd(mld, vif);
+ return 0;
+ } else if (old_state == IEEE80211_STA_AUTH &&
+ new_state == IEEE80211_STA_ASSOC) {
+ ret = iwl_mld_update_all_link_stations(mld, sta);
+
+ if (vif->type == NL80211_IFTYPE_STATION)
+ iwl_mld_link_set_2mhz_block(mld, vif, sta);
+ /* Now the link_sta's capabilities are set, update the FW */
+ iwl_mld_config_tlc(mld, vif, sta);
+
+ if (vif->type == NL80211_IFTYPE_AP) {
+ /* Update MAC_CFG_FILTER_ACCEPT_BEACON if at least
+ * one sta is associated
+ */
+ if (++mld_vif->num_associated_stas == 1)
+ ret = iwl_mld_mac_fw_action(mld, vif,
+ FW_CTXT_ACTION_MODIFY);
+ }
+
+ return ret;
+ } else if (old_state == IEEE80211_STA_ASSOC &&
+ new_state == IEEE80211_STA_AUTHORIZED) {
+ ret = 0;
+
+ if (!sta->tdls) {
+ mld_vif->authorized = true;
+
+ /* Ensure any block due to a non-BSS link is synced */
+ iwl_mld_emlsr_check_non_bss_block(mld, 0);
+
+ /* Block EMLSR until a certain throughput it reached */
+ if (!mld->fw_status.in_hw_restart &&
+ IWL_MLD_ENTER_EMLSR_TPT_THRESH > 0)
+ iwl_mld_block_emlsr(mld_vif->mld, vif,
+ IWL_MLD_EMLSR_BLOCKED_TPT,
+ 0);
+
+ /* clear COEX_HIGH_PRIORITY_ENABLE */
+ ret = iwl_mld_mac_fw_action(mld, vif,
+ FW_CTXT_ACTION_MODIFY);
+ if (ret)
+ return ret;
+ iwl_mld_smps_workaround(mld, vif, vif->cfg.ps);
+ }
+
+ /* MFP is set by default before the station is authorized.
+ * Clear it here in case it's not used.
+ */
+ if (!sta->mfp)
+ ret = iwl_mld_update_all_link_stations(mld, sta);
+
+ /* We can use wide bandwidth now, not only 20 MHz */
+ iwl_mld_config_tlc(mld, vif, sta);
+
+ return ret;
+ } else {
+ return -EINVAL;
+ }
+}
+
+static int iwl_mld_move_sta_state_down(struct iwl_mld *mld,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta,
+ enum ieee80211_sta_state old_state,
+ enum ieee80211_sta_state new_state)
+{
+ struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
+
+ if (old_state == IEEE80211_STA_AUTHORIZED &&
+ new_state == IEEE80211_STA_ASSOC) {
+ if (!sta->tdls) {
+ mld_vif->authorized = false;
+
+ memset(&mld_vif->emlsr.zeroed_on_not_authorized, 0,
+ sizeof(mld_vif->emlsr.zeroed_on_not_authorized));
+
+ wiphy_delayed_work_cancel(mld->wiphy,
+ &mld_vif->emlsr.prevent_done_wk);
+ wiphy_delayed_work_cancel(mld->wiphy,
+ &mld_vif->emlsr.tmp_non_bss_done_wk);
+ wiphy_work_cancel(mld->wiphy, &mld_vif->emlsr.unblock_tpt_wk);
+ wiphy_delayed_work_cancel(mld->wiphy,
+ &mld_vif->emlsr.check_tpt_wk);
+
+ iwl_mld_reset_cca_40mhz_workaround(mld, vif);
+ iwl_mld_smps_workaround(mld, vif, true);
+ }
+
+ /* once we move into assoc state, need to update the FW to
+ * stop using wide bandwidth
+ */
+ iwl_mld_config_tlc(mld, vif, sta);
+ } else if (old_state == IEEE80211_STA_ASSOC &&
+ new_state == IEEE80211_STA_AUTH) {
+ if (vif->type == NL80211_IFTYPE_AP &&
+ !WARN_ON(!mld_vif->num_associated_stas)) {
+ /* Update MAC_CFG_FILTER_ACCEPT_BEACON if the last sta
+ * is disassociating
+ */
+ if (--mld_vif->num_associated_stas == 0)
+ iwl_mld_mac_fw_action(mld, vif,
+ FW_CTXT_ACTION_MODIFY);
+ }
+ } else if (old_state == IEEE80211_STA_AUTH &&
+ new_state == IEEE80211_STA_NONE) {
+ /* nothing */
+ } else if (old_state == IEEE80211_STA_NONE &&
+ new_state == IEEE80211_STA_NOTEXIST) {
+ iwl_mld_remove_sta(mld, sta);
+
+ if (sta->tdls && iwl_mld_tdls_sta_count(mld) == 0) {
+ /* just removed last TDLS STA, so enable PM */
+ iwl_mld_update_mac_power(mld, vif, false);
+ }
+ } else {
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int iwl_mld_mac80211_sta_state(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta,
+ enum ieee80211_sta_state old_state,
+ enum ieee80211_sta_state new_state)
+{
+ struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw);
+ struct iwl_mld_sta *mld_sta = iwl_mld_sta_from_mac80211(sta);
+
+ IWL_DEBUG_MAC80211(mld, "station %pM state change %d->%d\n",
+ sta->addr, old_state, new_state);
+
+ mld_sta->sta_state = new_state;
+
+ if (old_state < new_state)
+ return iwl_mld_move_sta_state_up(mld, vif, sta, old_state,
+ new_state);
+ else
+ return iwl_mld_move_sta_state_down(mld, vif, sta, old_state,
+ new_state);
+}
+
+static void iwl_mld_mac80211_flush(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ u32 queues, bool drop)
+{
+ struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw);
+
+ /* Make sure we're done with the deferred traffic before flushing */
+ iwl_mld_add_txq_list(mld);
+
+ for (int i = 0; i < mld->fw->ucode_capa.num_stations; i++) {
+ struct ieee80211_link_sta *link_sta =
+ wiphy_dereference(mld->wiphy,
+ mld->fw_id_to_link_sta[i]);
+
+ if (IS_ERR_OR_NULL(link_sta))
+ continue;
+
+ /* Check that the sta belongs to the given vif */
+ if (vif && vif != iwl_mld_sta_from_mac80211(link_sta->sta)->vif)
+ continue;
+
+ if (drop)
+ iwl_mld_flush_sta_txqs(mld, link_sta->sta);
+ else
+ iwl_mld_wait_sta_txqs_empty(mld, link_sta->sta);
+ }
+}
+
+static void iwl_mld_mac80211_flush_sta(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta)
+{
+ struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw);
+
+ iwl_mld_flush_sta_txqs(mld, sta);
+}
+
+static int
+iwl_mld_mac80211_ampdu_action(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_ampdu_params *params)
+{
+ struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw);
+ struct ieee80211_sta *sta = params->sta;
+ enum ieee80211_ampdu_mlme_action action = params->action;
+ u16 tid = params->tid;
+ u16 ssn = params->ssn;
+ u16 buf_size = params->buf_size;
+ u16 timeout = params->timeout;
+ int ret;
+
+ IWL_DEBUG_HT(mld, "A-MPDU action on addr %pM tid: %d action: %d\n",
+ sta->addr, tid, action);
+
+ switch (action) {
+ case IEEE80211_AMPDU_RX_START:
+ ret = iwl_mld_ampdu_rx_start(mld, sta, tid, ssn, buf_size,
+ timeout);
+ break;
+ case IEEE80211_AMPDU_RX_STOP:
+ ret = iwl_mld_ampdu_rx_stop(mld, sta, tid);
+ break;
+ default:
+ /* The mac80211 TX_AMPDU_SETUP_IN_HW flag is set for all
+ * devices, since all support TX A-MPDU offload in hardware.
+ * Therefore, no TX action should be requested here.
+ */
+ WARN_ON_ONCE(1);
+ return -EINVAL;
+ }
+
+ return ret;
+}
+
+static bool iwl_mld_can_hw_csum(struct sk_buff *skb)
+{
+ u8 protocol = ip_hdr(skb)->protocol;
+
+ return protocol == IPPROTO_TCP || protocol == IPPROTO_UDP;
+}
+
+static bool iwl_mld_mac80211_can_aggregate(struct ieee80211_hw *hw,
+ struct sk_buff *head,
+ struct sk_buff *skb)
+{
+ if (!IS_ENABLED(CONFIG_INET))
+ return false;
+
+ /* For now don't aggregate IPv6 in AMSDU */
+ if (skb->protocol != htons(ETH_P_IP))
+ return false;
+
+ /* Allow aggregation only if both frames have the same HW csum offload
+ * capability, ensuring consistent HW or SW csum handling in A-MSDU.
+ */
+ return iwl_mld_can_hw_csum(skb) == iwl_mld_can_hw_csum(head);
+}
+
+static void iwl_mld_mac80211_sync_rx_queues(struct ieee80211_hw *hw)
+{
+ struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw);
+
+ iwl_mld_sync_rx_queues(mld, IWL_MLD_RXQ_EMPTY, NULL, 0);
+}
+
+static void iwl_mld_sta_rc_update(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_link_sta *link_sta,
+ u32 changed)
+{
+ struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw);
+
+ if (changed & (IEEE80211_RC_BW_CHANGED |
+ IEEE80211_RC_SUPP_RATES_CHANGED |
+ IEEE80211_RC_NSS_CHANGED)) {
+ struct ieee80211_bss_conf *link =
+ link_conf_dereference_check(vif, link_sta->link_id);
+
+ if (WARN_ON(!link))
+ return;
+
+ iwl_mld_config_tlc_link(mld, vif, link, link_sta);
+ }
+}
+
+#ifdef CONFIG_PM_SLEEP
+static void iwl_mld_set_wakeup(struct ieee80211_hw *hw, bool enabled)
+{
+ struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw);
+
+ device_set_wakeup_enable(mld->trans->dev, enabled);
+}
+
+/* Returns 0 on success. 1 if failed to suspend with wowlan:
+ * If the circumstances didn't satisfy the conditions for suspension
+ * with wowlan, mac80211 would use the no_wowlan flow.
+ * If an error had occurred we update the trans status and state here
+ * and the result will be stopping the FW.
+ */
+static int
+iwl_mld_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan)
+{
+ struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw);
+ int ret;
+
+ iwl_fw_runtime_suspend(&mld->fwrt);
+
+ ret = iwl_mld_wowlan_suspend(mld, wowlan);
+ if (ret) {
+ if (ret < 0) {
+ mld->trans->state = IWL_TRANS_NO_FW;
+ set_bit(STATUS_FW_ERROR, &mld->trans->status);
+ }
+ return 1;
+ }
+
+ if (iwl_mld_no_wowlan_suspend(mld))
+ return 1;
+
+ return 0;
+}
+
+static int iwl_mld_resume(struct ieee80211_hw *hw)
+{
+ struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw);
+ int ret;
+
+ ret = iwl_mld_wowlan_resume(mld);
+ if (ret)
+ return ret;
+
+ iwl_fw_runtime_resume(&mld->fwrt);
+
+ iwl_mld_low_latency_restart(mld);
+
+ return 0;
+}
+#endif
+
+static int iwl_mld_alloc_ptk_pn(struct iwl_mld *mld,
+ struct iwl_mld_sta *mld_sta,
+ struct ieee80211_key_conf *key,
+ struct iwl_mld_ptk_pn **ptk_pn)
+{
+ u8 num_rx_queues = mld->trans->info.num_rxqs;
+ int keyidx = key->keyidx;
+ struct ieee80211_key_seq seq;
+
+ if (WARN_ON(keyidx >= ARRAY_SIZE(mld_sta->ptk_pn)))
+ return -EINVAL;
+
+ WARN_ON(rcu_access_pointer(mld_sta->ptk_pn[keyidx]));
+ *ptk_pn = kzalloc(struct_size(*ptk_pn, q, num_rx_queues),
+ GFP_KERNEL);
+ if (!*ptk_pn)
+ return -ENOMEM;
+
+ for (u8 tid = 0; tid < IWL_MAX_TID_COUNT; tid++) {
+ ieee80211_get_key_rx_seq(key, tid, &seq);
+ for (u8 q = 0; q < num_rx_queues; q++)
+ memcpy((*ptk_pn)->q[q].pn[tid], seq.ccmp.pn,
+ IEEE80211_CCMP_PN_LEN);
+ }
+
+ rcu_assign_pointer(mld_sta->ptk_pn[keyidx], *ptk_pn);
+
+ return 0;
+}
+
+static int iwl_mld_set_key_add(struct iwl_mld *mld,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta,
+ struct ieee80211_key_conf *key)
+{
+ struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
+ struct iwl_mld_sta *mld_sta =
+ sta ? iwl_mld_sta_from_mac80211(sta) : NULL;
+ struct iwl_mld_ptk_pn *ptk_pn = NULL;
+ int keyidx = key->keyidx;
+ int ret;
+
+ /* Will be set to 0 if added successfully */
+ key->hw_key_idx = STA_KEY_IDX_INVALID;
+
+ switch (key->cipher) {
+ case WLAN_CIPHER_SUITE_WEP40:
+ case WLAN_CIPHER_SUITE_WEP104:
+ IWL_DEBUG_MAC80211(mld, "Use SW encryption for WEP\n");
+ return -EOPNOTSUPP;
+ case WLAN_CIPHER_SUITE_TKIP:
+ if (vif->type == NL80211_IFTYPE_STATION) {
+ key->flags |= IEEE80211_KEY_FLAG_PUT_MIC_SPACE;
+ break;
+ }
+ IWL_DEBUG_MAC80211(mld, "Use SW encryption for TKIP\n");
+ return -EOPNOTSUPP;
+ case WLAN_CIPHER_SUITE_CCMP:
+ case WLAN_CIPHER_SUITE_GCMP:
+ case WLAN_CIPHER_SUITE_GCMP_256:
+ case WLAN_CIPHER_SUITE_AES_CMAC:
+ case WLAN_CIPHER_SUITE_BIP_GMAC_128:
+ case WLAN_CIPHER_SUITE_BIP_GMAC_256:
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ if (vif->type == NL80211_IFTYPE_STATION &&
+ (keyidx == 6 || keyidx == 7))
+ rcu_assign_pointer(mld_vif->bigtks[keyidx - 6], key);
+
+ /* After exiting from RFKILL, hostapd configures GTK/ITGK before the
+ * AP is started, but those keys can't be sent to the FW before the
+ * MCAST/BCAST STAs are added to it (which happens upon AP start).
+ * Store it here to be sent later when the AP is started.
+ */
+ if ((vif->type == NL80211_IFTYPE_ADHOC ||
+ vif->type == NL80211_IFTYPE_AP) && !sta &&
+ !mld_vif->ap_ibss_active)
+ return iwl_mld_store_ap_early_key(mld, key, mld_vif);
+
+ if (!mld->fw_status.in_hw_restart && mld_sta &&
+ key->flags & IEEE80211_KEY_FLAG_PAIRWISE &&
+ (key->cipher == WLAN_CIPHER_SUITE_CCMP ||
+ key->cipher == WLAN_CIPHER_SUITE_GCMP ||
+ key->cipher == WLAN_CIPHER_SUITE_GCMP_256)) {
+ ret = iwl_mld_alloc_ptk_pn(mld, mld_sta, key, &ptk_pn);
+ if (ret)
+ return ret;
+ }
+
+ IWL_DEBUG_MAC80211(mld, "set hwcrypto key (sta:%pM, id:%d)\n",
+ sta ? sta->addr : NULL, keyidx);
+
+ ret = iwl_mld_add_key(mld, vif, sta, key);
+ if (ret) {
+ IWL_WARN(mld, "set key failed (%d)\n", ret);
+ if (ptk_pn) {
+ RCU_INIT_POINTER(mld_sta->ptk_pn[keyidx], NULL);
+ kfree(ptk_pn);
+ }
+
+ return -EOPNOTSUPP;
+ }
+
+ return 0;
+}
+
+static void iwl_mld_set_key_remove(struct iwl_mld *mld,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta,
+ struct ieee80211_key_conf *key)
+{
+ struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
+ struct iwl_mld_sta *mld_sta =
+ sta ? iwl_mld_sta_from_mac80211(sta) : NULL;
+ int keyidx = key->keyidx;
+
+ if (vif->type == NL80211_IFTYPE_STATION &&
+ (keyidx == 6 || keyidx == 7))
+ RCU_INIT_POINTER(mld_vif->bigtks[keyidx - 6], NULL);
+
+ if (mld_sta && key->flags & IEEE80211_KEY_FLAG_PAIRWISE &&
+ (key->cipher == WLAN_CIPHER_SUITE_CCMP ||
+ key->cipher == WLAN_CIPHER_SUITE_GCMP ||
+ key->cipher == WLAN_CIPHER_SUITE_GCMP_256)) {
+ struct iwl_mld_ptk_pn *ptk_pn;
+
+ if (WARN_ON(keyidx >= ARRAY_SIZE(mld_sta->ptk_pn)))
+ return;
+
+ ptk_pn = wiphy_dereference(mld->wiphy,
+ mld_sta->ptk_pn[keyidx]);
+ RCU_INIT_POINTER(mld_sta->ptk_pn[keyidx], NULL);
+ if (!WARN_ON(!ptk_pn))
+ kfree_rcu(ptk_pn, rcu_head);
+ }
+
+ /* if this key was stored to be added later to the FW - free it here */
+ if (!(key->flags & IEEE80211_KEY_FLAG_PAIRWISE))
+ iwl_mld_free_ap_early_key(mld, key, mld_vif);
+
+ /* We already removed it */
+ if (key->hw_key_idx == STA_KEY_IDX_INVALID)
+ return;
+
+ IWL_DEBUG_MAC80211(mld, "disable hwcrypto key\n");
+
+ iwl_mld_remove_key(mld, vif, sta, key);
+}
+
+static int iwl_mld_mac80211_set_key(struct ieee80211_hw *hw,
+ enum set_key_cmd cmd,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta,
+ struct ieee80211_key_conf *key)
+{
+ struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw);
+ int ret;
+
+ switch (cmd) {
+ case SET_KEY:
+ ret = iwl_mld_set_key_add(mld, vif, sta, key);
+ if (ret)
+ ret = -EOPNOTSUPP;
+ break;
+ case DISABLE_KEY:
+ iwl_mld_set_key_remove(mld, vif, sta, key);
+ ret = 0;
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+static int
+iwl_mld_pre_channel_switch(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_channel_switch *chsw)
+{
+ struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw);
+ struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
+ struct iwl_mld_link *mld_link =
+ iwl_mld_link_dereference_check(mld_vif, chsw->link_id);
+ u8 primary;
+ int selected;
+
+ lockdep_assert_wiphy(mld->wiphy);
+
+ if (WARN_ON(!mld_link))
+ return -EINVAL;
+
+ IWL_DEBUG_MAC80211(mld, "pre CSA to freq %d\n",
+ chsw->chandef.center_freq1);
+
+ if (!iwl_mld_emlsr_active(vif))
+ return 0;
+
+ primary = iwl_mld_get_primary_link(vif);
+
+ /* stay on the primary link unless it is undergoing a CSA with quiet */
+ if (chsw->link_id == primary && chsw->block_tx)
+ selected = iwl_mld_get_other_link(vif, primary);
+ else
+ selected = primary;
+
+ /* Remember to tell the firmware that this link can't tx
+ * Note that this logic seems to be unrelated to emlsr, but it
+ * really is needed only when emlsr is active. When we have a
+ * single link, the firmware will handle all this on its own.
+ * In multi-link scenarios, we can learn about the CSA from
+ * another link and this logic is too complex for the firmware
+ * to track.
+ * Since we want to de-activate the link that got a CSA with mode=1,
+ * we need to tell the firmware not to send any frame on that link
+ * as the firmware may not be aware that link is under a CSA
+ * with mode=1 (no Tx allowed).
+ */
+ mld_link->silent_deactivation = chsw->block_tx;
+ iwl_mld_exit_emlsr(mld, vif, IWL_MLD_EMLSR_EXIT_CSA, selected);
+
+ return 0;
+}
+
+static void
+iwl_mld_channel_switch(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_channel_switch *chsw)
+{
+ struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw);
+
+ /* By implementing this operation, we prevent mac80211 from
+ * starting its own channel switch timer, so that we can call
+ * ieee80211_chswitch_done() ourselves at the right time
+ * (Upon receiving the channel_switch_start notification from the fw)
+ */
+ IWL_DEBUG_MAC80211(mld,
+ "dummy channel switch op\n");
+}
+
+static int
+iwl_mld_post_channel_switch(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_bss_conf *link_conf)
+{
+ struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw);
+ struct iwl_mld_link *mld_link = iwl_mld_link_from_mac80211(link_conf);
+
+ lockdep_assert_wiphy(mld->wiphy);
+
+ WARN_ON(mld_link->silent_deactivation);
+
+ return 0;
+}
+
+static void
+iwl_mld_abort_channel_switch(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_bss_conf *link_conf)
+{
+ struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw);
+ struct iwl_mld_link *mld_link = iwl_mld_link_from_mac80211(link_conf);
+
+ IWL_DEBUG_MAC80211(mld,
+ "abort channel switch op\n");
+ mld_link->silent_deactivation = false;
+}
+
+static int
+iwl_mld_switch_vif_chanctx_swap(struct ieee80211_hw *hw,
+ struct ieee80211_vif_chanctx_switch *vifs)
+{
+ struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw);
+ int ret;
+
+ iwl_mld_unassign_vif_chanctx(hw, vifs[0].vif, vifs[0].link_conf,
+ vifs[0].old_ctx);
+ iwl_mld_remove_chanctx(hw, vifs[0].old_ctx);
+
+ ret = iwl_mld_add_chanctx(hw, vifs[0].new_ctx);
+ if (ret) {
+ IWL_ERR(mld, "failed to add new_ctx during channel switch\n");
+ goto out_reassign;
+ }
+
+ ret = iwl_mld_assign_vif_chanctx(hw, vifs[0].vif, vifs[0].link_conf,
+ vifs[0].new_ctx);
+ if (ret) {
+ IWL_ERR(mld,
+ "failed to assign new_ctx during channel switch\n");
+ goto out_remove;
+ }
+
+ return 0;
+
+ out_remove:
+ iwl_mld_remove_chanctx(hw, vifs[0].new_ctx);
+ out_reassign:
+ if (iwl_mld_add_chanctx(hw, vifs[0].old_ctx)) {
+ IWL_ERR(mld, "failed to add old_ctx after failure\n");
+ return ret;
+ }
+
+ if (iwl_mld_assign_vif_chanctx(hw, vifs[0].vif, vifs[0].link_conf,
+ vifs[0].old_ctx))
+ IWL_ERR(mld, "failed to reassign old_ctx after failure\n");
+
+ return ret;
+}
+
+static int
+iwl_mld_switch_vif_chanctx_reassign(struct ieee80211_hw *hw,
+ struct ieee80211_vif_chanctx_switch *vifs)
+{
+ struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw);
+ int ret;
+
+ iwl_mld_unassign_vif_chanctx(hw, vifs[0].vif, vifs[0].link_conf,
+ vifs[0].old_ctx);
+ ret = iwl_mld_assign_vif_chanctx(hw, vifs[0].vif, vifs[0].link_conf,
+ vifs[0].new_ctx);
+ if (ret) {
+ IWL_ERR(mld,
+ "failed to assign new_ctx during channel switch\n");
+ goto out_reassign;
+ }
+
+ return 0;
+
+out_reassign:
+ if (iwl_mld_assign_vif_chanctx(hw, vifs[0].vif, vifs[0].link_conf,
+ vifs[0].old_ctx))
+ IWL_ERR(mld, "failed to reassign old_ctx after failure\n");
+
+ return ret;
+}
+
+static int
+iwl_mld_switch_vif_chanctx(struct ieee80211_hw *hw,
+ struct ieee80211_vif_chanctx_switch *vifs,
+ int n_vifs,
+ enum ieee80211_chanctx_switch_mode mode)
+{
+ int ret;
+
+ /* we only support a single-vif right now */
+ if (n_vifs > 1)
+ return -EOPNOTSUPP;
+
+ switch (mode) {
+ case CHANCTX_SWMODE_SWAP_CONTEXTS:
+ ret = iwl_mld_switch_vif_chanctx_swap(hw, vifs);
+ break;
+ case CHANCTX_SWMODE_REASSIGN_VIF:
+ ret = iwl_mld_switch_vif_chanctx_reassign(hw, vifs);
+ break;
+ default:
+ ret = -EOPNOTSUPP;
+ break;
+ }
+
+ return ret;
+}
+
+static void iwl_mld_sta_pre_rcu_remove(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta)
+{
+ struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw);
+ struct iwl_mld_sta *mld_sta = iwl_mld_sta_from_mac80211(sta);
+ struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
+ struct iwl_mld_link_sta *mld_link_sta;
+ u8 link_id;
+
+ lockdep_assert_wiphy(mld->wiphy);
+
+ /* This is called before mac80211 does RCU synchronisation,
+ * so here we already invalidate our internal RCU-protected
+ * station pointer. The rest of the code will thus no longer
+ * be able to find the station this way, and we don't rely
+ * on further RCU synchronisation after the sta_state()
+ * callback deleted the station.
+ */
+ for_each_mld_link_sta(mld_sta, mld_link_sta, link_id)
+ RCU_INIT_POINTER(mld->fw_id_to_link_sta[mld_link_sta->fw_id],
+ NULL);
+
+ if (sta == mld_vif->ap_sta)
+ mld_vif->ap_sta = NULL;
+}
+
+static void
+iwl_mld_mac80211_mgd_protect_tdls_discover(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ unsigned int link_id)
+{
+ struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw);
+ struct ieee80211_bss_conf *link_conf;
+ u32 duration;
+ int ret;
+
+ link_conf = wiphy_dereference(hw->wiphy, vif->link_conf[link_id]);
+ if (WARN_ON_ONCE(!link_conf))
+ return;
+
+ /* Protect the session to hear the TDLS setup response on the channel */
+
+ duration = 2 * link_conf->dtim_period * link_conf->beacon_int;
+
+ ret = iwl_mld_start_session_protection(mld, vif, duration, duration,
+ link_id, HZ / 5);
+ if (ret)
+ IWL_ERR(mld,
+ "Failed to start session protection for TDLS: %d\n",
+ ret);
+}
+
+static bool iwl_mld_can_activate_links(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ u16 desired_links)
+{
+ struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw);
+ int n_links = hweight16(desired_links);
+
+ /* Check if HW supports the wanted number of links */
+ return n_links <= iwl_mld_max_active_links(mld, vif);
+}
+
+static int
+iwl_mld_change_vif_links(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ u16 old_links, u16 new_links,
+ struct ieee80211_bss_conf *old[IEEE80211_MLD_MAX_NUM_LINKS])
+{
+ struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
+ struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw);
+ struct ieee80211_bss_conf *link_conf;
+ u16 removed = old_links & ~new_links;
+ u16 added = new_links & ~old_links;
+ int err;
+
+ lockdep_assert_wiphy(mld->wiphy);
+
+ /*
+ * No bits designate non-MLO mode. We can handle MLO exit/enter by
+ * simply mapping that to link ID zero internally.
+ * Note that mac80211 does such a non-MLO to MLO switch during restart
+ * if it was in MLO before. In that case, we do not have a link to
+ * remove.
+ */
+ if (old_links == 0 && !mld->fw_status.in_hw_restart)
+ removed |= BIT(0);
+
+ if (new_links == 0)
+ added |= BIT(0);
+
+ for (int i = 0; i < IEEE80211_MLD_MAX_NUM_LINKS; i++) {
+ if (removed & BIT(i) && !WARN_ON(!old[i]))
+ iwl_mld_remove_link(mld, old[i]);
+ }
+
+ for (int i = 0; i < IEEE80211_MLD_MAX_NUM_LINKS; i++) {
+ if (added & BIT(i)) {
+ link_conf = link_conf_dereference_protected(vif, i);
+ if (!link_conf) {
+ err = -EINVAL;
+ goto remove_added_links;
+ }
+
+ err = iwl_mld_add_link(mld, link_conf);
+ if (err)
+ goto remove_added_links;
+ }
+ }
+
+ /*
+ * Ensure we always have a valid primary_link. When using multiple
+ * links the proper value is set in assign_vif_chanctx.
+ */
+ mld_vif->emlsr.primary = new_links ? __ffs(new_links) : 0;
+
+ /*
+ * Special MLO restart case. We did not have a link when the interface
+ * was added, so do the power configuration now.
+ */
+ if (old_links == 0 && mld->fw_status.in_hw_restart)
+ iwl_mld_update_mac_power(mld, vif, false);
+
+ return 0;
+
+remove_added_links:
+ for (int i = 0; i < IEEE80211_MLD_MAX_NUM_LINKS; i++) {
+ if (!(added & BIT(i)))
+ continue;
+
+ link_conf = link_conf_dereference_protected(vif, i);
+ if (!link_conf || !iwl_mld_link_from_mac80211(link_conf))
+ continue;
+
+ iwl_mld_remove_link(mld, link_conf);
+ }
+
+ if (WARN_ON(!iwl_mld_error_before_recovery(mld)))
+ return err;
+
+ /* reconfig will fix us anyway */
+ return 0;
+}
+
+static int iwl_mld_change_sta_links(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta,
+ u16 old_links, u16 new_links)
+{
+ struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw);
+
+ return iwl_mld_update_link_stas(mld, vif, sta, old_links, new_links);
+}
+
+static int iwl_mld_mac80211_join_ibss(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif)
+{
+ return iwl_mld_start_ap_ibss(hw, vif, &vif->bss_conf);
+}
+
+static void iwl_mld_mac80211_leave_ibss(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif)
+{
+ return iwl_mld_stop_ap_ibss(hw, vif, &vif->bss_conf);
+}
+
+static int iwl_mld_mac80211_tx_last_beacon(struct ieee80211_hw *hw)
+{
+ struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw);
+
+ return mld->ibss_manager;
+}
+
+#define IWL_MLD_EMLSR_BLOCKED_TMP_NON_BSS_TIMEOUT (5 * HZ)
+
+static void iwl_mld_vif_iter_emlsr_block_tmp_non_bss(void *_data, u8 *mac,
+ struct ieee80211_vif *vif)
+{
+ struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
+ int ret;
+
+ if (!iwl_mld_vif_has_emlsr_cap(vif))
+ return;
+
+ ret = iwl_mld_block_emlsr_sync(mld_vif->mld, vif,
+ IWL_MLD_EMLSR_BLOCKED_TMP_NON_BSS,
+ iwl_mld_get_primary_link(vif));
+ if (ret)
+ return;
+
+ wiphy_delayed_work_queue(mld_vif->mld->wiphy,
+ &mld_vif->emlsr.tmp_non_bss_done_wk,
+ IWL_MLD_EMLSR_BLOCKED_TMP_NON_BSS_TIMEOUT);
+}
+
+static void iwl_mld_prep_add_interface(struct ieee80211_hw *hw,
+ enum nl80211_iftype type)
+{
+ struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw);
+
+ IWL_DEBUG_MAC80211(mld, "prep_add_interface: type=%u\n", type);
+
+ if (!(type == NL80211_IFTYPE_AP ||
+ type == NL80211_IFTYPE_P2P_GO ||
+ type == NL80211_IFTYPE_P2P_CLIENT))
+ return;
+
+ ieee80211_iterate_active_interfaces_mtx(mld->hw,
+ IEEE80211_IFACE_ITER_NORMAL,
+ iwl_mld_vif_iter_emlsr_block_tmp_non_bss,
+ NULL);
+}
+
+static int iwl_mld_set_hw_timestamp(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct cfg80211_set_hw_timestamp *hwts)
+{
+ struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw);
+ u32 protocols = 0;
+
+ /* HW timestamping is only supported for a specific station */
+ if (!hwts->macaddr)
+ return -EOPNOTSUPP;
+
+ if (hwts->enable)
+ protocols =
+ IWL_TIME_SYNC_PROTOCOL_TM | IWL_TIME_SYNC_PROTOCOL_FTM;
+
+ return iwl_mld_time_sync_config(mld, hwts->macaddr, protocols);
+}
+
+static int iwl_mld_start_pmsr(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct cfg80211_pmsr_request *request)
+{
+ struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw);
+
+ return iwl_mld_ftm_start(mld, vif, request);
+}
+
+const struct ieee80211_ops iwl_mld_hw_ops = {
+ .tx = iwl_mld_mac80211_tx,
+ .start = iwl_mld_mac80211_start,
+ .stop = iwl_mld_mac80211_stop,
+ .config = iwl_mld_mac80211_config,
+ .add_interface = iwl_mld_mac80211_add_interface,
+ .remove_interface = iwl_mld_mac80211_remove_interface,
+ .conf_tx = iwl_mld_mac80211_conf_tx,
+ .prepare_multicast = iwl_mld_mac80211_prepare_multicast,
+ .configure_filter = iwl_mld_mac80211_configure_filter,
+ .reconfig_complete = iwl_mld_mac80211_reconfig_complete,
+ .wake_tx_queue = iwl_mld_mac80211_wake_tx_queue,
+ .add_chanctx = iwl_mld_add_chanctx,
+ .remove_chanctx = iwl_mld_remove_chanctx,
+ .change_chanctx = iwl_mld_change_chanctx,
+ .assign_vif_chanctx = iwl_mld_assign_vif_chanctx,
+ .unassign_vif_chanctx = iwl_mld_unassign_vif_chanctx,
+ .set_rts_threshold = iwl_mld_mac80211_set_rts_threshold,
+ .link_info_changed = iwl_mld_mac80211_link_info_changed,
+ .vif_cfg_changed = iwl_mld_mac80211_vif_cfg_changed,
+ .set_key = iwl_mld_mac80211_set_key,
+ .hw_scan = iwl_mld_mac80211_hw_scan,
+ .cancel_hw_scan = iwl_mld_mac80211_cancel_hw_scan,
+ .sched_scan_start = iwl_mld_mac80211_sched_scan_start,
+ .sched_scan_stop = iwl_mld_mac80211_sched_scan_stop,
+ .mgd_prepare_tx = iwl_mld_mac80211_mgd_prepare_tx,
+ .mgd_complete_tx = iwl_mld_mac_mgd_complete_tx,
+ .sta_state = iwl_mld_mac80211_sta_state,
+ .sta_statistics = iwl_mld_mac80211_sta_statistics,
+ .flush = iwl_mld_mac80211_flush,
+ .flush_sta = iwl_mld_mac80211_flush_sta,
+ .ampdu_action = iwl_mld_mac80211_ampdu_action,
+ .can_aggregate_in_amsdu = iwl_mld_mac80211_can_aggregate,
+ .sync_rx_queues = iwl_mld_mac80211_sync_rx_queues,
+ .link_sta_rc_update = iwl_mld_sta_rc_update,
+ .start_ap = iwl_mld_start_ap_ibss,
+ .stop_ap = iwl_mld_stop_ap_ibss,
+ .pre_channel_switch = iwl_mld_pre_channel_switch,
+ .channel_switch = iwl_mld_channel_switch,
+ .post_channel_switch = iwl_mld_post_channel_switch,
+ .abort_channel_switch = iwl_mld_abort_channel_switch,
+ .switch_vif_chanctx = iwl_mld_switch_vif_chanctx,
+ .sta_pre_rcu_remove = iwl_mld_sta_pre_rcu_remove,
+ .remain_on_channel = iwl_mld_start_roc,
+ .cancel_remain_on_channel = iwl_mld_cancel_roc,
+ .can_activate_links = iwl_mld_can_activate_links,
+ .change_vif_links = iwl_mld_change_vif_links,
+ .change_sta_links = iwl_mld_change_sta_links,
+#ifdef CONFIG_PM_SLEEP
+ .suspend = iwl_mld_suspend,
+ .resume = iwl_mld_resume,
+ .set_wakeup = iwl_mld_set_wakeup,
+ .set_rekey_data = iwl_mld_set_rekey_data,
+#if IS_ENABLED(CONFIG_IPV6)
+ .ipv6_addr_change = iwl_mld_ipv6_addr_change,
+#endif /* IS_ENABLED(CONFIG_IPV6) */
+#endif /* CONFIG_PM_SLEEP */
+#ifdef CONFIG_IWLWIFI_DEBUGFS
+ .vif_add_debugfs = iwl_mld_add_vif_debugfs,
+ .link_add_debugfs = iwl_mld_add_link_debugfs,
+ .link_sta_add_debugfs = iwl_mld_add_link_sta_debugfs,
+#endif
+ .mgd_protect_tdls_discover = iwl_mld_mac80211_mgd_protect_tdls_discover,
+ .join_ibss = iwl_mld_mac80211_join_ibss,
+ .leave_ibss = iwl_mld_mac80211_leave_ibss,
+ .tx_last_beacon = iwl_mld_mac80211_tx_last_beacon,
+ .prep_add_interface = iwl_mld_prep_add_interface,
+ .set_hw_timestamp = iwl_mld_set_hw_timestamp,
+ .start_pmsr = iwl_mld_start_pmsr,
+};
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/mac80211.h b/drivers/net/wireless/intel/iwlwifi/mld/mac80211.h
new file mode 100644
index 000000000000..aad04d7b2617
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/mld/mac80211.h
@@ -0,0 +1,13 @@
+/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
+/*
+ * Copyright (C) 2024 Intel Corporation
+ */
+#ifndef __iwl_mld_mac80211_h__
+#define __iwl_mld_mac80211_h__
+
+#include "mld.h"
+
+int iwl_mld_register_hw(struct iwl_mld *mld);
+void iwl_mld_recalc_multicast_filter(struct iwl_mld *mld);
+
+#endif /* __iwl_mld_mac80211_h__ */
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/mcc.c b/drivers/net/wireless/intel/iwlwifi/mld/mcc.c
new file mode 100644
index 000000000000..19cb562e7a73
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/mld/mcc.c
@@ -0,0 +1,329 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
+/*
+ * Copyright (C) 2024-2025 Intel Corporation
+ */
+
+#include <net/cfg80211.h>
+#include <net/mac80211.h>
+
+#include <fw/dbg.h>
+#include <iwl-nvm-parse.h>
+
+#include "mld.h"
+#include "hcmd.h"
+#include "mcc.h"
+
+/* It is the caller's responsibility to free the pointer returned here */
+static struct iwl_mcc_update_resp_v8 *
+iwl_mld_parse_mcc_update_resp_v8(const struct iwl_rx_packet *pkt)
+{
+ const struct iwl_mcc_update_resp_v8 *mcc_resp_v8 = (const void *)pkt->data;
+ int n_channels = __le32_to_cpu(mcc_resp_v8->n_channels);
+ struct iwl_mcc_update_resp_v8 *resp_cp;
+ int notif_len = struct_size(resp_cp, channels, n_channels);
+
+ if (iwl_rx_packet_payload_len(pkt) != notif_len)
+ return ERR_PTR(-EINVAL);
+
+ resp_cp = kmemdup(mcc_resp_v8, notif_len, GFP_KERNEL);
+ if (!resp_cp)
+ return ERR_PTR(-ENOMEM);
+
+ return resp_cp;
+}
+
+/* It is the caller's responsibility to free the pointer returned here */
+static struct iwl_mcc_update_resp_v8 *
+iwl_mld_parse_mcc_update_resp_v5_v6(const struct iwl_rx_packet *pkt)
+{
+ const struct iwl_mcc_update_resp_v4 *mcc_resp_v4 = (const void *)pkt->data;
+ struct iwl_mcc_update_resp_v8 *resp_cp;
+ int n_channels = __le32_to_cpu(mcc_resp_v4->n_channels);
+ int resp_len;
+
+ if (iwl_rx_packet_payload_len(pkt) !=
+ struct_size(mcc_resp_v4, channels, n_channels))
+ return ERR_PTR(-EINVAL);
+
+ resp_len = struct_size(resp_cp, channels, n_channels);
+ resp_cp = kzalloc(resp_len, GFP_KERNEL);
+ if (!resp_cp)
+ return ERR_PTR(-ENOMEM);
+
+ resp_cp->status = mcc_resp_v4->status;
+ resp_cp->mcc = mcc_resp_v4->mcc;
+ resp_cp->cap = cpu_to_le32(le16_to_cpu(mcc_resp_v4->cap));
+ resp_cp->source_id = mcc_resp_v4->source_id;
+ resp_cp->geo_info = mcc_resp_v4->geo_info;
+ resp_cp->n_channels = mcc_resp_v4->n_channels;
+ memcpy(resp_cp->channels, mcc_resp_v4->channels,
+ n_channels * sizeof(__le32));
+
+ return resp_cp;
+}
+
+/* It is the caller's responsibility to free the pointer returned here */
+static struct iwl_mcc_update_resp_v8 *
+iwl_mld_update_mcc(struct iwl_mld *mld, const char *alpha2,
+ enum iwl_mcc_source src_id)
+{
+ int resp_ver = iwl_fw_lookup_notif_ver(mld->fw, LONG_GROUP,
+ MCC_UPDATE_CMD, 0);
+ struct iwl_mcc_update_cmd mcc_update_cmd = {
+ .mcc = cpu_to_le16(alpha2[0] << 8 | alpha2[1]),
+ .source_id = (u8)src_id,
+ };
+ struct iwl_mcc_update_resp_v8 *resp_cp;
+ struct iwl_rx_packet *pkt;
+ struct iwl_host_cmd cmd = {
+ .id = MCC_UPDATE_CMD,
+ .flags = CMD_WANT_SKB,
+ .data = { &mcc_update_cmd },
+ .len[0] = sizeof(mcc_update_cmd),
+ };
+ int ret;
+ u16 mcc;
+
+ IWL_DEBUG_LAR(mld, "send MCC update to FW with '%c%c' src = %d\n",
+ alpha2[0], alpha2[1], src_id);
+
+ ret = iwl_mld_send_cmd(mld, &cmd);
+ if (ret)
+ return ERR_PTR(ret);
+
+ pkt = cmd.resp_pkt;
+
+ /* For Wifi-7 radios, we get version 8
+ * For Wifi-6E radios, we get version 6
+ * For Wifi-6 radios, we get version 5, but 5, 6, and 4 are compatible.
+ */
+ switch (resp_ver) {
+ case 5:
+ case 6:
+ resp_cp = iwl_mld_parse_mcc_update_resp_v5_v6(pkt);
+ break;
+ case 8:
+ resp_cp = iwl_mld_parse_mcc_update_resp_v8(pkt);
+ break;
+ default:
+ IWL_FW_CHECK_FAILED(mld, "Unknown MCC_UPDATE_CMD version %d\n", resp_ver);
+ resp_cp = ERR_PTR(-EINVAL);
+ }
+
+ if (IS_ERR(resp_cp))
+ goto exit;
+
+ mcc = le16_to_cpu(resp_cp->mcc);
+
+ IWL_FW_CHECK(mld, !mcc, "mcc can't be 0: %d\n", mcc);
+
+ IWL_DEBUG_LAR(mld,
+ "MCC response status: 0x%x. new MCC: 0x%x ('%c%c')\n",
+ le32_to_cpu(resp_cp->status), mcc, mcc >> 8, mcc & 0xff);
+
+exit:
+ iwl_free_resp(&cmd);
+ return resp_cp;
+}
+
+/* It is the caller's responsibility to free the pointer returned here */
+struct ieee80211_regdomain *
+iwl_mld_get_regdomain(struct iwl_mld *mld,
+ const char *alpha2,
+ enum iwl_mcc_source src_id,
+ bool *changed)
+{
+ struct ieee80211_regdomain *regd = NULL;
+ struct iwl_mcc_update_resp_v8 *resp;
+ u8 resp_ver = iwl_fw_lookup_notif_ver(mld->fw, IWL_ALWAYS_LONG_GROUP,
+ MCC_UPDATE_CMD, 0);
+
+ IWL_DEBUG_LAR(mld, "Getting regdomain data for %s from FW\n", alpha2);
+
+ lockdep_assert_wiphy(mld->wiphy);
+
+ resp = iwl_mld_update_mcc(mld, alpha2, src_id);
+ if (IS_ERR(resp)) {
+ IWL_DEBUG_LAR(mld, "Could not get update from FW %ld\n",
+ PTR_ERR(resp));
+ resp = NULL;
+ goto out;
+ }
+
+ if (changed) {
+ u32 status = le32_to_cpu(resp->status);
+
+ *changed = (status == MCC_RESP_NEW_CHAN_PROFILE ||
+ status == MCC_RESP_ILLEGAL);
+ }
+ IWL_DEBUG_LAR(mld, "MCC update response version: %d\n", resp_ver);
+
+ regd = iwl_parse_nvm_mcc_info(mld->trans,
+ __le32_to_cpu(resp->n_channels),
+ resp->channels,
+ __le16_to_cpu(resp->mcc),
+ __le16_to_cpu(resp->geo_info),
+ le32_to_cpu(resp->cap), resp_ver);
+
+ if (IS_ERR(regd)) {
+ IWL_DEBUG_LAR(mld, "Could not get parse update from FW %ld\n",
+ PTR_ERR(regd));
+ goto out;
+ }
+
+ IWL_DEBUG_LAR(mld, "setting alpha2 from FW to %s (0x%x, 0x%x) src=%d\n",
+ regd->alpha2, regd->alpha2[0],
+ regd->alpha2[1], resp->source_id);
+
+ mld->mcc_src = resp->source_id;
+
+ if (!iwl_puncturing_is_allowed_in_bios(mld->bios_enable_puncturing,
+ le16_to_cpu(resp->mcc)))
+ ieee80211_hw_set(mld->hw, DISALLOW_PUNCTURING);
+ else
+ __clear_bit(IEEE80211_HW_DISALLOW_PUNCTURING, mld->hw->flags);
+
+out:
+ kfree(resp);
+ return regd;
+}
+
+/* It is the caller's responsibility to free the pointer returned here */
+static struct ieee80211_regdomain *
+iwl_mld_get_current_regdomain(struct iwl_mld *mld,
+ bool *changed)
+{
+ return iwl_mld_get_regdomain(mld, "ZZ",
+ MCC_SOURCE_GET_CURRENT, changed);
+}
+
+void iwl_mld_update_changed_regdomain(struct iwl_mld *mld)
+{
+ struct ieee80211_regdomain *regd;
+ bool changed;
+
+ regd = iwl_mld_get_current_regdomain(mld, &changed);
+
+ if (IS_ERR_OR_NULL(regd))
+ return;
+
+ if (changed)
+ regulatory_set_wiphy_regd(mld->wiphy, regd);
+ kfree(regd);
+}
+
+static int iwl_mld_apply_last_mcc(struct iwl_mld *mld,
+ const char *alpha2)
+{
+ struct ieee80211_regdomain *regd;
+ u32 used_src;
+ bool changed;
+ int ret;
+
+ /* save the last source in case we overwrite it below */
+ used_src = mld->mcc_src;
+
+ /* Notify the firmware we support wifi location updates */
+ regd = iwl_mld_get_current_regdomain(mld, NULL);
+ if (!IS_ERR_OR_NULL(regd))
+ kfree(regd);
+
+ /* Now set our last stored MCC and source */
+ regd = iwl_mld_get_regdomain(mld, alpha2, used_src,
+ &changed);
+ if (IS_ERR_OR_NULL(regd))
+ return -EIO;
+
+ /* update cfg80211 if the regdomain was changed */
+ if (changed)
+ ret = regulatory_set_wiphy_regd_sync(mld->wiphy, regd);
+ else
+ ret = 0;
+
+ kfree(regd);
+ return ret;
+}
+
+int iwl_mld_init_mcc(struct iwl_mld *mld)
+{
+ const struct ieee80211_regdomain *r;
+ struct ieee80211_regdomain *regd;
+ char mcc[3];
+ int retval;
+
+ /* try to replay the last set MCC to FW */
+ r = wiphy_dereference(mld->wiphy, mld->wiphy->regd);
+
+ if (r)
+ return iwl_mld_apply_last_mcc(mld, r->alpha2);
+
+ regd = iwl_mld_get_current_regdomain(mld, NULL);
+ if (IS_ERR_OR_NULL(regd))
+ return -EIO;
+
+ if (!iwl_bios_get_mcc(&mld->fwrt, mcc)) {
+ kfree(regd);
+ regd = iwl_mld_get_regdomain(mld, mcc, MCC_SOURCE_BIOS, NULL);
+ if (IS_ERR_OR_NULL(regd))
+ return -EIO;
+ }
+
+ retval = regulatory_set_wiphy_regd_sync(mld->wiphy, regd);
+
+ kfree(regd);
+ return retval;
+}
+
+static void iwl_mld_find_assoc_vif_iterator(void *data, u8 *mac,
+ struct ieee80211_vif *vif)
+{
+ bool *assoc = data;
+
+ if (vif->type == NL80211_IFTYPE_STATION &&
+ vif->cfg.assoc)
+ *assoc = true;
+}
+
+static bool iwl_mld_is_a_vif_assoc(struct iwl_mld *mld)
+{
+ bool assoc = false;
+
+ ieee80211_iterate_active_interfaces_atomic(mld->hw,
+ IEEE80211_IFACE_ITER_NORMAL,
+ iwl_mld_find_assoc_vif_iterator,
+ &assoc);
+ return assoc;
+}
+
+void iwl_mld_handle_update_mcc(struct iwl_mld *mld, struct iwl_rx_packet *pkt)
+{
+ struct iwl_mcc_chub_notif *notif = (void *)pkt->data;
+ enum iwl_mcc_source src;
+ char mcc[3];
+ struct ieee80211_regdomain *regd;
+ bool changed;
+
+ lockdep_assert_wiphy(mld->wiphy);
+
+ if (iwl_mld_is_a_vif_assoc(mld) &&
+ notif->source_id == MCC_SOURCE_WIFI) {
+ IWL_DEBUG_LAR(mld, "Ignore mcc update while associated\n");
+ return;
+ }
+
+ mcc[0] = le16_to_cpu(notif->mcc) >> 8;
+ mcc[1] = le16_to_cpu(notif->mcc) & 0xff;
+ mcc[2] = '\0';
+ src = notif->source_id;
+
+ IWL_DEBUG_LAR(mld,
+ "RX: received chub update mcc cmd (mcc '%s' src %d)\n",
+ mcc, src);
+ regd = iwl_mld_get_regdomain(mld, mcc, src, &changed);
+ if (IS_ERR_OR_NULL(regd))
+ return;
+
+ if (changed)
+ regulatory_set_wiphy_regd(mld->hw->wiphy, regd);
+ kfree(regd);
+}
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/mcc.h b/drivers/net/wireless/intel/iwlwifi/mld/mcc.h
new file mode 100644
index 000000000000..2b31e5b5e2ed
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/mld/mcc.h
@@ -0,0 +1,17 @@
+/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
+/*
+ * Copyright (C) 2024 Intel Corporation
+ */
+#ifndef __iwl_mld_mcc_h__
+#define __iwl_mld_mcc_h__
+
+int iwl_mld_init_mcc(struct iwl_mld *mld);
+void iwl_mld_handle_update_mcc(struct iwl_mld *mld, struct iwl_rx_packet *pkt);
+void iwl_mld_update_changed_regdomain(struct iwl_mld *mld);
+struct ieee80211_regdomain *
+iwl_mld_get_regdomain(struct iwl_mld *mld,
+ const char *alpha2,
+ enum iwl_mcc_source src_id,
+ bool *changed);
+
+#endif /* __iwl_mld_mcc_h__ */
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/mld.c b/drivers/net/wireless/intel/iwlwifi/mld/mld.c
new file mode 100644
index 000000000000..8cdd960c5245
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/mld/mld.c
@@ -0,0 +1,748 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
+/*
+ * Copyright (C) 2024-2025 Intel Corporation
+ */
+#include <linux/rtnetlink.h>
+#include <net/mac80211.h>
+
+#include "fw/api/rx.h"
+#include "fw/api/datapath.h"
+#include "fw/api/commands.h"
+#include "fw/api/offload.h"
+#include "fw/api/coex.h"
+#include "fw/dbg.h"
+#include "fw/uefi.h"
+
+#include "mld.h"
+#include "mlo.h"
+#include "mac80211.h"
+#include "led.h"
+#include "scan.h"
+#include "tx.h"
+#include "sta.h"
+#include "regulatory.h"
+#include "thermal.h"
+#include "low_latency.h"
+#include "hcmd.h"
+#include "fw/api/location.h"
+
+#include "iwl-nvm-parse.h"
+
+#define DRV_DESCRIPTION "Intel(R) MLD wireless driver for Linux"
+MODULE_DESCRIPTION(DRV_DESCRIPTION);
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("IWLWIFI");
+
+static const struct iwl_op_mode_ops iwl_mld_ops;
+
+static int __init iwl_mld_init(void)
+{
+ int ret = iwl_opmode_register("iwlmld", &iwl_mld_ops);
+
+ if (ret)
+ pr_err("Unable to register MLD op_mode: %d\n", ret);
+
+ return ret;
+}
+module_init(iwl_mld_init);
+
+static void __exit iwl_mld_exit(void)
+{
+ iwl_opmode_deregister("iwlmld");
+}
+module_exit(iwl_mld_exit);
+
+static void iwl_mld_hw_set_regulatory(struct iwl_mld *mld)
+{
+ struct wiphy *wiphy = mld->wiphy;
+
+ wiphy->regulatory_flags |= REGULATORY_WIPHY_SELF_MANAGED;
+ wiphy->regulatory_flags |= REGULATORY_ENABLE_RELAX_NO_IR;
+}
+
+VISIBLE_IF_IWLWIFI_KUNIT
+void iwl_construct_mld(struct iwl_mld *mld, struct iwl_trans *trans,
+ const struct iwl_rf_cfg *cfg, const struct iwl_fw *fw,
+ struct ieee80211_hw *hw, struct dentry *dbgfs_dir)
+{
+ mld->dev = trans->dev;
+ mld->trans = trans;
+ mld->cfg = cfg;
+ mld->fw = fw;
+ mld->hw = hw;
+ mld->wiphy = hw->wiphy;
+ mld->debugfs_dir = dbgfs_dir;
+
+ iwl_notification_wait_init(&mld->notif_wait);
+
+ /* Setup async RX handling */
+ spin_lock_init(&mld->async_handlers_lock);
+ wiphy_work_init(&mld->async_handlers_wk,
+ iwl_mld_async_handlers_wk);
+
+ /* Dynamic Queue Allocation */
+ spin_lock_init(&mld->add_txqs_lock);
+ INIT_LIST_HEAD(&mld->txqs_to_add);
+ wiphy_work_init(&mld->add_txqs_wk, iwl_mld_add_txqs_wk);
+
+ /* Setup RX queues sync wait queue */
+ init_waitqueue_head(&mld->rxq_sync.waitq);
+}
+EXPORT_SYMBOL_IF_IWLWIFI_KUNIT(iwl_construct_mld);
+
+static void __acquires(&mld->wiphy->mtx)
+iwl_mld_fwrt_dump_start(void *ctx)
+{
+ struct iwl_mld *mld = ctx;
+
+ wiphy_lock(mld->wiphy);
+}
+
+static void __releases(&mld->wiphy->mtx)
+iwl_mld_fwrt_dump_end(void *ctx)
+{
+ struct iwl_mld *mld = ctx;
+
+ wiphy_unlock(mld->wiphy);
+}
+
+static bool iwl_mld_d3_debug_enable(void *ctx)
+{
+ return IWL_MLD_D3_DEBUG;
+}
+
+static int iwl_mld_fwrt_send_hcmd(void *ctx, struct iwl_host_cmd *host_cmd)
+{
+ struct iwl_mld *mld = (struct iwl_mld *)ctx;
+ int ret;
+
+ wiphy_lock(mld->wiphy);
+ ret = iwl_mld_send_cmd(mld, host_cmd);
+ wiphy_unlock(mld->wiphy);
+
+ return ret;
+}
+
+static const struct iwl_fw_runtime_ops iwl_mld_fwrt_ops = {
+ .dump_start = iwl_mld_fwrt_dump_start,
+ .dump_end = iwl_mld_fwrt_dump_end,
+ .send_hcmd = iwl_mld_fwrt_send_hcmd,
+ .d3_debug_enable = iwl_mld_d3_debug_enable,
+};
+
+static void
+iwl_mld_construct_fw_runtime(struct iwl_mld *mld, struct iwl_trans *trans,
+ const struct iwl_fw *fw,
+ struct dentry *debugfs_dir)
+{
+ iwl_fw_runtime_init(&mld->fwrt, trans, fw, &iwl_mld_fwrt_ops, mld,
+ NULL, NULL, debugfs_dir);
+
+ iwl_fw_set_current_image(&mld->fwrt, IWL_UCODE_REGULAR);
+}
+
+/* Please keep this array *SORTED* by hex value.
+ * Access is done through binary search
+ */
+static const struct iwl_hcmd_names iwl_mld_legacy_names[] = {
+ HCMD_NAME(UCODE_ALIVE_NTFY),
+ HCMD_NAME(INIT_COMPLETE_NOTIF),
+ HCMD_NAME(PHY_CONTEXT_CMD),
+ HCMD_NAME(SCAN_CFG_CMD),
+ HCMD_NAME(SCAN_REQ_UMAC),
+ HCMD_NAME(SCAN_ABORT_UMAC),
+ HCMD_NAME(SCAN_COMPLETE_UMAC),
+ HCMD_NAME(TX_CMD),
+ HCMD_NAME(TXPATH_FLUSH),
+ HCMD_NAME(LEDS_CMD),
+ HCMD_NAME(WNM_80211V_TIMING_MEASUREMENT_NOTIFICATION),
+ HCMD_NAME(WNM_80211V_TIMING_MEASUREMENT_CONFIRM_NOTIFICATION),
+ HCMD_NAME(SCAN_OFFLOAD_UPDATE_PROFILES_CMD),
+ HCMD_NAME(POWER_TABLE_CMD),
+ HCMD_NAME(PSM_UAPSD_AP_MISBEHAVING_NOTIFICATION),
+ HCMD_NAME(BEACON_NOTIFICATION),
+ HCMD_NAME(BEACON_TEMPLATE_CMD),
+ HCMD_NAME(TX_ANT_CONFIGURATION_CMD),
+ HCMD_NAME(REDUCE_TX_POWER_CMD),
+ HCMD_NAME(MISSED_BEACONS_NOTIFICATION),
+ HCMD_NAME(MAC_PM_POWER_TABLE),
+ HCMD_NAME(MFUART_LOAD_NOTIFICATION),
+ HCMD_NAME(RSS_CONFIG_CMD),
+ HCMD_NAME(SCAN_ITERATION_COMPLETE_UMAC),
+ HCMD_NAME(REPLY_RX_MPDU_CMD),
+ HCMD_NAME(BA_NOTIF),
+ HCMD_NAME(MCC_UPDATE_CMD),
+ HCMD_NAME(MCC_CHUB_UPDATE_CMD),
+ HCMD_NAME(MCAST_FILTER_CMD),
+ HCMD_NAME(REPLY_BEACON_FILTERING_CMD),
+ HCMD_NAME(PROT_OFFLOAD_CONFIG_CMD),
+ HCMD_NAME(MATCH_FOUND_NOTIFICATION),
+ HCMD_NAME(WOWLAN_PATTERNS),
+ HCMD_NAME(WOWLAN_CONFIGURATION),
+ HCMD_NAME(WOWLAN_TSC_RSC_PARAM),
+ HCMD_NAME(WOWLAN_KEK_KCK_MATERIAL),
+ HCMD_NAME(DEBUG_HOST_COMMAND),
+ HCMD_NAME(LDBG_CONFIG_CMD),
+};
+
+/* Please keep this array *SORTED* by hex value.
+ * Access is done through binary search
+ */
+static const struct iwl_hcmd_names iwl_mld_system_names[] = {
+ HCMD_NAME(SHARED_MEM_CFG_CMD),
+ HCMD_NAME(SOC_CONFIGURATION_CMD),
+ HCMD_NAME(INIT_EXTENDED_CFG_CMD),
+ HCMD_NAME(FW_ERROR_RECOVERY_CMD),
+ HCMD_NAME(RFI_CONFIG_CMD),
+ HCMD_NAME(RFI_GET_FREQ_TABLE_CMD),
+ HCMD_NAME(SYSTEM_STATISTICS_CMD),
+ HCMD_NAME(SYSTEM_STATISTICS_END_NOTIF),
+};
+
+/* Please keep this array *SORTED* by hex value.
+ * Access is done through binary search
+ */
+static const struct iwl_hcmd_names iwl_mld_reg_and_nvm_names[] = {
+ HCMD_NAME(LARI_CONFIG_CHANGE),
+ HCMD_NAME(NVM_GET_INFO),
+ HCMD_NAME(TAS_CONFIG),
+ HCMD_NAME(SAR_OFFSET_MAPPING_TABLE_CMD),
+ HCMD_NAME(MCC_ALLOWED_AP_TYPE_CMD),
+};
+
+/* Please keep this array *SORTED* by hex value.
+ * Access is done through binary search
+ */
+static const struct iwl_hcmd_names iwl_mld_debug_names[] = {
+ HCMD_NAME(HOST_EVENT_CFG),
+ HCMD_NAME(DBGC_SUSPEND_RESUME),
+};
+
+/* Please keep this array *SORTED* by hex value.
+ * Access is done through binary search
+ */
+static const struct iwl_hcmd_names iwl_mld_mac_conf_names[] = {
+ HCMD_NAME(LOW_LATENCY_CMD),
+ HCMD_NAME(SESSION_PROTECTION_CMD),
+ HCMD_NAME(MAC_CONFIG_CMD),
+ HCMD_NAME(LINK_CONFIG_CMD),
+ HCMD_NAME(STA_CONFIG_CMD),
+ HCMD_NAME(AUX_STA_CMD),
+ HCMD_NAME(STA_REMOVE_CMD),
+ HCMD_NAME(ROC_CMD),
+ HCMD_NAME(MISSED_BEACONS_NOTIF),
+ HCMD_NAME(EMLSR_TRANS_FAIL_NOTIF),
+ HCMD_NAME(ROC_NOTIF),
+ HCMD_NAME(CHANNEL_SWITCH_ERROR_NOTIF),
+ HCMD_NAME(SESSION_PROTECTION_NOTIF),
+ HCMD_NAME(PROBE_RESPONSE_DATA_NOTIF),
+ HCMD_NAME(CHANNEL_SWITCH_START_NOTIF),
+};
+
+/* Please keep this array *SORTED* by hex value.
+ * Access is done through binary search
+ */
+static const struct iwl_hcmd_names iwl_mld_data_path_names[] = {
+ HCMD_NAME(TRIGGER_RX_QUEUES_NOTIF_CMD),
+ HCMD_NAME(WNM_PLATFORM_PTM_REQUEST_CMD),
+ HCMD_NAME(WNM_80211V_TIMING_MEASUREMENT_CONFIG_CMD),
+ HCMD_NAME(RFH_QUEUE_CONFIG_CMD),
+ HCMD_NAME(TLC_MNG_CONFIG_CMD),
+ HCMD_NAME(RX_BAID_ALLOCATION_CONFIG_CMD),
+ HCMD_NAME(SCD_QUEUE_CONFIG_CMD),
+ HCMD_NAME(OMI_SEND_STATUS_NOTIF),
+ HCMD_NAME(ESR_MODE_NOTIF),
+ HCMD_NAME(MONITOR_NOTIF),
+ HCMD_NAME(TLC_MNG_UPDATE_NOTIF),
+ HCMD_NAME(MU_GROUP_MGMT_NOTIF),
+};
+
+/* Please keep this array *SORTED* by hex value.
+ * Access is done through binary search
+ */
+static const struct iwl_hcmd_names iwl_mld_location_names[] = {
+ HCMD_NAME(TOF_RANGE_REQ_CMD),
+ HCMD_NAME(TOF_RANGE_RESPONSE_NOTIF),
+};
+
+/* Please keep this array *SORTED* by hex value.
+ * Access is done through binary search
+ */
+static const struct iwl_hcmd_names iwl_mld_phy_names[] = {
+ HCMD_NAME(CMD_DTS_MEASUREMENT_TRIGGER_WIDE),
+ HCMD_NAME(CTDP_CONFIG_CMD),
+ HCMD_NAME(TEMP_REPORTING_THRESHOLDS_CMD),
+ HCMD_NAME(PER_CHAIN_LIMIT_OFFSET_CMD),
+ HCMD_NAME(CT_KILL_NOTIFICATION),
+ HCMD_NAME(DTS_MEASUREMENT_NOTIF_WIDE),
+};
+
+/* Please keep this array *SORTED* by hex value.
+ * Access is done through binary search
+ */
+static const struct iwl_hcmd_names iwl_mld_statistics_names[] = {
+ HCMD_NAME(STATISTICS_OPER_NOTIF),
+ HCMD_NAME(STATISTICS_OPER_PART1_NOTIF),
+};
+
+/* Please keep this array *SORTED* by hex value.
+ * Access is done through binary search
+ */
+static const struct iwl_hcmd_names iwl_mld_prot_offload_names[] = {
+ HCMD_NAME(WOWLAN_WAKE_PKT_NOTIFICATION),
+ HCMD_NAME(WOWLAN_INFO_NOTIFICATION),
+ HCMD_NAME(D3_END_NOTIFICATION),
+};
+
+/* Please keep this array *SORTED* by hex value.
+ * Access is done through binary search
+ */
+static const struct iwl_hcmd_names iwl_mld_coex_names[] = {
+ HCMD_NAME(PROFILE_NOTIF),
+};
+
+VISIBLE_IF_IWLWIFI_KUNIT
+const struct iwl_hcmd_arr iwl_mld_groups[] = {
+ [LEGACY_GROUP] = HCMD_ARR(iwl_mld_legacy_names),
+ [LONG_GROUP] = HCMD_ARR(iwl_mld_legacy_names),
+ [SYSTEM_GROUP] = HCMD_ARR(iwl_mld_system_names),
+ [MAC_CONF_GROUP] = HCMD_ARR(iwl_mld_mac_conf_names),
+ [DATA_PATH_GROUP] = HCMD_ARR(iwl_mld_data_path_names),
+ [LOCATION_GROUP] = HCMD_ARR(iwl_mld_location_names),
+ [REGULATORY_AND_NVM_GROUP] = HCMD_ARR(iwl_mld_reg_and_nvm_names),
+ [DEBUG_GROUP] = HCMD_ARR(iwl_mld_debug_names),
+ [PHY_OPS_GROUP] = HCMD_ARR(iwl_mld_phy_names),
+ [STATISTICS_GROUP] = HCMD_ARR(iwl_mld_statistics_names),
+ [PROT_OFFLOAD_GROUP] = HCMD_ARR(iwl_mld_prot_offload_names),
+ [BT_COEX_GROUP] = HCMD_ARR(iwl_mld_coex_names),
+};
+EXPORT_SYMBOL_IF_IWLWIFI_KUNIT(iwl_mld_groups);
+
+#if IS_ENABLED(CONFIG_IWLWIFI_KUNIT_TESTS)
+const unsigned int global_iwl_mld_goups_size = ARRAY_SIZE(iwl_mld_groups);
+EXPORT_SYMBOL_IF_IWLWIFI_KUNIT(global_iwl_mld_goups_size);
+#endif
+
+static void
+iwl_mld_configure_trans(struct iwl_op_mode *op_mode)
+{
+ struct iwl_mld *mld = IWL_OP_MODE_GET_MLD(op_mode);
+ static const u8 no_reclaim_cmds[] = {TX_CMD};
+ struct iwl_trans *trans = mld->trans;
+ u32 eckv_value;
+
+ iwl_bios_setup_step(trans, &mld->fwrt);
+ iwl_uefi_get_step_table(trans);
+
+ if (iwl_bios_get_eckv(&mld->fwrt, &eckv_value))
+ IWL_DEBUG_RADIO(mld, "ECKV table doesn't exist in BIOS\n");
+ else
+ trans->conf.ext_32khz_clock_valid = !!eckv_value;
+
+ trans->conf.rx_buf_size = iwl_amsdu_size_to_rxb_size();
+ trans->conf.command_groups = iwl_mld_groups;
+ trans->conf.command_groups_size = ARRAY_SIZE(iwl_mld_groups);
+ trans->conf.fw_reset_handshake = true;
+ trans->conf.queue_alloc_cmd_ver =
+ iwl_fw_lookup_cmd_ver(mld->fw, WIDE_ID(DATA_PATH_GROUP,
+ SCD_QUEUE_CONFIG_CMD),
+ 0);
+ trans->conf.cb_data_offs = offsetof(struct ieee80211_tx_info,
+ driver_data[2]);
+ BUILD_BUG_ON(sizeof(no_reclaim_cmds) >
+ sizeof(trans->conf.no_reclaim_cmds));
+ memcpy(trans->conf.no_reclaim_cmds, no_reclaim_cmds,
+ sizeof(no_reclaim_cmds));
+ trans->conf.n_no_reclaim_cmds = ARRAY_SIZE(no_reclaim_cmds);
+
+ trans->conf.rx_mpdu_cmd = REPLY_RX_MPDU_CMD;
+ trans->conf.rx_mpdu_cmd_hdr_size = sizeof(struct iwl_rx_mpdu_res_start);
+ trans->conf.wide_cmd_header = true;
+
+ iwl_trans_op_mode_enter(trans, op_mode);
+}
+
+/*
+ *****************************************************
+ * op mode ops functions
+ *****************************************************
+ */
+
+#define NUM_FW_LOAD_RETRIES 3
+static struct iwl_op_mode *
+iwl_op_mode_mld_start(struct iwl_trans *trans, const struct iwl_rf_cfg *cfg,
+ const struct iwl_fw *fw, struct dentry *dbgfs_dir)
+{
+ struct ieee80211_hw *hw;
+ struct iwl_op_mode *op_mode;
+ struct iwl_mld *mld;
+ int ret;
+
+ /* Allocate and initialize a new hardware device */
+ hw = ieee80211_alloc_hw(sizeof(struct iwl_op_mode) +
+ sizeof(struct iwl_mld),
+ &iwl_mld_hw_ops);
+ if (!hw)
+ return ERR_PTR(-ENOMEM);
+
+ op_mode = hw->priv;
+
+ op_mode->ops = &iwl_mld_ops;
+
+ mld = IWL_OP_MODE_GET_MLD(op_mode);
+
+ iwl_construct_mld(mld, trans, cfg, fw, hw, dbgfs_dir);
+
+ /* we'll verify later it matches between commands */
+ mld->fw_rates_ver_3 = iwl_fw_lookup_cmd_ver(mld->fw, TX_CMD, 0) >= 11;
+
+ iwl_mld_construct_fw_runtime(mld, trans, fw, dbgfs_dir);
+
+ iwl_mld_get_bios_tables(mld);
+ iwl_uefi_get_sgom_table(trans, &mld->fwrt);
+ mld->bios_enable_puncturing = iwl_uefi_get_puncturing(&mld->fwrt);
+
+ iwl_mld_hw_set_regulatory(mld);
+
+ /* Configure transport layer with the opmode specific params */
+ iwl_mld_configure_trans(op_mode);
+
+ /* needed for regulatory init */
+ rtnl_lock();
+ /* Needed for sending commands */
+ wiphy_lock(mld->wiphy);
+
+ for (int i = 0; i < NUM_FW_LOAD_RETRIES; i++) {
+ ret = iwl_mld_load_fw(mld);
+ if (!ret)
+ break;
+ }
+
+ if (!ret) {
+ mld->nvm_data = iwl_get_nvm(mld->trans, mld->fw, 0, 0);
+ if (IS_ERR(mld->nvm_data)) {
+ IWL_ERR(mld, "Failed to read NVM: %d\n", ret);
+ ret = PTR_ERR(mld->nvm_data);
+ }
+ }
+
+ if (ret) {
+ wiphy_unlock(mld->wiphy);
+ rtnl_unlock();
+ goto err;
+ }
+
+ /* We are about to stop the FW. Notifications may require an
+ * operational FW, so handle them all here before we stop.
+ */
+ wiphy_work_flush(mld->wiphy, &mld->async_handlers_wk);
+
+ iwl_mld_stop_fw(mld);
+
+ wiphy_unlock(mld->wiphy);
+ rtnl_unlock();
+
+ ret = iwl_mld_leds_init(mld);
+ if (ret)
+ goto free_nvm;
+
+ ret = iwl_mld_alloc_scan_cmd(mld);
+ if (ret)
+ goto leds_exit;
+
+ ret = iwl_mld_low_latency_init(mld);
+ if (ret)
+ goto free_scan_cmd;
+
+ ret = iwl_mld_register_hw(mld);
+ if (ret)
+ goto low_latency_free;
+
+ iwl_mld_toggle_tx_ant(mld, &mld->mgmt_tx_ant);
+
+ iwl_mld_add_debugfs_files(mld, dbgfs_dir);
+ iwl_mld_thermal_initialize(mld);
+
+ iwl_mld_ptp_init(mld);
+
+ return op_mode;
+
+low_latency_free:
+ iwl_mld_low_latency_free(mld);
+free_scan_cmd:
+ kfree(mld->scan.cmd);
+leds_exit:
+ iwl_mld_leds_exit(mld);
+free_nvm:
+ kfree(mld->nvm_data);
+err:
+ iwl_trans_op_mode_leave(mld->trans);
+ ieee80211_free_hw(mld->hw);
+ return ERR_PTR(ret);
+}
+
+static void
+iwl_op_mode_mld_stop(struct iwl_op_mode *op_mode)
+{
+ struct iwl_mld *mld = IWL_OP_MODE_GET_MLD(op_mode);
+
+ iwl_mld_ptp_remove(mld);
+ iwl_mld_leds_exit(mld);
+
+ iwl_mld_thermal_exit(mld);
+
+ wiphy_lock(mld->wiphy);
+ iwl_mld_low_latency_stop(mld);
+ iwl_mld_deinit_time_sync(mld);
+ wiphy_unlock(mld->wiphy);
+
+ ieee80211_unregister_hw(mld->hw);
+
+ iwl_fw_runtime_free(&mld->fwrt);
+ iwl_mld_low_latency_free(mld);
+
+ iwl_trans_op_mode_leave(mld->trans);
+
+ kfree(mld->nvm_data);
+ kfree(mld->scan.cmd);
+ kfree(mld->error_recovery_buf);
+ kfree(mld->mcast_filter_cmd);
+
+ ieee80211_free_hw(mld->hw);
+}
+
+static void iwl_mld_queue_state_change(struct iwl_op_mode *op_mode,
+ int hw_queue, bool queue_full)
+{
+ struct iwl_mld *mld = IWL_OP_MODE_GET_MLD(op_mode);
+ struct ieee80211_txq *txq;
+ struct iwl_mld_sta *mld_sta;
+ struct iwl_mld_txq *mld_txq;
+
+ rcu_read_lock();
+
+ txq = rcu_dereference(mld->fw_id_to_txq[hw_queue]);
+ if (!txq) {
+ rcu_read_unlock();
+
+ if (queue_full) {
+ /* An internal queue is not expected to become full */
+ IWL_WARN(mld,
+ "Internal hw_queue %d is full! stopping all queues\n",
+ hw_queue);
+ /* Stop all queues, as an internal queue is not
+ * mapped to a mac80211 one
+ */
+ ieee80211_stop_queues(mld->hw);
+ } else {
+ ieee80211_wake_queues(mld->hw);
+ }
+
+ return;
+ }
+
+ mld_txq = iwl_mld_txq_from_mac80211(txq);
+ mld_sta = txq->sta ? iwl_mld_sta_from_mac80211(txq->sta) : NULL;
+
+ mld_txq->status.stop_full = queue_full;
+
+ if (!queue_full && mld_sta &&
+ mld_sta->sta_state != IEEE80211_STA_NOTEXIST) {
+ local_bh_disable();
+ iwl_mld_tx_from_txq(mld, txq);
+ local_bh_enable();
+ }
+
+ rcu_read_unlock();
+}
+
+static void
+iwl_mld_queue_full(struct iwl_op_mode *op_mode, int hw_queue)
+{
+ iwl_mld_queue_state_change(op_mode, hw_queue, true);
+}
+
+static void
+iwl_mld_queue_not_full(struct iwl_op_mode *op_mode, int hw_queue)
+{
+ iwl_mld_queue_state_change(op_mode, hw_queue, false);
+}
+
+static bool
+iwl_mld_set_hw_rfkill_state(struct iwl_op_mode *op_mode, bool state)
+{
+ struct iwl_mld *mld = IWL_OP_MODE_GET_MLD(op_mode);
+
+ iwl_mld_set_hwkill(mld, state);
+
+ return false;
+}
+
+static void
+iwl_mld_free_skb(struct iwl_op_mode *op_mode, struct sk_buff *skb)
+{
+ struct iwl_mld *mld = IWL_OP_MODE_GET_MLD(op_mode);
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+
+ iwl_trans_free_tx_cmd(mld->trans, info->driver_data[1]);
+ ieee80211_free_txskb(mld->hw, skb);
+}
+
+static void iwl_mld_read_error_recovery_buffer(struct iwl_mld *mld)
+{
+ u32 src_size = mld->fw->ucode_capa.error_log_size;
+ u32 src_addr = mld->fw->ucode_capa.error_log_addr;
+ u8 *recovery_buf;
+ int ret;
+
+ /* no recovery buffer size defined in a TLV */
+ if (!src_size)
+ return;
+
+ recovery_buf = kzalloc(src_size, GFP_ATOMIC);
+ if (!recovery_buf)
+ return;
+
+ ret = iwl_trans_read_mem_bytes(mld->trans, src_addr,
+ recovery_buf, src_size);
+ if (ret) {
+ IWL_ERR(mld, "Failed to read error recovery buffer (%d)\n",
+ ret);
+ kfree(recovery_buf);
+ return;
+ }
+
+ mld->error_recovery_buf = recovery_buf;
+}
+
+static void iwl_mld_restart_nic(struct iwl_mld *mld)
+{
+ iwl_mld_read_error_recovery_buffer(mld);
+
+ mld->fwrt.trans->dbg.restart_required = false;
+
+ ieee80211_restart_hw(mld->hw);
+}
+
+static void
+iwl_mld_nic_error(struct iwl_op_mode *op_mode,
+ enum iwl_fw_error_type type)
+{
+ struct iwl_mld *mld = IWL_OP_MODE_GET_MLD(op_mode);
+ bool trans_dead = test_bit(STATUS_TRANS_DEAD, &mld->trans->status);
+
+ if (type == IWL_ERR_TYPE_CMD_QUEUE_FULL)
+ IWL_ERR(mld, "Command queue full!\n");
+ else if (!trans_dead && !mld->fw_status.do_not_dump_once)
+ iwl_fwrt_dump_error_logs(&mld->fwrt);
+
+ mld->fw_status.do_not_dump_once = false;
+
+ /* It is necessary to abort any os scan here because mac80211 requires
+ * having the scan cleared before restarting.
+ * We'll reset the scan_status to NONE in restart cleanup in
+ * the next drv_start() call from mac80211. If ieee80211_hw_restart
+ * isn't called scan status will stay busy.
+ */
+ iwl_mld_report_scan_aborted(mld);
+
+ /*
+ * This should be first thing before trying to collect any
+ * data to avoid endless loops if any HW error happens while
+ * collecting debug data.
+ * It might not actually be true that we'll restart, but the
+ * setting doesn't matter if we're going to be unbound either.
+ */
+ if (type != IWL_ERR_TYPE_RESET_HS_TIMEOUT)
+ mld->fw_status.in_hw_restart = true;
+}
+
+static void iwl_mld_dump_error(struct iwl_op_mode *op_mode,
+ struct iwl_fw_error_dump_mode *mode)
+{
+ struct iwl_mld *mld = IWL_OP_MODE_GET_MLD(op_mode);
+
+ /* if we come in from opmode we have the mutex held */
+ if (mode->context == IWL_ERR_CONTEXT_FROM_OPMODE) {
+ lockdep_assert_wiphy(mld->wiphy);
+ iwl_fw_error_collect(&mld->fwrt);
+ } else {
+ wiphy_lock(mld->wiphy);
+ if (mode->context != IWL_ERR_CONTEXT_ABORT)
+ iwl_fw_error_collect(&mld->fwrt);
+ wiphy_unlock(mld->wiphy);
+ }
+}
+
+static bool iwl_mld_sw_reset(struct iwl_op_mode *op_mode,
+ enum iwl_fw_error_type type)
+{
+ struct iwl_mld *mld = IWL_OP_MODE_GET_MLD(op_mode);
+
+ /* SW reset can happen for TOP error w/o NIC error, so
+ * also abort scan here and set in_hw_restart, when we
+ * had a NIC error both were already done.
+ */
+ iwl_mld_report_scan_aborted(mld);
+ mld->fw_status.in_hw_restart = true;
+
+ /* Do restart only in the following conditions are met:
+ * - we consider the FW as running
+ * - The trigger that brought us here is defined as one that requires
+ * a restart (in the debug TLVs)
+ */
+ if (!mld->fw_status.running || !mld->fwrt.trans->dbg.restart_required)
+ return false;
+
+ iwl_mld_restart_nic(mld);
+ return true;
+}
+
+static void
+iwl_mld_time_point(struct iwl_op_mode *op_mode,
+ enum iwl_fw_ini_time_point tp_id,
+ union iwl_dbg_tlv_tp_data *tp_data)
+{
+ struct iwl_mld *mld = IWL_OP_MODE_GET_MLD(op_mode);
+
+ iwl_dbg_tlv_time_point(&mld->fwrt, tp_id, tp_data);
+}
+
+#ifdef CONFIG_PM_SLEEP
+static void iwl_mld_device_powered_off(struct iwl_op_mode *op_mode)
+{
+ struct iwl_mld *mld = IWL_OP_MODE_GET_MLD(op_mode);
+
+ wiphy_lock(mld->wiphy);
+ iwl_mld_stop_fw(mld);
+ mld->fw_status.in_d3 = false;
+ wiphy_unlock(mld->wiphy);
+}
+#else
+static void iwl_mld_device_powered_off(struct iwl_op_mode *op_mode)
+{}
+#endif
+
+static const struct iwl_op_mode_ops iwl_mld_ops = {
+ .start = iwl_op_mode_mld_start,
+ .stop = iwl_op_mode_mld_stop,
+ .rx = iwl_mld_rx,
+ .rx_rss = iwl_mld_rx_rss,
+ .queue_full = iwl_mld_queue_full,
+ .queue_not_full = iwl_mld_queue_not_full,
+ .hw_rf_kill = iwl_mld_set_hw_rfkill_state,
+ .free_skb = iwl_mld_free_skb,
+ .nic_error = iwl_mld_nic_error,
+ .dump_error = iwl_mld_dump_error,
+ .sw_reset = iwl_mld_sw_reset,
+ .time_point = iwl_mld_time_point,
+ .device_powered_off = pm_sleep_ptr(iwl_mld_device_powered_off),
+};
+
+struct iwl_mld_mod_params iwlmld_mod_params = {
+ .power_scheme = IWL_POWER_SCHEME_BPS,
+};
+
+module_param_named(power_scheme, iwlmld_mod_params.power_scheme, int, 0444);
+MODULE_PARM_DESC(power_scheme,
+ "power management scheme: 1-active, 2-balanced, default: 2");
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/mld.h b/drivers/net/wireless/intel/iwlwifi/mld/mld.h
new file mode 100644
index 000000000000..1a2c44f44eff
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/mld/mld.h
@@ -0,0 +1,586 @@
+/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
+/*
+ * Copyright (C) 2024-2025 Intel Corporation
+ */
+#ifndef __iwl_mld_h__
+#define __iwl_mld_h__
+
+#include <linux/leds.h>
+#include <net/mac80211.h>
+
+#include "iwl-trans.h"
+#include "iwl-op-mode.h"
+#include "fw/runtime.h"
+#include "fw/notif-wait.h"
+#include "fw/api/commands.h"
+#include "fw/api/scan.h"
+#include "fw/api/mac-cfg.h"
+#include "fw/api/mac.h"
+#include "fw/api/phy-ctxt.h"
+#include "fw/api/datapath.h"
+#include "fw/api/rx.h"
+#include "fw/api/rs.h"
+#include "fw/api/context.h"
+#include "fw/api/coex.h"
+#include "fw/api/location.h"
+
+#include "fw/dbg.h"
+
+#include "notif.h"
+#include "scan.h"
+#include "rx.h"
+#include "thermal.h"
+#include "low_latency.h"
+#include "constants.h"
+#include "ptp.h"
+#include "time_sync.h"
+#include "ftm-initiator.h"
+
+/**
+ * DOC: Introduction
+ *
+ * iwlmld is an operation mode (a.k.a. op_mode) for Intel wireless devices.
+ * It is used for devices that ship after 2024 which typically support
+ * the WiFi-7 features. MLD stands for multi-link device. Note that there are
+ * devices that do not support WiFi-7 or even WiFi 6E and yet use iwlmld, but
+ * the firmware APIs used in this driver are WiFi-7 compatible.
+ *
+ * In the architecture of iwlwifi, an op_mode is a layer that translates
+ * mac80211's APIs into commands for the firmware and, of course, notifications
+ * from the firmware to mac80211's APIs. An op_mode must implement the
+ * interface defined in iwl-op-mode.h to interact with the transport layer
+ * which allows to send and receive data to the device, start the hardware,
+ * etc...
+ */
+
+/**
+ * DOC: Locking policy
+ *
+ * iwlmld has a very simple locking policy: it doesn't have any mutexes. It
+ * relies on cfg80211's wiphy->mtx and takes the lock when needed. All the
+ * control flows originating from mac80211 already acquired the lock, so that
+ * part is trivial, but also notifications that are received from the firmware
+ * and handled asynchronously are handled only after having taken the lock.
+ * This is described in notif.c.
+ * There are spin_locks needed to synchronize with the data path, around the
+ * allocation of the queues, for example.
+ */
+
+/**
+ * DOC: Debugfs
+ *
+ * iwlmld adds its share of debugfs hooks and its handlers are synchronized
+ * with the wiphy_lock using wiphy_locked_debugfs. This avoids races against
+ * resources deletion while the debugfs hook is being used.
+ */
+
+/**
+ * DOC: Main resources
+ *
+ * iwlmld is designed with the life cycle of the resource in mind. The
+ * resources are:
+ *
+ * - struct iwl_mld (matches mac80211's struct ieee80211_hw)
+ *
+ * - struct iwl_mld_vif (matches macu80211's struct ieee80211_vif)
+ * iwl_mld_vif contains an array of pointers to struct iwl_mld_link
+ * which describe the links for this vif.
+ *
+ * - struct iwl_mld_sta (matches mac80211's struct ieee80211_sta)
+ * iwl_mld_sta contains an array of points to struct iwl_mld_link_sta
+ * which describes the link stations for this station
+ *
+ * Each object has properties that can survive a firmware reset or not.
+ * Asynchronous firmware notifications can declare themselves as dependent on a
+ * certain instance of those resources and that means that the notifications
+ * will be cancelled once the instance is destroyed.
+ */
+
+#define IWL_MLD_MAX_ADDRESSES 5
+
+/**
+ * struct iwl_mld - MLD op mode
+ *
+ * @fw_id_to_bss_conf: maps a fw id of a link to the corresponding
+ * ieee80211_bss_conf.
+ * @fw_id_to_vif: maps a fw id of a MAC context to the corresponding
+ * ieee80211_vif. Mapping is valid only when the MAC exists in the fw.
+ * @fw_id_to_txq: maps a fw id of a txq to the corresponding
+ * ieee80211_txq.
+ * @used_phy_ids: a bitmap of the phy IDs used. If a bit is set, it means
+ * that the index of this bit is already used as a PHY id.
+ * @num_igtks: the number if iGTKs that were sent to the FW.
+ * @monitor: monitor related data
+ * @monitor.on: does a monitor vif exist (singleton hence bool)
+ * @monitor.ampdu_ref: the id of the A-MPDU for sniffer
+ * @monitor.ampdu_toggle: the state of the previous packet to track A-MPDU
+ * @monitor.cur_aid: current association id tracked by the sniffer
+ * @monitor.cur_bssid: current bssid tracked by the sniffer
+ * @monitor.ptp_time: set the Rx mactime using the device's PTP clock time
+ * @monitor.p80: primary channel position relative to he whole bandwidth, in
+ * steps of 80 MHz
+ * @fw_id_to_link_sta: maps a fw id of a sta to the corresponding
+ * ieee80211_link_sta. This is not cleaned up on restart since we want to
+ * preserve the fw sta ids during a restart (for SN/PN restoring).
+ * FW ids of internal stations will be mapped to ERR_PTR, and will be
+ * re-allocated during a restart, so make sure to free it in restart
+ * cleanup using iwl_mld_free_internal_sta
+ * @netdetect: indicates the FW is in suspend mode with netdetect configured
+ * @p2p_device_vif: points to the p2p device vif if exists
+ * @dev: pointer to device struct. For printing purposes
+ * @trans: pointer to the transport layer
+ * @cfg: pointer to the device configuration
+ * @fw: a pointer to the fw object
+ * @hw: pointer to the hw object.
+ * @wiphy: a pointer to the wiphy struct, for easier access to it.
+ * @nvm_data: pointer to the nvm_data that includes all our capabilities
+ * @fwrt: fw runtime data
+ * @debugfs_dir: debugfs directory
+ * @notif_wait: notification wait related data.
+ * @async_handlers_list: a list of all async RX handlers. When a notifciation
+ * with an async handler is received, it is added to this list.
+ * When &async_handlers_wk runs - it runs these handlers one by one.
+ * @async_handlers_lock: a lock for &async_handlers_list. Sync
+ * &async_handlers_wk and RX notifcation path.
+ * @async_handlers_wk: A work to run all async RX handlers from
+ * &async_handlers_list.
+ * @ct_kill_exit_wk: worker to exit thermal kill
+ * @fw_status: bitmap of fw status bits
+ * @running: true if the firmware is running
+ * @do_not_dump_once: true if firmware dump must be prevented once
+ * @in_d3: indicates FW is in suspend mode and should be resumed
+ * @in_hw_restart: indicates that we are currently in restart flow.
+ * rather than restarted. Should be unset upon restart.
+ * @radio_kill: bitmap of radio kill status
+ * @radio_kill.hw: radio is killed by hw switch
+ * @radio_kill.ct: radio is killed because the device it too hot
+ * @power_budget_mw: maximum cTDP power budget as defined for this system and
+ * device
+ * @addresses: device MAC addresses.
+ * @scan: instance of the scan object
+ * @wowlan: WoWLAN support data.
+ * @led: the led device
+ * @mcc_src: the source id of the MCC, comes from the firmware
+ * @bios_enable_puncturing: is puncturing enabled by bios
+ * @fw_id_to_ba: maps a fw (BA) id to a corresponding Block Ack session data.
+ * @num_rx_ba_sessions: tracks the number of active Rx Block Ack (BA) sessions.
+ * the driver ensures that new BA sessions are blocked once the maximum
+ * supported by the firmware is reached, preventing firmware asserts.
+ * @rxq_sync: manages RX queue sync state
+ * @txqs_to_add: a list of &ieee80211_txq's to allocate in &add_txqs_wk
+ * @add_txqs_wk: a worker to allocate txqs.
+ * @add_txqs_lock: to lock the &txqs_to_add list.
+ * @error_recovery_buf: pointer to the recovery buffer that will be read
+ * from firmware upon fw/hw error and sent back to the firmware in
+ * reconfig flow (after NIC reset).
+ * @mcast_filter_cmd: pointer to the multicast filter command.
+ * @mgmt_tx_ant: stores the last TX antenna index; used for setting
+ * TX rate_n_flags for non-STA mgmt frames (toggles on every TX failure).
+ * @fw_rates_ver_3: FW rates are in version 3
+ * @low_latency: low-latency manager.
+ * @tzone: thermal zone device's data
+ * @cooling_dev: cooling device's related data
+ * @ibss_manager: in IBSS mode (only one vif can be active), indicates what
+ * firmware indicated about having transmitted the last beacon, i.e.
+ * being IBSS manager for that time and needing to respond to probe
+ * requests
+ * @ptp_data: data of the PTP clock
+ * @time_sync: time sync data.
+ * @ftm_initiator: FTM initiator data
+ * @last_bt_notif: last received BT Coex notif
+ */
+struct iwl_mld {
+ /* Add here fields that need clean up on restart */
+ struct_group(zeroed_on_hw_restart,
+ struct ieee80211_bss_conf __rcu *fw_id_to_bss_conf[IWL_FW_MAX_LINK_ID + 1];
+ struct ieee80211_vif __rcu *fw_id_to_vif[NUM_MAC_INDEX_DRIVER];
+ struct ieee80211_txq __rcu *fw_id_to_txq[IWL_MAX_TVQM_QUEUES];
+ u8 used_phy_ids: NUM_PHY_CTX;
+ u8 num_igtks;
+ struct {
+ bool on;
+ u32 ampdu_ref;
+ bool ampdu_toggle;
+ u8 p80;
+#ifdef CONFIG_IWLWIFI_DEBUGFS
+ __le16 cur_aid;
+ u8 cur_bssid[ETH_ALEN];
+ bool ptp_time;
+#endif
+ } monitor;
+#ifdef CONFIG_PM_SLEEP
+ bool netdetect;
+#endif /* CONFIG_PM_SLEEP */
+ struct ieee80211_vif *p2p_device_vif;
+ struct iwl_bt_coex_profile_notif last_bt_notif;
+ );
+ struct ieee80211_link_sta __rcu *fw_id_to_link_sta[IWL_STATION_COUNT_MAX];
+ /* And here fields that survive a fw restart */
+ struct device *dev;
+ struct iwl_trans *trans;
+ const struct iwl_rf_cfg *cfg;
+ const struct iwl_fw *fw;
+ struct ieee80211_hw *hw;
+ struct wiphy *wiphy;
+ struct iwl_nvm_data *nvm_data;
+ struct iwl_fw_runtime fwrt;
+ struct dentry *debugfs_dir;
+ struct iwl_notif_wait_data notif_wait;
+ struct list_head async_handlers_list;
+ spinlock_t async_handlers_lock;
+ struct wiphy_work async_handlers_wk;
+ struct wiphy_delayed_work ct_kill_exit_wk;
+
+ struct {
+ u32 running:1,
+ do_not_dump_once:1,
+#ifdef CONFIG_PM_SLEEP
+ in_d3:1,
+#endif
+ in_hw_restart:1;
+
+ } fw_status;
+
+ struct {
+ u32 hw:1,
+ ct:1;
+ } radio_kill;
+
+ u32 power_budget_mw;
+
+ struct mac_address addresses[IWL_MLD_MAX_ADDRESSES];
+ struct iwl_mld_scan scan;
+#ifdef CONFIG_PM_SLEEP
+ struct wiphy_wowlan_support wowlan;
+#endif /* CONFIG_PM_SLEEP */
+#ifdef CONFIG_IWLWIFI_LEDS
+ struct led_classdev led;
+#endif
+ enum iwl_mcc_source mcc_src;
+ bool bios_enable_puncturing;
+
+ struct iwl_mld_baid_data __rcu *fw_id_to_ba[IWL_MAX_BAID];
+ u8 num_rx_ba_sessions;
+
+ struct iwl_mld_rx_queues_sync rxq_sync;
+
+ struct list_head txqs_to_add;
+ struct wiphy_work add_txqs_wk;
+ spinlock_t add_txqs_lock;
+
+ u8 *error_recovery_buf;
+ struct iwl_mcast_filter_cmd *mcast_filter_cmd;
+
+ u8 mgmt_tx_ant;
+
+ bool fw_rates_ver_3;
+
+ struct iwl_mld_low_latency low_latency;
+
+ bool ibss_manager;
+#ifdef CONFIG_THERMAL
+ struct thermal_zone_device *tzone;
+ struct iwl_mld_cooling_device cooling_dev;
+#endif
+
+ struct ptp_data ptp_data;
+
+ struct iwl_mld_time_sync_data __rcu *time_sync;
+
+ struct ftm_initiator_data ftm_initiator;
+};
+
+/* memset the part of the struct that requires cleanup on restart */
+#define CLEANUP_STRUCT(_ptr) \
+ memset((void *)&(_ptr)->zeroed_on_hw_restart, 0, \
+ sizeof((_ptr)->zeroed_on_hw_restart))
+
+/* Cleanup function for struct iwl_mld, will be called in restart */
+static inline void
+iwl_cleanup_mld(struct iwl_mld *mld)
+{
+ CLEANUP_STRUCT(mld);
+ CLEANUP_STRUCT(&mld->scan);
+
+#ifdef CONFIG_PM_SLEEP
+ mld->fw_status.in_d3 = false;
+#endif
+
+ iwl_mld_low_latency_restart_cleanup(mld);
+}
+
+enum iwl_power_scheme {
+ IWL_POWER_SCHEME_CAM = 1,
+ IWL_POWER_SCHEME_BPS,
+};
+
+/**
+ * struct iwl_mld_mod_params - module parameters for iwlmld
+ * @power_scheme: one of enum iwl_power_scheme
+ */
+struct iwl_mld_mod_params {
+ int power_scheme;
+};
+
+extern struct iwl_mld_mod_params iwlmld_mod_params;
+
+/* Extract MLD priv from op_mode */
+#define IWL_OP_MODE_GET_MLD(_iwl_op_mode) \
+ ((struct iwl_mld *)(_iwl_op_mode)->op_mode_specific)
+
+#define IWL_MAC80211_GET_MLD(_hw) \
+ IWL_OP_MODE_GET_MLD((struct iwl_op_mode *)((_hw)->priv))
+
+#ifdef CONFIG_IWLWIFI_DEBUGFS
+void
+iwl_mld_add_debugfs_files(struct iwl_mld *mld, struct dentry *debugfs_dir);
+#else
+static inline void
+iwl_mld_add_debugfs_files(struct iwl_mld *mld, struct dentry *debugfs_dir)
+{}
+#endif
+
+int iwl_mld_load_fw(struct iwl_mld *mld);
+void iwl_mld_stop_fw(struct iwl_mld *mld);
+int iwl_mld_start_fw(struct iwl_mld *mld);
+void iwl_mld_send_recovery_cmd(struct iwl_mld *mld, u32 flags);
+
+static inline void iwl_mld_set_ctkill(struct iwl_mld *mld, bool state)
+{
+ mld->radio_kill.ct = state;
+
+ wiphy_rfkill_set_hw_state(mld->wiphy,
+ mld->radio_kill.hw || mld->radio_kill.ct);
+}
+
+static inline void iwl_mld_set_hwkill(struct iwl_mld *mld, bool state)
+{
+ mld->radio_kill.hw = state;
+
+ wiphy_rfkill_set_hw_state(mld->wiphy,
+ mld->radio_kill.hw || mld->radio_kill.ct);
+}
+
+static inline u8 iwl_mld_get_valid_tx_ant(const struct iwl_mld *mld)
+{
+ u8 tx_ant = mld->fw->valid_tx_ant;
+
+ if (mld->nvm_data && mld->nvm_data->valid_tx_ant)
+ tx_ant &= mld->nvm_data->valid_tx_ant;
+
+ return tx_ant;
+}
+
+static inline u8 iwl_mld_get_valid_rx_ant(const struct iwl_mld *mld)
+{
+ u8 rx_ant = mld->fw->valid_rx_ant;
+
+ if (mld->nvm_data && mld->nvm_data->valid_rx_ant)
+ rx_ant &= mld->nvm_data->valid_rx_ant;
+
+ return rx_ant;
+}
+
+static inline u8 iwl_mld_nl80211_band_to_fw(enum nl80211_band band)
+{
+ switch (band) {
+ case NL80211_BAND_2GHZ:
+ return PHY_BAND_24;
+ case NL80211_BAND_5GHZ:
+ return PHY_BAND_5;
+ case NL80211_BAND_6GHZ:
+ return PHY_BAND_6;
+ default:
+ WARN_ONCE(1, "Unsupported band (%u)\n", band);
+ return PHY_BAND_5;
+ }
+}
+
+static inline u8 iwl_mld_phy_band_to_nl80211(u8 phy_band)
+{
+ switch (phy_band) {
+ case PHY_BAND_24:
+ return NL80211_BAND_2GHZ;
+ case PHY_BAND_5:
+ return NL80211_BAND_5GHZ;
+ case PHY_BAND_6:
+ return NL80211_BAND_6GHZ;
+ default:
+ WARN_ONCE(1, "Unsupported phy band (%u)\n", phy_band);
+ return NL80211_BAND_5GHZ;
+ }
+}
+
+static inline int
+iwl_mld_legacy_hw_idx_to_mac80211_idx(u32 rate_n_flags,
+ enum nl80211_band band)
+{
+ int format = rate_n_flags & RATE_MCS_MOD_TYPE_MSK;
+ int rate = rate_n_flags & RATE_LEGACY_RATE_MSK;
+ bool is_lb = band == NL80211_BAND_2GHZ;
+
+ if (format == RATE_MCS_MOD_TYPE_LEGACY_OFDM)
+ return is_lb ? rate + IWL_FIRST_OFDM_RATE : rate;
+
+ /* CCK is not allowed in 5 GHz */
+ return is_lb ? rate : -1;
+}
+
+extern const struct ieee80211_ops iwl_mld_hw_ops;
+
+/**
+ * enum iwl_rx_handler_context: context for Rx handler
+ * @RX_HANDLER_SYNC: this means that it will be called in the Rx path
+ * which can't acquire the wiphy->mutex.
+ * @RX_HANDLER_ASYNC: If the handler needs to hold wiphy->mutex
+ * (and only in this case!), it should be set as ASYNC. In that case,
+ * it will be called from a worker with wiphy->mutex held.
+ */
+enum iwl_rx_handler_context {
+ RX_HANDLER_SYNC,
+ RX_HANDLER_ASYNC,
+};
+
+/**
+ * struct iwl_rx_handler: handler for FW notification
+ * @val_fn: input validation function.
+ * @sizes: an array that mapps a version to the expected size.
+ * @fn: the function is called when notification is handled
+ * @cmd_id: command id
+ * @n_sizes: number of elements in &sizes.
+ * @context: see &iwl_rx_handler_context
+ * @obj_type: the type of the object that this handler is related to.
+ * See &iwl_mld_object_type. Use IWL_MLD_OBJECT_TYPE_NONE if not related.
+ * @cancel: function to cancel the notification. valid only if obj_type is not
+ * IWL_MLD_OBJECT_TYPE_NONE.
+ */
+struct iwl_rx_handler {
+ union {
+ bool (*val_fn)(struct iwl_mld *mld, struct iwl_rx_packet *pkt);
+ const struct iwl_notif_struct_size *sizes;
+ };
+ void (*fn)(struct iwl_mld *mld, struct iwl_rx_packet *pkt);
+ u16 cmd_id;
+ u8 n_sizes;
+ u8 context;
+ enum iwl_mld_object_type obj_type;
+ bool (*cancel)(struct iwl_mld *mld, struct iwl_rx_packet *pkt,
+ u32 obj_id);
+};
+
+/**
+ * struct iwl_notif_struct_size: map a notif ver to the expected size
+ *
+ * @size: the size to expect
+ * @ver: the version of the notification
+ */
+struct iwl_notif_struct_size {
+ u32 size:24, ver:8;
+};
+
+#if IS_ENABLED(CONFIG_IWLWIFI_KUNIT_TESTS)
+extern const struct iwl_hcmd_arr iwl_mld_groups[];
+extern const unsigned int global_iwl_mld_goups_size;
+extern const struct iwl_rx_handler iwl_mld_rx_handlers[];
+extern const unsigned int iwl_mld_rx_handlers_num;
+
+bool
+iwl_mld_is_dup(struct iwl_mld *mld, struct ieee80211_sta *sta,
+ struct ieee80211_hdr *hdr,
+ const struct iwl_rx_mpdu_desc *mpdu_desc,
+ struct ieee80211_rx_status *rx_status, int queue);
+
+void iwl_construct_mld(struct iwl_mld *mld, struct iwl_trans *trans,
+ const struct iwl_rf_cfg *cfg, const struct iwl_fw *fw,
+ struct ieee80211_hw *hw, struct dentry *dbgfs_dir);
+#endif
+
+#define IWL_MLD_INVALID_FW_ID 0xff
+
+#define IWL_MLD_ALLOC_FN(_type, _mac80211_type) \
+static int \
+iwl_mld_allocate_##_type##_fw_id(struct iwl_mld *mld, \
+ u8 *fw_id, \
+ struct ieee80211_##_mac80211_type *mac80211_ptr) \
+{ \
+ u8 rand = IWL_MLD_DIS_RANDOM_FW_ID ? 0 : get_random_u8(); \
+ u8 arr_sz = ARRAY_SIZE(mld->fw_id_to_##_mac80211_type); \
+ if (__builtin_types_compatible_p(typeof(*mac80211_ptr), \
+ struct ieee80211_link_sta)) \
+ arr_sz = mld->fw->ucode_capa.num_stations; \
+ if (__builtin_types_compatible_p(typeof(*mac80211_ptr), \
+ struct ieee80211_bss_conf)) \
+ arr_sz = mld->fw->ucode_capa.num_links; \
+ for (int i = 0; i < arr_sz; i++) { \
+ u8 idx = (i + rand) % arr_sz; \
+ if (rcu_access_pointer(mld->fw_id_to_##_mac80211_type[idx])) \
+ continue; \
+ IWL_DEBUG_INFO(mld, "Allocated at index %d / %d\n", idx, arr_sz); \
+ *fw_id = idx; \
+ rcu_assign_pointer(mld->fw_id_to_##_mac80211_type[idx], mac80211_ptr); \
+ return 0; \
+ } \
+ return -ENOSPC; \
+}
+
+static inline struct ieee80211_bss_conf *
+iwl_mld_fw_id_to_link_conf(struct iwl_mld *mld, u8 fw_link_id)
+{
+ if (IWL_FW_CHECK(mld, fw_link_id >= mld->fw->ucode_capa.num_links,
+ "Invalid fw_link_id: %d\n", fw_link_id))
+ return NULL;
+
+ return wiphy_dereference(mld->wiphy,
+ mld->fw_id_to_bss_conf[fw_link_id]);
+}
+
+#define MSEC_TO_TU(_msec) ((_msec) * 1000 / 1024)
+
+void iwl_mld_add_vif_debugfs(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif);
+void iwl_mld_add_link_debugfs(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_bss_conf *link_conf,
+ struct dentry *dir);
+void iwl_mld_add_link_sta_debugfs(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_link_sta *link_sta,
+ struct dentry *dir);
+
+/* Utilities */
+
+static inline u8 iwl_mld_mac80211_ac_to_fw_tx_fifo(enum ieee80211_ac_numbers ac)
+{
+ static const u8 mac80211_ac_to_fw_tx_fifo[] = {
+ IWL_BZ_EDCA_TX_FIFO_VO,
+ IWL_BZ_EDCA_TX_FIFO_VI,
+ IWL_BZ_EDCA_TX_FIFO_BE,
+ IWL_BZ_EDCA_TX_FIFO_BK,
+ IWL_BZ_TRIG_TX_FIFO_VO,
+ IWL_BZ_TRIG_TX_FIFO_VI,
+ IWL_BZ_TRIG_TX_FIFO_BE,
+ IWL_BZ_TRIG_TX_FIFO_BK,
+ };
+ return mac80211_ac_to_fw_tx_fifo[ac];
+}
+
+static inline u32
+iwl_mld_get_lmac_id(struct iwl_mld *mld, enum nl80211_band band)
+{
+ if (!fw_has_capa(&mld->fw->ucode_capa,
+ IWL_UCODE_TLV_CAPA_CDB_SUPPORT) ||
+ band == NL80211_BAND_2GHZ)
+ return IWL_LMAC_24G_INDEX;
+ return IWL_LMAC_5G_INDEX;
+}
+
+/* Check if we had an error, but reconfig flow didn't start yet */
+static inline bool iwl_mld_error_before_recovery(struct iwl_mld *mld)
+{
+ return mld->fw_status.in_hw_restart &&
+ !iwl_trans_fw_running(mld->trans);
+}
+
+int iwl_mld_tdls_sta_count(struct iwl_mld *mld);
+
+#endif /* __iwl_mld_h__ */
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/mlo.c b/drivers/net/wireless/intel/iwlwifi/mld/mlo.c
new file mode 100644
index 000000000000..dba5379ed009
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/mld/mlo.c
@@ -0,0 +1,1241 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
+/*
+ * Copyright (C) 2024-2025 Intel Corporation
+ */
+#include "mlo.h"
+#include "phy.h"
+
+/* Block reasons helper */
+#define HANDLE_EMLSR_BLOCKED_REASONS(HOW) \
+ HOW(PREVENTION) \
+ HOW(WOWLAN) \
+ HOW(ROC) \
+ HOW(NON_BSS) \
+ HOW(TMP_NON_BSS) \
+ HOW(TPT)
+
+static const char *
+iwl_mld_get_emlsr_blocked_string(enum iwl_mld_emlsr_blocked blocked)
+{
+ /* Using switch without "default" will warn about missing entries */
+ switch (blocked) {
+#define REASON_CASE(x) case IWL_MLD_EMLSR_BLOCKED_##x: return #x;
+ HANDLE_EMLSR_BLOCKED_REASONS(REASON_CASE)
+#undef REASON_CASE
+ }
+
+ return "ERROR";
+}
+
+static void iwl_mld_print_emlsr_blocked(struct iwl_mld *mld, u32 mask)
+{
+#define NAME_FMT(x) "%s"
+#define NAME_PR(x) (mask & IWL_MLD_EMLSR_BLOCKED_##x) ? "[" #x "]" : "",
+ IWL_DEBUG_INFO(mld,
+ "EMLSR blocked = " HANDLE_EMLSR_BLOCKED_REASONS(NAME_FMT)
+ " (0x%x)\n",
+ HANDLE_EMLSR_BLOCKED_REASONS(NAME_PR)
+ mask);
+#undef NAME_FMT
+#undef NAME_PR
+}
+
+/* Exit reasons helper */
+#define HANDLE_EMLSR_EXIT_REASONS(HOW) \
+ HOW(BLOCK) \
+ HOW(MISSED_BEACON) \
+ HOW(FAIL_ENTRY) \
+ HOW(CSA) \
+ HOW(EQUAL_BAND) \
+ HOW(LOW_RSSI) \
+ HOW(LINK_USAGE) \
+ HOW(BT_COEX) \
+ HOW(CHAN_LOAD) \
+ HOW(RFI) \
+ HOW(FW_REQUEST) \
+ HOW(INVALID)
+
+static const char *
+iwl_mld_get_emlsr_exit_string(enum iwl_mld_emlsr_exit exit)
+{
+ /* Using switch without "default" will warn about missing entries */
+ switch (exit) {
+#define REASON_CASE(x) case IWL_MLD_EMLSR_EXIT_##x: return #x;
+ HANDLE_EMLSR_EXIT_REASONS(REASON_CASE)
+#undef REASON_CASE
+ }
+
+ return "ERROR";
+}
+
+static void iwl_mld_print_emlsr_exit(struct iwl_mld *mld, u32 mask)
+{
+#define NAME_FMT(x) "%s"
+#define NAME_PR(x) (mask & IWL_MLD_EMLSR_EXIT_##x) ? "[" #x "]" : "",
+ IWL_DEBUG_INFO(mld,
+ "EMLSR exit = " HANDLE_EMLSR_EXIT_REASONS(NAME_FMT)
+ " (0x%x)\n",
+ HANDLE_EMLSR_EXIT_REASONS(NAME_PR)
+ mask);
+#undef NAME_FMT
+#undef NAME_PR
+}
+
+void iwl_mld_emlsr_prevent_done_wk(struct wiphy *wiphy, struct wiphy_work *wk)
+{
+ struct iwl_mld_vif *mld_vif = container_of(wk, struct iwl_mld_vif,
+ emlsr.prevent_done_wk.work);
+ struct ieee80211_vif *vif =
+ container_of((void *)mld_vif, struct ieee80211_vif, drv_priv);
+
+ if (WARN_ON(!(mld_vif->emlsr.blocked_reasons &
+ IWL_MLD_EMLSR_BLOCKED_PREVENTION)))
+ return;
+
+ iwl_mld_unblock_emlsr(mld_vif->mld, vif,
+ IWL_MLD_EMLSR_BLOCKED_PREVENTION);
+}
+
+void iwl_mld_emlsr_tmp_non_bss_done_wk(struct wiphy *wiphy,
+ struct wiphy_work *wk)
+{
+ struct iwl_mld_vif *mld_vif = container_of(wk, struct iwl_mld_vif,
+ emlsr.tmp_non_bss_done_wk.work);
+ struct ieee80211_vif *vif =
+ container_of((void *)mld_vif, struct ieee80211_vif, drv_priv);
+
+ if (WARN_ON(!(mld_vif->emlsr.blocked_reasons &
+ IWL_MLD_EMLSR_BLOCKED_TMP_NON_BSS)))
+ return;
+
+ iwl_mld_unblock_emlsr(mld_vif->mld, vif,
+ IWL_MLD_EMLSR_BLOCKED_TMP_NON_BSS);
+}
+
+#define IWL_MLD_TRIGGER_LINK_SEL_TIME (HZ * IWL_MLD_TRIGGER_LINK_SEL_TIME_SEC)
+#define IWL_MLD_SCAN_EXPIRE_TIME (HZ * IWL_MLD_SCAN_EXPIRE_TIME_SEC)
+
+/* Exit reasons that can cause longer EMLSR prevention */
+#define IWL_MLD_PREVENT_EMLSR_REASONS (IWL_MLD_EMLSR_EXIT_MISSED_BEACON | \
+ IWL_MLD_EMLSR_EXIT_LINK_USAGE | \
+ IWL_MLD_EMLSR_EXIT_FW_REQUEST)
+#define IWL_MLD_PREVENT_EMLSR_TIMEOUT (HZ * 400)
+
+#define IWL_MLD_EMLSR_PREVENT_SHORT (HZ * 300)
+#define IWL_MLD_EMLSR_PREVENT_LONG (HZ * 600)
+
+static void iwl_mld_check_emlsr_prevention(struct iwl_mld *mld,
+ struct iwl_mld_vif *mld_vif,
+ enum iwl_mld_emlsr_exit reason)
+{
+ unsigned long delay;
+
+ /*
+ * Reset the counter if more than 400 seconds have passed between one
+ * exit and the other, or if we exited due to a different reason.
+ * Will also reset the counter after the long prevention is done.
+ */
+ if (time_after(jiffies, mld_vif->emlsr.last_exit_ts +
+ IWL_MLD_PREVENT_EMLSR_TIMEOUT) ||
+ mld_vif->emlsr.last_exit_reason != reason)
+ mld_vif->emlsr.exit_repeat_count = 0;
+
+ mld_vif->emlsr.last_exit_reason = reason;
+ mld_vif->emlsr.last_exit_ts = jiffies;
+ mld_vif->emlsr.exit_repeat_count++;
+
+ /*
+ * Do not add a prevention when the reason was a block. For a block,
+ * EMLSR will be enabled again on unblock.
+ */
+ if (reason == IWL_MLD_EMLSR_EXIT_BLOCK)
+ return;
+
+ /* Set prevention for a minimum of 30 seconds */
+ mld_vif->emlsr.blocked_reasons |= IWL_MLD_EMLSR_BLOCKED_PREVENTION;
+ delay = IWL_MLD_TRIGGER_LINK_SEL_TIME;
+
+ /* Handle repeats for reasons that can cause long prevention */
+ if (mld_vif->emlsr.exit_repeat_count > 1 &&
+ reason & IWL_MLD_PREVENT_EMLSR_REASONS) {
+ if (mld_vif->emlsr.exit_repeat_count == 2)
+ delay = IWL_MLD_EMLSR_PREVENT_SHORT;
+ else
+ delay = IWL_MLD_EMLSR_PREVENT_LONG;
+
+ /*
+ * The timeouts are chosen so that this will not happen, i.e.
+ * IWL_MLD_EMLSR_PREVENT_LONG > IWL_MLD_PREVENT_EMLSR_TIMEOUT
+ */
+ WARN_ON(mld_vif->emlsr.exit_repeat_count > 3);
+ }
+
+ IWL_DEBUG_INFO(mld,
+ "Preventing EMLSR for %ld seconds due to %u exits with the reason = %s (0x%x)\n",
+ delay / HZ, mld_vif->emlsr.exit_repeat_count,
+ iwl_mld_get_emlsr_exit_string(reason), reason);
+
+ wiphy_delayed_work_queue(mld->wiphy,
+ &mld_vif->emlsr.prevent_done_wk, delay);
+}
+
+static void iwl_mld_clear_avg_chan_load_iter(struct ieee80211_hw *hw,
+ struct ieee80211_chanctx_conf *ctx,
+ void *dat)
+{
+ struct iwl_mld_phy *phy = iwl_mld_phy_from_mac80211(ctx);
+
+ /* It is ok to do it for all chanctx (and not only for the ones that
+ * belong to the EMLSR vif) since EMLSR is not allowed if there is
+ * another vif.
+ */
+ phy->avg_channel_load_not_by_us = 0;
+}
+
+static int _iwl_mld_exit_emlsr(struct iwl_mld *mld, struct ieee80211_vif *vif,
+ enum iwl_mld_emlsr_exit exit, u8 link_to_keep,
+ bool sync)
+{
+ struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
+ u16 new_active_links;
+ int ret = 0;
+
+ lockdep_assert_wiphy(mld->wiphy);
+
+ /* On entry failure need to exit anyway, even if entered from debugfs */
+ if (exit != IWL_MLD_EMLSR_EXIT_FAIL_ENTRY && !IWL_MLD_AUTO_EML_ENABLE)
+ return 0;
+
+ /* Ignore exit request if EMLSR is not active */
+ if (!iwl_mld_emlsr_active(vif))
+ return 0;
+
+ if (WARN_ON(!ieee80211_vif_is_mld(vif) || !mld_vif->authorized))
+ return 0;
+
+ if (WARN_ON(!(vif->active_links & BIT(link_to_keep))))
+ link_to_keep = __ffs(vif->active_links);
+
+ new_active_links = BIT(link_to_keep);
+ IWL_DEBUG_INFO(mld,
+ "Exiting EMLSR. reason = %s (0x%x). Current active links=0x%x, new active links = 0x%x\n",
+ iwl_mld_get_emlsr_exit_string(exit), exit,
+ vif->active_links, new_active_links);
+
+ if (sync)
+ ret = ieee80211_set_active_links(vif, new_active_links);
+ else
+ ieee80211_set_active_links_async(vif, new_active_links);
+
+ /* Update latest exit reason and check EMLSR prevention */
+ iwl_mld_check_emlsr_prevention(mld, mld_vif, exit);
+
+ /* channel_load_not_by_us is invalid when in EMLSR.
+ * Clear it so wrong values won't be used.
+ */
+ ieee80211_iter_chan_contexts_atomic(mld->hw,
+ iwl_mld_clear_avg_chan_load_iter,
+ NULL);
+
+ return ret;
+}
+
+void iwl_mld_exit_emlsr(struct iwl_mld *mld, struct ieee80211_vif *vif,
+ enum iwl_mld_emlsr_exit exit, u8 link_to_keep)
+{
+ _iwl_mld_exit_emlsr(mld, vif, exit, link_to_keep, false);
+}
+
+static int _iwl_mld_emlsr_block(struct iwl_mld *mld, struct ieee80211_vif *vif,
+ enum iwl_mld_emlsr_blocked reason,
+ u8 link_to_keep, bool sync)
+{
+ struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
+
+ lockdep_assert_wiphy(mld->wiphy);
+
+ if (!IWL_MLD_AUTO_EML_ENABLE || !iwl_mld_vif_has_emlsr_cap(vif))
+ return 0;
+
+ if (mld_vif->emlsr.blocked_reasons & reason)
+ return 0;
+
+ mld_vif->emlsr.blocked_reasons |= reason;
+
+ IWL_DEBUG_INFO(mld,
+ "Blocking EMLSR mode. reason = %s (0x%x)\n",
+ iwl_mld_get_emlsr_blocked_string(reason), reason);
+ iwl_mld_print_emlsr_blocked(mld, mld_vif->emlsr.blocked_reasons);
+
+ if (reason == IWL_MLD_EMLSR_BLOCKED_TPT)
+ wiphy_delayed_work_cancel(mld_vif->mld->wiphy,
+ &mld_vif->emlsr.check_tpt_wk);
+
+ return _iwl_mld_exit_emlsr(mld, vif, IWL_MLD_EMLSR_EXIT_BLOCK,
+ link_to_keep, sync);
+}
+
+void iwl_mld_block_emlsr(struct iwl_mld *mld, struct ieee80211_vif *vif,
+ enum iwl_mld_emlsr_blocked reason, u8 link_to_keep)
+{
+ _iwl_mld_emlsr_block(mld, vif, reason, link_to_keep, false);
+}
+
+int iwl_mld_block_emlsr_sync(struct iwl_mld *mld, struct ieee80211_vif *vif,
+ enum iwl_mld_emlsr_blocked reason, u8 link_to_keep)
+{
+ return _iwl_mld_emlsr_block(mld, vif, reason, link_to_keep, true);
+}
+
+static void _iwl_mld_select_links(struct iwl_mld *mld,
+ struct ieee80211_vif *vif);
+
+void iwl_mld_unblock_emlsr(struct iwl_mld *mld, struct ieee80211_vif *vif,
+ enum iwl_mld_emlsr_blocked reason)
+{
+ struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
+
+ lockdep_assert_wiphy(mld->wiphy);
+
+ if (!IWL_MLD_AUTO_EML_ENABLE || !iwl_mld_vif_has_emlsr_cap(vif))
+ return;
+
+ if (!(mld_vif->emlsr.blocked_reasons & reason))
+ return;
+
+ mld_vif->emlsr.blocked_reasons &= ~reason;
+
+ IWL_DEBUG_INFO(mld,
+ "Unblocking EMLSR mode. reason = %s (0x%x)\n",
+ iwl_mld_get_emlsr_blocked_string(reason), reason);
+ iwl_mld_print_emlsr_blocked(mld, mld_vif->emlsr.blocked_reasons);
+
+ if (reason == IWL_MLD_EMLSR_BLOCKED_TPT)
+ wiphy_delayed_work_queue(mld_vif->mld->wiphy,
+ &mld_vif->emlsr.check_tpt_wk,
+ round_jiffies_relative(IWL_MLD_TPT_COUNT_WINDOW));
+
+ if (mld_vif->emlsr.blocked_reasons)
+ return;
+
+ IWL_DEBUG_INFO(mld, "EMLSR is unblocked\n");
+ iwl_mld_int_mlo_scan(mld, vif);
+}
+
+static void
+iwl_mld_vif_iter_emlsr_mode_notif(void *data, u8 *mac,
+ struct ieee80211_vif *vif)
+{
+ const struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
+ enum iwl_mvm_fw_esr_recommendation action;
+ const struct iwl_esr_mode_notif *notif = NULL;
+
+ if (iwl_fw_lookup_notif_ver(mld_vif->mld->fw, DATA_PATH_GROUP,
+ ESR_MODE_NOTIF, 0) > 1) {
+ notif = (void *)data;
+ action = le32_to_cpu(notif->action);
+ } else {
+ const struct iwl_esr_mode_notif_v1 *notif_v1 = (void *)data;
+
+ action = le32_to_cpu(notif_v1->action);
+ }
+
+ if (!iwl_mld_vif_has_emlsr_cap(vif))
+ return;
+
+ switch (action) {
+ case ESR_RECOMMEND_LEAVE:
+ if (notif)
+ IWL_DEBUG_INFO(mld_vif->mld,
+ "FW recommend leave reason = 0x%x\n",
+ le32_to_cpu(notif->leave_reason_mask));
+
+ iwl_mld_exit_emlsr(mld_vif->mld, vif,
+ IWL_MLD_EMLSR_EXIT_FW_REQUEST,
+ iwl_mld_get_primary_link(vif));
+ break;
+ case ESR_FORCE_LEAVE:
+ if (notif)
+ IWL_DEBUG_INFO(mld_vif->mld,
+ "FW force leave reason = 0x%x\n",
+ le32_to_cpu(notif->leave_reason_mask));
+ fallthrough;
+ case ESR_RECOMMEND_ENTER:
+ default:
+ IWL_WARN(mld_vif->mld, "Unexpected EMLSR notification: %d\n",
+ action);
+ }
+}
+
+void iwl_mld_handle_emlsr_mode_notif(struct iwl_mld *mld,
+ struct iwl_rx_packet *pkt)
+{
+ ieee80211_iterate_active_interfaces_mtx(mld->hw,
+ IEEE80211_IFACE_ITER_NORMAL,
+ iwl_mld_vif_iter_emlsr_mode_notif,
+ pkt->data);
+}
+
+static void
+iwl_mld_vif_iter_disconnect_emlsr(void *data, u8 *mac,
+ struct ieee80211_vif *vif)
+{
+ if (!iwl_mld_vif_has_emlsr_cap(vif))
+ return;
+
+ ieee80211_connection_loss(vif);
+}
+
+void iwl_mld_handle_emlsr_trans_fail_notif(struct iwl_mld *mld,
+ struct iwl_rx_packet *pkt)
+{
+ const struct iwl_esr_trans_fail_notif *notif = (const void *)pkt->data;
+ u32 fw_link_id = le32_to_cpu(notif->link_id);
+ struct ieee80211_bss_conf *bss_conf =
+ iwl_mld_fw_id_to_link_conf(mld, fw_link_id);
+
+ IWL_DEBUG_INFO(mld, "Failed to %s EMLSR on link %d (FW: %d), reason %d\n",
+ le32_to_cpu(notif->activation) ? "enter" : "exit",
+ bss_conf ? bss_conf->link_id : -1,
+ le32_to_cpu(notif->link_id),
+ le32_to_cpu(notif->err_code));
+
+ if (IWL_FW_CHECK(mld, !bss_conf,
+ "FW reported failure to %sactivate EMLSR on a non-existing link: %d\n",
+ le32_to_cpu(notif->activation) ? "" : "de",
+ fw_link_id)) {
+ ieee80211_iterate_active_interfaces_mtx(
+ mld->hw, IEEE80211_IFACE_ITER_NORMAL,
+ iwl_mld_vif_iter_disconnect_emlsr, NULL);
+ return;
+ }
+
+ /* Disconnect if we failed to deactivate a link */
+ if (!le32_to_cpu(notif->activation)) {
+ ieee80211_connection_loss(bss_conf->vif);
+ return;
+ }
+
+ /*
+ * We failed to activate the second link, go back to the link specified
+ * by the firmware as that is the one that is still valid now.
+ */
+ iwl_mld_exit_emlsr(mld, bss_conf->vif, IWL_MLD_EMLSR_EXIT_FAIL_ENTRY,
+ bss_conf->link_id);
+}
+
+/* Active non-station link tracking */
+static void iwl_mld_count_non_bss_links(void *_data, u8 *mac,
+ struct ieee80211_vif *vif)
+{
+ struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
+ int *count = _data;
+
+ if (ieee80211_vif_type_p2p(vif) == NL80211_IFTYPE_STATION)
+ return;
+
+ *count += iwl_mld_count_active_links(mld_vif->mld, vif);
+}
+
+struct iwl_mld_update_emlsr_block_data {
+ bool block;
+ int result;
+};
+
+static void
+iwl_mld_vif_iter_update_emlsr_non_bss_block(void *_data, u8 *mac,
+ struct ieee80211_vif *vif)
+{
+ struct iwl_mld_update_emlsr_block_data *data = _data;
+ struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
+ int ret;
+
+ if (data->block) {
+ ret = iwl_mld_block_emlsr_sync(mld_vif->mld, vif,
+ IWL_MLD_EMLSR_BLOCKED_NON_BSS,
+ iwl_mld_get_primary_link(vif));
+ if (ret)
+ data->result = ret;
+ } else {
+ iwl_mld_unblock_emlsr(mld_vif->mld, vif,
+ IWL_MLD_EMLSR_BLOCKED_NON_BSS);
+ }
+}
+
+int iwl_mld_emlsr_check_non_bss_block(struct iwl_mld *mld,
+ int pending_link_changes)
+{
+ /* An active link of a non-station vif blocks EMLSR. Upon activation
+ * block EMLSR on the bss vif. Upon deactivation, check if this link
+ * was the last non-station link active, and if so unblock the bss vif
+ */
+ struct iwl_mld_update_emlsr_block_data block_data = {};
+ int count = pending_link_changes;
+
+ /* No need to count if we are activating a non-BSS link */
+ if (count <= 0)
+ ieee80211_iterate_active_interfaces_mtx(mld->hw,
+ IEEE80211_IFACE_ITER_NORMAL,
+ iwl_mld_count_non_bss_links,
+ &count);
+
+ /*
+ * We could skip updating it if the block change did not change (and
+ * pending_link_changes is non-zero).
+ */
+ block_data.block = !!count;
+
+ ieee80211_iterate_active_interfaces_mtx(mld->hw,
+ IEEE80211_IFACE_ITER_NORMAL,
+ iwl_mld_vif_iter_update_emlsr_non_bss_block,
+ &block_data);
+
+ return block_data.result;
+}
+
+#define EMLSR_SEC_LINK_MIN_PERC 10
+#define EMLSR_MIN_TX 3000
+#define EMLSR_MIN_RX 400
+
+void iwl_mld_emlsr_check_tpt(struct wiphy *wiphy, struct wiphy_work *wk)
+{
+ struct iwl_mld_vif *mld_vif = container_of(wk, struct iwl_mld_vif,
+ emlsr.check_tpt_wk.work);
+ struct ieee80211_vif *vif =
+ container_of((void *)mld_vif, struct ieee80211_vif, drv_priv);
+ struct iwl_mld *mld = mld_vif->mld;
+ struct iwl_mld_sta *mld_sta;
+ struct iwl_mld_link *sec_link;
+ unsigned long total_tx = 0, total_rx = 0;
+ unsigned long sec_link_tx = 0, sec_link_rx = 0;
+ u8 sec_link_tx_perc, sec_link_rx_perc;
+ s8 sec_link_id;
+
+ if (!iwl_mld_vif_has_emlsr_cap(vif) || !mld_vif->ap_sta)
+ return;
+
+ mld_sta = iwl_mld_sta_from_mac80211(mld_vif->ap_sta);
+
+ /* We only count for the AP sta in a MLO connection */
+ if (!mld_sta->mpdu_counters)
+ return;
+
+ /* This wk should only run when the TPT blocker isn't set.
+ * When the blocker is set, the decision to remove it, as well as
+ * clearing the counters is done in DP (to avoid having a wk every
+ * 5 seconds when idle. When the blocker is unset, we are not idle anyway)
+ */
+ if (WARN_ON(mld_vif->emlsr.blocked_reasons & IWL_MLD_EMLSR_BLOCKED_TPT))
+ return;
+ /*
+ * TPT is unblocked, need to check if the TPT criteria is still met.
+ *
+ * If EMLSR is active, then we also need to check the secondar link
+ * requirements.
+ */
+ if (iwl_mld_emlsr_active(vif)) {
+ sec_link_id = iwl_mld_get_other_link(vif, iwl_mld_get_primary_link(vif));
+ sec_link = iwl_mld_link_dereference_check(mld_vif, sec_link_id);
+ if (WARN_ON_ONCE(!sec_link))
+ return;
+ /* We need the FW ID here */
+ sec_link_id = sec_link->fw_id;
+ } else {
+ sec_link_id = -1;
+ }
+
+ /* Sum up RX and TX MPDUs from the different queues/links */
+ for (int q = 0; q < mld->trans->info.num_rxqs; q++) {
+ struct iwl_mld_per_q_mpdu_counter *queue_counter =
+ &mld_sta->mpdu_counters[q];
+
+ spin_lock_bh(&queue_counter->lock);
+
+ /* The link IDs that doesn't exist will contain 0 */
+ for (int link = 0;
+ link < ARRAY_SIZE(queue_counter->per_link);
+ link++) {
+ total_tx += queue_counter->per_link[link].tx;
+ total_rx += queue_counter->per_link[link].rx;
+ }
+
+ if (sec_link_id != -1) {
+ sec_link_tx += queue_counter->per_link[sec_link_id].tx;
+ sec_link_rx += queue_counter->per_link[sec_link_id].rx;
+ }
+
+ memset(queue_counter->per_link, 0,
+ sizeof(queue_counter->per_link));
+
+ spin_unlock_bh(&queue_counter->lock);
+ }
+
+ IWL_DEBUG_INFO(mld, "total Tx MPDUs: %ld. total Rx MPDUs: %ld\n",
+ total_tx, total_rx);
+
+ /* If we don't have enough MPDUs - exit EMLSR */
+ if (total_tx < IWL_MLD_ENTER_EMLSR_TPT_THRESH &&
+ total_rx < IWL_MLD_ENTER_EMLSR_TPT_THRESH) {
+ iwl_mld_block_emlsr(mld, vif, IWL_MLD_EMLSR_BLOCKED_TPT,
+ iwl_mld_get_primary_link(vif));
+ return;
+ }
+
+ /* EMLSR is not active */
+ if (sec_link_id == -1)
+ return;
+
+ IWL_DEBUG_INFO(mld, "Secondary Link %d: Tx MPDUs: %ld. Rx MPDUs: %ld\n",
+ sec_link_id, sec_link_tx, sec_link_rx);
+
+ /* Calculate the percentage of the secondary link TX/RX */
+ sec_link_tx_perc = total_tx ? sec_link_tx * 100 / total_tx : 0;
+ sec_link_rx_perc = total_rx ? sec_link_rx * 100 / total_rx : 0;
+
+ /*
+ * The TX/RX percentage is checked only if it exceeds the required
+ * minimum. In addition, RX is checked only if the TX check failed.
+ */
+ if ((total_tx > EMLSR_MIN_TX &&
+ sec_link_tx_perc < EMLSR_SEC_LINK_MIN_PERC) ||
+ (total_rx > EMLSR_MIN_RX &&
+ sec_link_rx_perc < EMLSR_SEC_LINK_MIN_PERC)) {
+ iwl_mld_exit_emlsr(mld, vif, IWL_MLD_EMLSR_EXIT_LINK_USAGE,
+ iwl_mld_get_primary_link(vif));
+ return;
+ }
+
+ /* Check again when the next window ends */
+ wiphy_delayed_work_queue(mld_vif->mld->wiphy,
+ &mld_vif->emlsr.check_tpt_wk,
+ round_jiffies_relative(IWL_MLD_TPT_COUNT_WINDOW));
+}
+
+void iwl_mld_emlsr_unblock_tpt_wk(struct wiphy *wiphy, struct wiphy_work *wk)
+{
+ struct iwl_mld_vif *mld_vif = container_of(wk, struct iwl_mld_vif,
+ emlsr.unblock_tpt_wk);
+ struct ieee80211_vif *vif =
+ container_of((void *)mld_vif, struct ieee80211_vif, drv_priv);
+
+ iwl_mld_unblock_emlsr(mld_vif->mld, vif, IWL_MLD_EMLSR_BLOCKED_TPT);
+}
+
+/*
+ * Link selection
+ */
+
+s8 iwl_mld_get_emlsr_rssi_thresh(struct iwl_mld *mld,
+ const struct cfg80211_chan_def *chandef,
+ bool low)
+{
+ if (WARN_ON(chandef->chan->band != NL80211_BAND_2GHZ &&
+ chandef->chan->band != NL80211_BAND_5GHZ &&
+ chandef->chan->band != NL80211_BAND_6GHZ))
+ return S8_MAX;
+
+#define RSSI_THRESHOLD(_low, _bw) \
+ (_low) ? IWL_MLD_LOW_RSSI_THRESH_##_bw##MHZ \
+ : IWL_MLD_HIGH_RSSI_THRESH_##_bw##MHZ
+
+ switch (chandef->width) {
+ case NL80211_CHAN_WIDTH_20_NOHT:
+ case NL80211_CHAN_WIDTH_20:
+ /* 320 MHz has the same thresholds as 20 MHz */
+ case NL80211_CHAN_WIDTH_320:
+ return RSSI_THRESHOLD(low, 20);
+ case NL80211_CHAN_WIDTH_40:
+ return RSSI_THRESHOLD(low, 40);
+ case NL80211_CHAN_WIDTH_80:
+ return RSSI_THRESHOLD(low, 80);
+ case NL80211_CHAN_WIDTH_160:
+ return RSSI_THRESHOLD(low, 160);
+ default:
+ WARN_ON(1);
+ return S8_MAX;
+ }
+#undef RSSI_THRESHOLD
+}
+
+#define IWL_MLD_BT_COEX_DISABLE_EMLSR_RSSI_THRESH -69
+#define IWL_MLD_BT_COEX_ENABLE_EMLSR_RSSI_THRESH -63
+#define IWL_MLD_BT_COEX_WIFI_LOSS_THRESH 7
+
+VISIBLE_IF_IWLWIFI_KUNIT
+bool
+iwl_mld_bt_allows_emlsr(struct iwl_mld *mld, struct ieee80211_bss_conf *link,
+ bool check_entry)
+{
+ int bt_penalty, rssi_thresh;
+ s32 link_rssi;
+
+ if (WARN_ON_ONCE(!link->bss))
+ return false;
+
+ link_rssi = MBM_TO_DBM(link->bss->signal);
+ rssi_thresh = check_entry ?
+ IWL_MLD_BT_COEX_ENABLE_EMLSR_RSSI_THRESH :
+ IWL_MLD_BT_COEX_DISABLE_EMLSR_RSSI_THRESH;
+ /* No valid RSSI - force to take low rssi */
+ if (!link_rssi)
+ link_rssi = rssi_thresh - 1;
+
+ if (link_rssi > rssi_thresh)
+ bt_penalty = max(mld->last_bt_notif.wifi_loss_mid_high_rssi[PHY_BAND_24][0],
+ mld->last_bt_notif.wifi_loss_mid_high_rssi[PHY_BAND_24][1]);
+ else
+ bt_penalty = max(mld->last_bt_notif.wifi_loss_low_rssi[PHY_BAND_24][0],
+ mld->last_bt_notif.wifi_loss_low_rssi[PHY_BAND_24][1]);
+
+ IWL_DEBUG_EHT(mld, "BT penalty for link-id %0X is %d\n",
+ link->link_id, bt_penalty);
+ return bt_penalty < IWL_MLD_BT_COEX_WIFI_LOSS_THRESH;
+}
+EXPORT_SYMBOL_IF_IWLWIFI_KUNIT(iwl_mld_bt_allows_emlsr);
+
+static u32
+iwl_mld_emlsr_disallowed_with_link(struct iwl_mld *mld,
+ struct ieee80211_vif *vif,
+ struct iwl_mld_link_sel_data *link,
+ bool primary)
+{
+ struct wiphy *wiphy = mld->wiphy;
+ struct ieee80211_bss_conf *conf;
+ u32 ret = 0;
+
+ conf = wiphy_dereference(wiphy, vif->link_conf[link->link_id]);
+ if (WARN_ON_ONCE(!conf))
+ return IWL_MLD_EMLSR_EXIT_INVALID;
+
+ if (link->chandef->chan->band == NL80211_BAND_2GHZ &&
+ !iwl_mld_bt_allows_emlsr(mld, conf, true))
+ ret |= IWL_MLD_EMLSR_EXIT_BT_COEX;
+
+ if (link->signal <
+ iwl_mld_get_emlsr_rssi_thresh(mld, link->chandef, false))
+ ret |= IWL_MLD_EMLSR_EXIT_LOW_RSSI;
+
+ if (conf->csa_active)
+ ret |= IWL_MLD_EMLSR_EXIT_CSA;
+
+ if (ret) {
+ IWL_DEBUG_INFO(mld,
+ "Link %d is not allowed for EMLSR as %s\n",
+ link->link_id,
+ primary ? "primary" : "secondary");
+ iwl_mld_print_emlsr_exit(mld, ret);
+ }
+
+ return ret;
+}
+
+static u8
+iwl_mld_set_link_sel_data(struct iwl_mld *mld,
+ struct ieee80211_vif *vif,
+ struct iwl_mld_link_sel_data *data,
+ unsigned long usable_links,
+ u8 *best_link_idx)
+{
+ u8 n_data = 0;
+ u16 max_grade = 0;
+ unsigned long link_id;
+
+ /*
+ * TODO: don't select links that weren't discovered in the last scan
+ * This requires mac80211 (or cfg80211) changes to forward/track when
+ * a BSS was last updated. cfg80211 already tracks this information but
+ * it is not exposed within the kernel.
+ */
+ for_each_set_bit(link_id, &usable_links, IEEE80211_MLD_MAX_NUM_LINKS) {
+ struct ieee80211_bss_conf *link_conf =
+ link_conf_dereference_protected(vif, link_id);
+
+ if (WARN_ON_ONCE(!link_conf))
+ continue;
+
+ /* Ignore any BSS that was not seen in the last MLO scan */
+ if (ktime_before(link_conf->bss->ts_boottime,
+ mld->scan.last_mlo_scan_time))
+ continue;
+
+ data[n_data].link_id = link_id;
+ data[n_data].chandef = &link_conf->chanreq.oper;
+ data[n_data].signal = MBM_TO_DBM(link_conf->bss->signal);
+ data[n_data].grade = iwl_mld_get_link_grade(mld, link_conf);
+
+ if (n_data == 0 || data[n_data].grade > max_grade) {
+ max_grade = data[n_data].grade;
+ *best_link_idx = n_data;
+ }
+ n_data++;
+ }
+
+ return n_data;
+}
+
+static u32
+iwl_mld_get_min_chan_load_thresh(struct ieee80211_chanctx_conf *chanctx)
+{
+ const struct iwl_mld_phy *phy = iwl_mld_phy_from_mac80211(chanctx);
+
+ switch (phy->chandef.width) {
+ case NL80211_CHAN_WIDTH_320:
+ case NL80211_CHAN_WIDTH_160:
+ return 5;
+ case NL80211_CHAN_WIDTH_80:
+ return 7;
+ default:
+ break;
+ }
+ return 10;
+}
+
+static bool
+iwl_mld_channel_load_allows_emlsr(struct iwl_mld *mld,
+ struct ieee80211_vif *vif,
+ const struct iwl_mld_link_sel_data *a,
+ const struct iwl_mld_link_sel_data *b)
+{
+ struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
+ struct iwl_mld_link *link_a =
+ iwl_mld_link_dereference_check(mld_vif, a->link_id);
+ struct ieee80211_chanctx_conf *chanctx_a = NULL;
+ u32 bw_a, bw_b, ratio;
+ u32 primary_load_perc;
+
+ if (!link_a || !link_a->active) {
+ IWL_DEBUG_EHT(mld, "Primary link is not active. Can't enter EMLSR\n");
+ return false;
+ }
+
+ chanctx_a = wiphy_dereference(mld->wiphy, link_a->chan_ctx);
+
+ if (WARN_ON(!chanctx_a))
+ return false;
+
+ primary_load_perc =
+ iwl_mld_phy_from_mac80211(chanctx_a)->avg_channel_load_not_by_us;
+
+ IWL_DEBUG_EHT(mld, "Average channel load not by us: %u\n", primary_load_perc);
+
+ if (primary_load_perc < iwl_mld_get_min_chan_load_thresh(chanctx_a)) {
+ IWL_DEBUG_EHT(mld, "Channel load is below the minimum threshold\n");
+ return false;
+ }
+
+ if (iwl_mld_vif_low_latency(mld_vif)) {
+ IWL_DEBUG_EHT(mld, "Low latency vif, EMLSR is allowed\n");
+ return true;
+ }
+
+ if (a->chandef->width <= b->chandef->width)
+ return true;
+
+ bw_a = cfg80211_chandef_get_width(a->chandef);
+ bw_b = cfg80211_chandef_get_width(b->chandef);
+ ratio = bw_a / bw_b;
+
+ switch (ratio) {
+ case 2:
+ return primary_load_perc > 25;
+ case 4:
+ return primary_load_perc > 40;
+ case 8:
+ case 16:
+ return primary_load_perc > 50;
+ }
+
+ return false;
+}
+
+VISIBLE_IF_IWLWIFI_KUNIT u32
+iwl_mld_emlsr_pair_state(struct ieee80211_vif *vif,
+ struct iwl_mld_link_sel_data *a,
+ struct iwl_mld_link_sel_data *b)
+{
+ struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
+ struct iwl_mld *mld = mld_vif->mld;
+ u32 reason_mask = 0;
+
+ /* Per-link considerations */
+ reason_mask = iwl_mld_emlsr_disallowed_with_link(mld, vif, a, true);
+ if (reason_mask)
+ return reason_mask;
+
+ reason_mask = iwl_mld_emlsr_disallowed_with_link(mld, vif, b, false);
+ if (reason_mask)
+ return reason_mask;
+
+ if (a->chandef->chan->band == b->chandef->chan->band) {
+ const struct cfg80211_chan_def *c_low = a->chandef;
+ const struct cfg80211_chan_def *c_high = b->chandef;
+ u32 c_low_upper_edge, c_high_lower_edge;
+
+ if (c_low->chan->center_freq > c_high->chan->center_freq)
+ swap(c_low, c_high);
+
+ c_low_upper_edge = c_low->chan->center_freq +
+ cfg80211_chandef_get_width(c_low) / 2;
+ c_high_lower_edge = c_high->chan->center_freq -
+ cfg80211_chandef_get_width(c_high) / 2;
+
+ if (a->chandef->chan->band == NL80211_BAND_5GHZ &&
+ c_low_upper_edge <= 5330 && c_high_lower_edge >= 5490) {
+ /* This case is fine - HW/FW can deal with it, there's
+ * enough separation between the two channels.
+ */
+ } else {
+ reason_mask |= IWL_MLD_EMLSR_EXIT_EQUAL_BAND;
+ }
+ }
+ if (!iwl_mld_channel_load_allows_emlsr(mld, vif, a, b))
+ reason_mask |= IWL_MLD_EMLSR_EXIT_CHAN_LOAD;
+
+ if (reason_mask) {
+ IWL_DEBUG_INFO(mld,
+ "Links %d and %d are not a valid pair for EMLSR\n",
+ a->link_id, b->link_id);
+ IWL_DEBUG_INFO(mld,
+ "Links bandwidth are: %d and %d\n",
+ nl80211_chan_width_to_mhz(a->chandef->width),
+ nl80211_chan_width_to_mhz(b->chandef->width));
+ iwl_mld_print_emlsr_exit(mld, reason_mask);
+ }
+
+ return reason_mask;
+}
+EXPORT_SYMBOL_IF_IWLWIFI_KUNIT(iwl_mld_emlsr_pair_state);
+
+/* Calculation is done with fixed-point with a scaling factor of 1/256 */
+#define SCALE_FACTOR 256
+
+/*
+ * Returns the combined grade of two given links.
+ * Returns 0 if EMLSR is not allowed with these 2 links.
+ */
+static
+unsigned int iwl_mld_get_emlsr_grade(struct iwl_mld *mld,
+ struct ieee80211_vif *vif,
+ struct iwl_mld_link_sel_data *a,
+ struct iwl_mld_link_sel_data *b,
+ u8 *primary_id)
+{
+ struct ieee80211_bss_conf *primary_conf;
+ struct wiphy *wiphy = ieee80211_vif_to_wdev(vif)->wiphy;
+ unsigned int primary_load;
+
+ lockdep_assert_wiphy(wiphy);
+
+ /* a is always primary, b is always secondary */
+ if (b->grade > a->grade)
+ swap(a, b);
+
+ *primary_id = a->link_id;
+
+ if (iwl_mld_emlsr_pair_state(vif, a, b))
+ return 0;
+
+ primary_conf = wiphy_dereference(wiphy, vif->link_conf[*primary_id]);
+
+ if (WARN_ON_ONCE(!primary_conf))
+ return 0;
+
+ primary_load = iwl_mld_get_chan_load(mld, primary_conf);
+
+ /* The more the primary link is loaded, the more worthwhile EMLSR becomes */
+ return a->grade + ((b->grade * primary_load) / SCALE_FACTOR);
+}
+
+static void _iwl_mld_select_links(struct iwl_mld *mld,
+ struct ieee80211_vif *vif)
+{
+ struct iwl_mld_link_sel_data data[IEEE80211_MLD_MAX_NUM_LINKS];
+ struct iwl_mld_link_sel_data *best_link;
+ struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
+ int max_active_links = iwl_mld_max_active_links(mld, vif);
+ u16 new_active, usable_links = ieee80211_vif_usable_links(vif);
+ u8 best_idx, new_primary, n_data;
+ u16 max_grade;
+
+ lockdep_assert_wiphy(mld->wiphy);
+
+ if (!mld_vif->authorized || hweight16(usable_links) <= 1)
+ return;
+
+ if (WARN(ktime_before(mld->scan.last_mlo_scan_time,
+ ktime_sub_ns(ktime_get_boottime_ns(),
+ 5ULL * NSEC_PER_SEC)),
+ "Last MLO scan was too long ago, can't select links\n"))
+ return;
+
+ /* The logic below is simple and not suited for more than 2 links */
+ WARN_ON_ONCE(max_active_links > 2);
+
+ n_data = iwl_mld_set_link_sel_data(mld, vif, data, usable_links,
+ &best_idx);
+
+ if (!n_data) {
+ IWL_DEBUG_EHT(mld,
+ "Couldn't find a valid grade for any link!\n");
+ return;
+ }
+
+ /* Default to selecting the single best link */
+ best_link = &data[best_idx];
+ new_primary = best_link->link_id;
+ new_active = BIT(best_link->link_id);
+ max_grade = best_link->grade;
+
+ /* If EMLSR is not possible, activate the best link */
+ if (max_active_links == 1 || n_data == 1 ||
+ !iwl_mld_vif_has_emlsr_cap(vif) || !IWL_MLD_AUTO_EML_ENABLE ||
+ mld_vif->emlsr.blocked_reasons)
+ goto set_active;
+
+ /* Try to find the best link combination */
+ for (u8 a = 0; a < n_data; a++) {
+ for (u8 b = a + 1; b < n_data; b++) {
+ u8 best_in_pair;
+ u16 emlsr_grade =
+ iwl_mld_get_emlsr_grade(mld, vif,
+ &data[a], &data[b],
+ &best_in_pair);
+
+ /*
+ * Prefer (new) EMLSR combination to prefer EMLSR over
+ * a single link.
+ */
+ if (emlsr_grade < max_grade)
+ continue;
+
+ max_grade = emlsr_grade;
+ new_primary = best_in_pair;
+ new_active = BIT(data[a].link_id) |
+ BIT(data[b].link_id);
+ }
+ }
+
+set_active:
+ IWL_DEBUG_INFO(mld, "Link selection result: 0x%x. Primary = %d\n",
+ new_active, new_primary);
+
+ mld_vif->emlsr.selected_primary = new_primary;
+ mld_vif->emlsr.selected_links = new_active;
+
+ ieee80211_set_active_links_async(vif, new_active);
+}
+
+static void iwl_mld_vif_iter_select_links(void *_data, u8 *mac,
+ struct ieee80211_vif *vif)
+{
+ struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
+ struct iwl_mld *mld = mld_vif->mld;
+
+ _iwl_mld_select_links(mld, vif);
+}
+
+void iwl_mld_select_links(struct iwl_mld *mld)
+{
+ ieee80211_iterate_active_interfaces_mtx(mld->hw,
+ IEEE80211_IFACE_ITER_NORMAL,
+ iwl_mld_vif_iter_select_links,
+ NULL);
+}
+
+static void iwl_mld_emlsr_check_bt_iter(void *_data, u8 *mac,
+ struct ieee80211_vif *vif)
+{
+ struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
+ const struct iwl_bt_coex_profile_notif zero_notif = {};
+ struct iwl_mld *mld = mld_vif->mld;
+ struct ieee80211_bss_conf *link;
+ unsigned int link_id;
+ const struct iwl_bt_coex_profile_notif *notif = &mld->last_bt_notif;
+
+ if (!iwl_mld_vif_has_emlsr_cap(vif))
+ return;
+
+ /* zeroed structure means that BT is OFF */
+ if (!memcmp(notif, &zero_notif, sizeof(*notif))) {
+ iwl_mld_retry_emlsr(mld, vif);
+ return;
+ }
+
+ for_each_vif_active_link(vif, link, link_id) {
+ bool emlsr_active, emlsr_allowed;
+
+ if (WARN_ON(!link->chanreq.oper.chan))
+ continue;
+
+ if (link->chanreq.oper.chan->band != NL80211_BAND_2GHZ)
+ continue;
+
+ emlsr_active = iwl_mld_emlsr_active(vif);
+ emlsr_allowed = iwl_mld_bt_allows_emlsr(mld, link,
+ !emlsr_active);
+ if (emlsr_allowed && !emlsr_active) {
+ iwl_mld_retry_emlsr(mld, vif);
+ return;
+ }
+
+ if (!emlsr_allowed && emlsr_active) {
+ iwl_mld_exit_emlsr(mld, vif,
+ IWL_MLD_EMLSR_EXIT_BT_COEX,
+ iwl_mld_get_primary_link(vif));
+ return;
+ }
+ }
+}
+
+void iwl_mld_emlsr_check_bt(struct iwl_mld *mld)
+{
+ ieee80211_iterate_active_interfaces_mtx(mld->hw,
+ IEEE80211_IFACE_ITER_NORMAL,
+ iwl_mld_emlsr_check_bt_iter,
+ NULL);
+}
+
+struct iwl_mld_chan_load_data {
+ struct iwl_mld_phy *phy;
+ u32 prev_chan_load_not_by_us;
+};
+
+static void iwl_mld_chan_load_update_iter(void *_data, u8 *mac,
+ struct ieee80211_vif *vif)
+{
+ struct iwl_mld_chan_load_data *data = _data;
+ const struct iwl_mld_phy *phy = data->phy;
+ struct ieee80211_chanctx_conf *chanctx =
+ container_of((const void *)phy, struct ieee80211_chanctx_conf,
+ drv_priv);
+ struct iwl_mld *mld = iwl_mld_vif_from_mac80211(vif)->mld;
+ struct ieee80211_bss_conf *prim_link;
+ unsigned int prim_link_id;
+
+ prim_link_id = iwl_mld_get_primary_link(vif);
+ prim_link = link_conf_dereference_protected(vif, prim_link_id);
+
+ if (WARN_ON(!prim_link))
+ return;
+
+ if (chanctx != rcu_access_pointer(prim_link->chanctx_conf))
+ return;
+
+ if (iwl_mld_emlsr_active(vif)) {
+ int chan_load = iwl_mld_get_chan_load_by_others(mld, prim_link,
+ true);
+
+ if (chan_load < 0)
+ return;
+
+ /* chan_load is in range [0,255] */
+ if (chan_load < NORMALIZE_PERCENT_TO_255(IWL_MLD_EXIT_EMLSR_CHAN_LOAD))
+ iwl_mld_exit_emlsr(mld, vif,
+ IWL_MLD_EMLSR_EXIT_CHAN_LOAD,
+ prim_link_id);
+ } else {
+ u32 old_chan_load = data->prev_chan_load_not_by_us;
+ u32 new_chan_load = phy->avg_channel_load_not_by_us;
+ u32 min_thresh = iwl_mld_get_min_chan_load_thresh(chanctx);
+
+#define THRESHOLD_CROSSED(threshold) \
+ (old_chan_load <= (threshold) && new_chan_load > (threshold))
+
+ if (THRESHOLD_CROSSED(min_thresh) || THRESHOLD_CROSSED(25) ||
+ THRESHOLD_CROSSED(40) || THRESHOLD_CROSSED(50))
+ iwl_mld_retry_emlsr(mld, vif);
+#undef THRESHOLD_CROSSED
+ }
+}
+
+void iwl_mld_emlsr_check_chan_load(struct ieee80211_hw *hw,
+ struct iwl_mld_phy *phy,
+ u32 prev_chan_load_not_by_us)
+{
+ struct iwl_mld_chan_load_data data = {
+ .phy = phy,
+ .prev_chan_load_not_by_us = prev_chan_load_not_by_us,
+ };
+
+ ieee80211_iterate_active_interfaces_mtx(hw,
+ IEEE80211_IFACE_ITER_NORMAL,
+ iwl_mld_chan_load_update_iter,
+ &data);
+}
+
+void iwl_mld_retry_emlsr(struct iwl_mld *mld, struct ieee80211_vif *vif)
+{
+ struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
+
+ if (!iwl_mld_vif_has_emlsr_cap(vif) || iwl_mld_emlsr_active(vif) ||
+ mld_vif->emlsr.blocked_reasons)
+ return;
+
+ iwl_mld_int_mlo_scan(mld, vif);
+}
+
+static void iwl_mld_ignore_tpt_iter(void *data, u8 *mac,
+ struct ieee80211_vif *vif)
+{
+ struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
+ struct iwl_mld *mld = mld_vif->mld;
+ struct iwl_mld_sta *mld_sta;
+ bool *start = (void *)data;
+
+ /* check_tpt_wk is only used when TPT block isn't set */
+ if (mld_vif->emlsr.blocked_reasons & IWL_MLD_EMLSR_BLOCKED_TPT ||
+ !IWL_MLD_AUTO_EML_ENABLE || !mld_vif->ap_sta)
+ return;
+
+ mld_sta = iwl_mld_sta_from_mac80211(mld_vif->ap_sta);
+
+ /* We only count for the AP sta in a MLO connection */
+ if (!mld_sta->mpdu_counters)
+ return;
+
+ if (*start) {
+ wiphy_delayed_work_cancel(mld_vif->mld->wiphy,
+ &mld_vif->emlsr.check_tpt_wk);
+ IWL_DEBUG_EHT(mld, "TPT check disabled\n");
+ return;
+ }
+
+ /* Clear the counters so we start from the beginning */
+ for (int q = 0; q < mld->trans->info.num_rxqs; q++) {
+ struct iwl_mld_per_q_mpdu_counter *queue_counter =
+ &mld_sta->mpdu_counters[q];
+
+ spin_lock_bh(&queue_counter->lock);
+
+ memset(queue_counter->per_link, 0,
+ sizeof(queue_counter->per_link));
+
+ spin_unlock_bh(&queue_counter->lock);
+ }
+
+ /* Schedule the check in 5 seconds */
+ wiphy_delayed_work_queue(mld_vif->mld->wiphy,
+ &mld_vif->emlsr.check_tpt_wk,
+ round_jiffies_relative(IWL_MLD_TPT_COUNT_WINDOW));
+ IWL_DEBUG_EHT(mld, "TPT check enabled\n");
+}
+
+void iwl_mld_start_ignoring_tpt_updates(struct iwl_mld *mld)
+{
+ bool start = true;
+
+ ieee80211_iterate_active_interfaces_mtx(mld->hw,
+ IEEE80211_IFACE_ITER_NORMAL,
+ iwl_mld_ignore_tpt_iter,
+ &start);
+}
+
+void iwl_mld_stop_ignoring_tpt_updates(struct iwl_mld *mld)
+{
+ bool start = false;
+
+ ieee80211_iterate_active_interfaces_mtx(mld->hw,
+ IEEE80211_IFACE_ITER_NORMAL,
+ iwl_mld_ignore_tpt_iter,
+ &start);
+}
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/mlo.h b/drivers/net/wireless/intel/iwlwifi/mld/mlo.h
new file mode 100644
index 000000000000..9afa3d6ea649
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/mld/mlo.h
@@ -0,0 +1,173 @@
+/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
+/*
+ * Copyright (C) 2024-2025 Intel Corporation
+ */
+#ifndef __iwl_mld_mlo_h__
+#define __iwl_mld_mlo_h__
+
+#include <linux/ieee80211.h>
+#include <linux/types.h>
+#include <net/mac80211.h>
+#include "iwl-config.h"
+#include "iwl-trans.h"
+#include "iface.h"
+#include "phy.h"
+
+struct iwl_mld;
+
+void iwl_mld_emlsr_prevent_done_wk(struct wiphy *wiphy, struct wiphy_work *wk);
+void iwl_mld_emlsr_tmp_non_bss_done_wk(struct wiphy *wiphy,
+ struct wiphy_work *wk);
+
+static inline bool iwl_mld_emlsr_active(struct ieee80211_vif *vif)
+{
+ /* Set on phy context activation, so should be a good proxy */
+ return !!(vif->driver_flags & IEEE80211_VIF_EML_ACTIVE);
+}
+
+static inline bool iwl_mld_vif_has_emlsr_cap(struct ieee80211_vif *vif)
+{
+ struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
+
+ /* We only track/permit EMLSR state once authorized */
+ if (!mld_vif->authorized)
+ return false;
+
+ /* No EMLSR on dual radio devices */
+ return ieee80211_vif_type_p2p(vif) == NL80211_IFTYPE_STATION &&
+ ieee80211_vif_is_mld(vif) &&
+ vif->cfg.eml_cap & IEEE80211_EML_CAP_EMLSR_SUPP &&
+ !CSR_HW_RFID_IS_CDB(mld_vif->mld->trans->info.hw_rf_id);
+}
+
+static inline int
+iwl_mld_max_active_links(struct iwl_mld *mld, struct ieee80211_vif *vif)
+{
+ if (vif->type == NL80211_IFTYPE_AP)
+ return mld->fw->ucode_capa.num_beacons;
+
+ if (ieee80211_vif_type_p2p(vif) == NL80211_IFTYPE_STATION)
+ return IWL_FW_MAX_ACTIVE_LINKS_NUM;
+
+ /* For now, do not accept more links on other interface types */
+ return 1;
+}
+
+static inline int
+iwl_mld_count_active_links(struct iwl_mld *mld, struct ieee80211_vif *vif)
+{
+ struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
+ struct iwl_mld_link *mld_link;
+ int n_active = 0;
+
+ for_each_mld_vif_valid_link(mld_vif, mld_link) {
+ if (rcu_access_pointer(mld_link->chan_ctx))
+ n_active++;
+ }
+
+ return n_active;
+}
+
+static inline u8 iwl_mld_get_primary_link(struct ieee80211_vif *vif)
+{
+ struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
+
+ lockdep_assert_wiphy(mld_vif->mld->wiphy);
+
+ if (!ieee80211_vif_is_mld(vif) || WARN_ON(!vif->active_links))
+ return 0;
+
+ /* In AP mode, there is no primary link */
+ if (vif->type == NL80211_IFTYPE_AP)
+ return __ffs(vif->active_links);
+
+ if (iwl_mld_emlsr_active(vif) &&
+ !WARN_ON(!(BIT(mld_vif->emlsr.primary) & vif->active_links)))
+ return mld_vif->emlsr.primary;
+
+ return __ffs(vif->active_links);
+}
+
+/*
+ * For non-MLO/single link, this will return the deflink/single active link,
+ * respectively
+ */
+static inline u8 iwl_mld_get_other_link(struct ieee80211_vif *vif, u8 link_id)
+{
+ switch (hweight16(vif->active_links)) {
+ case 0:
+ return 0;
+ default:
+ WARN_ON(1);
+ fallthrough;
+ case 1:
+ return __ffs(vif->active_links);
+ case 2:
+ return __ffs(vif->active_links & ~BIT(link_id));
+ }
+}
+
+s8 iwl_mld_get_emlsr_rssi_thresh(struct iwl_mld *mld,
+ const struct cfg80211_chan_def *chandef,
+ bool low);
+
+/* EMLSR block/unblock and exit */
+void iwl_mld_block_emlsr(struct iwl_mld *mld, struct ieee80211_vif *vif,
+ enum iwl_mld_emlsr_blocked reason, u8 link_to_keep);
+int iwl_mld_block_emlsr_sync(struct iwl_mld *mld, struct ieee80211_vif *vif,
+ enum iwl_mld_emlsr_blocked reason, u8 link_to_keep);
+void iwl_mld_unblock_emlsr(struct iwl_mld *mld, struct ieee80211_vif *vif,
+ enum iwl_mld_emlsr_blocked reason);
+void iwl_mld_exit_emlsr(struct iwl_mld *mld, struct ieee80211_vif *vif,
+ enum iwl_mld_emlsr_exit exit, u8 link_to_keep);
+
+int iwl_mld_emlsr_check_non_bss_block(struct iwl_mld *mld,
+ int pending_link_changes);
+
+void iwl_mld_handle_emlsr_mode_notif(struct iwl_mld *mld,
+ struct iwl_rx_packet *pkt);
+void iwl_mld_handle_emlsr_trans_fail_notif(struct iwl_mld *mld,
+ struct iwl_rx_packet *pkt);
+
+void iwl_mld_emlsr_check_tpt(struct wiphy *wiphy, struct wiphy_work *wk);
+void iwl_mld_emlsr_unblock_tpt_wk(struct wiphy *wiphy, struct wiphy_work *wk);
+
+void iwl_mld_select_links(struct iwl_mld *mld);
+
+void iwl_mld_emlsr_check_bt(struct iwl_mld *mld);
+
+void iwl_mld_emlsr_check_chan_load(struct ieee80211_hw *hw,
+ struct iwl_mld_phy *phy,
+ u32 prev_chan_load_not_by_us);
+
+/**
+ * iwl_mld_retry_emlsr - Retry entering EMLSR
+ * @mld: MLD context
+ * @vif: VIF to retry EMLSR on
+ *
+ * Retry entering EMLSR on the given VIF.
+ * Use this if one of the parameters that can prevent EMLSR has changed.
+ */
+void iwl_mld_retry_emlsr(struct iwl_mld *mld, struct ieee80211_vif *vif);
+
+struct iwl_mld_link_sel_data {
+ u8 link_id;
+ const struct cfg80211_chan_def *chandef;
+ s32 signal;
+ u16 grade;
+};
+
+#if IS_ENABLED(CONFIG_IWLWIFI_KUNIT_TESTS)
+u32 iwl_mld_emlsr_pair_state(struct ieee80211_vif *vif,
+ struct iwl_mld_link_sel_data *a,
+ struct iwl_mld_link_sel_data *b);
+
+bool iwl_mld_bt_allows_emlsr(struct iwl_mld *mld,
+ struct ieee80211_bss_conf *link,
+ bool entry_criteria);
+#endif
+
+void iwl_mld_start_ignoring_tpt_updates(struct iwl_mld *mld);
+void iwl_mld_stop_ignoring_tpt_updates(struct iwl_mld *mld);
+
+#endif /* __iwl_mld_mlo_h__ */
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/notif.c b/drivers/net/wireless/intel/iwlwifi/mld/notif.c
new file mode 100644
index 000000000000..c0e62d46aba6
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/mld/notif.c
@@ -0,0 +1,722 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
+/*
+ * Copyright (C) 2024-2025 Intel Corporation
+ */
+
+#include "mld.h"
+#include "notif.h"
+#include "scan.h"
+#include "iface.h"
+#include "mlo.h"
+#include "iwl-trans.h"
+#include "fw/file.h"
+#include "fw/dbg.h"
+#include "fw/api/cmdhdr.h"
+#include "fw/api/mac-cfg.h"
+#include "session-protect.h"
+#include "fw/api/time-event.h"
+#include "fw/api/tx.h"
+#include "fw/api/rs.h"
+#include "fw/api/offload.h"
+#include "fw/api/stats.h"
+#include "fw/api/rfi.h"
+#include "fw/api/coex.h"
+
+#include "mcc.h"
+#include "link.h"
+#include "tx.h"
+#include "rx.h"
+#include "tlc.h"
+#include "agg.h"
+#include "mac80211.h"
+#include "thermal.h"
+#include "roc.h"
+#include "stats.h"
+#include "coex.h"
+#include "time_sync.h"
+#include "ftm-initiator.h"
+
+/* Please use this in an increasing order of the versions */
+#define CMD_VER_ENTRY(_ver, _struct) \
+ { .size = sizeof(struct _struct), .ver = _ver },
+#define CMD_VERSIONS(name, ...) \
+ static const struct iwl_notif_struct_size \
+ iwl_notif_struct_sizes_##name[] = { __VA_ARGS__ };
+
+#define RX_HANDLER_NO_OBJECT(_grp, _cmd, _name, _context) \
+ {.cmd_id = WIDE_ID(_grp, _cmd), \
+ .context = _context, \
+ .fn = iwl_mld_handle_##_name, \
+ .sizes = iwl_notif_struct_sizes_##_name, \
+ .n_sizes = ARRAY_SIZE(iwl_notif_struct_sizes_##_name), \
+ },
+
+/* Use this for Rx handlers that do not need notification validation */
+#define RX_HANDLER_NO_VAL(_grp, _cmd, _name, _context) \
+ {.cmd_id = WIDE_ID(_grp, _cmd), \
+ .context = _context, \
+ .fn = iwl_mld_handle_##_name, \
+ },
+
+#define RX_HANDLER_VAL_FN(_grp, _cmd, _name, _context) \
+ { .cmd_id = WIDE_ID(_grp, _cmd), \
+ .context = _context, \
+ .fn = iwl_mld_handle_##_name, \
+ .val_fn = iwl_mld_validate_##_name, \
+ },
+
+#define DEFINE_SIMPLE_CANCELLATION(name, notif_struct, id_member) \
+static bool iwl_mld_cancel_##name##_notif(struct iwl_mld *mld, \
+ struct iwl_rx_packet *pkt, \
+ u32 obj_id) \
+{ \
+ const struct notif_struct *notif = (const void *)pkt->data; \
+ \
+ return obj_id == _Generic((notif)->id_member, \
+ __le32: le32_to_cpu((notif)->id_member), \
+ __le16: le16_to_cpu((notif)->id_member), \
+ u8: (notif)->id_member); \
+}
+
+static bool iwl_mld_always_cancel(struct iwl_mld *mld,
+ struct iwl_rx_packet *pkt,
+ u32 obj_id)
+{
+ return true;
+}
+
+/* Currently only defined for the RX_HANDLER_SIZES options. Use this for
+ * notifications that belong to a specific object, and that should be
+ * canceled when the object is removed
+ */
+#define RX_HANDLER_OF_OBJ(_grp, _cmd, _name, _obj_type) \
+ {.cmd_id = WIDE_ID(_grp, _cmd), \
+ /* Only async handlers can be canceled */ \
+ .context = RX_HANDLER_ASYNC, \
+ .fn = iwl_mld_handle_##_name, \
+ .sizes = iwl_notif_struct_sizes_##_name, \
+ .n_sizes = ARRAY_SIZE(iwl_notif_struct_sizes_##_name), \
+ .obj_type = IWL_MLD_OBJECT_TYPE_##_obj_type, \
+ .cancel = iwl_mld_cancel_##_name, \
+ },
+
+#define RX_HANDLER_OF_LINK(_grp, _cmd, _name) \
+ RX_HANDLER_OF_OBJ(_grp, _cmd, _name, LINK) \
+
+#define RX_HANDLER_OF_VIF(_grp, _cmd, _name) \
+ RX_HANDLER_OF_OBJ(_grp, _cmd, _name, VIF) \
+
+#define RX_HANDLER_OF_STA(_grp, _cmd, _name) \
+ RX_HANDLER_OF_OBJ(_grp, _cmd, _name, STA) \
+
+#define RX_HANDLER_OF_ROC(_grp, _cmd, _name) \
+ RX_HANDLER_OF_OBJ(_grp, _cmd, _name, ROC)
+
+#define RX_HANDLER_OF_SCAN(_grp, _cmd, _name) \
+ RX_HANDLER_OF_OBJ(_grp, _cmd, _name, SCAN)
+
+#define RX_HANDLER_OF_FTM_REQ(_grp, _cmd, _name) \
+ RX_HANDLER_OF_OBJ(_grp, _cmd, _name, FTM_REQ)
+
+static void iwl_mld_handle_mfuart_notif(struct iwl_mld *mld,
+ struct iwl_rx_packet *pkt)
+{
+ struct iwl_mfuart_load_notif *mfuart_notif = (void *)pkt->data;
+
+ IWL_DEBUG_INFO(mld,
+ "MFUART: installed ver: 0x%08x, external ver: 0x%08x\n",
+ le32_to_cpu(mfuart_notif->installed_ver),
+ le32_to_cpu(mfuart_notif->external_ver));
+ IWL_DEBUG_INFO(mld,
+ "MFUART: status: 0x%08x, duration: 0x%08x image size: 0x%08x\n",
+ le32_to_cpu(mfuart_notif->status),
+ le32_to_cpu(mfuart_notif->duration),
+ le32_to_cpu(mfuart_notif->image_size));
+}
+
+static void iwl_mld_mu_mimo_iface_iterator(void *_data, u8 *mac,
+ struct ieee80211_vif *vif)
+{
+ struct ieee80211_bss_conf *bss_conf = &vif->bss_conf;
+ unsigned int link_id = 0;
+
+ if (WARN(hweight16(vif->active_links) > 1,
+ "no support for this notif while in EMLSR 0x%x\n",
+ vif->active_links))
+ return;
+
+ if (ieee80211_vif_is_mld(vif)) {
+ link_id = __ffs(vif->active_links);
+ bss_conf = link_conf_dereference_check(vif, link_id);
+ }
+
+ if (!WARN_ON(!bss_conf) && bss_conf->mu_mimo_owner) {
+ const struct iwl_mu_group_mgmt_notif *notif = _data;
+
+ BUILD_BUG_ON(sizeof(notif->membership_status) !=
+ WLAN_MEMBERSHIP_LEN);
+ BUILD_BUG_ON(sizeof(notif->user_position) !=
+ WLAN_USER_POSITION_LEN);
+
+ /* MU-MIMO Group Id action frame is little endian. We treat
+ * the data received from firmware as if it came from the
+ * action frame, so no conversion is needed.
+ */
+ ieee80211_update_mu_groups(vif, link_id,
+ (u8 *)&notif->membership_status,
+ (u8 *)&notif->user_position);
+ }
+}
+
+/* This handler is called in SYNC mode because it needs to be serialized with
+ * Rx as specified in ieee80211_update_mu_groups()'s documentation.
+ */
+static void iwl_mld_handle_mu_mimo_grp_notif(struct iwl_mld *mld,
+ struct iwl_rx_packet *pkt)
+{
+ struct iwl_mu_group_mgmt_notif *notif = (void *)pkt->data;
+
+ ieee80211_iterate_active_interfaces_atomic(mld->hw,
+ IEEE80211_IFACE_ITER_NORMAL,
+ iwl_mld_mu_mimo_iface_iterator,
+ notif);
+}
+
+static void
+iwl_mld_handle_channel_switch_start_notif(struct iwl_mld *mld,
+ struct iwl_rx_packet *pkt)
+{
+ struct iwl_channel_switch_start_notif *notif = (void *)pkt->data;
+ u32 link_id = le32_to_cpu(notif->link_id);
+ struct ieee80211_bss_conf *link_conf =
+ iwl_mld_fw_id_to_link_conf(mld, link_id);
+ struct ieee80211_vif *vif;
+
+ if (WARN_ON(!link_conf))
+ return;
+
+ vif = link_conf->vif;
+
+ IWL_DEBUG_INFO(mld,
+ "CSA Start Notification with vif type: %d, link_id: %d\n",
+ vif->type,
+ link_conf->link_id);
+
+ switch (vif->type) {
+ case NL80211_IFTYPE_AP:
+ /* We don't support canceling a CSA as it was advertised
+ * by the AP itself
+ */
+ if (!link_conf->csa_active)
+ return;
+
+ ieee80211_csa_finish(vif, link_conf->link_id);
+ break;
+ case NL80211_IFTYPE_STATION:
+ if (!link_conf->csa_active) {
+ /* Either unexpected cs notif or mac80211 chose to
+ * ignore, for example in channel switch to same channel
+ */
+ struct iwl_cancel_channel_switch_cmd cmd = {
+ .id = cpu_to_le32(link_id),
+ };
+
+ if (iwl_mld_send_cmd_pdu(mld,
+ WIDE_ID(MAC_CONF_GROUP,
+ CANCEL_CHANNEL_SWITCH_CMD),
+ &cmd))
+ IWL_ERR(mld,
+ "Failed to cancel the channel switch\n");
+ return;
+ }
+
+ ieee80211_chswitch_done(vif, true, link_conf->link_id);
+ break;
+
+ default:
+ WARN(1, "CSA on invalid vif type: %d", vif->type);
+ }
+}
+
+static void
+iwl_mld_handle_channel_switch_error_notif(struct iwl_mld *mld,
+ struct iwl_rx_packet *pkt)
+{
+ struct iwl_channel_switch_error_notif *notif = (void *)pkt->data;
+ struct ieee80211_bss_conf *link_conf;
+ struct ieee80211_vif *vif;
+ u32 link_id = le32_to_cpu(notif->link_id);
+ u32 csa_err_mask = le32_to_cpu(notif->csa_err_mask);
+
+ link_conf = iwl_mld_fw_id_to_link_conf(mld, link_id);
+ if (WARN_ON(!link_conf))
+ return;
+
+ vif = link_conf->vif;
+
+ IWL_DEBUG_INFO(mld, "FW reports CSA error: id=%u, csa_err_mask=%u\n",
+ link_id, csa_err_mask);
+
+ if (csa_err_mask & (CS_ERR_COUNT_ERROR |
+ CS_ERR_LONG_DELAY_AFTER_CS |
+ CS_ERR_TX_BLOCK_TIMER_EXPIRED))
+ ieee80211_channel_switch_disconnect(vif);
+}
+
+static void iwl_mld_handle_beacon_notification(struct iwl_mld *mld,
+ struct iwl_rx_packet *pkt)
+{
+ struct iwl_extended_beacon_notif *beacon = (void *)pkt->data;
+
+ mld->ibss_manager = !!beacon->ibss_mgr_status;
+}
+
+/**
+ * DOC: Notification versioning
+ *
+ * The firmware's notifications change from time to time. In order to
+ * differentiate between different versions of the same notification, the
+ * firmware advertises the version of each notification.
+ * Here are listed all the notifications that are supported. Several versions
+ * of the same notification can be allowed at the same time:
+ *
+ * CMD_VERSION(my_multi_version_notif,
+ * CMD_VER_ENTRY(1, iwl_my_multi_version_notif_ver1)
+ * CMD_VER_ENTRY(2, iwl_my_multi_version_notif_ver2)
+ *
+ * etc...
+ *
+ * The driver will enforce that the notification coming from the firmware
+ * has its version listed here and it'll also enforce that the firmware sent
+ * at least enough bytes to cover the structure listed in the CMD_VER_ENTRY.
+ */
+
+CMD_VERSIONS(scan_complete_notif,
+ CMD_VER_ENTRY(1, iwl_umac_scan_complete))
+CMD_VERSIONS(scan_iter_complete_notif,
+ CMD_VER_ENTRY(2, iwl_umac_scan_iter_complete_notif))
+CMD_VERSIONS(mfuart_notif,
+ CMD_VER_ENTRY(2, iwl_mfuart_load_notif))
+CMD_VERSIONS(update_mcc,
+ CMD_VER_ENTRY(1, iwl_mcc_chub_notif))
+CMD_VERSIONS(session_prot_notif,
+ CMD_VER_ENTRY(3, iwl_session_prot_notif))
+CMD_VERSIONS(missed_beacon_notif,
+ CMD_VER_ENTRY(5, iwl_missed_beacons_notif))
+CMD_VERSIONS(tx_resp_notif,
+ CMD_VER_ENTRY(8, iwl_tx_resp)
+ CMD_VER_ENTRY(9, iwl_tx_resp))
+CMD_VERSIONS(compressed_ba_notif,
+ CMD_VER_ENTRY(5, iwl_compressed_ba_notif)
+ CMD_VER_ENTRY(6, iwl_compressed_ba_notif)
+ CMD_VER_ENTRY(7, iwl_compressed_ba_notif))
+CMD_VERSIONS(tlc_notif,
+ CMD_VER_ENTRY(3, iwl_tlc_update_notif)
+ CMD_VER_ENTRY(4, iwl_tlc_update_notif))
+CMD_VERSIONS(mu_mimo_grp_notif,
+ CMD_VER_ENTRY(1, iwl_mu_group_mgmt_notif))
+CMD_VERSIONS(channel_switch_start_notif,
+ CMD_VER_ENTRY(3, iwl_channel_switch_start_notif))
+CMD_VERSIONS(channel_switch_error_notif,
+ CMD_VER_ENTRY(2, iwl_channel_switch_error_notif))
+CMD_VERSIONS(ct_kill_notif,
+ CMD_VER_ENTRY(2, ct_kill_notif))
+CMD_VERSIONS(temp_notif,
+ CMD_VER_ENTRY(2, iwl_dts_measurement_notif))
+CMD_VERSIONS(roc_notif,
+ CMD_VER_ENTRY(1, iwl_roc_notif))
+CMD_VERSIONS(probe_resp_data_notif,
+ CMD_VER_ENTRY(1, iwl_probe_resp_data_notif))
+CMD_VERSIONS(datapath_monitor_notif,
+ CMD_VER_ENTRY(1, iwl_datapath_monitor_notif))
+CMD_VERSIONS(stats_oper_notif,
+ CMD_VER_ENTRY(3, iwl_system_statistics_notif_oper))
+CMD_VERSIONS(stats_oper_part1_notif,
+ CMD_VER_ENTRY(4, iwl_system_statistics_part1_notif_oper))
+CMD_VERSIONS(bt_coex_notif,
+ CMD_VER_ENTRY(1, iwl_bt_coex_profile_notif))
+CMD_VERSIONS(beacon_notification,
+ CMD_VER_ENTRY(6, iwl_extended_beacon_notif))
+CMD_VERSIONS(emlsr_mode_notif,
+ CMD_VER_ENTRY(1, iwl_esr_mode_notif_v1)
+ CMD_VER_ENTRY(2, iwl_esr_mode_notif))
+CMD_VERSIONS(emlsr_trans_fail_notif,
+ CMD_VER_ENTRY(1, iwl_esr_trans_fail_notif))
+CMD_VERSIONS(uapsd_misbehaving_ap_notif,
+ CMD_VER_ENTRY(1, iwl_uapsd_misbehaving_ap_notif))
+CMD_VERSIONS(time_msmt_notif,
+ CMD_VER_ENTRY(1, iwl_time_msmt_notify))
+CMD_VERSIONS(time_sync_confirm_notif,
+ CMD_VER_ENTRY(1, iwl_time_msmt_cfm_notify))
+CMD_VERSIONS(omi_status_notif,
+ CMD_VER_ENTRY(1, iwl_omi_send_status_notif))
+CMD_VERSIONS(ftm_resp_notif, CMD_VER_ENTRY(9, iwl_tof_range_rsp_ntfy))
+
+DEFINE_SIMPLE_CANCELLATION(session_prot, iwl_session_prot_notif, mac_link_id)
+DEFINE_SIMPLE_CANCELLATION(tlc, iwl_tlc_update_notif, sta_id)
+DEFINE_SIMPLE_CANCELLATION(channel_switch_start,
+ iwl_channel_switch_start_notif, link_id)
+DEFINE_SIMPLE_CANCELLATION(channel_switch_error,
+ iwl_channel_switch_error_notif, link_id)
+DEFINE_SIMPLE_CANCELLATION(datapath_monitor, iwl_datapath_monitor_notif,
+ link_id)
+DEFINE_SIMPLE_CANCELLATION(roc, iwl_roc_notif, activity)
+DEFINE_SIMPLE_CANCELLATION(scan_complete, iwl_umac_scan_complete, uid)
+DEFINE_SIMPLE_CANCELLATION(probe_resp_data, iwl_probe_resp_data_notif,
+ mac_id)
+DEFINE_SIMPLE_CANCELLATION(uapsd_misbehaving_ap, iwl_uapsd_misbehaving_ap_notif,
+ mac_id)
+#define iwl_mld_cancel_omi_status_notif iwl_mld_always_cancel
+DEFINE_SIMPLE_CANCELLATION(ftm_resp, iwl_tof_range_rsp_ntfy, request_id)
+
+/**
+ * DOC: Handlers for fw notifications
+ *
+ * Here are listed the notifications IDs (including the group ID), the handler
+ * of the notification and how it should be called:
+ *
+ * - RX_HANDLER_SYNC: will be called as part of the Rx path
+ * - RX_HANDLER_ASYNC: will be handled in a working with the wiphy_lock held
+ *
+ * This means that if the firmware sends two notifications A and B in that
+ * order and notification A is RX_HANDLER_ASYNC and notification is
+ * RX_HANDLER_SYNC, the handler of B will likely be called before the handler
+ * of A.
+ *
+ * This list should be in order of frequency for performance purposes.
+ * The handler can be one from two contexts, see &iwl_rx_handler_context
+ *
+ * A handler can declare that it relies on a specific object in which case it
+ * can be cancelled in case the object is deleted. In order to use this
+ * mechanism, a cancellation function is needed. The cancellation function must
+ * receive an object id (the index of that object in the firmware) and a
+ * notification payload. It'll return true if that specific notification should
+ * be cancelled upon the obliteration of the specific instance of the object.
+ *
+ * DEFINE_SIMPLE_CANCELLATION allows to easily create a cancellation function
+ * that wills simply return true if a given object id matches the object id in
+ * the firmware notification.
+ */
+
+VISIBLE_IF_IWLWIFI_KUNIT
+const struct iwl_rx_handler iwl_mld_rx_handlers[] = {
+ RX_HANDLER_NO_OBJECT(LEGACY_GROUP, TX_CMD, tx_resp_notif,
+ RX_HANDLER_SYNC)
+ RX_HANDLER_NO_OBJECT(LEGACY_GROUP, BA_NOTIF, compressed_ba_notif,
+ RX_HANDLER_SYNC)
+ RX_HANDLER_OF_SCAN(LEGACY_GROUP, SCAN_COMPLETE_UMAC,
+ scan_complete_notif)
+ RX_HANDLER_NO_OBJECT(LEGACY_GROUP, SCAN_ITERATION_COMPLETE_UMAC,
+ scan_iter_complete_notif,
+ RX_HANDLER_SYNC)
+ RX_HANDLER_NO_VAL(LEGACY_GROUP, MATCH_FOUND_NOTIFICATION,
+ match_found_notif, RX_HANDLER_SYNC)
+
+ RX_HANDLER_NO_OBJECT(STATISTICS_GROUP, STATISTICS_OPER_NOTIF,
+ stats_oper_notif, RX_HANDLER_ASYNC)
+ RX_HANDLER_NO_OBJECT(STATISTICS_GROUP, STATISTICS_OPER_PART1_NOTIF,
+ stats_oper_part1_notif, RX_HANDLER_ASYNC)
+
+ RX_HANDLER_NO_OBJECT(LEGACY_GROUP, MFUART_LOAD_NOTIFICATION,
+ mfuart_notif, RX_HANDLER_SYNC)
+
+ RX_HANDLER_NO_OBJECT(PHY_OPS_GROUP, DTS_MEASUREMENT_NOTIF_WIDE,
+ temp_notif, RX_HANDLER_ASYNC)
+ RX_HANDLER_OF_LINK(MAC_CONF_GROUP, SESSION_PROTECTION_NOTIF,
+ session_prot_notif)
+ RX_HANDLER_OF_LINK(MAC_CONF_GROUP, MISSED_BEACONS_NOTIF,
+ missed_beacon_notif)
+ RX_HANDLER_OF_STA(DATA_PATH_GROUP, TLC_MNG_UPDATE_NOTIF, tlc_notif)
+ RX_HANDLER_OF_LINK(MAC_CONF_GROUP, CHANNEL_SWITCH_START_NOTIF,
+ channel_switch_start_notif)
+ RX_HANDLER_OF_LINK(MAC_CONF_GROUP, CHANNEL_SWITCH_ERROR_NOTIF,
+ channel_switch_error_notif)
+ RX_HANDLER_OF_ROC(MAC_CONF_GROUP, ROC_NOTIF, roc_notif)
+ RX_HANDLER_NO_OBJECT(DATA_PATH_GROUP, MU_GROUP_MGMT_NOTIF,
+ mu_mimo_grp_notif, RX_HANDLER_SYNC)
+ RX_HANDLER_OF_VIF(MAC_CONF_GROUP, PROBE_RESPONSE_DATA_NOTIF,
+ probe_resp_data_notif)
+ RX_HANDLER_NO_OBJECT(PHY_OPS_GROUP, CT_KILL_NOTIFICATION,
+ ct_kill_notif, RX_HANDLER_ASYNC)
+ RX_HANDLER_OF_LINK(DATA_PATH_GROUP, MONITOR_NOTIF,
+ datapath_monitor_notif)
+ RX_HANDLER_NO_OBJECT(LEGACY_GROUP, MCC_CHUB_UPDATE_CMD, update_mcc,
+ RX_HANDLER_ASYNC)
+ RX_HANDLER_NO_OBJECT(BT_COEX_GROUP, PROFILE_NOTIF,
+ bt_coex_notif, RX_HANDLER_ASYNC)
+ RX_HANDLER_NO_OBJECT(LEGACY_GROUP, BEACON_NOTIFICATION,
+ beacon_notification, RX_HANDLER_ASYNC)
+ RX_HANDLER_NO_OBJECT(DATA_PATH_GROUP, ESR_MODE_NOTIF,
+ emlsr_mode_notif, RX_HANDLER_ASYNC)
+ RX_HANDLER_NO_OBJECT(MAC_CONF_GROUP, EMLSR_TRANS_FAIL_NOTIF,
+ emlsr_trans_fail_notif, RX_HANDLER_ASYNC)
+ RX_HANDLER_OF_VIF(LEGACY_GROUP, PSM_UAPSD_AP_MISBEHAVING_NOTIFICATION,
+ uapsd_misbehaving_ap_notif)
+ RX_HANDLER_NO_OBJECT(LEGACY_GROUP,
+ WNM_80211V_TIMING_MEASUREMENT_NOTIFICATION,
+ time_msmt_notif, RX_HANDLER_SYNC)
+ RX_HANDLER_NO_OBJECT(LEGACY_GROUP,
+ WNM_80211V_TIMING_MEASUREMENT_CONFIRM_NOTIFICATION,
+ time_sync_confirm_notif, RX_HANDLER_ASYNC)
+ RX_HANDLER_OF_LINK(DATA_PATH_GROUP, OMI_SEND_STATUS_NOTIF,
+ omi_status_notif)
+ RX_HANDLER_OF_FTM_REQ(LOCATION_GROUP, TOF_RANGE_RESPONSE_NOTIF,
+ ftm_resp_notif)
+};
+EXPORT_SYMBOL_IF_IWLWIFI_KUNIT(iwl_mld_rx_handlers);
+
+#if IS_ENABLED(CONFIG_IWLWIFI_KUNIT_TESTS)
+const unsigned int iwl_mld_rx_handlers_num = ARRAY_SIZE(iwl_mld_rx_handlers);
+EXPORT_SYMBOL_IF_IWLWIFI_KUNIT(iwl_mld_rx_handlers_num);
+#endif
+
+static bool
+iwl_mld_notif_is_valid(struct iwl_mld *mld, struct iwl_rx_packet *pkt,
+ const struct iwl_rx_handler *handler)
+{
+ unsigned int size = iwl_rx_packet_payload_len(pkt);
+ size_t notif_ver;
+
+ /* If n_sizes == 0, it indicates that a validation function may be used
+ * or that no validation is required.
+ */
+ if (!handler->n_sizes) {
+ if (handler->val_fn)
+ return handler->val_fn(mld, pkt);
+ return true;
+ }
+
+ notif_ver = iwl_fw_lookup_notif_ver(mld->fw,
+ iwl_cmd_groupid(handler->cmd_id),
+ iwl_cmd_opcode(handler->cmd_id),
+ IWL_FW_CMD_VER_UNKNOWN);
+
+ for (int i = 0; i < handler->n_sizes; i++) {
+ if (handler->sizes[i].ver != notif_ver)
+ continue;
+
+ if (IWL_FW_CHECK(mld, size < handler->sizes[i].size,
+ "unexpected notification 0x%04x size %d, need %d\n",
+ handler->cmd_id, size, handler->sizes[i].size))
+ return false;
+ return true;
+ }
+
+ IWL_FW_CHECK_FAILED(mld,
+ "notif 0x%04x ver %zu missing expected size, use version %u size\n",
+ handler->cmd_id, notif_ver,
+ handler->sizes[handler->n_sizes - 1].ver);
+
+ return size < handler->sizes[handler->n_sizes - 1].size;
+}
+
+struct iwl_async_handler_entry {
+ struct list_head list;
+ struct iwl_rx_cmd_buffer rxb;
+ const struct iwl_rx_handler *rx_h;
+};
+
+static void
+iwl_mld_log_async_handler_op(struct iwl_mld *mld, const char *op,
+ struct iwl_rx_cmd_buffer *rxb)
+{
+ struct iwl_rx_packet *pkt = rxb_addr(rxb);
+
+ IWL_DEBUG_HC(mld,
+ "%s async handler for notif %s (%.2x.%2x, seq 0x%x)\n",
+ op, iwl_get_cmd_string(mld->trans,
+ WIDE_ID(pkt->hdr.group_id, pkt->hdr.cmd)),
+ pkt->hdr.group_id, pkt->hdr.cmd,
+ le16_to_cpu(pkt->hdr.sequence));
+}
+
+static void iwl_mld_rx_notif(struct iwl_mld *mld,
+ struct iwl_rx_cmd_buffer *rxb,
+ struct iwl_rx_packet *pkt)
+{
+ for (int i = 0; i < ARRAY_SIZE(iwl_mld_rx_handlers); i++) {
+ const struct iwl_rx_handler *rx_h = &iwl_mld_rx_handlers[i];
+ struct iwl_async_handler_entry *entry;
+
+ if (rx_h->cmd_id != WIDE_ID(pkt->hdr.group_id, pkt->hdr.cmd))
+ continue;
+
+ if (!iwl_mld_notif_is_valid(mld, pkt, rx_h))
+ return;
+
+ if (rx_h->context == RX_HANDLER_SYNC) {
+ rx_h->fn(mld, pkt);
+ break;
+ }
+
+ entry = kzalloc(sizeof(*entry), GFP_ATOMIC);
+ /* we can't do much... */
+ if (!entry)
+ return;
+
+ /* Set the async handler entry */
+ entry->rxb._page = rxb_steal_page(rxb);
+ entry->rxb._offset = rxb->_offset;
+ entry->rxb._rx_page_order = rxb->_rx_page_order;
+
+ entry->rx_h = rx_h;
+
+ /* Add it to the list and queue the work */
+ spin_lock(&mld->async_handlers_lock);
+ list_add_tail(&entry->list, &mld->async_handlers_list);
+ spin_unlock(&mld->async_handlers_lock);
+
+ wiphy_work_queue(mld->hw->wiphy,
+ &mld->async_handlers_wk);
+
+ iwl_mld_log_async_handler_op(mld, "Queued", rxb);
+ break;
+ }
+
+ iwl_notification_wait_notify(&mld->notif_wait, pkt);
+}
+
+void iwl_mld_rx(struct iwl_op_mode *op_mode, struct napi_struct *napi,
+ struct iwl_rx_cmd_buffer *rxb)
+{
+ struct iwl_rx_packet *pkt = rxb_addr(rxb);
+ struct iwl_mld *mld = IWL_OP_MODE_GET_MLD(op_mode);
+ u16 cmd_id = WIDE_ID(pkt->hdr.group_id, pkt->hdr.cmd);
+
+ if (likely(cmd_id == WIDE_ID(LEGACY_GROUP, REPLY_RX_MPDU_CMD)))
+ iwl_mld_rx_mpdu(mld, napi, rxb, 0);
+ else if (cmd_id == WIDE_ID(LEGACY_GROUP, FRAME_RELEASE))
+ iwl_mld_handle_frame_release_notif(mld, napi, pkt, 0);
+ else if (cmd_id == WIDE_ID(LEGACY_GROUP, BAR_FRAME_RELEASE))
+ iwl_mld_handle_bar_frame_release_notif(mld, napi, pkt, 0);
+ else if (unlikely(cmd_id == WIDE_ID(DATA_PATH_GROUP,
+ RX_QUEUES_NOTIFICATION)))
+ iwl_mld_handle_rx_queues_sync_notif(mld, napi, pkt, 0);
+ else if (cmd_id == WIDE_ID(DATA_PATH_GROUP, RX_NO_DATA_NOTIF))
+ iwl_mld_rx_monitor_no_data(mld, napi, pkt, 0);
+ else
+ iwl_mld_rx_notif(mld, rxb, pkt);
+}
+
+void iwl_mld_rx_rss(struct iwl_op_mode *op_mode, struct napi_struct *napi,
+ struct iwl_rx_cmd_buffer *rxb, unsigned int queue)
+{
+ struct iwl_rx_packet *pkt = rxb_addr(rxb);
+ struct iwl_mld *mld = IWL_OP_MODE_GET_MLD(op_mode);
+ u16 cmd_id = WIDE_ID(pkt->hdr.group_id, pkt->hdr.cmd);
+
+ if (unlikely(queue >= mld->trans->info.num_rxqs))
+ return;
+
+ if (likely(cmd_id == WIDE_ID(LEGACY_GROUP, REPLY_RX_MPDU_CMD)))
+ iwl_mld_rx_mpdu(mld, napi, rxb, queue);
+ else if (unlikely(cmd_id == WIDE_ID(DATA_PATH_GROUP,
+ RX_QUEUES_NOTIFICATION)))
+ iwl_mld_handle_rx_queues_sync_notif(mld, napi, pkt, queue);
+ else if (unlikely(cmd_id == WIDE_ID(LEGACY_GROUP, FRAME_RELEASE)))
+ iwl_mld_handle_frame_release_notif(mld, napi, pkt, queue);
+}
+
+void iwl_mld_delete_handlers(struct iwl_mld *mld, const u16 *cmds, int n_cmds)
+{
+ struct iwl_async_handler_entry *entry, *tmp;
+
+ spin_lock_bh(&mld->async_handlers_lock);
+ list_for_each_entry_safe(entry, tmp, &mld->async_handlers_list, list) {
+ bool match = false;
+
+ for (int i = 0; i < n_cmds; i++) {
+ if (entry->rx_h->cmd_id == cmds[i]) {
+ match = true;
+ break;
+ }
+ }
+
+ if (!match)
+ continue;
+
+ iwl_mld_log_async_handler_op(mld, "Delete", &entry->rxb);
+ iwl_free_rxb(&entry->rxb);
+ list_del(&entry->list);
+ kfree(entry);
+ }
+ spin_unlock_bh(&mld->async_handlers_lock);
+}
+
+void iwl_mld_async_handlers_wk(struct wiphy *wiphy, struct wiphy_work *wk)
+{
+ struct iwl_mld *mld =
+ container_of(wk, struct iwl_mld, async_handlers_wk);
+ struct iwl_async_handler_entry *entry, *tmp;
+ LIST_HEAD(local_list);
+
+ /* Sync with Rx path with a lock. Remove all the entries from this
+ * list, add them to a local one (lock free), and then handle them.
+ */
+ spin_lock_bh(&mld->async_handlers_lock);
+ list_splice_init(&mld->async_handlers_list, &local_list);
+ spin_unlock_bh(&mld->async_handlers_lock);
+
+ list_for_each_entry_safe(entry, tmp, &local_list, list) {
+ iwl_mld_log_async_handler_op(mld, "Handle", &entry->rxb);
+ entry->rx_h->fn(mld, rxb_addr(&entry->rxb));
+ iwl_free_rxb(&entry->rxb);
+ list_del(&entry->list);
+ kfree(entry);
+ }
+}
+
+void iwl_mld_cancel_async_notifications(struct iwl_mld *mld)
+{
+ struct iwl_async_handler_entry *entry, *tmp;
+
+ lockdep_assert_wiphy(mld->wiphy);
+
+ wiphy_work_cancel(mld->wiphy, &mld->async_handlers_wk);
+
+ spin_lock_bh(&mld->async_handlers_lock);
+ list_for_each_entry_safe(entry, tmp, &mld->async_handlers_list, list) {
+ iwl_mld_log_async_handler_op(mld, "Purged", &entry->rxb);
+ iwl_free_rxb(&entry->rxb);
+ list_del(&entry->list);
+ kfree(entry);
+ }
+ spin_unlock_bh(&mld->async_handlers_lock);
+}
+
+void iwl_mld_cancel_notifications_of_object(struct iwl_mld *mld,
+ enum iwl_mld_object_type obj_type,
+ u32 obj_id)
+{
+ struct iwl_async_handler_entry *entry, *tmp;
+ LIST_HEAD(cancel_list);
+
+ lockdep_assert_wiphy(mld->wiphy);
+
+ if (WARN_ON(obj_type == IWL_MLD_OBJECT_TYPE_NONE))
+ return;
+
+ /* Sync with RX path and remove matching entries from the async list */
+ spin_lock_bh(&mld->async_handlers_lock);
+ list_for_each_entry_safe(entry, tmp, &mld->async_handlers_list, list) {
+ const struct iwl_rx_handler *rx_h = entry->rx_h;
+
+ if (rx_h->obj_type != obj_type || WARN_ON(!rx_h->cancel))
+ continue;
+
+ if (rx_h->cancel(mld, rxb_addr(&entry->rxb), obj_id)) {
+ iwl_mld_log_async_handler_op(mld, "Cancel", &entry->rxb);
+ list_del(&entry->list);
+ list_add_tail(&entry->list, &cancel_list);
+ }
+ }
+
+ spin_unlock_bh(&mld->async_handlers_lock);
+
+ /* Free the matching entries outside of the spinlock */
+ list_for_each_entry_safe(entry, tmp, &cancel_list, list) {
+ iwl_free_rxb(&entry->rxb);
+ list_del(&entry->list);
+ kfree(entry);
+ }
+}
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/notif.h b/drivers/net/wireless/intel/iwlwifi/mld/notif.h
new file mode 100644
index 000000000000..adcdd9dec192
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/mld/notif.h
@@ -0,0 +1,35 @@
+/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
+/*
+ * Copyright (C) 2024-2025 Intel Corporation
+ */
+#ifndef __iwl_mld_notif_h__
+#define __iwl_mld_notif_h__
+
+struct iwl_mld;
+
+void iwl_mld_rx(struct iwl_op_mode *op_mode, struct napi_struct *napi,
+ struct iwl_rx_cmd_buffer *rxb);
+
+void iwl_mld_rx_rss(struct iwl_op_mode *op_mode, struct napi_struct *napi,
+ struct iwl_rx_cmd_buffer *rxb, unsigned int queue);
+
+void iwl_mld_async_handlers_wk(struct wiphy *wiphy, struct wiphy_work *wk);
+
+void iwl_mld_cancel_async_notifications(struct iwl_mld *mld);
+
+enum iwl_mld_object_type {
+ IWL_MLD_OBJECT_TYPE_NONE,
+ IWL_MLD_OBJECT_TYPE_LINK,
+ IWL_MLD_OBJECT_TYPE_STA,
+ IWL_MLD_OBJECT_TYPE_VIF,
+ IWL_MLD_OBJECT_TYPE_ROC,
+ IWL_MLD_OBJECT_TYPE_SCAN,
+ IWL_MLD_OBJECT_TYPE_FTM_REQ,
+};
+
+void iwl_mld_cancel_notifications_of_object(struct iwl_mld *mld,
+ enum iwl_mld_object_type obj_type,
+ u32 obj_id);
+void iwl_mld_delete_handlers(struct iwl_mld *mld, const u16 *cmds, int n_cmds);
+
+#endif /* __iwl_mld_notif_h__ */
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/phy.c b/drivers/net/wireless/intel/iwlwifi/mld/phy.c
new file mode 100644
index 000000000000..d5a32ee56b92
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/mld/phy.c
@@ -0,0 +1,198 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
+/*
+ * Copyright (C) 2024-2025 Intel Corporation
+ */
+#include <net/mac80211.h>
+
+#include "phy.h"
+#include "hcmd.h"
+#include "fw/api/phy-ctxt.h"
+
+int iwl_mld_allocate_fw_phy_id(struct iwl_mld *mld)
+{
+ int id;
+ unsigned long used = mld->used_phy_ids;
+
+ for_each_clear_bit(id, &used, NUM_PHY_CTX) {
+ mld->used_phy_ids |= BIT(id);
+ return id;
+ }
+
+ return -ENOSPC;
+}
+EXPORT_SYMBOL_IF_IWLWIFI_KUNIT(iwl_mld_allocate_fw_phy_id);
+
+struct iwl_mld_chanctx_usage_data {
+ struct iwl_mld *mld;
+ struct ieee80211_chanctx_conf *ctx;
+ bool use_def;
+};
+
+static bool iwl_mld_chanctx_fils_enabled(struct ieee80211_vif *vif,
+ struct ieee80211_chanctx_conf *ctx)
+{
+ if (vif->type != NL80211_IFTYPE_AP)
+ return false;
+
+ return cfg80211_channel_is_psc(ctx->def.chan) ||
+ (ctx->def.chan->band == NL80211_BAND_6GHZ &&
+ ctx->def.width >= NL80211_CHAN_WIDTH_80);
+}
+
+static void iwl_mld_chanctx_usage_iter(void *_data, u8 *mac,
+ struct ieee80211_vif *vif)
+{
+ struct iwl_mld_chanctx_usage_data *data = _data;
+ struct ieee80211_bss_conf *link_conf;
+ int link_id;
+
+ for_each_vif_active_link(vif, link_conf, link_id) {
+ if (rcu_access_pointer(link_conf->chanctx_conf) != data->ctx)
+ continue;
+
+ if (vif->type == NL80211_IFTYPE_AP && link_conf->ftm_responder)
+ data->use_def = true;
+
+ if (iwl_mld_chanctx_fils_enabled(vif, data->ctx))
+ data->use_def = true;
+ }
+}
+
+struct cfg80211_chan_def *
+iwl_mld_get_chandef_from_chanctx(struct iwl_mld *mld,
+ struct ieee80211_chanctx_conf *ctx)
+{
+ struct iwl_mld_chanctx_usage_data data = {
+ .mld = mld,
+ .ctx = ctx,
+ };
+
+ ieee80211_iterate_active_interfaces_mtx(mld->hw,
+ IEEE80211_IFACE_ITER_NORMAL,
+ iwl_mld_chanctx_usage_iter,
+ &data);
+
+ return data.use_def ? &ctx->def : &ctx->min_def;
+}
+
+static u8
+iwl_mld_nl80211_width_to_fw(enum nl80211_chan_width width)
+{
+ switch (width) {
+ case NL80211_CHAN_WIDTH_20_NOHT:
+ case NL80211_CHAN_WIDTH_20:
+ return IWL_PHY_CHANNEL_MODE20;
+ case NL80211_CHAN_WIDTH_40:
+ return IWL_PHY_CHANNEL_MODE40;
+ case NL80211_CHAN_WIDTH_80:
+ return IWL_PHY_CHANNEL_MODE80;
+ case NL80211_CHAN_WIDTH_160:
+ return IWL_PHY_CHANNEL_MODE160;
+ case NL80211_CHAN_WIDTH_320:
+ return IWL_PHY_CHANNEL_MODE320;
+ default:
+ WARN(1, "Invalid channel width=%u", width);
+ return IWL_PHY_CHANNEL_MODE20;
+ }
+}
+
+/* Maps the driver specific control channel position (relative to the center
+ * freq) definitions to the fw values
+ */
+u8 iwl_mld_get_fw_ctrl_pos(const struct cfg80211_chan_def *chandef)
+{
+ int offs = chandef->chan->center_freq - chandef->center_freq1;
+ int abs_offs = abs(offs);
+ u8 ret;
+
+ if (offs == 0) {
+ /* The FW is expected to check the control channel position only
+ * when in HT/VHT and the channel width is not 20MHz. Return
+ * this value as the default one.
+ */
+ return 0;
+ }
+
+ /* this results in a value 0-7, i.e. fitting into 0b0111 */
+ ret = (abs_offs - 10) / 20;
+ /* But we need the value to be in 0b1011 because 0b0100 is
+ * IWL_PHY_CTRL_POS_ABOVE, so shift bit 2 up to land in
+ * IWL_PHY_CTRL_POS_OFFS_EXT (0b1000)
+ */
+ ret = (ret & IWL_PHY_CTRL_POS_OFFS_MSK) |
+ ((ret & BIT(2)) << 1);
+ /* and add the above bit */
+ ret |= (offs > 0) * IWL_PHY_CTRL_POS_ABOVE;
+
+ return ret;
+}
+
+int iwl_mld_phy_fw_action(struct iwl_mld *mld,
+ struct ieee80211_chanctx_conf *ctx, u32 action)
+{
+ struct iwl_mld_phy *phy = iwl_mld_phy_from_mac80211(ctx);
+ struct cfg80211_chan_def *chandef = &phy->chandef;
+ struct iwl_phy_context_cmd cmd = {
+ .id_and_color = cpu_to_le32(phy->fw_id),
+ .action = cpu_to_le32(action),
+ .puncture_mask = cpu_to_le16(chandef->punctured),
+ /* Channel info */
+ .ci.channel = cpu_to_le32(chandef->chan->hw_value),
+ .ci.band = iwl_mld_nl80211_band_to_fw(chandef->chan->band),
+ .ci.width = iwl_mld_nl80211_width_to_fw(chandef->width),
+ .ci.ctrl_pos = iwl_mld_get_fw_ctrl_pos(chandef),
+ };
+ int ret;
+
+ if (ctx->ap.chan) {
+ cmd.sbb_bandwidth =
+ iwl_mld_nl80211_width_to_fw(ctx->ap.width);
+ cmd.sbb_ctrl_channel_loc = iwl_mld_get_fw_ctrl_pos(&ctx->ap);
+ }
+
+ ret = iwl_mld_send_cmd_pdu(mld, PHY_CONTEXT_CMD, &cmd);
+ if (ret)
+ IWL_ERR(mld, "Failed to send PHY_CONTEXT_CMD ret = %d\n", ret);
+
+ return ret;
+}
+
+static u32 iwl_mld_get_phy_config(struct iwl_mld *mld)
+{
+ u32 phy_config = ~(FW_PHY_CFG_TX_CHAIN |
+ FW_PHY_CFG_RX_CHAIN);
+ u32 valid_rx_ant = iwl_mld_get_valid_rx_ant(mld);
+ u32 valid_tx_ant = iwl_mld_get_valid_tx_ant(mld);
+
+ phy_config |= valid_tx_ant << FW_PHY_CFG_TX_CHAIN_POS |
+ valid_rx_ant << FW_PHY_CFG_RX_CHAIN_POS;
+
+ return mld->fw->phy_config & phy_config;
+}
+
+int iwl_mld_send_phy_cfg_cmd(struct iwl_mld *mld)
+{
+ const struct iwl_tlv_calib_ctrl *default_calib =
+ &mld->fw->default_calib[IWL_UCODE_REGULAR];
+ struct iwl_phy_cfg_cmd_v3 cmd = {
+ .phy_cfg = cpu_to_le32(iwl_mld_get_phy_config(mld)),
+ .calib_control.event_trigger = default_calib->event_trigger,
+ .calib_control.flow_trigger = default_calib->flow_trigger,
+ .phy_specific_cfg = mld->fwrt.phy_filters,
+ };
+
+ IWL_INFO(mld, "Sending Phy CFG command: 0x%x\n", cmd.phy_cfg);
+
+ return iwl_mld_send_cmd_pdu(mld, PHY_CONFIGURATION_CMD, &cmd);
+}
+
+void iwl_mld_update_phy_chandef(struct iwl_mld *mld,
+ struct ieee80211_chanctx_conf *ctx)
+{
+ struct iwl_mld_phy *phy = iwl_mld_phy_from_mac80211(ctx);
+ struct cfg80211_chan_def *chandef =
+ iwl_mld_get_chandef_from_chanctx(mld, ctx);
+
+ phy->chandef = *chandef;
+ iwl_mld_phy_fw_action(mld, ctx, FW_CTXT_ACTION_MODIFY);
+}
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/phy.h b/drivers/net/wireless/intel/iwlwifi/mld/phy.h
new file mode 100644
index 000000000000..0deaf179f07c
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/mld/phy.h
@@ -0,0 +1,60 @@
+/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
+/*
+ * Copyright (C) 2024-2025 Intel Corporation
+ */
+#ifndef __iwl_mld_phy_h__
+#define __iwl_mld_phy_h__
+
+#include "mld.h"
+
+/**
+ * struct iwl_mld_phy - PHY configuration parameters
+ *
+ * @fw_id: fw id of the phy.
+ * @chandef: the last chandef that mac80211 configured the driver
+ * with. Used to detect a no-op when the chanctx changes.
+ * @channel_load_by_us: channel load on this channel caused by
+ * the NIC itself, as indicated by firmware
+ * @avg_channel_load_not_by_us: averaged channel load on this channel caused by
+ * others. This value is invalid when in EMLSR (due to FW limitations)
+ * @mld: pointer to the MLD context
+ */
+struct iwl_mld_phy {
+ /* Add here fields that need clean up on hw restart */
+ struct_group(zeroed_on_hw_restart,
+ u8 fw_id;
+ struct cfg80211_chan_def chandef;
+ );
+ /* And here fields that survive a hw restart */
+ u32 channel_load_by_us;
+ u32 avg_channel_load_not_by_us;
+ struct iwl_mld *mld;
+};
+
+static inline struct iwl_mld_phy *
+iwl_mld_phy_from_mac80211(struct ieee80211_chanctx_conf *channel)
+{
+ return (void *)channel->drv_priv;
+}
+
+/* Cleanup function for struct iwl_mld_phy, will be called in restart */
+static inline void
+iwl_mld_cleanup_phy(struct iwl_mld *mld, struct iwl_mld_phy *phy)
+{
+ CLEANUP_STRUCT(phy);
+}
+
+int iwl_mld_allocate_fw_phy_id(struct iwl_mld *mld);
+int iwl_mld_phy_fw_action(struct iwl_mld *mld,
+ struct ieee80211_chanctx_conf *ctx, u32 action);
+struct cfg80211_chan_def *
+iwl_mld_get_chandef_from_chanctx(struct iwl_mld *mld,
+ struct ieee80211_chanctx_conf *ctx);
+u8 iwl_mld_get_fw_ctrl_pos(const struct cfg80211_chan_def *chandef);
+
+int iwl_mld_send_phy_cfg_cmd(struct iwl_mld *mld);
+
+void iwl_mld_update_phy_chandef(struct iwl_mld *mld,
+ struct ieee80211_chanctx_conf *ctx);
+
+#endif /* __iwl_mld_phy_h__ */
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/power.c b/drivers/net/wireless/intel/iwlwifi/mld/power.c
new file mode 100644
index 000000000000..8cc276041360
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/mld/power.c
@@ -0,0 +1,399 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
+/*
+ * Copyright (C) 2024-2025 Intel Corporation
+ */
+#include <net/mac80211.h>
+
+#include "mld.h"
+#include "hcmd.h"
+#include "power.h"
+#include "iface.h"
+#include "link.h"
+#include "constants.h"
+
+static void iwl_mld_vif_ps_iterator(void *data, u8 *mac,
+ struct ieee80211_vif *vif)
+{
+ bool *ps_enable = (bool *)data;
+ struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
+
+ if (vif->type != NL80211_IFTYPE_STATION)
+ return;
+
+ *ps_enable &= !mld_vif->ps_disabled;
+}
+
+int iwl_mld_update_device_power(struct iwl_mld *mld, bool d3)
+{
+ struct iwl_device_power_cmd cmd = {};
+ bool enable_ps = false;
+
+ if (iwlmld_mod_params.power_scheme != IWL_POWER_SCHEME_CAM) {
+ enable_ps = true;
+
+ /* Disable power save if any STA interface has
+ * power save turned off
+ */
+ ieee80211_iterate_active_interfaces_mtx(mld->hw,
+ IEEE80211_IFACE_ITER_NORMAL,
+ iwl_mld_vif_ps_iterator,
+ &enable_ps);
+ }
+
+ if (enable_ps)
+ cmd.flags |=
+ cpu_to_le16(DEVICE_POWER_FLAGS_POWER_SAVE_ENA_MSK);
+
+ if (d3)
+ cmd.flags |=
+ cpu_to_le16(DEVICE_POWER_FLAGS_NO_SLEEP_TILL_D3_MSK);
+
+ IWL_DEBUG_POWER(mld,
+ "Sending device power command with flags = 0x%X\n",
+ cmd.flags);
+
+ return iwl_mld_send_cmd_pdu(mld, POWER_TABLE_CMD, &cmd);
+}
+
+int iwl_mld_enable_beacon_filter(struct iwl_mld *mld,
+ const struct ieee80211_bss_conf *link_conf,
+ bool d3)
+{
+ struct iwl_beacon_filter_cmd cmd = {
+ IWL_BF_CMD_CONFIG_DEFAULTS,
+ .bf_enable_beacon_filter = cpu_to_le32(1),
+ .ba_enable_beacon_abort = cpu_to_le32(1),
+ };
+
+ if (ieee80211_vif_type_p2p(link_conf->vif) != NL80211_IFTYPE_STATION)
+ return 0;
+
+#ifdef CONFIG_IWLWIFI_DEBUGFS
+ if (iwl_mld_vif_from_mac80211(link_conf->vif)->disable_bf)
+ return 0;
+#endif
+
+ if (link_conf->cqm_rssi_thold) {
+ cmd.bf_energy_delta =
+ cpu_to_le32(link_conf->cqm_rssi_hyst);
+ /* fw uses an absolute value for this */
+ cmd.bf_roaming_state =
+ cpu_to_le32(-link_conf->cqm_rssi_thold);
+ }
+
+ if (d3)
+ cmd.ba_escape_timer = cpu_to_le32(IWL_BA_ESCAPE_TIMER_D3);
+
+ return iwl_mld_send_cmd_pdu(mld, REPLY_BEACON_FILTERING_CMD,
+ &cmd);
+}
+
+int iwl_mld_disable_beacon_filter(struct iwl_mld *mld,
+ struct ieee80211_vif *vif)
+{
+ struct iwl_beacon_filter_cmd cmd = {};
+
+ if (ieee80211_vif_type_p2p(vif) != NL80211_IFTYPE_STATION)
+ return 0;
+
+ return iwl_mld_send_cmd_pdu(mld, REPLY_BEACON_FILTERING_CMD,
+ &cmd);
+}
+
+static bool iwl_mld_power_is_radar(struct iwl_mld *mld,
+ const struct ieee80211_bss_conf *link_conf)
+{
+ const struct ieee80211_chanctx_conf *chanctx_conf;
+
+ chanctx_conf = wiphy_dereference(mld->wiphy, link_conf->chanctx_conf);
+
+ if (WARN_ON(!chanctx_conf))
+ return false;
+
+ return chanctx_conf->def.chan->flags & IEEE80211_CHAN_RADAR;
+}
+
+static void iwl_mld_power_configure_uapsd(struct iwl_mld *mld,
+ struct iwl_mld_link *link,
+ struct iwl_mac_power_cmd *cmd,
+ bool ps_poll)
+{
+ bool tid_found = false;
+
+ cmd->rx_data_timeout_uapsd =
+ cpu_to_le32(IWL_MLD_UAPSD_RX_DATA_TIMEOUT);
+ cmd->tx_data_timeout_uapsd =
+ cpu_to_le32(IWL_MLD_UAPSD_TX_DATA_TIMEOUT);
+
+ /* set advanced pm flag with no uapsd ACs to enable ps-poll */
+ if (ps_poll) {
+ cmd->flags |= cpu_to_le16(POWER_FLAGS_ADVANCE_PM_ENA_MSK);
+ return;
+ }
+
+ for (enum ieee80211_ac_numbers ac = IEEE80211_AC_VO;
+ ac <= IEEE80211_AC_BK;
+ ac++) {
+ if (!link->queue_params[ac].uapsd)
+ continue;
+
+ cmd->flags |=
+ cpu_to_le16(POWER_FLAGS_ADVANCE_PM_ENA_MSK |
+ POWER_FLAGS_UAPSD_MISBEHAVING_ENA_MSK);
+
+ cmd->uapsd_ac_flags |= BIT(ac);
+
+ /* QNDP TID - the highest TID with no admission control */
+ if (!tid_found && !link->queue_params[ac].acm) {
+ tid_found = true;
+ switch (ac) {
+ case IEEE80211_AC_VO:
+ cmd->qndp_tid = 6;
+ break;
+ case IEEE80211_AC_VI:
+ cmd->qndp_tid = 5;
+ break;
+ case IEEE80211_AC_BE:
+ cmd->qndp_tid = 0;
+ break;
+ case IEEE80211_AC_BK:
+ cmd->qndp_tid = 1;
+ break;
+ }
+ }
+ }
+
+ if (cmd->uapsd_ac_flags == (BIT(IEEE80211_AC_VO) |
+ BIT(IEEE80211_AC_VI) |
+ BIT(IEEE80211_AC_BE) |
+ BIT(IEEE80211_AC_BK))) {
+ cmd->flags |= cpu_to_le16(POWER_FLAGS_SNOOZE_ENA_MSK);
+ cmd->snooze_interval = cpu_to_le16(IWL_MLD_PS_SNOOZE_INTERVAL);
+ cmd->snooze_window = cpu_to_le16(IWL_MLD_PS_SNOOZE_WINDOW);
+ }
+
+ cmd->uapsd_max_sp = mld->hw->uapsd_max_sp_len;
+}
+
+static void
+iwl_mld_power_config_skip_dtim(struct iwl_mld *mld,
+ const struct ieee80211_bss_conf *link_conf,
+ struct iwl_mac_power_cmd *cmd)
+{
+ unsigned int dtimper_tu;
+ unsigned int dtimper;
+ unsigned int skip;
+
+ dtimper = link_conf->dtim_period ?: 1;
+ dtimper_tu = dtimper * link_conf->beacon_int;
+
+ if (dtimper >= 10 || iwl_mld_power_is_radar(mld, link_conf))
+ return;
+
+ if (WARN_ON(!dtimper_tu))
+ return;
+
+ /* configure skip over dtim up to 900 TU DTIM interval */
+ skip = max_t(int, 1, 900 / dtimper_tu);
+
+ cmd->skip_dtim_periods = skip;
+ cmd->flags |= cpu_to_le16(POWER_FLAGS_SKIP_OVER_DTIM_MSK);
+}
+
+#define POWER_KEEP_ALIVE_PERIOD_SEC 25
+static void iwl_mld_power_build_cmd(struct iwl_mld *mld,
+ struct ieee80211_vif *vif,
+ struct iwl_mac_power_cmd *cmd,
+ bool d3)
+{
+ int dtimper, bi;
+ int keep_alive;
+ struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
+ struct ieee80211_bss_conf *link_conf = &vif->bss_conf;
+ struct iwl_mld_link *link = &mld_vif->deflink;
+ bool ps_poll = false;
+
+ cmd->id_and_color = cpu_to_le32(mld_vif->fw_id);
+
+ if (ieee80211_vif_is_mld(vif)) {
+ int link_id;
+
+ if (WARN_ON(!vif->active_links))
+ return;
+
+ /* The firmware consumes one single configuration for the vif
+ * and can't differentiate between links, just pick the lowest
+ * link_id's configuration and use that.
+ */
+ link_id = __ffs(vif->active_links);
+ link_conf = link_conf_dereference_check(vif, link_id);
+ link = iwl_mld_link_dereference_check(mld_vif, link_id);
+
+ if (WARN_ON(!link_conf || !link))
+ return;
+ }
+ dtimper = link_conf->dtim_period;
+ bi = link_conf->beacon_int;
+
+ /* Regardless of power management state the driver must set
+ * keep alive period. FW will use it for sending keep alive NDPs
+ * immediately after association. Check that keep alive period
+ * is at least 3 * DTIM
+ */
+ keep_alive = DIV_ROUND_UP(ieee80211_tu_to_usec(3 * dtimper * bi),
+ USEC_PER_SEC);
+ keep_alive = max(keep_alive, POWER_KEEP_ALIVE_PERIOD_SEC);
+ cmd->keep_alive_seconds = cpu_to_le16(keep_alive);
+
+ if (iwlmld_mod_params.power_scheme != IWL_POWER_SCHEME_CAM)
+ cmd->flags |= cpu_to_le16(POWER_FLAGS_POWER_SAVE_ENA_MSK);
+
+ if (!vif->cfg.ps || iwl_mld_tdls_sta_count(mld) > 0)
+ return;
+
+ cmd->flags |= cpu_to_le16(POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK);
+
+ if (iwl_fw_lookup_cmd_ver(mld->fw, MAC_PM_POWER_TABLE, 0) >= 2)
+ cmd->flags |= cpu_to_le16(POWER_FLAGS_ENABLE_SMPS_MSK);
+
+ /* firmware supports LPRX for beacons at rate 1 Mbps or 6 Mbps only */
+ if (link_conf->beacon_rate &&
+ (link_conf->beacon_rate->bitrate == 10 ||
+ link_conf->beacon_rate->bitrate == 60)) {
+ cmd->flags |= cpu_to_le16(POWER_FLAGS_LPRX_ENA_MSK);
+ cmd->lprx_rssi_threshold = POWER_LPRX_RSSI_THRESHOLD;
+ }
+
+ if (d3) {
+ iwl_mld_power_config_skip_dtim(mld, link_conf, cmd);
+ cmd->rx_data_timeout =
+ cpu_to_le32(IWL_MLD_WOWLAN_PS_RX_DATA_TIMEOUT);
+ cmd->tx_data_timeout =
+ cpu_to_le32(IWL_MLD_WOWLAN_PS_TX_DATA_TIMEOUT);
+ } else if (iwl_mld_vif_low_latency(mld_vif) && vif->p2p) {
+ cmd->tx_data_timeout =
+ cpu_to_le32(IWL_MLD_SHORT_PS_TX_DATA_TIMEOUT);
+ cmd->rx_data_timeout =
+ cpu_to_le32(IWL_MLD_SHORT_PS_RX_DATA_TIMEOUT);
+ } else {
+ cmd->rx_data_timeout =
+ cpu_to_le32(IWL_MLD_DEFAULT_PS_RX_DATA_TIMEOUT);
+ cmd->tx_data_timeout =
+ cpu_to_le32(IWL_MLD_DEFAULT_PS_TX_DATA_TIMEOUT);
+ }
+
+ /* uAPSD is only enabled for specific certifications. For those cases,
+ * mac80211 will allow uAPSD. Always call iwl_mld_power_configure_uapsd
+ * which will look at what mac80211 is saying.
+ */
+#ifdef CONFIG_IWLWIFI_DEBUGFS
+ ps_poll = mld_vif->use_ps_poll;
+#endif
+ iwl_mld_power_configure_uapsd(mld, link, cmd, ps_poll);
+}
+
+int iwl_mld_update_mac_power(struct iwl_mld *mld, struct ieee80211_vif *vif,
+ bool d3)
+{
+ struct iwl_mac_power_cmd cmd = {};
+
+ iwl_mld_power_build_cmd(mld, vif, &cmd, d3);
+
+ return iwl_mld_send_cmd_pdu(mld, MAC_PM_POWER_TABLE, &cmd);
+}
+
+static void
+iwl_mld_tpe_sta_cmd_data(struct iwl_txpower_constraints_cmd *cmd,
+ const struct ieee80211_bss_conf *link)
+{
+ u8 i;
+
+ /* NOTE: the 0 here is IEEE80211_TPE_CAT_6GHZ_DEFAULT,
+ * we fully ignore IEEE80211_TPE_CAT_6GHZ_SUBORDINATE
+ */
+
+ BUILD_BUG_ON(ARRAY_SIZE(cmd->psd_pwr) !=
+ ARRAY_SIZE(link->tpe.psd_local[0].power));
+
+ /* if not valid, mac80211 puts default (max value) */
+ for (i = 0; i < ARRAY_SIZE(cmd->psd_pwr); i++)
+ cmd->psd_pwr[i] = min(link->tpe.psd_local[0].power[i],
+ link->tpe.psd_reg_client[0].power[i]);
+
+ BUILD_BUG_ON(ARRAY_SIZE(cmd->eirp_pwr) !=
+ ARRAY_SIZE(link->tpe.max_local[0].power));
+
+ for (i = 0; i < ARRAY_SIZE(cmd->eirp_pwr); i++)
+ cmd->eirp_pwr[i] = min(link->tpe.max_local[0].power[i],
+ link->tpe.max_reg_client[0].power[i]);
+}
+
+void
+iwl_mld_send_ap_tx_power_constraint_cmd(struct iwl_mld *mld,
+ struct ieee80211_vif *vif,
+ struct ieee80211_bss_conf *link)
+{
+ struct iwl_txpower_constraints_cmd cmd = {};
+ struct iwl_mld_link *mld_link = iwl_mld_link_from_mac80211(link);
+ int ret;
+
+ lockdep_assert_wiphy(mld->wiphy);
+
+ if (!mld_link->active)
+ return;
+
+ if (link->chanreq.oper.chan->band != NL80211_BAND_6GHZ)
+ return;
+
+ cmd.link_id = cpu_to_le16(mld_link->fw_id);
+ memset(cmd.psd_pwr, DEFAULT_TPE_TX_POWER, sizeof(cmd.psd_pwr));
+ memset(cmd.eirp_pwr, DEFAULT_TPE_TX_POWER, sizeof(cmd.eirp_pwr));
+
+ if (vif->type == NL80211_IFTYPE_AP) {
+ cmd.ap_type = cpu_to_le16(IWL_6GHZ_AP_TYPE_VLP);
+ } else if (link->power_type == IEEE80211_REG_UNSET_AP) {
+ return;
+ } else {
+ cmd.ap_type = cpu_to_le16(link->power_type - 1);
+ iwl_mld_tpe_sta_cmd_data(&cmd, link);
+ }
+
+ ret = iwl_mld_send_cmd_pdu(mld,
+ WIDE_ID(PHY_OPS_GROUP,
+ AP_TX_POWER_CONSTRAINTS_CMD),
+ &cmd);
+ if (ret)
+ IWL_ERR(mld,
+ "failed to send AP_TX_POWER_CONSTRAINTS_CMD (%d)\n",
+ ret);
+}
+
+int iwl_mld_set_tx_power(struct iwl_mld *mld,
+ struct ieee80211_bss_conf *link_conf,
+ s16 tx_power)
+{
+ u32 cmd_id = REDUCE_TX_POWER_CMD;
+ struct iwl_mld_link *mld_link = iwl_mld_link_from_mac80211(link_conf);
+ u16 u_tx_power = tx_power == IWL_DEFAULT_MAX_TX_POWER ?
+ IWL_DEV_MAX_TX_POWER : 8 * tx_power;
+ struct iwl_dev_tx_power_cmd cmd = {
+ /* Those fields sit on the same place for v9 and v10 */
+ .common.set_mode = cpu_to_le32(IWL_TX_POWER_MODE_SET_LINK),
+ .common.pwr_restriction = cpu_to_le16(u_tx_power),
+ };
+ u8 cmd_ver = iwl_fw_lookup_cmd_ver(mld->fw, cmd_id,
+ IWL_FW_CMD_VER_UNKNOWN);
+ int len = sizeof(cmd.common);
+
+ if (WARN_ON(!mld_link))
+ return -ENODEV;
+
+ cmd.common.link_id = cpu_to_le32(mld_link->fw_id);
+
+ if (cmd_ver == 10)
+ len += sizeof(cmd.v10);
+ else if (cmd_ver == 9)
+ len += sizeof(cmd.v9);
+
+ return iwl_mld_send_cmd_pdu(mld, cmd_id, &cmd, len);
+}
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/power.h b/drivers/net/wireless/intel/iwlwifi/mld/power.h
new file mode 100644
index 000000000000..05ce27bef106
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/mld/power.h
@@ -0,0 +1,33 @@
+/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
+/*
+ * Copyright (C) 2024 Intel Corporation
+ */
+#ifndef __iwl_mld_power_h__
+#define __iwl_mld_power_h__
+
+#include <net/mac80211.h>
+
+#include "mld.h"
+
+int iwl_mld_update_device_power(struct iwl_mld *mld, bool d3);
+
+int iwl_mld_enable_beacon_filter(struct iwl_mld *mld,
+ const struct ieee80211_bss_conf *link_conf,
+ bool d3);
+
+int iwl_mld_disable_beacon_filter(struct iwl_mld *mld,
+ struct ieee80211_vif *vif);
+
+int iwl_mld_update_mac_power(struct iwl_mld *mld, struct ieee80211_vif *vif,
+ bool d3);
+
+void
+iwl_mld_send_ap_tx_power_constraint_cmd(struct iwl_mld *mld,
+ struct ieee80211_vif *vif,
+ struct ieee80211_bss_conf *link);
+
+int iwl_mld_set_tx_power(struct iwl_mld *mld,
+ struct ieee80211_bss_conf *link_conf,
+ s16 tx_power);
+
+#endif /* __iwl_mld_power_h__ */
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/ptp.c b/drivers/net/wireless/intel/iwlwifi/mld/ptp.c
new file mode 100644
index 000000000000..5ee38fc168c1
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/mld/ptp.c
@@ -0,0 +1,321 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
+/*
+ * Copyright (C) 2025 Intel Corporation
+ */
+
+#include "mld.h"
+#include "iwl-debug.h"
+#include "hcmd.h"
+#include "ptp.h"
+#include <linux/timekeeping.h>
+
+/* The scaled_ppm parameter is ppm (parts per million) with a 16-bit fractional
+ * part, which means that a value of 1 in one of those fields actually means
+ * 2^-16 ppm, and 2^16=65536 is 1 ppm.
+ */
+#define PTP_SCALE_FACTOR 65536000000ULL
+
+#define IWL_PTP_GP2_WRAP 0x100000000ULL
+#define IWL_PTP_WRAP_TIME (3600 * HZ)
+#define IWL_PTP_WRAP_THRESHOLD_USEC (5000)
+
+static int iwl_mld_get_systime(struct iwl_mld *mld, u32 *gp2)
+{
+ *gp2 = iwl_read_prph(mld->trans, mld->trans->mac_cfg->base->gp2_reg_addr);
+
+ if (*gp2 == 0x5a5a5a5a)
+ return -EINVAL;
+
+ return 0;
+}
+
+static void iwl_mld_ptp_update_new_read(struct iwl_mld *mld, u32 gp2)
+{
+ IWL_DEBUG_PTP(mld, "PTP: last_gp2=%u, new gp2 read=%u\n",
+ mld->ptp_data.last_gp2, gp2);
+
+ /* If the difference is above the threshold, assume it's a wraparound.
+ * Otherwise assume it's an old read and ignore it.
+ */
+ if (gp2 < mld->ptp_data.last_gp2) {
+ if (mld->ptp_data.last_gp2 - gp2 <
+ IWL_PTP_WRAP_THRESHOLD_USEC) {
+ IWL_DEBUG_PTP(mld,
+ "PTP: ignore old read (gp2=%u, last_gp2=%u)\n",
+ gp2, mld->ptp_data.last_gp2);
+ return;
+ }
+
+ mld->ptp_data.wrap_counter++;
+ IWL_DEBUG_PTP(mld,
+ "PTP: wraparound detected (new counter=%u)\n",
+ mld->ptp_data.wrap_counter);
+ }
+
+ mld->ptp_data.last_gp2 = gp2;
+ schedule_delayed_work(&mld->ptp_data.dwork, IWL_PTP_WRAP_TIME);
+}
+
+u64 iwl_mld_ptp_get_adj_time(struct iwl_mld *mld, u64 base_time_ns)
+{
+ struct ptp_data *data = &mld->ptp_data;
+ u64 scale_time_gp2_ns = mld->ptp_data.scale_update_gp2 * NSEC_PER_USEC;
+ u64 res;
+ u64 diff;
+ s64 scaled_diff;
+
+ lockdep_assert_held(&data->lock);
+
+ iwl_mld_ptp_update_new_read(mld,
+ div64_u64(base_time_ns, NSEC_PER_USEC));
+
+ base_time_ns = base_time_ns +
+ (data->wrap_counter * IWL_PTP_GP2_WRAP * NSEC_PER_USEC);
+
+ /* It is possible that a GP2 timestamp was received from fw before the
+ * last scale update.
+ */
+ if (base_time_ns < scale_time_gp2_ns) {
+ diff = scale_time_gp2_ns - base_time_ns;
+ scaled_diff = -mul_u64_u64_div_u64(diff,
+ data->scaled_freq,
+ PTP_SCALE_FACTOR);
+ } else {
+ diff = base_time_ns - scale_time_gp2_ns;
+ scaled_diff = mul_u64_u64_div_u64(diff,
+ data->scaled_freq,
+ PTP_SCALE_FACTOR);
+ }
+
+ IWL_DEBUG_PTP(mld, "base_time=%llu diff ns=%llu scaled_diff_ns=%lld\n",
+ (unsigned long long)base_time_ns,
+ (unsigned long long)diff, (long long)scaled_diff);
+
+ res = data->scale_update_adj_time_ns + data->delta + scaled_diff;
+
+ IWL_DEBUG_PTP(mld, "scale_update_ns=%llu delta=%lld adj=%llu\n",
+ (unsigned long long)data->scale_update_adj_time_ns,
+ (long long)data->delta, (unsigned long long)res);
+ return res;
+}
+
+static int iwl_mld_ptp_gettime(struct ptp_clock_info *ptp,
+ struct timespec64 *ts)
+{
+ struct iwl_mld *mld = container_of(ptp, struct iwl_mld,
+ ptp_data.ptp_clock_info);
+ struct ptp_data *data = &mld->ptp_data;
+ u32 gp2;
+ u64 ns;
+
+ if (iwl_mld_get_systime(mld, &gp2)) {
+ IWL_DEBUG_PTP(mld, "PTP: gettime: failed to read systime\n");
+ return -EIO;
+ }
+
+ spin_lock_bh(&data->lock);
+ ns = iwl_mld_ptp_get_adj_time(mld, (u64)gp2 * NSEC_PER_USEC);
+ spin_unlock_bh(&data->lock);
+
+ *ts = ns_to_timespec64(ns);
+ return 0;
+}
+
+static int iwl_mld_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta)
+{
+ struct iwl_mld *mld = container_of(ptp, struct iwl_mld,
+ ptp_data.ptp_clock_info);
+ struct ptp_data *data = &mld->ptp_data;
+
+ spin_lock_bh(&data->lock);
+ data->delta += delta;
+ IWL_DEBUG_PTP(mld, "delta=%lld, new delta=%lld\n", (long long)delta,
+ (long long)data->delta);
+ spin_unlock_bh(&data->lock);
+ return 0;
+}
+
+static int iwl_mld_ptp_adjfine(struct ptp_clock_info *ptp, long scaled_ppm)
+{
+ struct iwl_mld *mld = container_of(ptp, struct iwl_mld,
+ ptp_data.ptp_clock_info);
+ struct ptp_data *data = &mld->ptp_data;
+ u32 gp2;
+
+ /* Must call iwl_mld_ptp_get_adj_time() before updating
+ * data->scale_update_gp2 or data->scaled_freq since
+ * scale_update_adj_time_ns should reflect the previous scaled_freq.
+ */
+ if (iwl_mld_get_systime(mld, &gp2)) {
+ IWL_DEBUG_PTP(mld, "adjfine: failed to read systime\n");
+ return -EBUSY;
+ }
+
+ spin_lock_bh(&data->lock);
+ data->scale_update_adj_time_ns =
+ iwl_mld_ptp_get_adj_time(mld, gp2 * NSEC_PER_USEC);
+ data->scale_update_gp2 = gp2;
+
+ /* scale_update_adj_time_ns now relects the configured delta, the
+ * wrap_counter and the previous scaled frequency. Thus delta and
+ * wrap_counter should be reset, and the scale frequency is updated
+ * to the new frequency.
+ */
+ data->delta = 0;
+ data->wrap_counter = 0;
+ data->scaled_freq = PTP_SCALE_FACTOR + scaled_ppm;
+ IWL_DEBUG_PTP(mld, "adjfine: scaled_ppm=%ld new=%llu\n",
+ scaled_ppm, (unsigned long long)data->scaled_freq);
+ spin_unlock_bh(&data->lock);
+ return 0;
+}
+
+static void iwl_mld_ptp_work(struct work_struct *wk)
+{
+ struct iwl_mld *mld = container_of(wk, struct iwl_mld,
+ ptp_data.dwork.work);
+ struct ptp_data *data = &mld->ptp_data;
+ u32 gp2;
+
+ spin_lock_bh(&data->lock);
+ if (!iwl_mld_get_systime(mld, &gp2))
+ iwl_mld_ptp_update_new_read(mld, gp2);
+ else
+ IWL_DEBUG_PTP(mld, "PTP work: failed to read GP2\n");
+ spin_unlock_bh(&data->lock);
+}
+
+static int
+iwl_mld_get_crosstimestamp_fw(struct iwl_mld *mld, u32 *gp2, u64 *sys_time)
+{
+ struct iwl_synced_time_cmd synced_time_cmd = {
+ .operation = cpu_to_le32(IWL_SYNCED_TIME_OPERATION_READ_BOTH)
+ };
+ struct iwl_host_cmd cmd = {
+ .id = WIDE_ID(DATA_PATH_GROUP, WNM_PLATFORM_PTM_REQUEST_CMD),
+ .flags = CMD_WANT_SKB,
+ .data[0] = &synced_time_cmd,
+ .len[0] = sizeof(synced_time_cmd),
+ };
+ struct iwl_synced_time_rsp *resp;
+ struct iwl_rx_packet *pkt;
+ int ret;
+ u64 gp2_10ns;
+
+ wiphy_lock(mld->wiphy);
+ ret = iwl_mld_send_cmd(mld, &cmd);
+ wiphy_unlock(mld->wiphy);
+ if (ret)
+ return ret;
+
+ pkt = cmd.resp_pkt;
+
+ if (iwl_rx_packet_payload_len(pkt) != sizeof(*resp)) {
+ IWL_DEBUG_PTP(mld, "PTP: Invalid PTM command response\n");
+ iwl_free_resp(&cmd);
+ return -EIO;
+ }
+
+ resp = (void *)pkt->data;
+
+ gp2_10ns = (u64)le32_to_cpu(resp->gp2_timestamp_hi) << 32 |
+ le32_to_cpu(resp->gp2_timestamp_lo);
+ *gp2 = div_u64(gp2_10ns, 100);
+
+ *sys_time = (u64)le32_to_cpu(resp->platform_timestamp_hi) << 32 |
+ le32_to_cpu(resp->platform_timestamp_lo);
+
+ iwl_free_resp(&cmd);
+ return ret;
+}
+
+static int
+iwl_mld_phc_get_crosstimestamp(struct ptp_clock_info *ptp,
+ struct system_device_crosststamp *xtstamp)
+{
+ struct iwl_mld *mld = container_of(ptp, struct iwl_mld,
+ ptp_data.ptp_clock_info);
+ struct ptp_data *data = &mld->ptp_data;
+ int ret = 0;
+ /* Raw value read from GP2 register in usec */
+ u32 gp2;
+ /* GP2 value in ns*/
+ s64 gp2_ns;
+ /* System (wall) time */
+ ktime_t sys_time;
+
+ memset(xtstamp, 0, sizeof(struct system_device_crosststamp));
+
+ ret = iwl_mld_get_crosstimestamp_fw(mld, &gp2, &sys_time);
+ if (ret) {
+ IWL_DEBUG_PTP(mld,
+ "PTP: fw get_crosstimestamp failed (ret=%d)\n",
+ ret);
+ return ret;
+ }
+
+ spin_lock_bh(&data->lock);
+ gp2_ns = iwl_mld_ptp_get_adj_time(mld, (u64)gp2 * NSEC_PER_USEC);
+ spin_unlock_bh(&data->lock);
+
+ IWL_DEBUG_PTP(mld,
+ "Got Sync Time: GP2:%u, last_GP2: %u, GP2_ns: %lld, sys_time: %lld\n",
+ gp2, mld->ptp_data.last_gp2, gp2_ns, (s64)sys_time);
+
+ /* System monotonic raw time is not used */
+ xtstamp->device = ns_to_ktime(gp2_ns);
+ xtstamp->sys_realtime = sys_time;
+
+ return ret;
+}
+
+void iwl_mld_ptp_init(struct iwl_mld *mld)
+{
+ if (WARN_ON(mld->ptp_data.ptp_clock))
+ return;
+
+ spin_lock_init(&mld->ptp_data.lock);
+ INIT_DELAYED_WORK(&mld->ptp_data.dwork, iwl_mld_ptp_work);
+
+ mld->ptp_data.ptp_clock_info.owner = THIS_MODULE;
+ mld->ptp_data.ptp_clock_info.gettime64 = iwl_mld_ptp_gettime;
+ mld->ptp_data.ptp_clock_info.max_adj = 0x7fffffff;
+ mld->ptp_data.ptp_clock_info.adjtime = iwl_mld_ptp_adjtime;
+ mld->ptp_data.ptp_clock_info.adjfine = iwl_mld_ptp_adjfine;
+ mld->ptp_data.scaled_freq = PTP_SCALE_FACTOR;
+ mld->ptp_data.ptp_clock_info.getcrosststamp =
+ iwl_mld_phc_get_crosstimestamp;
+
+ /* Give a short 'friendly name' to identify the PHC clock */
+ snprintf(mld->ptp_data.ptp_clock_info.name,
+ sizeof(mld->ptp_data.ptp_clock_info.name),
+ "%s", "iwlwifi-PTP");
+
+ mld->ptp_data.ptp_clock =
+ ptp_clock_register(&mld->ptp_data.ptp_clock_info, mld->dev);
+
+ if (IS_ERR_OR_NULL(mld->ptp_data.ptp_clock)) {
+ IWL_ERR(mld, "Failed to register PHC clock (%ld)\n",
+ PTR_ERR(mld->ptp_data.ptp_clock));
+ mld->ptp_data.ptp_clock = NULL;
+ } else {
+ IWL_INFO(mld, "Registered PHC clock: %s, with index: %d\n",
+ mld->ptp_data.ptp_clock_info.name,
+ ptp_clock_index(mld->ptp_data.ptp_clock));
+ }
+}
+
+void iwl_mld_ptp_remove(struct iwl_mld *mld)
+{
+ if (mld->ptp_data.ptp_clock) {
+ IWL_INFO(mld, "Unregistering PHC clock: %s, with index: %d\n",
+ mld->ptp_data.ptp_clock_info.name,
+ ptp_clock_index(mld->ptp_data.ptp_clock));
+
+ ptp_clock_unregister(mld->ptp_data.ptp_clock);
+ mld->ptp_data.ptp_clock = NULL;
+ mld->ptp_data.last_gp2 = 0;
+ mld->ptp_data.wrap_counter = 0;
+ cancel_delayed_work_sync(&mld->ptp_data.dwork);
+ }
+}
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/ptp.h b/drivers/net/wireless/intel/iwlwifi/mld/ptp.h
new file mode 100644
index 000000000000..f3d18dd304e5
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/mld/ptp.h
@@ -0,0 +1,45 @@
+/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
+/*
+ * Copyright (C) 2025 Intel Corporation
+ */
+#ifndef __iwl_mld_ptp_h__
+#define __iwl_mld_ptp_h__
+
+#include <linux/ptp_clock_kernel.h>
+
+/**
+ * struct ptp_data - PTP hardware clock data
+ *
+ * @ptp_clock: struct ptp_clock pointer returned by the ptp_clock_register()
+ * function.
+ * @ptp_clock_info: struct ptp_clock_info that describes a PTP hardware clock
+ * @lock: protects the time adjustments data
+ * @delta: delta between hardware clock and ptp clock in nanoseconds
+ * @scale_update_gp2: GP2 time when the scale was last updated
+ * @scale_update_adj_time_ns: adjusted time when the scale was last updated,
+ * in nanoseconds
+ * @scaled_freq: clock frequency offset, scaled to 65536000000
+ * @last_gp2: the last GP2 reading from the hardware, used for tracking GP2
+ * wraparounds
+ * @wrap_counter: number of wraparounds since scale_update_adj_time_ns
+ * @dwork: worker scheduled every 1 hour to detect workarounds
+ */
+struct ptp_data {
+ struct ptp_clock *ptp_clock;
+ struct ptp_clock_info ptp_clock_info;
+
+ spinlock_t lock;
+ s64 delta;
+ u32 scale_update_gp2;
+ u64 scale_update_adj_time_ns;
+ u64 scaled_freq;
+ u32 last_gp2;
+ u32 wrap_counter;
+ struct delayed_work dwork;
+};
+
+void iwl_mld_ptp_init(struct iwl_mld *mld);
+void iwl_mld_ptp_remove(struct iwl_mld *mld);
+u64 iwl_mld_ptp_get_adj_time(struct iwl_mld *mld, u64 base_time_ns);
+
+#endif /* __iwl_mld_ptp_h__ */
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/regulatory.c b/drivers/net/wireless/intel/iwlwifi/mld/regulatory.c
new file mode 100644
index 000000000000..326c300470ea
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/mld/regulatory.c
@@ -0,0 +1,393 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
+/*
+ * Copyright (C) 2024-2025 Intel Corporation
+ */
+
+#include <linux/dmi.h>
+
+#include "fw/regulatory.h"
+#include "fw/acpi.h"
+#include "fw/uefi.h"
+
+#include "regulatory.h"
+#include "mld.h"
+#include "hcmd.h"
+
+void iwl_mld_get_bios_tables(struct iwl_mld *mld)
+{
+ int ret;
+
+ iwl_acpi_get_guid_lock_status(&mld->fwrt);
+
+ ret = iwl_bios_get_ppag_table(&mld->fwrt);
+ if (ret < 0) {
+ IWL_DEBUG_RADIO(mld,
+ "PPAG BIOS table invalid or unavailable. (%d)\n",
+ ret);
+ }
+
+ ret = iwl_bios_get_wrds_table(&mld->fwrt);
+ if (ret < 0) {
+ IWL_DEBUG_RADIO(mld,
+ "WRDS SAR BIOS table invalid or unavailable. (%d)\n",
+ ret);
+
+ /* If not available, don't fail and don't bother with EWRD and
+ * WGDS
+ */
+
+ if (!iwl_bios_get_wgds_table(&mld->fwrt)) {
+ /* If basic SAR is not available, we check for WGDS,
+ * which should *not* be available either. If it is
+ * available, issue an error, because we can't use SAR
+ * Geo without basic SAR.
+ */
+ IWL_ERR(mld, "BIOS contains WGDS but no WRDS\n");
+ }
+
+ } else {
+ ret = iwl_bios_get_ewrd_table(&mld->fwrt);
+ /* If EWRD is not available, we can still use
+ * WRDS, so don't fail.
+ */
+ if (ret < 0)
+ IWL_DEBUG_RADIO(mld,
+ "EWRD SAR BIOS table invalid or unavailable. (%d)\n",
+ ret);
+
+ ret = iwl_bios_get_wgds_table(&mld->fwrt);
+ if (ret < 0)
+ IWL_DEBUG_RADIO(mld,
+ "Geo SAR BIOS table invalid or unavailable. (%d)\n",
+ ret);
+ /* we don't fail if the table is not available */
+ }
+
+ iwl_uefi_get_uats_table(mld->trans, &mld->fwrt);
+
+ iwl_bios_get_phy_filters(&mld->fwrt);
+}
+
+static int iwl_mld_geo_sar_init(struct iwl_mld *mld)
+{
+ u32 cmd_id = WIDE_ID(PHY_OPS_GROUP, PER_CHAIN_LIMIT_OFFSET_CMD);
+ union iwl_geo_tx_power_profiles_cmd cmd;
+ u16 len;
+ u32 n_bands;
+ __le32 sk = cpu_to_le32(0);
+ int ret;
+ u8 cmd_ver = iwl_fw_lookup_cmd_ver(mld->fw, cmd_id,
+ IWL_FW_CMD_VER_UNKNOWN);
+
+ BUILD_BUG_ON(offsetof(struct iwl_geo_tx_power_profiles_cmd_v4, ops) !=
+ offsetof(struct iwl_geo_tx_power_profiles_cmd_v5, ops));
+
+ cmd.v4.ops = cpu_to_le32(IWL_PER_CHAIN_OFFSET_SET_TABLES);
+
+ /* Only set to South Korea if the table revision is 1 */
+ if (mld->fwrt.geo_rev == 1)
+ sk = cpu_to_le32(1);
+
+ if (cmd_ver == 5) {
+ len = sizeof(cmd.v5);
+ n_bands = ARRAY_SIZE(cmd.v5.table[0]);
+ cmd.v5.table_revision = sk;
+ } else if (cmd_ver == 4) {
+ len = sizeof(cmd.v4);
+ n_bands = ARRAY_SIZE(cmd.v4.table[0]);
+ cmd.v4.table_revision = sk;
+ } else {
+ return -EOPNOTSUPP;
+ }
+
+ BUILD_BUG_ON(offsetof(struct iwl_geo_tx_power_profiles_cmd_v4, table) !=
+ offsetof(struct iwl_geo_tx_power_profiles_cmd_v5, table));
+ /* the table is at the same position for all versions, so set use v4 */
+ ret = iwl_sar_geo_fill_table(&mld->fwrt, &cmd.v4.table[0][0],
+ n_bands, BIOS_GEO_MAX_PROFILE_NUM);
+
+ /* It is a valid scenario to not support SAR, or miss wgds table,
+ * but in that case there is no need to send the command.
+ */
+ if (ret)
+ return 0;
+
+ return iwl_mld_send_cmd_pdu(mld, cmd_id, &cmd, len);
+}
+
+int iwl_mld_config_sar_profile(struct iwl_mld *mld, int prof_a, int prof_b)
+{
+ u32 cmd_id = REDUCE_TX_POWER_CMD;
+ struct iwl_dev_tx_power_cmd cmd = {
+ .common.set_mode = cpu_to_le32(IWL_TX_POWER_MODE_SET_CHAINS),
+ };
+ __le16 *per_chain;
+ int ret;
+ u16 len = sizeof(cmd.common);
+ u32 n_subbands;
+ u8 cmd_ver = iwl_fw_lookup_cmd_ver(mld->fw, cmd_id,
+ IWL_FW_CMD_VER_UNKNOWN);
+
+ if (cmd_ver == 10) {
+ len += sizeof(cmd.v10);
+ n_subbands = IWL_NUM_SUB_BANDS_V2;
+ per_chain = &cmd.v10.per_chain[0][0][0];
+ cmd.v10.flags =
+ cpu_to_le32(mld->fwrt.reduced_power_flags);
+ } else if (cmd_ver == 9) {
+ len += sizeof(cmd.v9);
+ n_subbands = IWL_NUM_SUB_BANDS_V1;
+ per_chain = &cmd.v9.per_chain[0][0];
+ } else {
+ return -EOPNOTSUPP;
+ }
+
+ /* TODO: CDB - support IWL_NUM_CHAIN_TABLES_V2 */
+ ret = iwl_sar_fill_profile(&mld->fwrt, per_chain,
+ IWL_NUM_CHAIN_TABLES,
+ n_subbands, prof_a, prof_b);
+ /* return on error or if the profile is disabled (positive number) */
+ if (ret)
+ return ret;
+
+ return iwl_mld_send_cmd_pdu(mld, cmd_id, &cmd, len);
+}
+
+int iwl_mld_init_sar(struct iwl_mld *mld)
+{
+ int chain_a_prof = 1;
+ int chain_b_prof = 1;
+ int ret;
+
+ /* If no profile was chosen by the user yet, choose profile 1 (WRDS) as
+ * default for both chains
+ */
+ if (mld->fwrt.sar_chain_a_profile && mld->fwrt.sar_chain_b_profile) {
+ chain_a_prof = mld->fwrt.sar_chain_a_profile;
+ chain_b_prof = mld->fwrt.sar_chain_b_profile;
+ }
+
+ ret = iwl_mld_config_sar_profile(mld, chain_a_prof, chain_b_prof);
+ if (ret < 0)
+ return ret;
+
+ if (ret)
+ return 0;
+
+ return iwl_mld_geo_sar_init(mld);
+}
+
+int iwl_mld_init_sgom(struct iwl_mld *mld)
+{
+ int ret;
+ struct iwl_host_cmd cmd = {
+ .id = WIDE_ID(REGULATORY_AND_NVM_GROUP,
+ SAR_OFFSET_MAPPING_TABLE_CMD),
+ .data[0] = &mld->fwrt.sgom_table,
+ .len[0] = sizeof(mld->fwrt.sgom_table),
+ .dataflags[0] = IWL_HCMD_DFL_NOCOPY,
+ };
+
+ if (!mld->fwrt.sgom_enabled) {
+ IWL_DEBUG_RADIO(mld, "SGOM table is disabled\n");
+ return 0;
+ }
+
+ ret = iwl_mld_send_cmd(mld, &cmd);
+ if (ret)
+ IWL_ERR(mld,
+ "failed to send SAR_OFFSET_MAPPING_CMD (%d)\n", ret);
+
+ return ret;
+}
+
+static int iwl_mld_ppag_send_cmd(struct iwl_mld *mld)
+{
+ union iwl_ppag_table_cmd cmd = {};
+ int ret, len;
+
+ ret = iwl_fill_ppag_table(&mld->fwrt, &cmd, &len);
+ /* Not supporting PPAG table is a valid scenario */
+ if (ret < 0)
+ return 0;
+
+ IWL_DEBUG_RADIO(mld, "Sending PER_PLATFORM_ANT_GAIN_CMD\n");
+ ret = iwl_mld_send_cmd_pdu(mld, WIDE_ID(PHY_OPS_GROUP,
+ PER_PLATFORM_ANT_GAIN_CMD),
+ &cmd, len);
+ if (ret < 0)
+ IWL_ERR(mld, "failed to send PER_PLATFORM_ANT_GAIN_CMD (%d)\n",
+ ret);
+
+ return ret;
+}
+
+int iwl_mld_init_ppag(struct iwl_mld *mld)
+{
+ /* no need to read the table, done in INIT stage */
+
+ if (!(iwl_is_ppag_approved(&mld->fwrt)))
+ return 0;
+
+ return iwl_mld_ppag_send_cmd(mld);
+}
+
+void iwl_mld_configure_lari(struct iwl_mld *mld)
+{
+ struct iwl_fw_runtime *fwrt = &mld->fwrt;
+ struct iwl_lari_config_change_cmd cmd = {
+ .config_bitmap = iwl_get_lari_config_bitmap(fwrt),
+ };
+ int ret;
+ u32 value;
+
+ ret = iwl_bios_get_dsm(fwrt, DSM_FUNC_11AX_ENABLEMENT, &value);
+ if (!ret)
+ cmd.oem_11ax_allow_bitmap = cpu_to_le32(value);
+
+ ret = iwl_bios_get_dsm(fwrt, DSM_FUNC_ENABLE_UNII4_CHAN, &value);
+ if (!ret)
+ cmd.oem_unii4_allow_bitmap =
+ cpu_to_le32(value &= DSM_UNII4_ALLOW_BITMAP);
+
+ ret = iwl_bios_get_dsm(fwrt, DSM_FUNC_ACTIVATE_CHANNEL, &value);
+ if (!ret)
+ cmd.chan_state_active_bitmap = cpu_to_le32(value);
+
+ ret = iwl_bios_get_dsm(fwrt, DSM_FUNC_ENABLE_6E, &value);
+ if (!ret)
+ cmd.oem_uhb_allow_bitmap = cpu_to_le32(value);
+
+ ret = iwl_bios_get_dsm(fwrt, DSM_FUNC_FORCE_DISABLE_CHANNELS, &value);
+ if (!ret)
+ cmd.force_disable_channels_bitmap = cpu_to_le32(value);
+
+ ret = iwl_bios_get_dsm(fwrt, DSM_FUNC_ENERGY_DETECTION_THRESHOLD,
+ &value);
+ if (!ret)
+ cmd.edt_bitmap = cpu_to_le32(value);
+
+ ret = iwl_bios_get_wbem(fwrt, &value);
+ if (!ret)
+ cmd.oem_320mhz_allow_bitmap = cpu_to_le32(value);
+
+ ret = iwl_bios_get_dsm(fwrt, DSM_FUNC_ENABLE_11BE, &value);
+ if (!ret)
+ cmd.oem_11be_allow_bitmap = cpu_to_le32(value);
+
+ if (!cmd.config_bitmap &&
+ !cmd.oem_uhb_allow_bitmap &&
+ !cmd.oem_11ax_allow_bitmap &&
+ !cmd.oem_unii4_allow_bitmap &&
+ !cmd.chan_state_active_bitmap &&
+ !cmd.force_disable_channels_bitmap &&
+ !cmd.edt_bitmap &&
+ !cmd.oem_320mhz_allow_bitmap &&
+ !cmd.oem_11be_allow_bitmap)
+ return;
+
+ IWL_DEBUG_RADIO(mld,
+ "sending LARI_CONFIG_CHANGE, config_bitmap=0x%x, oem_11ax_allow_bitmap=0x%x\n",
+ le32_to_cpu(cmd.config_bitmap),
+ le32_to_cpu(cmd.oem_11ax_allow_bitmap));
+ IWL_DEBUG_RADIO(mld,
+ "sending LARI_CONFIG_CHANGE, oem_unii4_allow_bitmap=0x%x, chan_state_active_bitmap=0x%x\n",
+ le32_to_cpu(cmd.oem_unii4_allow_bitmap),
+ le32_to_cpu(cmd.chan_state_active_bitmap));
+ IWL_DEBUG_RADIO(mld,
+ "sending LARI_CONFIG_CHANGE, oem_uhb_allow_bitmap=0x%x, force_disable_channels_bitmap=0x%x\n",
+ le32_to_cpu(cmd.oem_uhb_allow_bitmap),
+ le32_to_cpu(cmd.force_disable_channels_bitmap));
+ IWL_DEBUG_RADIO(mld,
+ "sending LARI_CONFIG_CHANGE, edt_bitmap=0x%x, oem_320mhz_allow_bitmap=0x%x\n",
+ le32_to_cpu(cmd.edt_bitmap),
+ le32_to_cpu(cmd.oem_320mhz_allow_bitmap));
+ IWL_DEBUG_RADIO(mld,
+ "sending LARI_CONFIG_CHANGE, oem_11be_allow_bitmap=0x%x\n",
+ le32_to_cpu(cmd.oem_11be_allow_bitmap));
+
+ ret = iwl_mld_send_cmd_pdu(mld, WIDE_ID(REGULATORY_AND_NVM_GROUP,
+ LARI_CONFIG_CHANGE), &cmd);
+ if (ret)
+ IWL_DEBUG_RADIO(mld,
+ "Failed to send LARI_CONFIG_CHANGE (%d)\n",
+ ret);
+}
+
+void iwl_mld_init_uats(struct iwl_mld *mld)
+{
+ int ret;
+ struct iwl_host_cmd cmd = {
+ .id = WIDE_ID(REGULATORY_AND_NVM_GROUP,
+ MCC_ALLOWED_AP_TYPE_CMD),
+ .data[0] = &mld->fwrt.uats_table,
+ .len[0] = sizeof(mld->fwrt.uats_table),
+ .dataflags[0] = IWL_HCMD_DFL_NOCOPY,
+ };
+
+ if (!mld->fwrt.uats_valid)
+ return;
+
+ ret = iwl_mld_send_cmd(mld, &cmd);
+ if (ret)
+ IWL_ERR(mld, "failed to send MCC_ALLOWED_AP_TYPE_CMD (%d)\n",
+ ret);
+}
+
+void iwl_mld_init_tas(struct iwl_mld *mld)
+{
+ int ret;
+ struct iwl_tas_data data = {};
+ struct iwl_tas_config_cmd cmd = {};
+ u32 cmd_id = WIDE_ID(REGULATORY_AND_NVM_GROUP, TAS_CONFIG);
+
+ BUILD_BUG_ON(ARRAY_SIZE(data.block_list_array) !=
+ IWL_WTAS_BLACK_LIST_MAX);
+ BUILD_BUG_ON(ARRAY_SIZE(cmd.block_list_array) !=
+ IWL_WTAS_BLACK_LIST_MAX);
+
+ if (!fw_has_capa(&mld->fw->ucode_capa, IWL_UCODE_TLV_CAPA_TAS_CFG)) {
+ IWL_DEBUG_RADIO(mld, "TAS not enabled in FW\n");
+ return;
+ }
+
+ ret = iwl_bios_get_tas_table(&mld->fwrt, &data);
+ if (ret < 0) {
+ IWL_DEBUG_RADIO(mld,
+ "TAS table invalid or unavailable. (%d)\n",
+ ret);
+ return;
+ }
+
+ if (!iwl_is_tas_approved()) {
+ IWL_DEBUG_RADIO(mld,
+ "System vendor '%s' is not in the approved list, disabling TAS in US and Canada.\n",
+ dmi_get_system_info(DMI_SYS_VENDOR) ?: "<unknown>");
+ if ((!iwl_add_mcc_to_tas_block_list(data.block_list_array,
+ &data.block_list_size,
+ IWL_MCC_US)) ||
+ (!iwl_add_mcc_to_tas_block_list(data.block_list_array,
+ &data.block_list_size,
+ IWL_MCC_CANADA))) {
+ IWL_DEBUG_RADIO(mld,
+ "Unable to add US/Canada to TAS block list, disabling TAS\n");
+ return;
+ }
+ } else {
+ IWL_DEBUG_RADIO(mld,
+ "System vendor '%s' is in the approved list.\n",
+ dmi_get_system_info(DMI_SYS_VENDOR) ?: "<unknown>");
+ }
+
+ cmd.block_list_size = cpu_to_le16(data.block_list_size);
+ for (u8 i = 0; i < data.block_list_size; i++)
+ cmd.block_list_array[i] =
+ cpu_to_le16(data.block_list_array[i]);
+ cmd.tas_config_info.table_source = data.table_source;
+ cmd.tas_config_info.table_revision = data.table_revision;
+ cmd.tas_config_info.value = cpu_to_le32(data.tas_selection);
+
+ ret = iwl_mld_send_cmd_pdu(mld, cmd_id, &cmd);
+ if (ret)
+ IWL_DEBUG_RADIO(mld, "failed to send TAS_CONFIG (%d)\n", ret);
+}
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/regulatory.h b/drivers/net/wireless/intel/iwlwifi/mld/regulatory.h
new file mode 100644
index 000000000000..3b01c645adda
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/mld/regulatory.h
@@ -0,0 +1,23 @@
+/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
+/*
+ * Copyright (C) 2024-2025 Intel Corporation
+ */
+#ifndef __iwl_mld_regulatory_h__
+#define __iwl_mld_regulatory_h__
+
+#include "mld.h"
+
+void iwl_mld_get_bios_tables(struct iwl_mld *mld);
+void iwl_mld_configure_lari(struct iwl_mld *mld);
+void iwl_mld_init_uats(struct iwl_mld *mld);
+void iwl_mld_init_tas(struct iwl_mld *mld);
+
+int iwl_mld_init_ppag(struct iwl_mld *mld);
+
+int iwl_mld_init_sgom(struct iwl_mld *mld);
+
+int iwl_mld_init_sar(struct iwl_mld *mld);
+
+int iwl_mld_config_sar_profile(struct iwl_mld *mld, int prof_a, int prof_b);
+
+#endif /* __iwl_mld_regulatory_h__ */
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/roc.c b/drivers/net/wireless/intel/iwlwifi/mld/roc.c
new file mode 100644
index 000000000000..e85f45bce79a
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/mld/roc.c
@@ -0,0 +1,265 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
+/*
+ * Copyright (C) 2024 - 2025 Intel Corporation
+ */
+#include <net/cfg80211.h>
+#include <net/mac80211.h>
+
+#include "mld.h"
+#include "roc.h"
+#include "hcmd.h"
+#include "iface.h"
+#include "sta.h"
+#include "mlo.h"
+
+#include "fw/api/context.h"
+#include "fw/api/time-event.h"
+
+#define AUX_ROC_MAX_DELAY MSEC_TO_TU(200)
+
+static void
+iwl_mld_vif_iter_emlsr_block_roc(void *data, u8 *mac, struct ieee80211_vif *vif)
+{
+ struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
+ int *result = data;
+ int ret;
+
+ ret = iwl_mld_block_emlsr_sync(mld_vif->mld, vif,
+ IWL_MLD_EMLSR_BLOCKED_ROC,
+ iwl_mld_get_primary_link(vif));
+ if (ret)
+ *result = ret;
+}
+
+struct iwl_mld_roc_iter_data {
+ enum iwl_roc_activity activity;
+ struct ieee80211_vif *vif;
+ bool found;
+};
+
+static void iwl_mld_find_roc_vif_iter(void *data, u8 *mac,
+ struct ieee80211_vif *vif)
+{
+ struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
+ struct iwl_mld_roc_iter_data *roc_data = data;
+
+ if (mld_vif->roc_activity != roc_data->activity)
+ return;
+
+ /* The FW supports one ROC of each type simultaneously */
+ if (WARN_ON(roc_data->found)) {
+ roc_data->vif = NULL;
+ return;
+ }
+
+ roc_data->found = true;
+ roc_data->vif = vif;
+}
+
+static struct ieee80211_vif *
+iwl_mld_find_roc_vif(struct iwl_mld *mld, enum iwl_roc_activity activity)
+{
+ struct iwl_mld_roc_iter_data roc_data = {
+ .activity = activity,
+ .found = false,
+ };
+
+ ieee80211_iterate_active_interfaces_mtx(mld->hw,
+ IEEE80211_IFACE_ITER_NORMAL,
+ iwl_mld_find_roc_vif_iter,
+ &roc_data);
+
+ return roc_data.vif;
+}
+
+int iwl_mld_start_roc(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ struct ieee80211_channel *channel, int duration,
+ enum ieee80211_roc_type type)
+{
+ struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw);
+ struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
+ struct iwl_mld_int_sta *aux_sta = &mld_vif->aux_sta;
+ struct iwl_roc_req cmd = {
+ .action = cpu_to_le32(FW_CTXT_ACTION_ADD),
+ };
+ u8 ver = iwl_fw_lookup_cmd_ver(mld->fw,
+ WIDE_ID(MAC_CONF_GROUP, ROC_CMD), 0);
+ u16 cmd_len = ver < 6 ? sizeof(struct iwl_roc_req_v5) : sizeof(cmd);
+ enum iwl_roc_activity activity;
+ int ret = 0;
+
+ lockdep_assert_wiphy(mld->wiphy);
+
+ if (vif->type != NL80211_IFTYPE_P2P_DEVICE &&
+ vif->type != NL80211_IFTYPE_STATION) {
+ IWL_ERR(mld, "NOT SUPPORTED: ROC on vif->type %d\n",
+ vif->type);
+
+ return -EOPNOTSUPP;
+ }
+
+ if (vif->type == NL80211_IFTYPE_P2P_DEVICE) {
+ switch (type) {
+ case IEEE80211_ROC_TYPE_NORMAL:
+ activity = ROC_ACTIVITY_P2P_DISC;
+ break;
+ case IEEE80211_ROC_TYPE_MGMT_TX:
+ activity = ROC_ACTIVITY_P2P_NEG;
+ break;
+ default:
+ WARN_ONCE(1, "Got an invalid P2P ROC type\n");
+ return -EINVAL;
+ }
+ } else {
+ activity = ROC_ACTIVITY_HOTSPOT;
+ }
+
+ /* The FW supports one ROC of each type simultaneously */
+ if (WARN_ON(iwl_mld_find_roc_vif(mld, activity)))
+ return -EBUSY;
+
+ ieee80211_iterate_active_interfaces_mtx(mld->hw,
+ IEEE80211_IFACE_ITER_NORMAL,
+ iwl_mld_vif_iter_emlsr_block_roc,
+ &ret);
+ if (ret)
+ return ret;
+
+ ret = iwl_mld_add_aux_sta(mld, aux_sta);
+ if (ret)
+ return ret;
+
+ cmd.activity = cpu_to_le32(activity);
+ cmd.sta_id = cpu_to_le32(aux_sta->sta_id);
+ cmd.channel_info.channel = cpu_to_le32(channel->hw_value);
+ cmd.channel_info.band = iwl_mld_nl80211_band_to_fw(channel->band);
+ cmd.channel_info.width = IWL_PHY_CHANNEL_MODE20;
+ cmd.max_delay = cpu_to_le32(AUX_ROC_MAX_DELAY);
+ cmd.duration = cpu_to_le32(MSEC_TO_TU(duration));
+
+ memcpy(cmd.node_addr, vif->addr, ETH_ALEN);
+
+ ret = iwl_mld_send_cmd_pdu(mld, WIDE_ID(MAC_CONF_GROUP, ROC_CMD),
+ &cmd, cmd_len);
+ if (ret) {
+ IWL_ERR(mld, "Couldn't send the ROC_CMD\n");
+ return ret;
+ }
+
+ mld_vif->roc_activity = activity;
+
+ return 0;
+}
+
+static void
+iwl_mld_vif_iter_emlsr_unblock_roc(void *data, u8 *mac,
+ struct ieee80211_vif *vif)
+{
+ struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
+
+ iwl_mld_unblock_emlsr(mld_vif->mld, vif, IWL_MLD_EMLSR_BLOCKED_ROC);
+}
+
+static void iwl_mld_destroy_roc(struct iwl_mld *mld,
+ struct ieee80211_vif *vif,
+ struct iwl_mld_vif *mld_vif)
+{
+ mld_vif->roc_activity = ROC_NUM_ACTIVITIES;
+
+ ieee80211_iterate_active_interfaces_mtx(mld->hw,
+ IEEE80211_IFACE_ITER_NORMAL,
+ iwl_mld_vif_iter_emlsr_unblock_roc,
+ NULL);
+
+ /* wait until every tx has seen that roc_activity has been reset */
+ synchronize_net();
+ /* from here, no new tx will be added
+ * we can flush the Tx on the queues
+ */
+
+ iwl_mld_flush_link_sta_txqs(mld, mld_vif->aux_sta.sta_id);
+
+ iwl_mld_remove_aux_sta(mld, vif);
+}
+
+int iwl_mld_cancel_roc(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif)
+{
+ struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw);
+ struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
+ struct iwl_roc_req cmd = {
+ .action = cpu_to_le32(FW_CTXT_ACTION_REMOVE),
+ };
+ u8 ver = iwl_fw_lookup_cmd_ver(mld->fw,
+ WIDE_ID(MAC_CONF_GROUP, ROC_CMD), 0);
+ u16 cmd_len = ver < 6 ? sizeof(struct iwl_roc_req_v5) : sizeof(cmd);
+ int ret;
+
+ lockdep_assert_wiphy(mld->wiphy);
+
+ if (WARN_ON(vif->type != NL80211_IFTYPE_P2P_DEVICE &&
+ vif->type != NL80211_IFTYPE_STATION))
+ return -EOPNOTSUPP;
+
+ /* No roc activity running it's probably already done */
+ if (mld_vif->roc_activity == ROC_NUM_ACTIVITIES)
+ return 0;
+
+ cmd.activity = cpu_to_le32(mld_vif->roc_activity);
+
+ ret = iwl_mld_send_cmd_pdu(mld, WIDE_ID(MAC_CONF_GROUP, ROC_CMD),
+ &cmd, cmd_len);
+ if (ret)
+ IWL_ERR(mld, "Couldn't send the command to cancel the ROC\n");
+
+ /* We may have raced with the firmware expiring the ROC instance at
+ * this very moment. In that case, we can have a notification in the
+ * async processing queue. However, none can arrive _after_ this as
+ * ROC_CMD was sent synchronously, i.e. we waited for a response and
+ * the firmware cannot refer to this ROC after the response. Thus,
+ * if we just cancel the notification (if there's one) we'll be at a
+ * clean state for any possible next ROC.
+ */
+ iwl_mld_cancel_notifications_of_object(mld, IWL_MLD_OBJECT_TYPE_ROC,
+ mld_vif->roc_activity);
+
+ iwl_mld_destroy_roc(mld, vif, mld_vif);
+
+ return 0;
+}
+
+void iwl_mld_handle_roc_notif(struct iwl_mld *mld,
+ struct iwl_rx_packet *pkt)
+{
+ const struct iwl_roc_notif *notif = (void *)pkt->data;
+ u32 activity = le32_to_cpu(notif->activity);
+ struct iwl_mld_vif *mld_vif;
+ struct ieee80211_vif *vif;
+
+ vif = iwl_mld_find_roc_vif(mld, activity);
+ if (WARN_ON(!vif))
+ return;
+
+ mld_vif = iwl_mld_vif_from_mac80211(vif);
+ /* It is possible that the ROC was canceled
+ * but the notification was already fired.
+ */
+ if (mld_vif->roc_activity != activity)
+ return;
+
+ if (le32_to_cpu(notif->success) &&
+ le32_to_cpu(notif->started)) {
+ /* We had a successful start */
+ ieee80211_ready_on_channel(mld->hw);
+ } else {
+ /* ROC was not successful, tell the firmware to remove it */
+ if (le32_to_cpu(notif->started))
+ iwl_mld_cancel_roc(mld->hw, vif);
+ else
+ iwl_mld_destroy_roc(mld, vif, mld_vif);
+ /* we need to let know mac80211 about end OR
+ * an unsuccessful start
+ */
+ ieee80211_remain_on_channel_expired(mld->hw);
+ }
+}
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/roc.h b/drivers/net/wireless/intel/iwlwifi/mld/roc.h
new file mode 100644
index 000000000000..985d7f20a3d7
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/mld/roc.h
@@ -0,0 +1,20 @@
+/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
+/*
+ * Copyright (C) 2024 Intel Corporation
+ */
+#ifndef __iwl_mld_roc_h__
+#define __iwl_mld_roc_h__
+
+#include <net/mac80211.h>
+
+int iwl_mld_start_roc(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ struct ieee80211_channel *channel, int duration,
+ enum ieee80211_roc_type type);
+
+int iwl_mld_cancel_roc(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif);
+
+void iwl_mld_handle_roc_notif(struct iwl_mld *mld,
+ struct iwl_rx_packet *pkt);
+
+#endif /* __iwl_mld_roc_h__ */
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/rx.c b/drivers/net/wireless/intel/iwlwifi/mld/rx.c
new file mode 100644
index 000000000000..ce0093d5c638
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/mld/rx.c
@@ -0,0 +1,2071 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
+/*
+ * Copyright (C) 2024-2025 Intel Corporation
+ */
+
+#include <net/mac80211.h>
+#include <kunit/static_stub.h>
+
+#include "mld.h"
+#include "sta.h"
+#include "agg.h"
+#include "rx.h"
+#include "hcmd.h"
+#include "iface.h"
+#include "time_sync.h"
+#include "fw/dbg.h"
+#include "fw/api/rx.h"
+
+/* stores relevant PHY data fields extracted from iwl_rx_mpdu_desc */
+struct iwl_mld_rx_phy_data {
+ enum iwl_rx_phy_info_type info_type;
+ __le32 data0;
+ __le32 data1;
+ __le32 data2;
+ __le32 data3;
+ __le32 eht_data4;
+ __le32 data5;
+ __le16 data4;
+ bool first_subframe;
+ bool with_data;
+ __le32 rx_vec[4];
+ u32 rate_n_flags;
+ u32 gp2_on_air_rise;
+ u16 phy_info;
+ u8 energy_a, energy_b;
+};
+
+static void
+iwl_mld_fill_phy_data(struct iwl_mld *mld,
+ struct iwl_rx_mpdu_desc *desc,
+ struct iwl_mld_rx_phy_data *phy_data)
+{
+ phy_data->phy_info = le16_to_cpu(desc->phy_info);
+ phy_data->rate_n_flags = iwl_v3_rate_from_v2_v3(desc->v3.rate_n_flags,
+ mld->fw_rates_ver_3);
+ phy_data->gp2_on_air_rise = le32_to_cpu(desc->v3.gp2_on_air_rise);
+ phy_data->energy_a = desc->v3.energy_a;
+ phy_data->energy_b = desc->v3.energy_b;
+ phy_data->data0 = desc->v3.phy_data0;
+ phy_data->data1 = desc->v3.phy_data1;
+ phy_data->data2 = desc->v3.phy_data2;
+ phy_data->data3 = desc->v3.phy_data3;
+ phy_data->data4 = desc->phy_data4;
+ phy_data->eht_data4 = desc->phy_eht_data4;
+ phy_data->data5 = desc->v3.phy_data5;
+ phy_data->with_data = true;
+}
+
+static inline int iwl_mld_check_pn(struct iwl_mld *mld, struct sk_buff *skb,
+ int queue, struct ieee80211_sta *sta)
+{
+ struct ieee80211_hdr *hdr = (void *)skb_mac_header(skb);
+ struct ieee80211_rx_status *stats = IEEE80211_SKB_RXCB(skb);
+ struct iwl_mld_sta *mld_sta;
+ struct iwl_mld_ptk_pn *ptk_pn;
+ int res;
+ u8 tid, keyidx;
+ u8 pn[IEEE80211_CCMP_PN_LEN];
+ u8 *extiv;
+
+ /* multicast and non-data only arrives on default queue; avoid checking
+ * for default queue - we don't want to replicate all the logic that's
+ * necessary for checking the PN on fragmented frames, leave that
+ * to mac80211
+ */
+ if (queue == 0 || !ieee80211_is_data(hdr->frame_control) ||
+ is_multicast_ether_addr(hdr->addr1))
+ return 0;
+
+ if (!(stats->flag & RX_FLAG_DECRYPTED))
+ return 0;
+
+ /* if we are here - this for sure is either CCMP or GCMP */
+ if (!sta) {
+ IWL_DEBUG_DROP(mld,
+ "expected hw-decrypted unicast frame for station\n");
+ return -1;
+ }
+
+ mld_sta = iwl_mld_sta_from_mac80211(sta);
+
+ extiv = (u8 *)hdr + ieee80211_hdrlen(hdr->frame_control);
+ keyidx = extiv[3] >> 6;
+
+ ptk_pn = rcu_dereference(mld_sta->ptk_pn[keyidx]);
+ if (!ptk_pn)
+ return -1;
+
+ if (ieee80211_is_data_qos(hdr->frame_control))
+ tid = ieee80211_get_tid(hdr);
+ else
+ tid = 0;
+
+ /* we don't use HCCA/802.11 QoS TSPECs, so drop such frames */
+ if (tid >= IWL_MAX_TID_COUNT)
+ return -1;
+
+ /* load pn */
+ pn[0] = extiv[7];
+ pn[1] = extiv[6];
+ pn[2] = extiv[5];
+ pn[3] = extiv[4];
+ pn[4] = extiv[1];
+ pn[5] = extiv[0];
+
+ res = memcmp(pn, ptk_pn->q[queue].pn[tid], IEEE80211_CCMP_PN_LEN);
+ if (res < 0)
+ return -1;
+ if (!res && !(stats->flag & RX_FLAG_ALLOW_SAME_PN))
+ return -1;
+
+ memcpy(ptk_pn->q[queue].pn[tid], pn, IEEE80211_CCMP_PN_LEN);
+ stats->flag |= RX_FLAG_PN_VALIDATED;
+
+ return 0;
+}
+
+/* iwl_mld_pass_packet_to_mac80211 - passes the packet for mac80211 */
+void iwl_mld_pass_packet_to_mac80211(struct iwl_mld *mld,
+ struct napi_struct *napi,
+ struct sk_buff *skb, int queue,
+ struct ieee80211_sta *sta)
+{
+ KUNIT_STATIC_STUB_REDIRECT(iwl_mld_pass_packet_to_mac80211,
+ mld, napi, skb, queue, sta);
+
+ if (unlikely(iwl_mld_check_pn(mld, skb, queue, sta))) {
+ kfree_skb(skb);
+ return;
+ }
+
+ ieee80211_rx_napi(mld->hw, sta, skb, napi);
+}
+EXPORT_SYMBOL_IF_IWLWIFI_KUNIT(iwl_mld_pass_packet_to_mac80211);
+
+static void iwl_mld_fill_signal(struct iwl_mld *mld,
+ struct ieee80211_rx_status *rx_status,
+ struct iwl_mld_rx_phy_data *phy_data)
+{
+ u32 rate_n_flags = phy_data->rate_n_flags;
+ int energy_a = phy_data->energy_a;
+ int energy_b = phy_data->energy_b;
+ int max_energy;
+
+ energy_a = energy_a ? -energy_a : S8_MIN;
+ energy_b = energy_b ? -energy_b : S8_MIN;
+ max_energy = max(energy_a, energy_b);
+
+ IWL_DEBUG_STATS(mld, "energy in A %d B %d, and max %d\n",
+ energy_a, energy_b, max_energy);
+
+ rx_status->signal = max_energy;
+ rx_status->chains =
+ (rate_n_flags & RATE_MCS_ANT_AB_MSK) >> RATE_MCS_ANT_POS;
+ rx_status->chain_signal[0] = energy_a;
+ rx_status->chain_signal[1] = energy_b;
+}
+
+static void
+iwl_mld_decode_he_phy_ru_alloc(struct iwl_mld_rx_phy_data *phy_data,
+ struct ieee80211_radiotap_he *he,
+ struct ieee80211_radiotap_he_mu *he_mu,
+ struct ieee80211_rx_status *rx_status)
+{
+ /* Unfortunately, we have to leave the mac80211 data
+ * incorrect for the case that we receive an HE-MU
+ * transmission and *don't* have the HE phy data (due
+ * to the bits being used for TSF). This shouldn't
+ * happen though as management frames where we need
+ * the TSF/timers are not be transmitted in HE-MU.
+ */
+ u8 ru = le32_get_bits(phy_data->data1, IWL_RX_PHY_DATA1_HE_RU_ALLOC_MASK);
+ u32 rate_n_flags = phy_data->rate_n_flags;
+ u32 he_type = rate_n_flags & RATE_MCS_HE_TYPE_MSK;
+ u8 offs = 0;
+
+ rx_status->bw = RATE_INFO_BW_HE_RU;
+
+ he->data1 |= cpu_to_le16(IEEE80211_RADIOTAP_HE_DATA1_BW_RU_ALLOC_KNOWN);
+
+ switch (ru) {
+ case 0 ... 36:
+ rx_status->he_ru = NL80211_RATE_INFO_HE_RU_ALLOC_26;
+ offs = ru;
+ break;
+ case 37 ... 52:
+ rx_status->he_ru = NL80211_RATE_INFO_HE_RU_ALLOC_52;
+ offs = ru - 37;
+ break;
+ case 53 ... 60:
+ rx_status->he_ru = NL80211_RATE_INFO_HE_RU_ALLOC_106;
+ offs = ru - 53;
+ break;
+ case 61 ... 64:
+ rx_status->he_ru = NL80211_RATE_INFO_HE_RU_ALLOC_242;
+ offs = ru - 61;
+ break;
+ case 65 ... 66:
+ rx_status->he_ru = NL80211_RATE_INFO_HE_RU_ALLOC_484;
+ offs = ru - 65;
+ break;
+ case 67:
+ rx_status->he_ru = NL80211_RATE_INFO_HE_RU_ALLOC_996;
+ break;
+ case 68:
+ rx_status->he_ru = NL80211_RATE_INFO_HE_RU_ALLOC_2x996;
+ break;
+ }
+ he->data2 |= le16_encode_bits(offs,
+ IEEE80211_RADIOTAP_HE_DATA2_RU_OFFSET);
+ he->data2 |= cpu_to_le16(IEEE80211_RADIOTAP_HE_DATA2_PRISEC_80_KNOWN |
+ IEEE80211_RADIOTAP_HE_DATA2_RU_OFFSET_KNOWN);
+ if (phy_data->data1 & cpu_to_le32(IWL_RX_PHY_DATA1_HE_RU_ALLOC_SEC80))
+ he->data2 |=
+ cpu_to_le16(IEEE80211_RADIOTAP_HE_DATA2_PRISEC_80_SEC);
+
+#define CHECK_BW(bw) \
+ BUILD_BUG_ON(IEEE80211_RADIOTAP_HE_MU_FLAGS2_BW_FROM_SIG_A_BW_ ## bw ## MHZ != \
+ RATE_MCS_CHAN_WIDTH_##bw >> RATE_MCS_CHAN_WIDTH_POS); \
+ BUILD_BUG_ON(IEEE80211_RADIOTAP_HE_DATA6_TB_PPDU_BW_ ## bw ## MHZ != \
+ RATE_MCS_CHAN_WIDTH_##bw >> RATE_MCS_CHAN_WIDTH_POS)
+ CHECK_BW(20);
+ CHECK_BW(40);
+ CHECK_BW(80);
+ CHECK_BW(160);
+
+ if (he_mu)
+ he_mu->flags2 |=
+ le16_encode_bits(u32_get_bits(rate_n_flags,
+ RATE_MCS_CHAN_WIDTH_MSK),
+ IEEE80211_RADIOTAP_HE_MU_FLAGS2_BW_FROM_SIG_A_BW);
+ else if (he_type == RATE_MCS_HE_TYPE_TRIG)
+ he->data6 |=
+ cpu_to_le16(IEEE80211_RADIOTAP_HE_DATA6_TB_PPDU_BW_KNOWN) |
+ le16_encode_bits(u32_get_bits(rate_n_flags,
+ RATE_MCS_CHAN_WIDTH_MSK),
+ IEEE80211_RADIOTAP_HE_DATA6_TB_PPDU_BW);
+}
+
+static void
+iwl_mld_decode_he_mu_ext(struct iwl_mld_rx_phy_data *phy_data,
+ struct ieee80211_radiotap_he_mu *he_mu)
+{
+ u32 phy_data2 = le32_to_cpu(phy_data->data2);
+ u32 phy_data3 = le32_to_cpu(phy_data->data3);
+ u16 phy_data4 = le16_to_cpu(phy_data->data4);
+ u32 rate_n_flags = phy_data->rate_n_flags;
+
+ if (u32_get_bits(phy_data4, IWL_RX_PHY_DATA4_HE_MU_EXT_CH1_CRC_OK)) {
+ he_mu->flags1 |=
+ cpu_to_le16(IEEE80211_RADIOTAP_HE_MU_FLAGS1_CH1_RU_KNOWN |
+ IEEE80211_RADIOTAP_HE_MU_FLAGS1_CH1_CTR_26T_RU_KNOWN);
+
+ he_mu->flags1 |=
+ le16_encode_bits(u32_get_bits(phy_data4,
+ IWL_RX_PHY_DATA4_HE_MU_EXT_CH1_CTR_RU),
+ IEEE80211_RADIOTAP_HE_MU_FLAGS1_CH1_CTR_26T_RU);
+
+ he_mu->ru_ch1[0] = u32_get_bits(phy_data2,
+ IWL_RX_PHY_DATA2_HE_MU_EXT_CH1_RU0);
+ he_mu->ru_ch1[1] = u32_get_bits(phy_data3,
+ IWL_RX_PHY_DATA3_HE_MU_EXT_CH1_RU1);
+ he_mu->ru_ch1[2] = u32_get_bits(phy_data2,
+ IWL_RX_PHY_DATA2_HE_MU_EXT_CH1_RU2);
+ he_mu->ru_ch1[3] = u32_get_bits(phy_data3,
+ IWL_RX_PHY_DATA3_HE_MU_EXT_CH1_RU3);
+ }
+
+ if (u32_get_bits(phy_data4, IWL_RX_PHY_DATA4_HE_MU_EXT_CH2_CRC_OK) &&
+ (rate_n_flags & RATE_MCS_CHAN_WIDTH_MSK) != RATE_MCS_CHAN_WIDTH_20) {
+ he_mu->flags1 |=
+ cpu_to_le16(IEEE80211_RADIOTAP_HE_MU_FLAGS1_CH2_RU_KNOWN |
+ IEEE80211_RADIOTAP_HE_MU_FLAGS1_CH2_CTR_26T_RU_KNOWN);
+
+ he_mu->flags2 |=
+ le16_encode_bits(u32_get_bits(phy_data4,
+ IWL_RX_PHY_DATA4_HE_MU_EXT_CH2_CTR_RU),
+ IEEE80211_RADIOTAP_HE_MU_FLAGS2_CH2_CTR_26T_RU);
+
+ he_mu->ru_ch2[0] = u32_get_bits(phy_data2,
+ IWL_RX_PHY_DATA2_HE_MU_EXT_CH2_RU0);
+ he_mu->ru_ch2[1] = u32_get_bits(phy_data3,
+ IWL_RX_PHY_DATA3_HE_MU_EXT_CH2_RU1);
+ he_mu->ru_ch2[2] = u32_get_bits(phy_data2,
+ IWL_RX_PHY_DATA2_HE_MU_EXT_CH2_RU2);
+ he_mu->ru_ch2[3] = u32_get_bits(phy_data3,
+ IWL_RX_PHY_DATA3_HE_MU_EXT_CH2_RU3);
+ }
+}
+
+static void
+iwl_mld_decode_he_phy_data(struct iwl_mld_rx_phy_data *phy_data,
+ struct ieee80211_radiotap_he *he,
+ struct ieee80211_radiotap_he_mu *he_mu,
+ struct ieee80211_rx_status *rx_status,
+ int queue)
+{
+ switch (phy_data->info_type) {
+ case IWL_RX_PHY_INFO_TYPE_NONE:
+ case IWL_RX_PHY_INFO_TYPE_CCK:
+ case IWL_RX_PHY_INFO_TYPE_OFDM_LGCY:
+ case IWL_RX_PHY_INFO_TYPE_HT:
+ case IWL_RX_PHY_INFO_TYPE_VHT_SU:
+ case IWL_RX_PHY_INFO_TYPE_VHT_MU:
+ case IWL_RX_PHY_INFO_TYPE_EHT_MU:
+ case IWL_RX_PHY_INFO_TYPE_EHT_TB:
+ case IWL_RX_PHY_INFO_TYPE_EHT_MU_EXT:
+ case IWL_RX_PHY_INFO_TYPE_EHT_TB_EXT:
+ return;
+ case IWL_RX_PHY_INFO_TYPE_HE_TB_EXT:
+ he->data1 |= cpu_to_le16(IEEE80211_RADIOTAP_HE_DATA1_SPTL_REUSE_KNOWN |
+ IEEE80211_RADIOTAP_HE_DATA1_SPTL_REUSE2_KNOWN |
+ IEEE80211_RADIOTAP_HE_DATA1_SPTL_REUSE3_KNOWN |
+ IEEE80211_RADIOTAP_HE_DATA1_SPTL_REUSE4_KNOWN);
+ he->data4 |= le16_encode_bits(le32_get_bits(phy_data->data2,
+ IWL_RX_PHY_DATA2_HE_TB_EXT_SPTL_REUSE1),
+ IEEE80211_RADIOTAP_HE_DATA4_TB_SPTL_REUSE1);
+ he->data4 |= le16_encode_bits(le32_get_bits(phy_data->data2,
+ IWL_RX_PHY_DATA2_HE_TB_EXT_SPTL_REUSE2),
+ IEEE80211_RADIOTAP_HE_DATA4_TB_SPTL_REUSE2);
+ he->data4 |= le16_encode_bits(le32_get_bits(phy_data->data2,
+ IWL_RX_PHY_DATA2_HE_TB_EXT_SPTL_REUSE3),
+ IEEE80211_RADIOTAP_HE_DATA4_TB_SPTL_REUSE3);
+ he->data4 |= le16_encode_bits(le32_get_bits(phy_data->data2,
+ IWL_RX_PHY_DATA2_HE_TB_EXT_SPTL_REUSE4),
+ IEEE80211_RADIOTAP_HE_DATA4_TB_SPTL_REUSE4);
+ fallthrough;
+ case IWL_RX_PHY_INFO_TYPE_HE_SU:
+ case IWL_RX_PHY_INFO_TYPE_HE_MU:
+ case IWL_RX_PHY_INFO_TYPE_HE_MU_EXT:
+ case IWL_RX_PHY_INFO_TYPE_HE_TB:
+ /* HE common */
+ he->data1 |= cpu_to_le16(IEEE80211_RADIOTAP_HE_DATA1_LDPC_XSYMSEG_KNOWN |
+ IEEE80211_RADIOTAP_HE_DATA1_DOPPLER_KNOWN |
+ IEEE80211_RADIOTAP_HE_DATA1_BSS_COLOR_KNOWN);
+ he->data2 |= cpu_to_le16(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_NUM_LTF_SYMS_KNOWN);
+ he->data3 |= le16_encode_bits(le32_get_bits(phy_data->data0,
+ IWL_RX_PHY_DATA0_HE_BSS_COLOR_MASK),
+ IEEE80211_RADIOTAP_HE_DATA3_BSS_COLOR);
+ if (phy_data->info_type != IWL_RX_PHY_INFO_TYPE_HE_TB &&
+ phy_data->info_type != IWL_RX_PHY_INFO_TYPE_HE_TB_EXT) {
+ he->data1 |= cpu_to_le16(IEEE80211_RADIOTAP_HE_DATA1_UL_DL_KNOWN);
+ he->data3 |= le16_encode_bits(le32_get_bits(phy_data->data0,
+ IWL_RX_PHY_DATA0_HE_UPLINK),
+ IEEE80211_RADIOTAP_HE_DATA3_UL_DL);
+ }
+ he->data3 |= le16_encode_bits(le32_get_bits(phy_data->data0,
+ IWL_RX_PHY_DATA0_HE_LDPC_EXT_SYM),
+ IEEE80211_RADIOTAP_HE_DATA3_LDPC_XSYMSEG);
+ he->data5 |= le16_encode_bits(le32_get_bits(phy_data->data0,
+ IWL_RX_PHY_DATA0_HE_PRE_FEC_PAD_MASK),
+ IEEE80211_RADIOTAP_HE_DATA5_PRE_FEC_PAD);
+ he->data5 |= le16_encode_bits(le32_get_bits(phy_data->data0,
+ IWL_RX_PHY_DATA0_HE_PE_DISAMBIG),
+ IEEE80211_RADIOTAP_HE_DATA5_PE_DISAMBIG);
+ he->data5 |= le16_encode_bits(le32_get_bits(phy_data->data1,
+ IWL_RX_PHY_DATA1_HE_LTF_NUM_MASK),
+ IEEE80211_RADIOTAP_HE_DATA5_NUM_LTF_SYMS);
+ he->data6 |= le16_encode_bits(le32_get_bits(phy_data->data0,
+ IWL_RX_PHY_DATA0_HE_TXOP_DUR_MASK),
+ IEEE80211_RADIOTAP_HE_DATA6_TXOP);
+ he->data6 |= le16_encode_bits(le32_get_bits(phy_data->data0,
+ IWL_RX_PHY_DATA0_HE_DOPPLER),
+ IEEE80211_RADIOTAP_HE_DATA6_DOPPLER);
+ break;
+ }
+
+ switch (phy_data->info_type) {
+ case IWL_RX_PHY_INFO_TYPE_HE_MU_EXT:
+ case IWL_RX_PHY_INFO_TYPE_HE_MU:
+ case IWL_RX_PHY_INFO_TYPE_HE_SU:
+ he->data1 |= cpu_to_le16(IEEE80211_RADIOTAP_HE_DATA1_SPTL_REUSE_KNOWN);
+ he->data4 |= le16_encode_bits(le32_get_bits(phy_data->data0,
+ IWL_RX_PHY_DATA0_HE_SPATIAL_REUSE_MASK),
+ IEEE80211_RADIOTAP_HE_DATA4_SU_MU_SPTL_REUSE);
+ break;
+ default:
+ /* nothing here */
+ break;
+ }
+
+ switch (phy_data->info_type) {
+ case IWL_RX_PHY_INFO_TYPE_HE_MU_EXT:
+ he_mu->flags1 |=
+ le16_encode_bits(le16_get_bits(phy_data->data4,
+ IWL_RX_PHY_DATA4_HE_MU_EXT_SIGB_DCM),
+ IEEE80211_RADIOTAP_HE_MU_FLAGS1_SIG_B_DCM);
+ he_mu->flags1 |=
+ le16_encode_bits(le16_get_bits(phy_data->data4,
+ IWL_RX_PHY_DATA4_HE_MU_EXT_SIGB_MCS_MASK),
+ IEEE80211_RADIOTAP_HE_MU_FLAGS1_SIG_B_MCS);
+ he_mu->flags2 |=
+ le16_encode_bits(le16_get_bits(phy_data->data4,
+ IWL_RX_PHY_DATA4_HE_MU_EXT_PREAMBLE_PUNC_TYPE_MASK),
+ IEEE80211_RADIOTAP_HE_MU_FLAGS2_PUNC_FROM_SIG_A_BW);
+ iwl_mld_decode_he_mu_ext(phy_data, he_mu);
+ fallthrough;
+ case IWL_RX_PHY_INFO_TYPE_HE_MU:
+ he_mu->flags2 |=
+ le16_encode_bits(le32_get_bits(phy_data->data1,
+ IWL_RX_PHY_DATA1_HE_MU_SIBG_SYM_OR_USER_NUM_MASK),
+ IEEE80211_RADIOTAP_HE_MU_FLAGS2_SIG_B_SYMS_USERS);
+ he_mu->flags2 |=
+ le16_encode_bits(le32_get_bits(phy_data->data1,
+ IWL_RX_PHY_DATA1_HE_MU_SIGB_COMPRESSION),
+ IEEE80211_RADIOTAP_HE_MU_FLAGS2_SIG_B_COMP);
+ fallthrough;
+ case IWL_RX_PHY_INFO_TYPE_HE_TB:
+ case IWL_RX_PHY_INFO_TYPE_HE_TB_EXT:
+ iwl_mld_decode_he_phy_ru_alloc(phy_data, he, he_mu, rx_status);
+ break;
+ case IWL_RX_PHY_INFO_TYPE_HE_SU:
+ he->data1 |= cpu_to_le16(IEEE80211_RADIOTAP_HE_DATA1_BEAM_CHANGE_KNOWN);
+ he->data3 |= le16_encode_bits(le32_get_bits(phy_data->data0,
+ IWL_RX_PHY_DATA0_HE_BEAM_CHNG),
+ IEEE80211_RADIOTAP_HE_DATA3_BEAM_CHANGE);
+ break;
+ default:
+ /* nothing */
+ break;
+ }
+}
+
+static void iwl_mld_rx_he(struct iwl_mld *mld, struct sk_buff *skb,
+ struct iwl_mld_rx_phy_data *phy_data,
+ int queue)
+{
+ struct ieee80211_rx_status *rx_status = IEEE80211_SKB_RXCB(skb);
+ struct ieee80211_radiotap_he *he = NULL;
+ struct ieee80211_radiotap_he_mu *he_mu = NULL;
+ u32 rate_n_flags = phy_data->rate_n_flags;
+ u32 he_type = rate_n_flags & RATE_MCS_HE_TYPE_MSK;
+ u8 ltf;
+ static const struct ieee80211_radiotap_he known = {
+ .data1 = cpu_to_le16(IEEE80211_RADIOTAP_HE_DATA1_DATA_MCS_KNOWN |
+ IEEE80211_RADIOTAP_HE_DATA1_DATA_DCM_KNOWN |
+ IEEE80211_RADIOTAP_HE_DATA1_STBC_KNOWN |
+ IEEE80211_RADIOTAP_HE_DATA1_CODING_KNOWN),
+ .data2 = cpu_to_le16(IEEE80211_RADIOTAP_HE_DATA2_GI_KNOWN |
+ IEEE80211_RADIOTAP_HE_DATA2_TXBF_KNOWN),
+ };
+ static const struct ieee80211_radiotap_he_mu mu_known = {
+ .flags1 = cpu_to_le16(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_SYMS_USERS_KNOWN |
+ IEEE80211_RADIOTAP_HE_MU_FLAGS1_SIG_B_COMP_KNOWN),
+ .flags2 = cpu_to_le16(IEEE80211_RADIOTAP_HE_MU_FLAGS2_PUNC_FROM_SIG_A_BW_KNOWN |
+ IEEE80211_RADIOTAP_HE_MU_FLAGS2_BW_FROM_SIG_A_BW_KNOWN),
+ };
+ u16 phy_info = phy_data->phy_info;
+
+ he = skb_put_data(skb, &known, sizeof(known));
+ rx_status->flag |= RX_FLAG_RADIOTAP_HE;
+
+ if (phy_data->info_type == IWL_RX_PHY_INFO_TYPE_HE_MU ||
+ phy_data->info_type == IWL_RX_PHY_INFO_TYPE_HE_MU_EXT) {
+ he_mu = skb_put_data(skb, &mu_known, sizeof(mu_known));
+ rx_status->flag |= RX_FLAG_RADIOTAP_HE_MU;
+ }
+
+ /* report the AMPDU-EOF bit on single frames */
+ if (!queue && !(phy_info & IWL_RX_MPDU_PHY_AMPDU)) {
+ rx_status->flag |= RX_FLAG_AMPDU_DETAILS;
+ rx_status->flag |= RX_FLAG_AMPDU_EOF_BIT_KNOWN;
+ if (phy_data->data0 & cpu_to_le32(IWL_RX_PHY_DATA0_HE_DELIM_EOF))
+ rx_status->flag |= RX_FLAG_AMPDU_EOF_BIT;
+ }
+
+ if (phy_info & IWL_RX_MPDU_PHY_TSF_OVERLOAD)
+ iwl_mld_decode_he_phy_data(phy_data, he, he_mu, rx_status,
+ queue);
+
+ /* update aggregation data for monitor sake on default queue */
+ if (!queue && (phy_info & IWL_RX_MPDU_PHY_TSF_OVERLOAD) &&
+ (phy_info & IWL_RX_MPDU_PHY_AMPDU) && phy_data->first_subframe) {
+ rx_status->flag |= RX_FLAG_AMPDU_EOF_BIT_KNOWN;
+ if (phy_data->data0 & cpu_to_le32(IWL_RX_PHY_DATA0_EHT_DELIM_EOF))
+ rx_status->flag |= RX_FLAG_AMPDU_EOF_BIT;
+ }
+
+ if (he_type == RATE_MCS_HE_TYPE_EXT_SU &&
+ rate_n_flags & RATE_MCS_HE_106T_MSK) {
+ rx_status->bw = RATE_INFO_BW_HE_RU;
+ rx_status->he_ru = NL80211_RATE_INFO_HE_RU_ALLOC_106;
+ }
+
+ /* actually data is filled in mac80211 */
+ if (he_type == RATE_MCS_HE_TYPE_SU ||
+ he_type == RATE_MCS_HE_TYPE_EXT_SU)
+ he->data1 |=
+ cpu_to_le16(IEEE80211_RADIOTAP_HE_DATA1_BW_RU_ALLOC_KNOWN);
+
+#define CHECK_TYPE(F) \
+ BUILD_BUG_ON(IEEE80211_RADIOTAP_HE_DATA1_FORMAT_ ## F != \
+ (RATE_MCS_HE_TYPE_ ## F >> RATE_MCS_HE_TYPE_POS))
+
+ CHECK_TYPE(SU);
+ CHECK_TYPE(EXT_SU);
+ CHECK_TYPE(MU);
+ CHECK_TYPE(TRIG);
+
+ he->data1 |= cpu_to_le16(he_type >> RATE_MCS_HE_TYPE_POS);
+
+ if (rate_n_flags & RATE_MCS_BF_MSK)
+ he->data5 |= cpu_to_le16(IEEE80211_RADIOTAP_HE_DATA5_TXBF);
+
+ switch ((rate_n_flags & RATE_MCS_HE_GI_LTF_MSK) >>
+ RATE_MCS_HE_GI_LTF_POS) {
+ case 0:
+ if (he_type == RATE_MCS_HE_TYPE_TRIG)
+ rx_status->he_gi = NL80211_RATE_INFO_HE_GI_1_6;
+ else
+ rx_status->he_gi = NL80211_RATE_INFO_HE_GI_0_8;
+ if (he_type == RATE_MCS_HE_TYPE_MU)
+ ltf = IEEE80211_RADIOTAP_HE_DATA5_LTF_SIZE_4X;
+ else
+ ltf = IEEE80211_RADIOTAP_HE_DATA5_LTF_SIZE_1X;
+ break;
+ case 1:
+ if (he_type == RATE_MCS_HE_TYPE_TRIG)
+ rx_status->he_gi = NL80211_RATE_INFO_HE_GI_1_6;
+ else
+ rx_status->he_gi = NL80211_RATE_INFO_HE_GI_0_8;
+ ltf = IEEE80211_RADIOTAP_HE_DATA5_LTF_SIZE_2X;
+ break;
+ case 2:
+ if (he_type == RATE_MCS_HE_TYPE_TRIG) {
+ rx_status->he_gi = NL80211_RATE_INFO_HE_GI_3_2;
+ ltf = IEEE80211_RADIOTAP_HE_DATA5_LTF_SIZE_4X;
+ } else {
+ rx_status->he_gi = NL80211_RATE_INFO_HE_GI_1_6;
+ ltf = IEEE80211_RADIOTAP_HE_DATA5_LTF_SIZE_2X;
+ }
+ break;
+ case 3:
+ rx_status->he_gi = NL80211_RATE_INFO_HE_GI_3_2;
+ ltf = IEEE80211_RADIOTAP_HE_DATA5_LTF_SIZE_4X;
+ break;
+ case 4:
+ rx_status->he_gi = NL80211_RATE_INFO_HE_GI_0_8;
+ ltf = IEEE80211_RADIOTAP_HE_DATA5_LTF_SIZE_4X;
+ break;
+ default:
+ ltf = IEEE80211_RADIOTAP_HE_DATA5_LTF_SIZE_UNKNOWN;
+ }
+
+ he->data5 |= le16_encode_bits(ltf,
+ IEEE80211_RADIOTAP_HE_DATA5_LTF_SIZE);
+}
+
+static void iwl_mld_decode_lsig(struct sk_buff *skb,
+ struct iwl_mld_rx_phy_data *phy_data)
+{
+ struct ieee80211_rx_status *rx_status = IEEE80211_SKB_RXCB(skb);
+ struct ieee80211_radiotap_lsig *lsig;
+
+ switch (phy_data->info_type) {
+ case IWL_RX_PHY_INFO_TYPE_HT:
+ case IWL_RX_PHY_INFO_TYPE_VHT_SU:
+ case IWL_RX_PHY_INFO_TYPE_VHT_MU:
+ case IWL_RX_PHY_INFO_TYPE_HE_TB_EXT:
+ case IWL_RX_PHY_INFO_TYPE_HE_SU:
+ case IWL_RX_PHY_INFO_TYPE_HE_MU:
+ case IWL_RX_PHY_INFO_TYPE_HE_MU_EXT:
+ case IWL_RX_PHY_INFO_TYPE_HE_TB:
+ case IWL_RX_PHY_INFO_TYPE_EHT_MU:
+ case IWL_RX_PHY_INFO_TYPE_EHT_TB:
+ case IWL_RX_PHY_INFO_TYPE_EHT_MU_EXT:
+ case IWL_RX_PHY_INFO_TYPE_EHT_TB_EXT:
+ lsig = skb_put(skb, sizeof(*lsig));
+ lsig->data1 = cpu_to_le16(IEEE80211_RADIOTAP_LSIG_DATA1_LENGTH_KNOWN);
+ lsig->data2 = le16_encode_bits(le32_get_bits(phy_data->data1,
+ IWL_RX_PHY_DATA1_LSIG_LEN_MASK),
+ IEEE80211_RADIOTAP_LSIG_DATA2_LENGTH);
+ rx_status->flag |= RX_FLAG_RADIOTAP_LSIG;
+ break;
+ default:
+ break;
+ }
+}
+
+/* Put a TLV on the skb and return data pointer
+ *
+ * Also pad the len to 4 and zero out all data part
+ */
+static void *
+iwl_mld_radiotap_put_tlv(struct sk_buff *skb, u16 type, u16 len)
+{
+ struct ieee80211_radiotap_tlv *tlv;
+
+ tlv = skb_put(skb, sizeof(*tlv));
+ tlv->type = cpu_to_le16(type);
+ tlv->len = cpu_to_le16(len);
+ return skb_put_zero(skb, ALIGN(len, 4));
+}
+
+#define LE32_DEC_ENC(value, dec_bits, enc_bits) \
+ le32_encode_bits(le32_get_bits(value, dec_bits), enc_bits)
+
+#define IWL_MLD_ENC_USIG_VALUE_MASK(usig, in_value, dec_bits, enc_bits) do { \
+ typeof(enc_bits) _enc_bits = enc_bits; \
+ typeof(usig) _usig = usig; \
+ (_usig)->mask |= cpu_to_le32(_enc_bits); \
+ (_usig)->value |= LE32_DEC_ENC(in_value, dec_bits, _enc_bits); \
+} while (0)
+
+#define __IWL_MLD_ENC_EHT_RU(rt_data, rt_ru, fw_data, fw_ru) \
+ eht->data[(rt_data)] |= \
+ (cpu_to_le32 \
+ (IEEE80211_RADIOTAP_EHT_DATA ## rt_data ## _RU_ALLOC_CC_ ## rt_ru ## _KNOWN) | \
+ LE32_DEC_ENC(data ## fw_data, \
+ IWL_RX_PHY_DATA ## fw_data ## _EHT_MU_EXT_RU_ALLOC_ ## fw_ru, \
+ IEEE80211_RADIOTAP_EHT_DATA ## rt_data ## _RU_ALLOC_CC_ ## rt_ru))
+
+#define _IWL_MLD_ENC_EHT_RU(rt_data, rt_ru, fw_data, fw_ru) \
+ __IWL_MLD_ENC_EHT_RU(rt_data, rt_ru, fw_data, fw_ru)
+
+#define IEEE80211_RADIOTAP_RU_DATA_1_1_1 1
+#define IEEE80211_RADIOTAP_RU_DATA_2_1_1 2
+#define IEEE80211_RADIOTAP_RU_DATA_1_1_2 2
+#define IEEE80211_RADIOTAP_RU_DATA_2_1_2 2
+#define IEEE80211_RADIOTAP_RU_DATA_1_2_1 3
+#define IEEE80211_RADIOTAP_RU_DATA_2_2_1 3
+#define IEEE80211_RADIOTAP_RU_DATA_1_2_2 3
+#define IEEE80211_RADIOTAP_RU_DATA_2_2_2 4
+
+#define IWL_RX_RU_DATA_A1 2
+#define IWL_RX_RU_DATA_A2 2
+#define IWL_RX_RU_DATA_B1 2
+#define IWL_RX_RU_DATA_B2 4
+#define IWL_RX_RU_DATA_C1 3
+#define IWL_RX_RU_DATA_C2 3
+#define IWL_RX_RU_DATA_D1 4
+#define IWL_RX_RU_DATA_D2 4
+
+#define IWL_MLD_ENC_EHT_RU(rt_ru, fw_ru) \
+ _IWL_MLD_ENC_EHT_RU(IEEE80211_RADIOTAP_RU_DATA_ ## rt_ru, \
+ rt_ru, \
+ IWL_RX_RU_DATA_ ## fw_ru, \
+ fw_ru)
+
+static void iwl_mld_decode_eht_ext_mu(struct iwl_mld *mld,
+ struct iwl_mld_rx_phy_data *phy_data,
+ struct ieee80211_rx_status *rx_status,
+ struct ieee80211_radiotap_eht *eht,
+ struct ieee80211_radiotap_eht_usig *usig)
+{
+ if (phy_data->with_data) {
+ __le32 data1 = phy_data->data1;
+ __le32 data2 = phy_data->data2;
+ __le32 data3 = phy_data->data3;
+ __le32 data4 = phy_data->eht_data4;
+ __le32 data5 = phy_data->data5;
+ u32 phy_bw = phy_data->rate_n_flags & RATE_MCS_CHAN_WIDTH_MSK;
+
+ IWL_MLD_ENC_USIG_VALUE_MASK(usig, data5,
+ IWL_RX_PHY_DATA5_EHT_TYPE_AND_COMP,
+ IEEE80211_RADIOTAP_EHT_USIG2_MU_B0_B1_PPDU_TYPE);
+ IWL_MLD_ENC_USIG_VALUE_MASK(usig, data5,
+ IWL_RX_PHY_DATA5_EHT_MU_PUNC_CH_CODE,
+ IEEE80211_RADIOTAP_EHT_USIG2_MU_B3_B7_PUNCTURED_INFO);
+ IWL_MLD_ENC_USIG_VALUE_MASK(usig, data4,
+ IWL_RX_PHY_DATA4_EHT_MU_EXT_SIGB_MCS,
+ IEEE80211_RADIOTAP_EHT_USIG2_MU_B9_B10_SIG_MCS);
+ IWL_MLD_ENC_USIG_VALUE_MASK
+ (usig, data1, IWL_RX_PHY_DATA1_EHT_MU_NUM_SIG_SYM_USIGA2,
+ IEEE80211_RADIOTAP_EHT_USIG2_MU_B11_B15_EHT_SIG_SYMBOLS);
+
+ eht->user_info[0] |=
+ cpu_to_le32(IEEE80211_RADIOTAP_EHT_USER_INFO_STA_ID_KNOWN) |
+ LE32_DEC_ENC(data5, IWL_RX_PHY_DATA5_EHT_MU_STA_ID_USR,
+ IEEE80211_RADIOTAP_EHT_USER_INFO_STA_ID);
+
+ eht->known |= cpu_to_le32(IEEE80211_RADIOTAP_EHT_KNOWN_NR_NON_OFDMA_USERS_M);
+ eht->data[7] |= LE32_DEC_ENC
+ (data5, IWL_RX_PHY_DATA5_EHT_MU_NUM_USR_NON_OFDMA,
+ IEEE80211_RADIOTAP_EHT_DATA7_NUM_OF_NON_OFDMA_USERS);
+
+ /*
+ * Hardware labels the content channels/RU allocation values
+ * as follows:
+ * Content Channel 1 Content Channel 2
+ * 20 MHz: A1
+ * 40 MHz: A1 B1
+ * 80 MHz: A1 C1 B1 D1
+ * 160 MHz: A1 C1 A2 C2 B1 D1 B2 D2
+ * 320 MHz: A1 C1 A2 C2 A3 C3 A4 C4 B1 D1 B2 D2 B3 D3 B4 D4
+ *
+ * However firmware can only give us A1-D2, so the higher
+ * frequencies are missing.
+ */
+
+ switch (phy_bw) {
+ case RATE_MCS_CHAN_WIDTH_320:
+ /* additional values are missing in RX metadata */
+ fallthrough;
+ case RATE_MCS_CHAN_WIDTH_160:
+ /* content channel 1 */
+ IWL_MLD_ENC_EHT_RU(1_2_1, A2);
+ IWL_MLD_ENC_EHT_RU(1_2_2, C2);
+ /* content channel 2 */
+ IWL_MLD_ENC_EHT_RU(2_2_1, B2);
+ IWL_MLD_ENC_EHT_RU(2_2_2, D2);
+ fallthrough;
+ case RATE_MCS_CHAN_WIDTH_80:
+ /* content channel 1 */
+ IWL_MLD_ENC_EHT_RU(1_1_2, C1);
+ /* content channel 2 */
+ IWL_MLD_ENC_EHT_RU(2_1_2, D1);
+ fallthrough;
+ case RATE_MCS_CHAN_WIDTH_40:
+ /* content channel 2 */
+ IWL_MLD_ENC_EHT_RU(2_1_1, B1);
+ fallthrough;
+ case RATE_MCS_CHAN_WIDTH_20:
+ IWL_MLD_ENC_EHT_RU(1_1_1, A1);
+ break;
+ }
+ } else {
+ __le32 usig_a1 = phy_data->rx_vec[0];
+ __le32 usig_a2 = phy_data->rx_vec[1];
+
+ IWL_MLD_ENC_USIG_VALUE_MASK(usig, usig_a1,
+ IWL_RX_USIG_A1_DISREGARD,
+ IEEE80211_RADIOTAP_EHT_USIG1_MU_B20_B24_DISREGARD);
+ IWL_MLD_ENC_USIG_VALUE_MASK(usig, usig_a1,
+ IWL_RX_USIG_A1_VALIDATE,
+ IEEE80211_RADIOTAP_EHT_USIG1_MU_B25_VALIDATE);
+ IWL_MLD_ENC_USIG_VALUE_MASK(usig, usig_a2,
+ IWL_RX_USIG_A2_EHT_PPDU_TYPE,
+ IEEE80211_RADIOTAP_EHT_USIG2_MU_B0_B1_PPDU_TYPE);
+ IWL_MLD_ENC_USIG_VALUE_MASK(usig, usig_a2,
+ IWL_RX_USIG_A2_EHT_USIG2_VALIDATE_B2,
+ IEEE80211_RADIOTAP_EHT_USIG2_MU_B2_VALIDATE);
+ IWL_MLD_ENC_USIG_VALUE_MASK(usig, usig_a2,
+ IWL_RX_USIG_A2_EHT_PUNC_CHANNEL,
+ IEEE80211_RADIOTAP_EHT_USIG2_MU_B3_B7_PUNCTURED_INFO);
+ IWL_MLD_ENC_USIG_VALUE_MASK(usig, usig_a2,
+ IWL_RX_USIG_A2_EHT_USIG2_VALIDATE_B8,
+ IEEE80211_RADIOTAP_EHT_USIG2_MU_B8_VALIDATE);
+ IWL_MLD_ENC_USIG_VALUE_MASK(usig, usig_a2,
+ IWL_RX_USIG_A2_EHT_SIG_MCS,
+ IEEE80211_RADIOTAP_EHT_USIG2_MU_B9_B10_SIG_MCS);
+ IWL_MLD_ENC_USIG_VALUE_MASK
+ (usig, usig_a2, IWL_RX_USIG_A2_EHT_SIG_SYM_NUM,
+ IEEE80211_RADIOTAP_EHT_USIG2_MU_B11_B15_EHT_SIG_SYMBOLS);
+ IWL_MLD_ENC_USIG_VALUE_MASK(usig, usig_a2,
+ IWL_RX_USIG_A2_EHT_CRC_OK,
+ IEEE80211_RADIOTAP_EHT_USIG2_MU_B16_B19_CRC);
+ }
+}
+
+static void iwl_mld_decode_eht_ext_tb(struct iwl_mld *mld,
+ struct iwl_mld_rx_phy_data *phy_data,
+ struct ieee80211_rx_status *rx_status,
+ struct ieee80211_radiotap_eht *eht,
+ struct ieee80211_radiotap_eht_usig *usig)
+{
+ if (phy_data->with_data) {
+ __le32 data5 = phy_data->data5;
+
+ IWL_MLD_ENC_USIG_VALUE_MASK(usig, data5,
+ IWL_RX_PHY_DATA5_EHT_TYPE_AND_COMP,
+ IEEE80211_RADIOTAP_EHT_USIG2_TB_B0_B1_PPDU_TYPE);
+ IWL_MLD_ENC_USIG_VALUE_MASK(usig, data5,
+ IWL_RX_PHY_DATA5_EHT_TB_SPATIAL_REUSE1,
+ IEEE80211_RADIOTAP_EHT_USIG2_TB_B3_B6_SPATIAL_REUSE_1);
+
+ IWL_MLD_ENC_USIG_VALUE_MASK(usig, data5,
+ IWL_RX_PHY_DATA5_EHT_TB_SPATIAL_REUSE2,
+ IEEE80211_RADIOTAP_EHT_USIG2_TB_B7_B10_SPATIAL_REUSE_2);
+ } else {
+ __le32 usig_a1 = phy_data->rx_vec[0];
+ __le32 usig_a2 = phy_data->rx_vec[1];
+
+ IWL_MLD_ENC_USIG_VALUE_MASK(usig, usig_a1,
+ IWL_RX_USIG_A1_DISREGARD,
+ IEEE80211_RADIOTAP_EHT_USIG1_TB_B20_B25_DISREGARD);
+ IWL_MLD_ENC_USIG_VALUE_MASK(usig, usig_a2,
+ IWL_RX_USIG_A2_EHT_PPDU_TYPE,
+ IEEE80211_RADIOTAP_EHT_USIG2_TB_B0_B1_PPDU_TYPE);
+ IWL_MLD_ENC_USIG_VALUE_MASK(usig, usig_a2,
+ IWL_RX_USIG_A2_EHT_USIG2_VALIDATE_B2,
+ IEEE80211_RADIOTAP_EHT_USIG2_TB_B2_VALIDATE);
+ IWL_MLD_ENC_USIG_VALUE_MASK(usig, usig_a2,
+ IWL_RX_USIG_A2_EHT_TRIG_SPATIAL_REUSE_1,
+ IEEE80211_RADIOTAP_EHT_USIG2_TB_B3_B6_SPATIAL_REUSE_1);
+ IWL_MLD_ENC_USIG_VALUE_MASK(usig, usig_a2,
+ IWL_RX_USIG_A2_EHT_TRIG_SPATIAL_REUSE_2,
+ IEEE80211_RADIOTAP_EHT_USIG2_TB_B7_B10_SPATIAL_REUSE_2);
+ IWL_MLD_ENC_USIG_VALUE_MASK(usig, usig_a2,
+ IWL_RX_USIG_A2_EHT_TRIG_USIG2_DISREGARD,
+ IEEE80211_RADIOTAP_EHT_USIG2_TB_B11_B15_DISREGARD);
+ IWL_MLD_ENC_USIG_VALUE_MASK(usig, usig_a2,
+ IWL_RX_USIG_A2_EHT_CRC_OK,
+ IEEE80211_RADIOTAP_EHT_USIG2_TB_B16_B19_CRC);
+ }
+}
+
+static void iwl_mld_decode_eht_ru(struct iwl_mld *mld,
+ struct ieee80211_rx_status *rx_status,
+ struct ieee80211_radiotap_eht *eht)
+{
+ u32 ru = le32_get_bits(eht->data[8],
+ IEEE80211_RADIOTAP_EHT_DATA8_RU_ALLOC_TB_FMT_B7_B1);
+ enum nl80211_eht_ru_alloc nl_ru;
+
+ /* Using D1.5 Table 9-53a - Encoding of PS160 and RU Allocation subfields
+ * in an EHT variant User Info field
+ */
+
+ switch (ru) {
+ case 0 ... 36:
+ nl_ru = NL80211_RATE_INFO_EHT_RU_ALLOC_26;
+ break;
+ case 37 ... 52:
+ nl_ru = NL80211_RATE_INFO_EHT_RU_ALLOC_52;
+ break;
+ case 53 ... 60:
+ nl_ru = NL80211_RATE_INFO_EHT_RU_ALLOC_106;
+ break;
+ case 61 ... 64:
+ nl_ru = NL80211_RATE_INFO_EHT_RU_ALLOC_242;
+ break;
+ case 65 ... 66:
+ nl_ru = NL80211_RATE_INFO_EHT_RU_ALLOC_484;
+ break;
+ case 67:
+ nl_ru = NL80211_RATE_INFO_EHT_RU_ALLOC_996;
+ break;
+ case 68:
+ nl_ru = NL80211_RATE_INFO_EHT_RU_ALLOC_2x996;
+ break;
+ case 69:
+ nl_ru = NL80211_RATE_INFO_EHT_RU_ALLOC_4x996;
+ break;
+ case 70 ... 81:
+ nl_ru = NL80211_RATE_INFO_EHT_RU_ALLOC_52P26;
+ break;
+ case 82 ... 89:
+ nl_ru = NL80211_RATE_INFO_EHT_RU_ALLOC_106P26;
+ break;
+ case 90 ... 93:
+ nl_ru = NL80211_RATE_INFO_EHT_RU_ALLOC_484P242;
+ break;
+ case 94 ... 95:
+ nl_ru = NL80211_RATE_INFO_EHT_RU_ALLOC_996P484;
+ break;
+ case 96 ... 99:
+ nl_ru = NL80211_RATE_INFO_EHT_RU_ALLOC_996P484P242;
+ break;
+ case 100 ... 103:
+ nl_ru = NL80211_RATE_INFO_EHT_RU_ALLOC_2x996P484;
+ break;
+ case 104:
+ nl_ru = NL80211_RATE_INFO_EHT_RU_ALLOC_3x996;
+ break;
+ case 105 ... 106:
+ nl_ru = NL80211_RATE_INFO_EHT_RU_ALLOC_3x996P484;
+ break;
+ default:
+ return;
+ }
+
+ rx_status->bw = RATE_INFO_BW_EHT_RU;
+ rx_status->eht.ru = nl_ru;
+}
+
+static void iwl_mld_decode_eht_phy_data(struct iwl_mld *mld,
+ struct iwl_mld_rx_phy_data *phy_data,
+ struct ieee80211_rx_status *rx_status,
+ struct ieee80211_radiotap_eht *eht,
+ struct ieee80211_radiotap_eht_usig *usig)
+
+{
+ __le32 data0 = phy_data->data0;
+ __le32 data1 = phy_data->data1;
+ __le32 usig_a1 = phy_data->rx_vec[0];
+ u8 info_type = phy_data->info_type;
+
+ /* Not in EHT range */
+ if (info_type < IWL_RX_PHY_INFO_TYPE_EHT_MU ||
+ info_type > IWL_RX_PHY_INFO_TYPE_EHT_TB_EXT)
+ return;
+
+ usig->common |= cpu_to_le32
+ (IEEE80211_RADIOTAP_EHT_USIG_COMMON_UL_DL_KNOWN |
+ IEEE80211_RADIOTAP_EHT_USIG_COMMON_BSS_COLOR_KNOWN);
+ if (phy_data->with_data) {
+ usig->common |= LE32_DEC_ENC(data0,
+ IWL_RX_PHY_DATA0_EHT_UPLINK,
+ IEEE80211_RADIOTAP_EHT_USIG_COMMON_UL_DL);
+ usig->common |= LE32_DEC_ENC(data0,
+ IWL_RX_PHY_DATA0_EHT_BSS_COLOR_MASK,
+ IEEE80211_RADIOTAP_EHT_USIG_COMMON_BSS_COLOR);
+ } else {
+ usig->common |= LE32_DEC_ENC(usig_a1,
+ IWL_RX_USIG_A1_UL_FLAG,
+ IEEE80211_RADIOTAP_EHT_USIG_COMMON_UL_DL);
+ usig->common |= LE32_DEC_ENC(usig_a1,
+ IWL_RX_USIG_A1_BSS_COLOR,
+ IEEE80211_RADIOTAP_EHT_USIG_COMMON_BSS_COLOR);
+ }
+
+ usig->common |=
+ cpu_to_le32(IEEE80211_RADIOTAP_EHT_USIG_COMMON_VALIDATE_BITS_CHECKED);
+ usig->common |=
+ LE32_DEC_ENC(data0, IWL_RX_PHY_DATA0_EHT_VALIDATE,
+ IEEE80211_RADIOTAP_EHT_USIG_COMMON_VALIDATE_BITS_OK);
+
+ eht->known |= cpu_to_le32(IEEE80211_RADIOTAP_EHT_KNOWN_SPATIAL_REUSE);
+ eht->data[0] |= LE32_DEC_ENC(data0,
+ IWL_RX_PHY_DATA0_ETH_SPATIAL_REUSE_MASK,
+ IEEE80211_RADIOTAP_EHT_DATA0_SPATIAL_REUSE);
+
+ /* All RU allocating size/index is in TB format */
+ eht->known |= cpu_to_le32(IEEE80211_RADIOTAP_EHT_KNOWN_RU_ALLOC_TB_FMT);
+ eht->data[8] |= LE32_DEC_ENC(data0, IWL_RX_PHY_DATA0_EHT_PS160,
+ IEEE80211_RADIOTAP_EHT_DATA8_RU_ALLOC_TB_FMT_PS_160);
+ eht->data[8] |= LE32_DEC_ENC(data1, IWL_RX_PHY_DATA1_EHT_RU_ALLOC_B0,
+ IEEE80211_RADIOTAP_EHT_DATA8_RU_ALLOC_TB_FMT_B0);
+ eht->data[8] |= LE32_DEC_ENC(data1, IWL_RX_PHY_DATA1_EHT_RU_ALLOC_B1_B7,
+ IEEE80211_RADIOTAP_EHT_DATA8_RU_ALLOC_TB_FMT_B7_B1);
+
+ iwl_mld_decode_eht_ru(mld, rx_status, eht);
+
+ /* We only get here in case of IWL_RX_MPDU_PHY_TSF_OVERLOAD is set
+ * which is on only in case of monitor mode so no need to check monitor
+ * mode
+ */
+ eht->known |= cpu_to_le32(IEEE80211_RADIOTAP_EHT_KNOWN_PRIMARY_80);
+ eht->data[1] |=
+ le32_encode_bits(mld->monitor.p80,
+ IEEE80211_RADIOTAP_EHT_DATA1_PRIMARY_80);
+
+ usig->common |= cpu_to_le32(IEEE80211_RADIOTAP_EHT_USIG_COMMON_TXOP_KNOWN);
+ if (phy_data->with_data)
+ usig->common |= LE32_DEC_ENC(data0, IWL_RX_PHY_DATA0_EHT_TXOP_DUR_MASK,
+ IEEE80211_RADIOTAP_EHT_USIG_COMMON_TXOP);
+ else
+ usig->common |= LE32_DEC_ENC(usig_a1, IWL_RX_USIG_A1_TXOP_DURATION,
+ IEEE80211_RADIOTAP_EHT_USIG_COMMON_TXOP);
+
+ eht->known |= cpu_to_le32(IEEE80211_RADIOTAP_EHT_KNOWN_LDPC_EXTRA_SYM_OM);
+ eht->data[0] |= LE32_DEC_ENC(data0, IWL_RX_PHY_DATA0_EHT_LDPC_EXT_SYM,
+ IEEE80211_RADIOTAP_EHT_DATA0_LDPC_EXTRA_SYM_OM);
+
+ eht->known |= cpu_to_le32(IEEE80211_RADIOTAP_EHT_KNOWN_PRE_PADD_FACOR_OM);
+ eht->data[0] |= LE32_DEC_ENC(data0, IWL_RX_PHY_DATA0_EHT_PRE_FEC_PAD_MASK,
+ IEEE80211_RADIOTAP_EHT_DATA0_PRE_PADD_FACOR_OM);
+
+ eht->known |= cpu_to_le32(IEEE80211_RADIOTAP_EHT_KNOWN_PE_DISAMBIGUITY_OM);
+ eht->data[0] |= LE32_DEC_ENC(data0, IWL_RX_PHY_DATA0_EHT_PE_DISAMBIG,
+ IEEE80211_RADIOTAP_EHT_DATA0_PE_DISAMBIGUITY_OM);
+
+ /* TODO: what about IWL_RX_PHY_DATA0_EHT_BW320_SLOT */
+
+ if (!le32_get_bits(data0, IWL_RX_PHY_DATA0_EHT_SIGA_CRC_OK))
+ usig->common |= cpu_to_le32(IEEE80211_RADIOTAP_EHT_USIG_COMMON_BAD_USIG_CRC);
+
+ usig->common |= cpu_to_le32(IEEE80211_RADIOTAP_EHT_USIG_COMMON_PHY_VER_KNOWN);
+ usig->common |= LE32_DEC_ENC(data0, IWL_RX_PHY_DATA0_EHT_PHY_VER,
+ IEEE80211_RADIOTAP_EHT_USIG_COMMON_PHY_VER);
+
+ /*
+ * TODO: what about TB - IWL_RX_PHY_DATA1_EHT_TB_PILOT_TYPE,
+ * IWL_RX_PHY_DATA1_EHT_TB_LOW_SS
+ */
+
+ eht->known |= cpu_to_le32(IEEE80211_RADIOTAP_EHT_KNOWN_EHT_LTF);
+ eht->data[0] |= LE32_DEC_ENC(data1, IWL_RX_PHY_DATA1_EHT_SIG_LTF_NUM,
+ IEEE80211_RADIOTAP_EHT_DATA0_EHT_LTF);
+
+ if (info_type == IWL_RX_PHY_INFO_TYPE_EHT_TB_EXT ||
+ info_type == IWL_RX_PHY_INFO_TYPE_EHT_TB)
+ iwl_mld_decode_eht_ext_tb(mld, phy_data, rx_status, eht, usig);
+
+ if (info_type == IWL_RX_PHY_INFO_TYPE_EHT_MU_EXT ||
+ info_type == IWL_RX_PHY_INFO_TYPE_EHT_MU)
+ iwl_mld_decode_eht_ext_mu(mld, phy_data, rx_status, eht, usig);
+}
+
+static void iwl_mld_rx_eht(struct iwl_mld *mld, struct sk_buff *skb,
+ struct iwl_mld_rx_phy_data *phy_data,
+ int queue)
+{
+ struct ieee80211_rx_status *rx_status = IEEE80211_SKB_RXCB(skb);
+ struct ieee80211_radiotap_eht *eht;
+ struct ieee80211_radiotap_eht_usig *usig;
+ size_t eht_len = sizeof(*eht);
+
+ u32 rate_n_flags = phy_data->rate_n_flags;
+ u32 he_type = rate_n_flags & RATE_MCS_HE_TYPE_MSK;
+ /* EHT and HE have the same values for LTF */
+ u8 ltf = IEEE80211_RADIOTAP_HE_DATA5_LTF_SIZE_UNKNOWN;
+ u16 phy_info = phy_data->phy_info;
+ u32 bw;
+
+ /* u32 for 1 user_info */
+ if (phy_data->with_data)
+ eht_len += sizeof(u32);
+
+ eht = iwl_mld_radiotap_put_tlv(skb, IEEE80211_RADIOTAP_EHT, eht_len);
+
+ usig = iwl_mld_radiotap_put_tlv(skb, IEEE80211_RADIOTAP_EHT_USIG,
+ sizeof(*usig));
+ rx_status->flag |= RX_FLAG_RADIOTAP_TLV_AT_END;
+ usig->common |=
+ cpu_to_le32(IEEE80211_RADIOTAP_EHT_USIG_COMMON_BW_KNOWN);
+
+ /* specific handling for 320MHz */
+ bw = u32_get_bits(rate_n_flags, RATE_MCS_CHAN_WIDTH_MSK);
+ if (bw == RATE_MCS_CHAN_WIDTH_320_VAL)
+ bw += le32_get_bits(phy_data->data0,
+ IWL_RX_PHY_DATA0_EHT_BW320_SLOT);
+
+ usig->common |= cpu_to_le32
+ (FIELD_PREP(IEEE80211_RADIOTAP_EHT_USIG_COMMON_BW, bw));
+
+ /* report the AMPDU-EOF bit on single frames */
+ if (!queue && !(phy_info & IWL_RX_MPDU_PHY_AMPDU)) {
+ rx_status->flag |= RX_FLAG_AMPDU_DETAILS;
+ rx_status->flag |= RX_FLAG_AMPDU_EOF_BIT_KNOWN;
+ if (phy_data->data0 &
+ cpu_to_le32(IWL_RX_PHY_DATA0_EHT_DELIM_EOF))
+ rx_status->flag |= RX_FLAG_AMPDU_EOF_BIT;
+ }
+
+ if (phy_info & IWL_RX_MPDU_PHY_TSF_OVERLOAD)
+ iwl_mld_decode_eht_phy_data(mld, phy_data, rx_status, eht, usig);
+
+#define CHECK_TYPE(F) \
+ BUILD_BUG_ON(IEEE80211_RADIOTAP_HE_DATA1_FORMAT_ ## F != \
+ (RATE_MCS_HE_TYPE_ ## F >> RATE_MCS_HE_TYPE_POS))
+
+ CHECK_TYPE(SU);
+ CHECK_TYPE(EXT_SU);
+ CHECK_TYPE(MU);
+ CHECK_TYPE(TRIG);
+
+ switch (u32_get_bits(rate_n_flags, RATE_MCS_HE_GI_LTF_MSK)) {
+ case 0:
+ if (he_type == RATE_MCS_HE_TYPE_TRIG) {
+ rx_status->eht.gi = NL80211_RATE_INFO_EHT_GI_1_6;
+ ltf = IEEE80211_RADIOTAP_HE_DATA5_LTF_SIZE_1X;
+ } else {
+ rx_status->eht.gi = NL80211_RATE_INFO_EHT_GI_0_8;
+ ltf = IEEE80211_RADIOTAP_HE_DATA5_LTF_SIZE_2X;
+ }
+ break;
+ case 1:
+ rx_status->eht.gi = NL80211_RATE_INFO_EHT_GI_1_6;
+ ltf = IEEE80211_RADIOTAP_HE_DATA5_LTF_SIZE_2X;
+ break;
+ case 2:
+ ltf = IEEE80211_RADIOTAP_HE_DATA5_LTF_SIZE_4X;
+ if (he_type == RATE_MCS_HE_TYPE_TRIG)
+ rx_status->eht.gi = NL80211_RATE_INFO_EHT_GI_3_2;
+ else
+ rx_status->eht.gi = NL80211_RATE_INFO_EHT_GI_0_8;
+ break;
+ case 3:
+ if (he_type != RATE_MCS_HE_TYPE_TRIG) {
+ ltf = IEEE80211_RADIOTAP_HE_DATA5_LTF_SIZE_4X;
+ rx_status->eht.gi = NL80211_RATE_INFO_EHT_GI_3_2;
+ }
+ break;
+ default:
+ /* nothing here */
+ break;
+ }
+
+ if (ltf != IEEE80211_RADIOTAP_HE_DATA5_LTF_SIZE_UNKNOWN) {
+ eht->known |= cpu_to_le32(IEEE80211_RADIOTAP_EHT_KNOWN_GI);
+ eht->data[0] |= cpu_to_le32
+ (FIELD_PREP(IEEE80211_RADIOTAP_EHT_DATA0_LTF,
+ ltf) |
+ FIELD_PREP(IEEE80211_RADIOTAP_EHT_DATA0_GI,
+ rx_status->eht.gi));
+ }
+
+ if (!phy_data->with_data) {
+ eht->known |= cpu_to_le32(IEEE80211_RADIOTAP_EHT_KNOWN_NSS_S |
+ IEEE80211_RADIOTAP_EHT_KNOWN_BEAMFORMED_S);
+ eht->data[7] |=
+ le32_encode_bits(le32_get_bits(phy_data->rx_vec[2],
+ RX_NO_DATA_RX_VEC2_EHT_NSTS_MSK),
+ IEEE80211_RADIOTAP_EHT_DATA7_NSS_S);
+ if (rate_n_flags & RATE_MCS_BF_MSK)
+ eht->data[7] |=
+ cpu_to_le32(IEEE80211_RADIOTAP_EHT_DATA7_BEAMFORMED_S);
+ } else {
+ eht->user_info[0] |=
+ cpu_to_le32(IEEE80211_RADIOTAP_EHT_USER_INFO_MCS_KNOWN |
+ IEEE80211_RADIOTAP_EHT_USER_INFO_CODING_KNOWN |
+ IEEE80211_RADIOTAP_EHT_USER_INFO_NSS_KNOWN_O |
+ IEEE80211_RADIOTAP_EHT_USER_INFO_BEAMFORMING_KNOWN_O |
+ IEEE80211_RADIOTAP_EHT_USER_INFO_DATA_FOR_USER);
+
+ if (rate_n_flags & RATE_MCS_BF_MSK)
+ eht->user_info[0] |=
+ cpu_to_le32(IEEE80211_RADIOTAP_EHT_USER_INFO_BEAMFORMING_O);
+
+ if (rate_n_flags & RATE_MCS_LDPC_MSK)
+ eht->user_info[0] |=
+ cpu_to_le32(IEEE80211_RADIOTAP_EHT_USER_INFO_CODING);
+
+ eht->user_info[0] |= cpu_to_le32
+ (FIELD_PREP(IEEE80211_RADIOTAP_EHT_USER_INFO_MCS,
+ u32_get_bits(rate_n_flags,
+ RATE_VHT_MCS_RATE_CODE_MSK)) |
+ FIELD_PREP(IEEE80211_RADIOTAP_EHT_USER_INFO_NSS_O,
+ u32_get_bits(rate_n_flags,
+ RATE_MCS_NSS_MSK)));
+ }
+}
+
+#ifdef CONFIG_IWLWIFI_DEBUGFS
+static void iwl_mld_add_rtap_sniffer_config(struct iwl_mld *mld,
+ struct sk_buff *skb)
+{
+ struct ieee80211_rx_status *rx_status = IEEE80211_SKB_RXCB(skb);
+ struct ieee80211_radiotap_vendor_content *radiotap;
+ const u16 vendor_data_len = sizeof(mld->monitor.cur_aid);
+
+ if (!mld->monitor.cur_aid)
+ return;
+
+ radiotap =
+ iwl_mld_radiotap_put_tlv(skb,
+ IEEE80211_RADIOTAP_VENDOR_NAMESPACE,
+ sizeof(*radiotap) + vendor_data_len);
+
+ /* Intel OUI */
+ radiotap->oui[0] = 0xf6;
+ radiotap->oui[1] = 0x54;
+ radiotap->oui[2] = 0x25;
+ /* radiotap sniffer config sub-namespace */
+ radiotap->oui_subtype = 1;
+ radiotap->vendor_type = 0;
+
+ /* fill the data now */
+ memcpy(radiotap->data, &mld->monitor.cur_aid,
+ sizeof(mld->monitor.cur_aid));
+
+ rx_status->flag |= RX_FLAG_RADIOTAP_TLV_AT_END;
+}
+#endif
+
+static void iwl_mld_rx_fill_status(struct iwl_mld *mld, struct sk_buff *skb,
+ struct iwl_mld_rx_phy_data *phy_data,
+ int queue)
+{
+ struct ieee80211_rx_status *rx_status = IEEE80211_SKB_RXCB(skb);
+ u32 format = phy_data->rate_n_flags & RATE_MCS_MOD_TYPE_MSK;
+ u32 rate_n_flags = phy_data->rate_n_flags;
+ u8 stbc = u32_get_bits(rate_n_flags, RATE_MCS_STBC_MSK);
+ bool is_sgi = rate_n_flags & RATE_MCS_SGI_MSK;
+
+ phy_data->info_type = IWL_RX_PHY_INFO_TYPE_NONE;
+
+ if (phy_data->phy_info & IWL_RX_MPDU_PHY_TSF_OVERLOAD)
+ phy_data->info_type =
+ le32_get_bits(phy_data->data1,
+ IWL_RX_PHY_DATA1_INFO_TYPE_MASK);
+
+ /* set the preamble flag if appropriate */
+ if (format == RATE_MCS_MOD_TYPE_CCK &&
+ phy_data->phy_info & IWL_RX_MPDU_PHY_SHORT_PREAMBLE)
+ rx_status->enc_flags |= RX_ENC_FLAG_SHORTPRE;
+
+ iwl_mld_fill_signal(mld, rx_status, phy_data);
+
+ /* This may be overridden by iwl_mld_rx_he() to HE_RU */
+ switch (rate_n_flags & RATE_MCS_CHAN_WIDTH_MSK) {
+ case RATE_MCS_CHAN_WIDTH_20:
+ break;
+ case RATE_MCS_CHAN_WIDTH_40:
+ rx_status->bw = RATE_INFO_BW_40;
+ break;
+ case RATE_MCS_CHAN_WIDTH_80:
+ rx_status->bw = RATE_INFO_BW_80;
+ break;
+ case RATE_MCS_CHAN_WIDTH_160:
+ rx_status->bw = RATE_INFO_BW_160;
+ break;
+ case RATE_MCS_CHAN_WIDTH_320:
+ rx_status->bw = RATE_INFO_BW_320;
+ break;
+ }
+
+ /* must be before L-SIG data */
+ if (format == RATE_MCS_MOD_TYPE_HE)
+ iwl_mld_rx_he(mld, skb, phy_data, queue);
+
+ iwl_mld_decode_lsig(skb, phy_data);
+
+ rx_status->device_timestamp = phy_data->gp2_on_air_rise;
+
+ /* using TLV format and must be after all fixed len fields */
+ if (format == RATE_MCS_MOD_TYPE_EHT)
+ iwl_mld_rx_eht(mld, skb, phy_data, queue);
+
+#ifdef CONFIG_IWLWIFI_DEBUGFS
+ if (unlikely(mld->monitor.on)) {
+ iwl_mld_add_rtap_sniffer_config(mld, skb);
+
+ if (mld->monitor.ptp_time) {
+ u64 adj_time =
+ iwl_mld_ptp_get_adj_time(mld,
+ phy_data->gp2_on_air_rise *
+ NSEC_PER_USEC);
+
+ rx_status->mactime = div64_u64(adj_time, NSEC_PER_USEC);
+ rx_status->flag |= RX_FLAG_MACTIME_IS_RTAP_TS64;
+ rx_status->flag &= ~RX_FLAG_MACTIME;
+ }
+ }
+#endif
+
+ if (format != RATE_MCS_MOD_TYPE_CCK && is_sgi)
+ rx_status->enc_flags |= RX_ENC_FLAG_SHORT_GI;
+
+ if (rate_n_flags & RATE_MCS_LDPC_MSK)
+ rx_status->enc_flags |= RX_ENC_FLAG_LDPC;
+
+ switch (format) {
+ case RATE_MCS_MOD_TYPE_HT:
+ rx_status->encoding = RX_ENC_HT;
+ rx_status->rate_idx = RATE_HT_MCS_INDEX(rate_n_flags);
+ rx_status->enc_flags |= stbc << RX_ENC_FLAG_STBC_SHIFT;
+ break;
+ case RATE_MCS_MOD_TYPE_VHT:
+ case RATE_MCS_MOD_TYPE_HE:
+ case RATE_MCS_MOD_TYPE_EHT:
+ if (format == RATE_MCS_MOD_TYPE_VHT) {
+ rx_status->encoding = RX_ENC_VHT;
+ } else if (format == RATE_MCS_MOD_TYPE_HE) {
+ rx_status->encoding = RX_ENC_HE;
+ rx_status->he_dcm =
+ !!(rate_n_flags & RATE_HE_DUAL_CARRIER_MODE_MSK);
+ } else if (format == RATE_MCS_MOD_TYPE_EHT) {
+ rx_status->encoding = RX_ENC_EHT;
+ }
+
+ rx_status->nss = u32_get_bits(rate_n_flags,
+ RATE_MCS_NSS_MSK) + 1;
+ rx_status->rate_idx = rate_n_flags & RATE_MCS_CODE_MSK;
+ rx_status->enc_flags |= stbc << RX_ENC_FLAG_STBC_SHIFT;
+ break;
+ default: {
+ int rate =
+ iwl_mld_legacy_hw_idx_to_mac80211_idx(rate_n_flags,
+ rx_status->band);
+
+ /* valid rate */
+ if (rate >= 0 && rate <= 0xFF) {
+ rx_status->rate_idx = rate;
+ break;
+ }
+
+ /* invalid rate */
+ rx_status->rate_idx = 0;
+
+ if (net_ratelimit())
+ IWL_ERR(mld, "invalid rate_n_flags=0x%x, band=%d\n",
+ rate_n_flags, rx_status->band);
+ break;
+ }
+ }
+}
+
+/* iwl_mld_create_skb adds the rxb to a new skb */
+static int iwl_mld_build_rx_skb(struct iwl_mld *mld, struct sk_buff *skb,
+ struct ieee80211_hdr *hdr, u16 len,
+ u8 crypt_len, struct iwl_rx_cmd_buffer *rxb)
+{
+ struct iwl_rx_packet *pkt = rxb_addr(rxb);
+ struct iwl_rx_mpdu_desc *desc = (void *)pkt->data;
+ unsigned int headlen, fraglen, pad_len = 0;
+ unsigned int hdrlen = ieee80211_hdrlen(hdr->frame_control);
+ u8 mic_crc_len = u8_get_bits(desc->mac_flags1,
+ IWL_RX_MPDU_MFLG1_MIC_CRC_LEN_MASK) << 1;
+
+ if (desc->mac_flags2 & IWL_RX_MPDU_MFLG2_PAD) {
+ len -= 2;
+ pad_len = 2;
+ }
+
+ /* For non monitor interface strip the bytes the RADA might not have
+ * removed (it might be disabled, e.g. for mgmt frames). As a monitor
+ * interface cannot exist with other interfaces, this removal is safe
+ * and sufficient, in monitor mode there's no decryption being done.
+ */
+ if (len > mic_crc_len && !ieee80211_hw_check(mld->hw, RX_INCLUDES_FCS))
+ len -= mic_crc_len;
+
+ /* If frame is small enough to fit in skb->head, pull it completely.
+ * If not, only pull ieee80211_hdr (including crypto if present, and
+ * an additional 8 bytes for SNAP/ethertype, see below) so that
+ * splice() or TCP coalesce are more efficient.
+ *
+ * Since, in addition, ieee80211_data_to_8023() always pull in at
+ * least 8 bytes (possibly more for mesh) we can do the same here
+ * to save the cost of doing it later. That still doesn't pull in
+ * the actual IP header since the typical case has a SNAP header.
+ * If the latter changes (there are efforts in the standards group
+ * to do so) we should revisit this and ieee80211_data_to_8023().
+ */
+ headlen = (len <= skb_tailroom(skb)) ? len : hdrlen + crypt_len + 8;
+
+ /* The firmware may align the packet to DWORD.
+ * The padding is inserted after the IV.
+ * After copying the header + IV skip the padding if
+ * present before copying packet data.
+ */
+ hdrlen += crypt_len;
+
+ if (unlikely(headlen < hdrlen))
+ return -EINVAL;
+
+ /* Since data doesn't move data while putting data on skb and that is
+ * the only way we use, data + len is the next place that hdr would
+ * be put
+ */
+ skb_set_mac_header(skb, skb->len);
+ skb_put_data(skb, hdr, hdrlen);
+ skb_put_data(skb, (u8 *)hdr + hdrlen + pad_len, headlen - hdrlen);
+
+ if (skb->ip_summed == CHECKSUM_COMPLETE) {
+ struct {
+ u8 hdr[6];
+ __be16 type;
+ } __packed *shdr = (void *)((u8 *)hdr + hdrlen + pad_len);
+
+ if (unlikely(headlen - hdrlen < sizeof(*shdr) ||
+ !ether_addr_equal(shdr->hdr, rfc1042_header) ||
+ (shdr->type != htons(ETH_P_IP) &&
+ shdr->type != htons(ETH_P_ARP) &&
+ shdr->type != htons(ETH_P_IPV6) &&
+ shdr->type != htons(ETH_P_8021Q) &&
+ shdr->type != htons(ETH_P_PAE) &&
+ shdr->type != htons(ETH_P_TDLS))))
+ skb->ip_summed = CHECKSUM_NONE;
+ }
+
+ fraglen = len - headlen;
+
+ if (fraglen) {
+ int offset = (u8 *)hdr + headlen + pad_len -
+ (u8 *)rxb_addr(rxb) + rxb_offset(rxb);
+
+ skb_add_rx_frag(skb, 0, rxb_steal_page(rxb), offset,
+ fraglen, rxb->truesize);
+ }
+
+ return 0;
+}
+
+/* returns true if a packet is a duplicate or invalid tid and
+ * should be dropped. Updates AMSDU PN tracking info
+ */
+VISIBLE_IF_IWLWIFI_KUNIT
+bool
+iwl_mld_is_dup(struct iwl_mld *mld, struct ieee80211_sta *sta,
+ struct ieee80211_hdr *hdr,
+ const struct iwl_rx_mpdu_desc *mpdu_desc,
+ struct ieee80211_rx_status *rx_status, int queue)
+{
+ struct iwl_mld_sta *mld_sta;
+ struct iwl_mld_rxq_dup_data *dup_data;
+ u8 tid, sub_frame_idx;
+
+ if (WARN_ON(!sta))
+ return false;
+
+ mld_sta = iwl_mld_sta_from_mac80211(sta);
+
+ if (WARN_ON_ONCE(!mld_sta->dup_data))
+ return false;
+
+ dup_data = &mld_sta->dup_data[queue];
+
+ /* Drop duplicate 802.11 retransmissions
+ * (IEEE 802.11-2020: 10.3.2.14 "Duplicate detection and recovery")
+ */
+ if (ieee80211_is_ctl(hdr->frame_control) ||
+ ieee80211_is_any_nullfunc(hdr->frame_control) ||
+ is_multicast_ether_addr(hdr->addr1))
+ return false;
+
+ if (ieee80211_is_data_qos(hdr->frame_control)) {
+ /* frame has qos control */
+ tid = ieee80211_get_tid(hdr);
+ if (tid >= IWL_MAX_TID_COUNT)
+ return true;
+ } else {
+ tid = IWL_MAX_TID_COUNT;
+ }
+
+ /* If this wasn't a part of an A-MSDU the sub-frame index will be 0 */
+ sub_frame_idx = mpdu_desc->amsdu_info &
+ IWL_RX_MPDU_AMSDU_SUBFRAME_IDX_MASK;
+
+ if (IWL_FW_CHECK(mld,
+ sub_frame_idx > 0 &&
+ !(mpdu_desc->mac_flags2 & IWL_RX_MPDU_MFLG2_AMSDU),
+ "got sub_frame_idx=%d but A-MSDU flag is not set\n",
+ sub_frame_idx))
+ return true;
+
+ if (unlikely(ieee80211_has_retry(hdr->frame_control) &&
+ dup_data->last_seq[tid] == hdr->seq_ctrl &&
+ dup_data->last_sub_frame_idx[tid] >= sub_frame_idx))
+ return true;
+
+ /* Allow same PN as the first subframe for following sub frames */
+ if (dup_data->last_seq[tid] == hdr->seq_ctrl &&
+ sub_frame_idx > dup_data->last_sub_frame_idx[tid])
+ rx_status->flag |= RX_FLAG_ALLOW_SAME_PN;
+
+ dup_data->last_seq[tid] = hdr->seq_ctrl;
+ dup_data->last_sub_frame_idx[tid] = sub_frame_idx;
+
+ rx_status->flag |= RX_FLAG_DUP_VALIDATED;
+
+ return false;
+}
+EXPORT_SYMBOL_IF_IWLWIFI_KUNIT(iwl_mld_is_dup);
+
+static void iwl_mld_update_last_rx_timestamp(struct iwl_mld *mld, u8 baid)
+{
+ unsigned long now = jiffies;
+ unsigned long timeout;
+ struct iwl_mld_baid_data *ba_data;
+
+ ba_data = rcu_dereference(mld->fw_id_to_ba[baid]);
+ if (!ba_data) {
+ IWL_DEBUG_HT(mld, "BAID %d not found in map\n", baid);
+ return;
+ }
+
+ if (!ba_data->timeout)
+ return;
+
+ /* To minimize cache bouncing between RX queues, avoid frequent updates
+ * to last_rx_timestamp. update it only when the timeout period has
+ * passed. The worst-case scenario is the session expiring after
+ * approximately 2 * timeout, which is negligible (the update is
+ * atomic).
+ */
+ timeout = TU_TO_JIFFIES(ba_data->timeout);
+ if (time_is_before_jiffies(ba_data->last_rx_timestamp + timeout))
+ ba_data->last_rx_timestamp = now;
+}
+
+/* Processes received packets for a station.
+ * Sets *drop to true if the packet should be dropped.
+ * Returns the station if found, or NULL otherwise.
+ */
+static struct ieee80211_sta *
+iwl_mld_rx_with_sta(struct iwl_mld *mld, struct ieee80211_hdr *hdr,
+ struct sk_buff *skb,
+ const struct iwl_rx_mpdu_desc *mpdu_desc,
+ const struct iwl_rx_packet *pkt, int queue, bool *drop)
+{
+ struct ieee80211_sta *sta = NULL;
+ struct ieee80211_link_sta *link_sta = NULL;
+ struct ieee80211_rx_status *rx_status;
+ u8 baid;
+
+ if (mpdu_desc->status & cpu_to_le32(IWL_RX_MPDU_STATUS_SRC_STA_FOUND)) {
+ u8 sta_id = le32_get_bits(mpdu_desc->status,
+ IWL_RX_MPDU_STATUS_STA_ID);
+
+ if (IWL_FW_CHECK(mld,
+ sta_id >= mld->fw->ucode_capa.num_stations,
+ "rx_mpdu: invalid sta_id %d\n", sta_id))
+ return NULL;
+
+ link_sta = rcu_dereference(mld->fw_id_to_link_sta[sta_id]);
+ if (!IS_ERR_OR_NULL(link_sta))
+ sta = link_sta->sta;
+ } else if (!is_multicast_ether_addr(hdr->addr2)) {
+ /* Passing NULL is fine since we prevent two stations with the
+ * same address from being added.
+ */
+ sta = ieee80211_find_sta_by_ifaddr(mld->hw, hdr->addr2, NULL);
+ }
+
+ /* we may not have any station yet */
+ if (!sta)
+ return NULL;
+
+ rx_status = IEEE80211_SKB_RXCB(skb);
+
+ if (link_sta && sta->valid_links) {
+ rx_status->link_valid = true;
+ rx_status->link_id = link_sta->link_id;
+ }
+
+ /* fill checksum */
+ if (ieee80211_is_data(hdr->frame_control) &&
+ pkt->len_n_flags & cpu_to_le32(FH_RSCSR_RPA_EN)) {
+ u16 hwsum = be16_to_cpu(mpdu_desc->v3.raw_xsum);
+
+ skb->ip_summed = CHECKSUM_COMPLETE;
+ skb->csum = csum_unfold(~(__force __sum16)hwsum);
+ }
+
+ if (iwl_mld_is_dup(mld, sta, hdr, mpdu_desc, rx_status, queue)) {
+ IWL_DEBUG_DROP(mld, "Dropping duplicate packet 0x%x\n",
+ le16_to_cpu(hdr->seq_ctrl));
+ *drop = true;
+ return NULL;
+ }
+
+ baid = le32_get_bits(mpdu_desc->reorder_data,
+ IWL_RX_MPDU_REORDER_BAID_MASK);
+ if (baid != IWL_RX_REORDER_DATA_INVALID_BAID)
+ iwl_mld_update_last_rx_timestamp(mld, baid);
+
+ if (link_sta && ieee80211_is_data(hdr->frame_control)) {
+ u8 sub_frame_idx = mpdu_desc->amsdu_info &
+ IWL_RX_MPDU_AMSDU_SUBFRAME_IDX_MASK;
+
+ /* 0 means not an A-MSDU, and 1 means a new A-MSDU */
+ if (!sub_frame_idx || sub_frame_idx == 1)
+ iwl_mld_count_mpdu_rx(link_sta, queue, 1);
+
+ if (!is_multicast_ether_addr(hdr->addr1))
+ iwl_mld_low_latency_update_counters(mld, hdr, sta,
+ queue);
+ }
+
+ return sta;
+}
+
+#define KEY_IDX_LEN 2
+
+static int iwl_mld_rx_mgmt_prot(struct ieee80211_sta *sta,
+ struct ieee80211_hdr *hdr,
+ struct ieee80211_rx_status *rx_status,
+ u32 mpdu_status,
+ u32 mpdu_len)
+{
+ struct wireless_dev *wdev;
+ struct iwl_mld_sta *mld_sta;
+ struct iwl_mld_vif *mld_vif;
+ u8 keyidx;
+ struct ieee80211_key_conf *key;
+ const u8 *frame = (void *)hdr;
+
+ if ((mpdu_status & IWL_RX_MPDU_STATUS_SEC_MASK) ==
+ IWL_RX_MPDU_STATUS_SEC_NONE)
+ return 0;
+
+ /* For non-beacon, we don't really care. But beacons may
+ * be filtered out, and we thus need the firmware's replay
+ * detection, otherwise beacons the firmware previously
+ * filtered could be replayed, or something like that, and
+ * it can filter a lot - though usually only if nothing has
+ * changed.
+ */
+ if (!ieee80211_is_beacon(hdr->frame_control))
+ return 0;
+
+ if (!sta)
+ return -1;
+
+ mld_sta = iwl_mld_sta_from_mac80211(sta);
+ mld_vif = iwl_mld_vif_from_mac80211(mld_sta->vif);
+
+ /* key mismatch - will also report !MIC_OK but we shouldn't count it */
+ if (!(mpdu_status & IWL_RX_MPDU_STATUS_KEY_VALID))
+ goto report;
+
+ /* good cases */
+ if (likely(mpdu_status & IWL_RX_MPDU_STATUS_MIC_OK &&
+ !(mpdu_status & IWL_RX_MPDU_STATUS_REPLAY_ERROR))) {
+ rx_status->flag |= RX_FLAG_DECRYPTED;
+ return 0;
+ }
+
+ /* both keys will have the same cipher and MIC length, use
+ * whichever one is available
+ */
+ key = rcu_dereference(mld_vif->bigtks[0]);
+ if (!key) {
+ key = rcu_dereference(mld_vif->bigtks[1]);
+ if (!key)
+ goto report;
+ }
+
+ if (mpdu_len < key->icv_len + IEEE80211_GMAC_PN_LEN + KEY_IDX_LEN)
+ goto report;
+
+ /* get the real key ID */
+ keyidx = frame[mpdu_len - key->icv_len - IEEE80211_GMAC_PN_LEN - KEY_IDX_LEN];
+ /* and if that's the other key, look it up */
+ if (keyidx != key->keyidx) {
+ /* shouldn't happen since firmware checked, but be safe
+ * in case the MIC length is wrong too, for example
+ */
+ if (keyidx != 6 && keyidx != 7)
+ return -1;
+
+ key = rcu_dereference(mld_vif->bigtks[keyidx - 6]);
+ if (!key)
+ goto report;
+ }
+
+ /* Report status to mac80211 */
+ if (!(mpdu_status & IWL_RX_MPDU_STATUS_MIC_OK))
+ ieee80211_key_mic_failure(key);
+ else if (mpdu_status & IWL_RX_MPDU_STATUS_REPLAY_ERROR)
+ ieee80211_key_replay(key);
+report:
+ wdev = ieee80211_vif_to_wdev(mld_sta->vif);
+ if (wdev->netdev)
+ cfg80211_rx_unprot_mlme_mgmt(wdev->netdev, (void *)hdr,
+ mpdu_len);
+
+ return -1;
+}
+
+static int iwl_mld_rx_crypto(struct iwl_mld *mld,
+ struct ieee80211_sta *sta,
+ struct ieee80211_hdr *hdr,
+ struct ieee80211_rx_status *rx_status,
+ struct iwl_rx_mpdu_desc *desc, int queue,
+ u32 pkt_flags, u8 *crypto_len)
+{
+ u32 status = le32_to_cpu(desc->status);
+
+ if (unlikely(ieee80211_is_mgmt(hdr->frame_control) &&
+ !ieee80211_has_protected(hdr->frame_control)))
+ return iwl_mld_rx_mgmt_prot(sta, hdr, rx_status, status,
+ le16_to_cpu(desc->mpdu_len));
+
+ if (!ieee80211_has_protected(hdr->frame_control) ||
+ (status & IWL_RX_MPDU_STATUS_SEC_MASK) ==
+ IWL_RX_MPDU_STATUS_SEC_NONE)
+ return 0;
+
+ switch (status & IWL_RX_MPDU_STATUS_SEC_MASK) {
+ case IWL_RX_MPDU_STATUS_SEC_CCM:
+ case IWL_RX_MPDU_STATUS_SEC_GCM:
+ BUILD_BUG_ON(IEEE80211_CCMP_PN_LEN != IEEE80211_GCMP_PN_LEN);
+ if (!(status & IWL_RX_MPDU_STATUS_MIC_OK)) {
+ IWL_DEBUG_DROP(mld,
+ "Dropping packet, bad MIC (CCM/GCM)\n");
+ return -1;
+ }
+
+ rx_status->flag |= RX_FLAG_DECRYPTED | RX_FLAG_MIC_STRIPPED;
+ *crypto_len = IEEE80211_CCMP_HDR_LEN;
+ return 0;
+ case IWL_RX_MPDU_STATUS_SEC_TKIP:
+ if (!(status & IWL_RX_MPDU_STATUS_ICV_OK))
+ return -1;
+
+ if (!(status & RX_MPDU_RES_STATUS_MIC_OK))
+ rx_status->flag |= RX_FLAG_MMIC_ERROR;
+
+ if (pkt_flags & FH_RSCSR_RADA_EN) {
+ rx_status->flag |= RX_FLAG_ICV_STRIPPED;
+ rx_status->flag |= RX_FLAG_MMIC_STRIPPED;
+ }
+
+ *crypto_len = IEEE80211_TKIP_IV_LEN;
+ rx_status->flag |= RX_FLAG_DECRYPTED;
+ return 0;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static void iwl_mld_rx_update_ampdu_ref(struct iwl_mld *mld,
+ struct iwl_mld_rx_phy_data *phy_data,
+ struct ieee80211_rx_status *rx_status)
+{
+ bool toggle_bit =
+ phy_data->phy_info & IWL_RX_MPDU_PHY_AMPDU_TOGGLE;
+
+ rx_status->flag |= RX_FLAG_AMPDU_DETAILS;
+ /* Toggle is switched whenever new aggregation starts. Make
+ * sure ampdu_reference is never 0 so we can later use it to
+ * see if the frame was really part of an A-MPDU or not.
+ */
+ if (toggle_bit != mld->monitor.ampdu_toggle) {
+ mld->monitor.ampdu_ref++;
+ if (mld->monitor.ampdu_ref == 0)
+ mld->monitor.ampdu_ref++;
+ mld->monitor.ampdu_toggle = toggle_bit;
+ phy_data->first_subframe = true;
+ }
+ rx_status->ampdu_reference = mld->monitor.ampdu_ref;
+}
+
+static void
+iwl_mld_fill_rx_status_band_freq(struct ieee80211_rx_status *rx_status,
+ u8 band, u8 channel)
+{
+ rx_status->band = iwl_mld_phy_band_to_nl80211(band);
+ rx_status->freq = ieee80211_channel_to_frequency(channel,
+ rx_status->band);
+}
+
+void iwl_mld_rx_mpdu(struct iwl_mld *mld, struct napi_struct *napi,
+ struct iwl_rx_cmd_buffer *rxb, int queue)
+{
+ struct iwl_rx_packet *pkt = rxb_addr(rxb);
+ struct iwl_mld_rx_phy_data phy_data = {};
+ struct iwl_rx_mpdu_desc *mpdu_desc = (void *)pkt->data;
+ struct ieee80211_sta *sta;
+ struct ieee80211_hdr *hdr;
+ struct sk_buff *skb;
+ size_t mpdu_desc_size = sizeof(*mpdu_desc);
+ bool drop = false;
+ u8 crypto_len = 0, band;
+ u32 pkt_len = iwl_rx_packet_payload_len(pkt);
+ u32 mpdu_len;
+ enum iwl_mld_reorder_result reorder_res;
+ struct ieee80211_rx_status *rx_status;
+
+ if (unlikely(mld->fw_status.in_hw_restart))
+ return;
+
+ if (IWL_FW_CHECK(mld, pkt_len < mpdu_desc_size,
+ "Bad REPLY_RX_MPDU_CMD size (%d)\n", pkt_len))
+ return;
+
+ mpdu_len = le16_to_cpu(mpdu_desc->mpdu_len);
+
+ if (IWL_FW_CHECK(mld, mpdu_len + mpdu_desc_size > pkt_len,
+ "FW lied about packet len (%d)\n", pkt_len))
+ return;
+
+ /* Don't use dev_alloc_skb(), we'll have enough headroom once
+ * ieee80211_hdr pulled.
+ */
+ skb = alloc_skb(128, GFP_ATOMIC);
+ if (!skb) {
+ IWL_ERR(mld, "alloc_skb failed\n");
+ return;
+ }
+
+ hdr = (void *)(pkt->data + mpdu_desc_size);
+
+ iwl_mld_fill_phy_data(mld, mpdu_desc, &phy_data);
+
+ if (mpdu_desc->mac_flags2 & IWL_RX_MPDU_MFLG2_PAD) {
+ /* If the device inserted padding it means that (it thought)
+ * the 802.11 header wasn't a multiple of 4 bytes long. In
+ * this case, reserve two bytes at the start of the SKB to
+ * align the payload properly in case we end up copying it.
+ */
+ skb_reserve(skb, 2);
+ }
+
+ rx_status = IEEE80211_SKB_RXCB(skb);
+
+ /* this is needed early */
+ band = u8_get_bits(mpdu_desc->mac_phy_band,
+ IWL_RX_MPDU_MAC_PHY_BAND_BAND_MASK);
+ iwl_mld_fill_rx_status_band_freq(rx_status, band,
+ mpdu_desc->v3.channel);
+
+
+ rcu_read_lock();
+
+ sta = iwl_mld_rx_with_sta(mld, hdr, skb, mpdu_desc, pkt, queue, &drop);
+ if (drop)
+ goto drop;
+
+ /* update aggregation data for monitor sake on default queue */
+ if (!queue && (phy_data.phy_info & IWL_RX_MPDU_PHY_AMPDU))
+ iwl_mld_rx_update_ampdu_ref(mld, &phy_data, rx_status);
+
+ /* Keep packets with CRC errors (and with overrun) for monitor mode
+ * (otherwise the firmware discards them) but mark them as bad.
+ */
+ if (!(mpdu_desc->status & cpu_to_le32(IWL_RX_MPDU_STATUS_CRC_OK)) ||
+ !(mpdu_desc->status & cpu_to_le32(IWL_RX_MPDU_STATUS_OVERRUN_OK))) {
+ IWL_DEBUG_RX(mld, "Bad CRC or FIFO: 0x%08X.\n",
+ le32_to_cpu(mpdu_desc->status));
+ rx_status->flag |= RX_FLAG_FAILED_FCS_CRC;
+ }
+
+ if (likely(!(phy_data.phy_info & IWL_RX_MPDU_PHY_TSF_OVERLOAD))) {
+ rx_status->mactime =
+ le64_to_cpu(mpdu_desc->v3.tsf_on_air_rise);
+
+ /* TSF as indicated by the firmware is at INA time */
+ rx_status->flag |= RX_FLAG_MACTIME_PLCP_START;
+ }
+
+ /* management stuff on default queue */
+ if (!queue && unlikely(ieee80211_is_beacon(hdr->frame_control) ||
+ ieee80211_is_probe_resp(hdr->frame_control))) {
+ rx_status->boottime_ns = ktime_get_boottime_ns();
+
+ if (mld->scan.pass_all_sched_res ==
+ SCHED_SCAN_PASS_ALL_STATE_ENABLED)
+ mld->scan.pass_all_sched_res =
+ SCHED_SCAN_PASS_ALL_STATE_FOUND;
+ }
+
+ iwl_mld_rx_fill_status(mld, skb, &phy_data, queue);
+
+ if (iwl_mld_rx_crypto(mld, sta, hdr, rx_status, mpdu_desc, queue,
+ le32_to_cpu(pkt->len_n_flags), &crypto_len))
+ goto drop;
+
+ if (iwl_mld_build_rx_skb(mld, skb, hdr, mpdu_len, crypto_len, rxb))
+ goto drop;
+
+ /* time sync frame is saved and will be released later when the
+ * notification with the timestamps arrives.
+ */
+ if (iwl_mld_time_sync_frame(mld, skb, hdr->addr2))
+ goto out;
+
+ reorder_res = iwl_mld_reorder(mld, napi, queue, sta, skb, mpdu_desc);
+ switch (reorder_res) {
+ case IWL_MLD_PASS_SKB:
+ break;
+ case IWL_MLD_DROP_SKB:
+ goto drop;
+ case IWL_MLD_BUFFERED_SKB:
+ goto out;
+ default:
+ WARN_ON(1);
+ goto drop;
+ }
+
+ iwl_mld_pass_packet_to_mac80211(mld, napi, skb, queue, sta);
+
+ goto out;
+
+drop:
+ kfree_skb(skb);
+out:
+ rcu_read_unlock();
+}
+
+#define SYNC_RX_QUEUE_TIMEOUT (HZ)
+void iwl_mld_sync_rx_queues(struct iwl_mld *mld,
+ enum iwl_mld_internal_rxq_notif_type type,
+ const void *notif_payload, u32 notif_payload_size)
+{
+ u8 num_rx_queues = mld->trans->info.num_rxqs;
+ struct {
+ struct iwl_rxq_sync_cmd sync_cmd;
+ struct iwl_mld_internal_rxq_notif notif;
+ } __packed cmd = {
+ .sync_cmd.rxq_mask = cpu_to_le32(BIT(num_rx_queues) - 1),
+ .sync_cmd.count =
+ cpu_to_le32(sizeof(struct iwl_mld_internal_rxq_notif) +
+ notif_payload_size),
+ .notif.type = type,
+ .notif.cookie = mld->rxq_sync.cookie,
+ };
+ struct iwl_host_cmd hcmd = {
+ .id = WIDE_ID(DATA_PATH_GROUP, TRIGGER_RX_QUEUES_NOTIF_CMD),
+ .data[0] = &cmd,
+ .len[0] = sizeof(cmd),
+ .data[1] = notif_payload,
+ .len[1] = notif_payload_size,
+ };
+ int ret;
+
+ /* size must be a multiple of DWORD */
+ if (WARN_ON(cmd.sync_cmd.count & cpu_to_le32(3)))
+ return;
+
+ mld->rxq_sync.state = (1 << num_rx_queues) - 1;
+
+ ret = iwl_mld_send_cmd(mld, &hcmd);
+ if (ret) {
+ IWL_ERR(mld, "Failed to trigger RX queues sync (%d)\n", ret);
+ goto out;
+ }
+
+ ret = wait_event_timeout(mld->rxq_sync.waitq,
+ READ_ONCE(mld->rxq_sync.state) == 0,
+ SYNC_RX_QUEUE_TIMEOUT);
+ WARN_ONCE(!ret, "RXQ sync failed: state=0x%lx, cookie=%d\n",
+ mld->rxq_sync.state, mld->rxq_sync.cookie);
+
+out:
+ mld->rxq_sync.state = 0;
+ mld->rxq_sync.cookie++;
+}
+
+void iwl_mld_handle_rx_queues_sync_notif(struct iwl_mld *mld,
+ struct napi_struct *napi,
+ struct iwl_rx_packet *pkt, int queue)
+{
+ struct iwl_rxq_sync_notification *notif;
+ struct iwl_mld_internal_rxq_notif *internal_notif;
+ u32 len = iwl_rx_packet_payload_len(pkt);
+ size_t combined_notif_len = sizeof(*notif) + sizeof(*internal_notif);
+
+ notif = (void *)pkt->data;
+ internal_notif = (void *)notif->payload;
+
+ if (IWL_FW_CHECK(mld, len < combined_notif_len,
+ "invalid notification size %u (%zu)\n",
+ len, combined_notif_len))
+ return;
+
+ len -= combined_notif_len;
+
+ if (IWL_FW_CHECK(mld, mld->rxq_sync.cookie != internal_notif->cookie,
+ "received expired RX queue sync message (cookie=%d expected=%d q[%d])\n",
+ internal_notif->cookie, mld->rxq_sync.cookie, queue))
+ return;
+
+ switch (internal_notif->type) {
+ case IWL_MLD_RXQ_EMPTY:
+ IWL_FW_CHECK(mld, len,
+ "invalid empty notification size %d\n", len);
+ break;
+ case IWL_MLD_RXQ_NOTIF_DEL_BA:
+ if (IWL_FW_CHECK(mld, len != sizeof(struct iwl_mld_delba_data),
+ "invalid delba notification size %u (%zu)\n",
+ len, sizeof(struct iwl_mld_delba_data)))
+ break;
+ iwl_mld_del_ba(mld, queue, (void *)internal_notif->payload);
+ break;
+ default:
+ WARN_ON_ONCE(1);
+ }
+
+ IWL_FW_CHECK(mld, !test_and_clear_bit(queue, &mld->rxq_sync.state),
+ "RXQ sync: queue %d responded a second time!\n", queue);
+
+ if (READ_ONCE(mld->rxq_sync.state) == 0)
+ wake_up(&mld->rxq_sync.waitq);
+}
+
+void iwl_mld_rx_monitor_no_data(struct iwl_mld *mld, struct napi_struct *napi,
+ struct iwl_rx_packet *pkt, int queue)
+{
+ struct iwl_rx_no_data_ver_3 *desc;
+ struct iwl_mld_rx_phy_data phy_data;
+ struct ieee80211_rx_status *rx_status;
+ struct sk_buff *skb;
+ u32 format, rssi;
+ u8 channel;
+
+ if (unlikely(mld->fw_status.in_hw_restart))
+ return;
+
+ if (IWL_FW_CHECK(mld, iwl_rx_packet_payload_len(pkt) < sizeof(*desc),
+ "Bad RX_NO_DATA_NOTIF size (%d)\n",
+ iwl_rx_packet_payload_len(pkt)))
+ return;
+
+ desc = (void *)pkt->data;
+
+ rssi = le32_to_cpu(desc->rssi);
+ channel = u32_get_bits(rssi, RX_NO_DATA_CHANNEL_MSK);
+
+ phy_data.energy_a = u32_get_bits(rssi, RX_NO_DATA_CHAIN_A_MSK);
+ phy_data.energy_b = u32_get_bits(rssi, RX_NO_DATA_CHAIN_B_MSK);
+ phy_data.data0 = desc->phy_info[0];
+ phy_data.data1 = desc->phy_info[1];
+ phy_data.phy_info = IWL_RX_MPDU_PHY_TSF_OVERLOAD;
+ phy_data.gp2_on_air_rise = le32_to_cpu(desc->on_air_rise_time);
+ phy_data.rate_n_flags = iwl_v3_rate_from_v2_v3(desc->rate,
+ mld->fw_rates_ver_3);
+ phy_data.with_data = false;
+
+ BUILD_BUG_ON(sizeof(phy_data.rx_vec) != sizeof(desc->rx_vec));
+ memcpy(phy_data.rx_vec, desc->rx_vec, sizeof(phy_data.rx_vec));
+
+ format = phy_data.rate_n_flags & RATE_MCS_MOD_TYPE_MSK;
+
+ /* Don't use dev_alloc_skb(), we'll have enough headroom once
+ * ieee80211_hdr pulled.
+ */
+ skb = alloc_skb(128, GFP_ATOMIC);
+ if (!skb) {
+ IWL_ERR(mld, "alloc_skb failed\n");
+ return;
+ }
+
+ rx_status = IEEE80211_SKB_RXCB(skb);
+
+ /* 0-length PSDU */
+ rx_status->flag |= RX_FLAG_NO_PSDU;
+
+ /* mark as failed PLCP on any errors to skip checks in mac80211 */
+ if (le32_get_bits(desc->info, RX_NO_DATA_INFO_ERR_MSK) !=
+ RX_NO_DATA_INFO_ERR_NONE)
+ rx_status->flag |= RX_FLAG_FAILED_PLCP_CRC;
+
+ switch (le32_get_bits(desc->info, RX_NO_DATA_INFO_TYPE_MSK)) {
+ case RX_NO_DATA_INFO_TYPE_NDP:
+ rx_status->zero_length_psdu_type =
+ IEEE80211_RADIOTAP_ZERO_LEN_PSDU_SOUNDING;
+ break;
+ case RX_NO_DATA_INFO_TYPE_MU_UNMATCHED:
+ case RX_NO_DATA_INFO_TYPE_TB_UNMATCHED:
+ rx_status->zero_length_psdu_type =
+ IEEE80211_RADIOTAP_ZERO_LEN_PSDU_NOT_CAPTURED;
+ break;
+ default:
+ rx_status->zero_length_psdu_type =
+ IEEE80211_RADIOTAP_ZERO_LEN_PSDU_VENDOR;
+ break;
+ }
+
+ rx_status->band = channel > 14 ? NL80211_BAND_5GHZ :
+ NL80211_BAND_2GHZ;
+
+ rx_status->freq = ieee80211_channel_to_frequency(channel,
+ rx_status->band);
+
+ iwl_mld_rx_fill_status(mld, skb, &phy_data, queue);
+
+ /* No more radiotap info should be added after this point.
+ * Mark it as mac header for upper layers to know where
+ * the radiotap header ends.
+ */
+ skb_set_mac_header(skb, skb->len);
+
+ /* Override the nss from the rx_vec since the rate_n_flags has
+ * only 1 bit for the nss which gives a max of 2 ss but there
+ * may be up to 8 spatial streams.
+ */
+ switch (format) {
+ case RATE_MCS_MOD_TYPE_VHT:
+ rx_status->nss =
+ le32_get_bits(desc->rx_vec[0],
+ RX_NO_DATA_RX_VEC0_VHT_NSTS_MSK) + 1;
+ break;
+ case RATE_MCS_MOD_TYPE_HE:
+ rx_status->nss =
+ le32_get_bits(desc->rx_vec[0],
+ RX_NO_DATA_RX_VEC0_HE_NSTS_MSK) + 1;
+ break;
+ case RATE_MCS_MOD_TYPE_EHT:
+ rx_status->nss =
+ le32_get_bits(desc->rx_vec[2],
+ RX_NO_DATA_RX_VEC2_EHT_NSTS_MSK) + 1;
+ }
+
+ /* pass the packet to mac80211 */
+ rcu_read_lock();
+ ieee80211_rx_napi(mld->hw, NULL, skb, napi);
+ rcu_read_unlock();
+}
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/rx.h b/drivers/net/wireless/intel/iwlwifi/mld/rx.h
new file mode 100644
index 000000000000..2beabd7e70b1
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/mld/rx.h
@@ -0,0 +1,72 @@
+/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
+/*
+ * Copyright (C) 2024-2025 Intel Corporation
+ */
+#ifndef __iwl_mld_rx_h__
+#define __iwl_mld_rx_h__
+
+#include "mld.h"
+
+/**
+ * enum iwl_mld_internal_rxq_notif_type - RX queue sync notif types
+ *
+ * @IWL_MLD_RXQ_EMPTY: empty sync notification
+ * @IWL_MLD_RXQ_NOTIF_DEL_BA: notify RSS queues of delBA
+ */
+enum iwl_mld_internal_rxq_notif_type {
+ IWL_MLD_RXQ_EMPTY,
+ IWL_MLD_RXQ_NOTIF_DEL_BA,
+};
+
+/**
+ * struct iwl_mld_internal_rxq_notif - @iwl_rxq_sync_cmd internal data.
+ * This data is echoed by the firmware to all RSS queues and should be DWORD
+ * aligned. FW is agnostic to the data, so there are no endianness requirements
+ *
+ * @type: one of &iwl_mld_internal_rxq_notif_type
+ * @cookie: unique internal cookie to identify old notifications
+ * @reserved: reserved for alignment
+ * @payload: data to send to RX queues based on the type (may be empty)
+ */
+struct iwl_mld_internal_rxq_notif {
+ u8 type;
+ u8 reserved[3];
+ u32 cookie;
+ u8 payload[];
+} __packed;
+
+/**
+ * struct iwl_mld_rx_queues_sync - RX queues sync data
+ *
+ * @waitq: wait queue for RX queues sync completion
+ * @cookie: unique id to correlate sync requests with responses
+ * @state: bitmask representing the sync state of RX queues
+ * all RX queues bits are set before sending the command, and the
+ * corresponding queue bit cleared upon handling the notification
+ */
+struct iwl_mld_rx_queues_sync {
+ wait_queue_head_t waitq;
+ u32 cookie;
+ unsigned long state;
+};
+
+void iwl_mld_rx_mpdu(struct iwl_mld *mld, struct napi_struct *napi,
+ struct iwl_rx_cmd_buffer *rxb, int queue);
+
+void iwl_mld_sync_rx_queues(struct iwl_mld *mld,
+ enum iwl_mld_internal_rxq_notif_type type,
+ const void *notif_payload, u32 notif_payload_size);
+
+void iwl_mld_handle_rx_queues_sync_notif(struct iwl_mld *mld,
+ struct napi_struct *napi,
+ struct iwl_rx_packet *pkt, int queue);
+
+void iwl_mld_pass_packet_to_mac80211(struct iwl_mld *mld,
+ struct napi_struct *napi,
+ struct sk_buff *skb, int queue,
+ struct ieee80211_sta *sta);
+
+void iwl_mld_rx_monitor_no_data(struct iwl_mld *mld, struct napi_struct *napi,
+ struct iwl_rx_packet *pkt, int queue);
+
+#endif /* __iwl_mld_agg_h__ */
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/scan.c b/drivers/net/wireless/intel/iwlwifi/mld/scan.c
new file mode 100644
index 000000000000..3fce7cd2d512
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/mld/scan.c
@@ -0,0 +1,2011 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
+/*
+ * Copyright (C) 2024-2025 Intel Corporation
+ */
+#include <linux/crc32.h>
+
+#include "mld.h"
+#include "scan.h"
+#include "hcmd.h"
+#include "iface.h"
+#include "phy.h"
+#include "mlo.h"
+
+#include "fw/api/scan.h"
+#include "fw/dbg.h"
+
+#define IWL_SCAN_DWELL_ACTIVE 10
+#define IWL_SCAN_DWELL_PASSIVE 110
+#define IWL_SCAN_NUM_OF_FRAGS 3
+
+/* adaptive dwell max budget time [TU] for full scan */
+#define IWL_SCAN_ADWELL_MAX_BUDGET_FULL_SCAN 300
+
+/* adaptive dwell max budget time [TU] for directed scan */
+#define IWL_SCAN_ADWELL_MAX_BUDGET_DIRECTED_SCAN 100
+
+/* adaptive dwell default high band APs number */
+#define IWL_SCAN_ADWELL_DEFAULT_HB_N_APS 8
+
+/* adaptive dwell default low band APs number */
+#define IWL_SCAN_ADWELL_DEFAULT_LB_N_APS 2
+
+/* adaptive dwell default APs number for P2P social channels (1, 6, 11) */
+#define IWL_SCAN_ADWELL_DEFAULT_N_APS_SOCIAL 10
+
+/* adaptive dwell number of APs override for P2P friendly GO channels */
+#define IWL_SCAN_ADWELL_N_APS_GO_FRIENDLY 10
+
+/* adaptive dwell number of APs override for P2P social channels */
+#define IWL_SCAN_ADWELL_N_APS_SOCIAL_CHS 2
+
+/* adaptive dwell number of APs override mask for p2p friendly GO */
+#define IWL_SCAN_ADWELL_N_APS_GO_FRIENDLY_BIT BIT(20)
+
+/* adaptive dwell number of APs override mask for social channels */
+#define IWL_SCAN_ADWELL_N_APS_SOCIAL_CHS_BIT BIT(21)
+
+#define SCAN_TIMEOUT_MSEC (30000 * HZ)
+
+/* minimal number of 2GHz and 5GHz channels in the regular scan request */
+#define IWL_MLD_6GHZ_PASSIVE_SCAN_MIN_CHANS 4
+
+enum iwl_mld_scan_type {
+ IWL_SCAN_TYPE_NOT_SET,
+ IWL_SCAN_TYPE_UNASSOC,
+ IWL_SCAN_TYPE_WILD,
+ IWL_SCAN_TYPE_MILD,
+ IWL_SCAN_TYPE_FRAGMENTED,
+ IWL_SCAN_TYPE_FAST_BALANCE,
+};
+
+struct iwl_mld_scan_timing_params {
+ u32 suspend_time;
+ u32 max_out_time;
+};
+
+static const struct iwl_mld_scan_timing_params scan_timing[] = {
+ [IWL_SCAN_TYPE_UNASSOC] = {
+ .suspend_time = 0,
+ .max_out_time = 0,
+ },
+ [IWL_SCAN_TYPE_WILD] = {
+ .suspend_time = 30,
+ .max_out_time = 120,
+ },
+ [IWL_SCAN_TYPE_MILD] = {
+ .suspend_time = 120,
+ .max_out_time = 120,
+ },
+ [IWL_SCAN_TYPE_FRAGMENTED] = {
+ .suspend_time = 95,
+ .max_out_time = 44,
+ },
+ [IWL_SCAN_TYPE_FAST_BALANCE] = {
+ .suspend_time = 30,
+ .max_out_time = 37,
+ },
+};
+
+struct iwl_mld_scan_params {
+ enum iwl_mld_scan_type type;
+ u32 n_channels;
+ u16 delay;
+ int n_ssids;
+ struct cfg80211_ssid *ssids;
+ struct ieee80211_channel **channels;
+ u32 flags;
+ u8 *mac_addr;
+ u8 *mac_addr_mask;
+ bool no_cck;
+ bool pass_all;
+ int n_match_sets;
+ struct iwl_scan_probe_req preq;
+ struct cfg80211_match_set *match_sets;
+ int n_scan_plans;
+ struct cfg80211_sched_scan_plan *scan_plans;
+ bool iter_notif;
+ bool respect_p2p_go;
+ u8 fw_link_id;
+ struct cfg80211_scan_6ghz_params *scan_6ghz_params;
+ u32 n_6ghz_params;
+ bool scan_6ghz;
+ bool enable_6ghz_passive;
+ u8 bssid[ETH_ALEN] __aligned(2);
+};
+
+struct iwl_mld_scan_respect_p2p_go_iter_data {
+ struct ieee80211_vif *current_vif;
+ bool p2p_go;
+};
+
+static void iwl_mld_scan_respect_p2p_go_iter(void *_data, u8 *mac,
+ struct ieee80211_vif *vif)
+{
+ struct iwl_mld_scan_respect_p2p_go_iter_data *data = _data;
+
+ /* exclude the given vif */
+ if (vif == data->current_vif)
+ return;
+
+ /* TODO: CDB check the band of the GO */
+ if (ieee80211_vif_type_p2p(vif) == NL80211_IFTYPE_P2P_GO &&
+ iwl_mld_vif_from_mac80211(vif)->ap_ibss_active)
+ data->p2p_go = true;
+}
+
+static bool iwl_mld_get_respect_p2p_go(struct iwl_mld *mld,
+ struct ieee80211_vif *vif,
+ bool low_latency)
+{
+ struct iwl_mld_scan_respect_p2p_go_iter_data data = {
+ .current_vif = vif,
+ .p2p_go = false,
+ };
+
+ if (!low_latency)
+ return false;
+
+ ieee80211_iterate_active_interfaces_mtx(mld->hw,
+ IEEE80211_IFACE_ITER_NORMAL,
+ iwl_mld_scan_respect_p2p_go_iter,
+ &data);
+
+ return data.p2p_go;
+}
+
+struct iwl_mld_scan_iter_data {
+ struct ieee80211_vif *current_vif;
+ bool active_vif;
+ bool is_dcm_with_p2p_go;
+ bool global_low_latency;
+};
+
+static void iwl_mld_scan_iterator(void *_data, u8 *mac,
+ struct ieee80211_vif *vif)
+{
+ struct iwl_mld_scan_iter_data *data = _data;
+ struct ieee80211_vif *curr_vif = data->current_vif;
+ struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
+ struct iwl_mld_vif *curr_mld_vif;
+ unsigned long curr_vif_active_links;
+ u16 link_id;
+
+ data->global_low_latency |= iwl_mld_vif_low_latency(mld_vif);
+
+ if ((ieee80211_vif_is_mld(vif) && vif->active_links) ||
+ (vif->type != NL80211_IFTYPE_P2P_DEVICE &&
+ mld_vif->deflink.active))
+ data->active_vif = true;
+
+ if (vif == curr_vif)
+ return;
+
+ if (ieee80211_vif_type_p2p(vif) != NL80211_IFTYPE_P2P_GO)
+ return;
+
+ /* Currently P2P GO can't be AP MLD so the logic below assumes that */
+ WARN_ON_ONCE(ieee80211_vif_is_mld(vif));
+
+ curr_vif_active_links =
+ ieee80211_vif_is_mld(curr_vif) ? curr_vif->active_links : 1;
+
+ curr_mld_vif = iwl_mld_vif_from_mac80211(curr_vif);
+
+ for_each_set_bit(link_id, &curr_vif_active_links,
+ IEEE80211_MLD_MAX_NUM_LINKS) {
+ struct iwl_mld_link *curr_mld_link =
+ iwl_mld_link_dereference_check(curr_mld_vif, link_id);
+
+ if (WARN_ON(!curr_mld_link))
+ return;
+
+ if (rcu_access_pointer(curr_mld_link->chan_ctx) &&
+ rcu_access_pointer(mld_vif->deflink.chan_ctx) !=
+ rcu_access_pointer(curr_mld_link->chan_ctx)) {
+ data->is_dcm_with_p2p_go = true;
+ return;
+ }
+ }
+}
+
+static enum
+iwl_mld_scan_type iwl_mld_get_scan_type(struct iwl_mld *mld,
+ struct ieee80211_vif *vif,
+ struct iwl_mld_scan_iter_data *data)
+{
+ enum iwl_mld_traffic_load load = mld->scan.traffic_load.status;
+
+ /* A scanning AP interface probably wants to generate a survey to do
+ * ACS (automatic channel selection).
+ * Force a non-fragmented scan in that case.
+ */
+ if (ieee80211_vif_type_p2p(vif) == NL80211_IFTYPE_AP)
+ return IWL_SCAN_TYPE_WILD;
+
+ if (!data->active_vif)
+ return IWL_SCAN_TYPE_UNASSOC;
+
+ if ((load == IWL_MLD_TRAFFIC_HIGH || data->global_low_latency) &&
+ vif->type != NL80211_IFTYPE_P2P_DEVICE)
+ return IWL_SCAN_TYPE_FRAGMENTED;
+
+ /* In case of DCM with P2P GO set all scan requests as
+ * fast-balance scan
+ */
+ if (vif->type == NL80211_IFTYPE_STATION &&
+ data->is_dcm_with_p2p_go)
+ return IWL_SCAN_TYPE_FAST_BALANCE;
+
+ if (load >= IWL_MLD_TRAFFIC_MEDIUM || data->global_low_latency)
+ return IWL_SCAN_TYPE_MILD;
+
+ return IWL_SCAN_TYPE_WILD;
+}
+
+static u8 *
+iwl_mld_scan_add_2ghz_elems(struct iwl_mld *mld, const u8 *ies,
+ size_t len, u8 *const pos)
+{
+ static const u8 before_ds_params[] = {
+ WLAN_EID_SSID,
+ WLAN_EID_SUPP_RATES,
+ WLAN_EID_REQUEST,
+ WLAN_EID_EXT_SUPP_RATES,
+ };
+ size_t offs;
+ u8 *newpos = pos;
+
+ offs = ieee80211_ie_split(ies, len,
+ before_ds_params,
+ ARRAY_SIZE(before_ds_params),
+ 0);
+
+ memcpy(newpos, ies, offs);
+ newpos += offs;
+
+ /* Add a placeholder for DS Parameter Set element */
+ *newpos++ = WLAN_EID_DS_PARAMS;
+ *newpos++ = 1;
+ *newpos++ = 0;
+
+ memcpy(newpos, ies + offs, len - offs);
+ newpos += len - offs;
+
+ return newpos;
+}
+
+static void
+iwl_mld_scan_add_tpc_report_elem(u8 *pos)
+{
+ pos[0] = WLAN_EID_VENDOR_SPECIFIC;
+ pos[1] = WFA_TPC_IE_LEN - 2;
+ pos[2] = (WLAN_OUI_MICROSOFT >> 16) & 0xff;
+ pos[3] = (WLAN_OUI_MICROSOFT >> 8) & 0xff;
+ pos[4] = WLAN_OUI_MICROSOFT & 0xff;
+ pos[5] = WLAN_OUI_TYPE_MICROSOFT_TPC;
+ pos[6] = 0;
+ /* pos[7] - tx power will be inserted by the FW */
+ pos[7] = 0;
+ pos[8] = 0;
+}
+
+static u32
+iwl_mld_scan_ooc_priority(enum iwl_mld_scan_status scan_status)
+{
+ if (scan_status == IWL_MLD_SCAN_REGULAR)
+ return IWL_SCAN_PRIORITY_EXT_6;
+ if (scan_status == IWL_MLD_SCAN_INT_MLO)
+ return IWL_SCAN_PRIORITY_EXT_4;
+
+ return IWL_SCAN_PRIORITY_EXT_2;
+}
+
+static bool
+iwl_mld_scan_is_regular(struct iwl_mld_scan_params *params)
+{
+ return params->n_scan_plans == 1 &&
+ params->scan_plans[0].iterations == 1;
+}
+
+static bool
+iwl_mld_scan_is_fragmented(enum iwl_mld_scan_type type)
+{
+ return (type == IWL_SCAN_TYPE_FRAGMENTED ||
+ type == IWL_SCAN_TYPE_FAST_BALANCE);
+}
+
+static int
+iwl_mld_scan_uid_by_status(struct iwl_mld *mld, int status)
+{
+ for (int i = 0; i < ARRAY_SIZE(mld->scan.uid_status); i++)
+ if (mld->scan.uid_status[i] == status)
+ return i;
+
+ return -ENOENT;
+}
+
+static const char *
+iwl_mld_scan_ebs_status_str(enum iwl_scan_ebs_status status)
+{
+ switch (status) {
+ case IWL_SCAN_EBS_SUCCESS:
+ return "successful";
+ case IWL_SCAN_EBS_INACTIVE:
+ return "inactive";
+ case IWL_SCAN_EBS_FAILED:
+ case IWL_SCAN_EBS_CHAN_NOT_FOUND:
+ default:
+ return "failed";
+ }
+}
+
+static int
+iwl_mld_scan_ssid_exist(u8 *ssid, u8 ssid_len, struct iwl_ssid_ie *ssid_list)
+{
+ for (int i = 0; i < PROBE_OPTION_MAX; i++) {
+ if (!ssid_list[i].len)
+ return -1;
+ if (ssid_list[i].len == ssid_len &&
+ !memcmp(ssid_list[i].ssid, ssid, ssid_len))
+ return i;
+ }
+
+ return -1;
+}
+
+static bool
+iwl_mld_scan_fits(struct iwl_mld *mld, int n_ssids,
+ struct ieee80211_scan_ies *ies, int n_channels)
+{
+ return ((n_ssids <= PROBE_OPTION_MAX) &&
+ (n_channels <= mld->fw->ucode_capa.n_scan_channels) &
+ (ies->common_ie_len + ies->len[NL80211_BAND_2GHZ] +
+ ies->len[NL80211_BAND_5GHZ] + ies->len[NL80211_BAND_6GHZ] <=
+ iwl_mld_scan_max_template_size()));
+}
+
+static void
+iwl_mld_scan_build_probe_req(struct iwl_mld *mld, struct ieee80211_vif *vif,
+ struct ieee80211_scan_ies *ies,
+ struct iwl_mld_scan_params *params)
+{
+ struct ieee80211_mgmt *frame = (void *)params->preq.buf;
+ u8 *pos, *newpos;
+ const u8 *mac_addr = params->flags & NL80211_SCAN_FLAG_RANDOM_ADDR ?
+ params->mac_addr : NULL;
+
+ if (mac_addr)
+ get_random_mask_addr(frame->sa, mac_addr,
+ params->mac_addr_mask);
+ else
+ memcpy(frame->sa, vif->addr, ETH_ALEN);
+
+ frame->frame_control = cpu_to_le16(IEEE80211_STYPE_PROBE_REQ);
+ eth_broadcast_addr(frame->da);
+ ether_addr_copy(frame->bssid, params->bssid);
+ frame->seq_ctrl = 0;
+
+ pos = frame->u.probe_req.variable;
+ *pos++ = WLAN_EID_SSID;
+ *pos++ = 0;
+
+ params->preq.mac_header.offset = 0;
+ params->preq.mac_header.len = cpu_to_le16(24 + 2);
+
+ /* Insert DS parameter set element on 2.4 GHz band */
+ newpos = iwl_mld_scan_add_2ghz_elems(mld,
+ ies->ies[NL80211_BAND_2GHZ],
+ ies->len[NL80211_BAND_2GHZ],
+ pos);
+ params->preq.band_data[0].offset = cpu_to_le16(pos - params->preq.buf);
+ params->preq.band_data[0].len = cpu_to_le16(newpos - pos);
+ pos = newpos;
+
+ memcpy(pos, ies->ies[NL80211_BAND_5GHZ],
+ ies->len[NL80211_BAND_5GHZ]);
+ params->preq.band_data[1].offset = cpu_to_le16(pos - params->preq.buf);
+ params->preq.band_data[1].len =
+ cpu_to_le16(ies->len[NL80211_BAND_5GHZ]);
+ pos += ies->len[NL80211_BAND_5GHZ];
+
+ memcpy(pos, ies->ies[NL80211_BAND_6GHZ],
+ ies->len[NL80211_BAND_6GHZ]);
+ params->preq.band_data[2].offset = cpu_to_le16(pos - params->preq.buf);
+ params->preq.band_data[2].len =
+ cpu_to_le16(ies->len[NL80211_BAND_6GHZ]);
+ pos += ies->len[NL80211_BAND_6GHZ];
+
+ memcpy(pos, ies->common_ies, ies->common_ie_len);
+ params->preq.common_data.offset = cpu_to_le16(pos - params->preq.buf);
+
+ iwl_mld_scan_add_tpc_report_elem(pos + ies->common_ie_len);
+ params->preq.common_data.len = cpu_to_le16(ies->common_ie_len +
+ WFA_TPC_IE_LEN);
+}
+
+static u16
+iwl_mld_scan_get_cmd_gen_flags(struct iwl_mld *mld,
+ struct iwl_mld_scan_params *params,
+ struct ieee80211_vif *vif,
+ enum iwl_mld_scan_status scan_status)
+{
+ u16 flags = 0;
+
+ /* If no direct SSIDs are provided perform a passive scan. Otherwise,
+ * if there is a single SSID which is not the broadcast SSID, assume
+ * that the scan is intended for roaming purposes and thus enable Rx on
+ * all chains to improve chances of hearing the beacons/probe responses.
+ */
+ if (params->n_ssids == 0)
+ flags |= IWL_UMAC_SCAN_GEN_FLAGS_V2_FORCE_PASSIVE;
+ else if (params->n_ssids == 1 && params->ssids[0].ssid_len)
+ flags |= IWL_UMAC_SCAN_GEN_FLAGS_V2_USE_ALL_RX_CHAINS;
+
+ if (params->pass_all)
+ flags |= IWL_UMAC_SCAN_GEN_FLAGS_V2_PASS_ALL;
+ else
+ flags |= IWL_UMAC_SCAN_GEN_FLAGS_V2_MATCH;
+
+ if (iwl_mld_scan_is_fragmented(params->type))
+ flags |= IWL_UMAC_SCAN_GEN_FLAGS_V2_FRAGMENTED_LMAC1;
+
+ if (!iwl_mld_scan_is_regular(params))
+ flags |= IWL_UMAC_SCAN_GEN_FLAGS_V2_PERIODIC;
+
+ if (params->iter_notif ||
+ mld->scan.pass_all_sched_res == SCHED_SCAN_PASS_ALL_STATE_ENABLED)
+ flags |= IWL_UMAC_SCAN_GEN_FLAGS_V2_NTFY_ITER_COMPLETE;
+
+ if (scan_status == IWL_MLD_SCAN_SCHED ||
+ scan_status == IWL_MLD_SCAN_NETDETECT)
+ flags |= IWL_UMAC_SCAN_GEN_FLAGS_V2_PREEMPTIVE;
+
+ if (params->flags & (NL80211_SCAN_FLAG_ACCEPT_BCAST_PROBE_RESP |
+ NL80211_SCAN_FLAG_OCE_PROBE_REQ_HIGH_TX_RATE |
+ NL80211_SCAN_FLAG_FILS_MAX_CHANNEL_TIME))
+ flags |= IWL_UMAC_SCAN_GEN_FLAGS_V2_OCE;
+
+ if ((scan_status == IWL_MLD_SCAN_SCHED ||
+ scan_status == IWL_MLD_SCAN_NETDETECT) &&
+ params->flags & NL80211_SCAN_FLAG_COLOCATED_6GHZ)
+ flags |= IWL_UMAC_SCAN_GEN_FLAGS_V2_TRIGGER_UHB_SCAN;
+
+ if (params->enable_6ghz_passive)
+ flags |= IWL_UMAC_SCAN_GEN_FLAGS_V2_6GHZ_PASSIVE_SCAN;
+
+ flags |= IWL_UMAC_SCAN_GEN_FLAGS_V2_ADAPTIVE_DWELL;
+
+ return flags;
+}
+
+static u8
+iwl_mld_scan_get_cmd_gen_flags2(struct iwl_mld *mld,
+ struct iwl_mld_scan_params *params,
+ struct ieee80211_vif *vif, u16 gen_flags)
+{
+ u8 flags = 0;
+
+ /* TODO: CDB */
+ if (params->respect_p2p_go)
+ flags |= IWL_UMAC_SCAN_GEN_PARAMS_FLAGS2_RESPECT_P2P_GO_LB |
+ IWL_UMAC_SCAN_GEN_PARAMS_FLAGS2_RESPECT_P2P_GO_HB;
+
+ if (params->scan_6ghz)
+ flags |= IWL_UMAC_SCAN_GEN_PARAMS_FLAGS2_DONT_TOGGLE_ANT;
+
+ return flags;
+}
+
+static void
+iwl_mld_scan_cmd_set_dwell(struct iwl_mld *mld,
+ struct iwl_scan_general_params_v11 *gp,
+ struct iwl_mld_scan_params *params)
+{
+ const struct iwl_mld_scan_timing_params *timing =
+ &scan_timing[params->type];
+
+ gp->adwell_default_social_chn =
+ IWL_SCAN_ADWELL_DEFAULT_N_APS_SOCIAL;
+ gp->adwell_default_2g = IWL_SCAN_ADWELL_DEFAULT_LB_N_APS;
+ gp->adwell_default_5g = IWL_SCAN_ADWELL_DEFAULT_HB_N_APS;
+
+ if (params->n_ssids && params->ssids[0].ssid_len)
+ gp->adwell_max_budget =
+ cpu_to_le16(IWL_SCAN_ADWELL_MAX_BUDGET_DIRECTED_SCAN);
+ else
+ gp->adwell_max_budget =
+ cpu_to_le16(IWL_SCAN_ADWELL_MAX_BUDGET_FULL_SCAN);
+
+ gp->scan_priority = cpu_to_le32(IWL_SCAN_PRIORITY_EXT_6);
+
+ gp->max_out_of_time[SCAN_LB_LMAC_IDX] = cpu_to_le32(timing->max_out_time);
+ gp->suspend_time[SCAN_LB_LMAC_IDX] = cpu_to_le32(timing->suspend_time);
+
+ gp->active_dwell[SCAN_LB_LMAC_IDX] = IWL_SCAN_DWELL_ACTIVE;
+ gp->passive_dwell[SCAN_LB_LMAC_IDX] = IWL_SCAN_DWELL_PASSIVE;
+ gp->active_dwell[SCAN_HB_LMAC_IDX] = IWL_SCAN_DWELL_ACTIVE;
+ gp->passive_dwell[SCAN_HB_LMAC_IDX] = IWL_SCAN_DWELL_PASSIVE;
+
+ IWL_DEBUG_SCAN(mld,
+ "Scan: adwell_max_budget=%d max_out_of_time=%d suspend_time=%d\n",
+ gp->adwell_max_budget,
+ gp->max_out_of_time[SCAN_LB_LMAC_IDX],
+ gp->suspend_time[SCAN_LB_LMAC_IDX]);
+}
+
+static void
+iwl_mld_scan_cmd_set_gen_params(struct iwl_mld *mld,
+ struct iwl_mld_scan_params *params,
+ struct ieee80211_vif *vif,
+ struct iwl_scan_general_params_v11 *gp,
+ enum iwl_mld_scan_status scan_status)
+{
+ u16 gen_flags = iwl_mld_scan_get_cmd_gen_flags(mld, params, vif,
+ scan_status);
+ u8 gen_flags2 = iwl_mld_scan_get_cmd_gen_flags2(mld, params, vif,
+ gen_flags);
+
+ IWL_DEBUG_SCAN(mld, "General: flags=0x%x, flags2=0x%x\n",
+ gen_flags, gen_flags2);
+
+ gp->flags = cpu_to_le16(gen_flags);
+ gp->flags2 = gen_flags2;
+
+ iwl_mld_scan_cmd_set_dwell(mld, gp, params);
+
+ if (gen_flags & IWL_UMAC_SCAN_GEN_FLAGS_V2_FRAGMENTED_LMAC1)
+ gp->num_of_fragments[SCAN_LB_LMAC_IDX] = IWL_SCAN_NUM_OF_FRAGS;
+
+ if (params->fw_link_id != IWL_MLD_INVALID_FW_ID)
+ gp->scan_start_mac_or_link_id = params->fw_link_id;
+}
+
+static int
+iwl_mld_scan_cmd_set_sched_params(struct iwl_mld_scan_params *params,
+ struct iwl_scan_umac_schedule *schedule,
+ __le16 *delay)
+{
+ if (WARN_ON(!params->n_scan_plans ||
+ params->n_scan_plans > IWL_MAX_SCHED_SCAN_PLANS))
+ return -EINVAL;
+
+ for (int i = 0; i < params->n_scan_plans; i++) {
+ struct cfg80211_sched_scan_plan *scan_plan =
+ &params->scan_plans[i];
+
+ schedule[i].iter_count = scan_plan->iterations;
+ schedule[i].interval =
+ cpu_to_le16(scan_plan->interval);
+ }
+
+ /* If the number of iterations of the last scan plan is set to zero,
+ * it should run infinitely. However, this is not always the case.
+ * For example, when regular scan is requested the driver sets one scan
+ * plan with one iteration.
+ */
+ if (!schedule[params->n_scan_plans - 1].iter_count)
+ schedule[params->n_scan_plans - 1].iter_count = 0xff;
+
+ *delay = cpu_to_le16(params->delay);
+
+ return 0;
+}
+
+/* We insert the SSIDs in an inverted order, because the FW will
+ * invert it back.
+ */
+static void
+iwl_mld_scan_cmd_build_ssids(struct iwl_mld_scan_params *params,
+ struct iwl_ssid_ie *ssids, u32 *ssid_bitmap)
+{
+ int i, j;
+ int index;
+ u32 tmp_bitmap = 0;
+
+ /* copy SSIDs from match list. iwl_config_sched_scan_profiles()
+ * uses the order of these ssids to config match list.
+ */
+ for (i = 0, j = params->n_match_sets - 1;
+ j >= 0 && i < PROBE_OPTION_MAX;
+ i++, j--) {
+ /* skip empty SSID match_sets */
+ if (!params->match_sets[j].ssid.ssid_len)
+ continue;
+
+ ssids[i].id = WLAN_EID_SSID;
+ ssids[i].len = params->match_sets[j].ssid.ssid_len;
+ memcpy(ssids[i].ssid, params->match_sets[j].ssid.ssid,
+ ssids[i].len);
+ }
+
+ /* add SSIDs from scan SSID list */
+ for (j = params->n_ssids - 1;
+ j >= 0 && i < PROBE_OPTION_MAX;
+ i++, j--) {
+ index = iwl_mld_scan_ssid_exist(params->ssids[j].ssid,
+ params->ssids[j].ssid_len,
+ ssids);
+ if (index < 0) {
+ ssids[i].id = WLAN_EID_SSID;
+ ssids[i].len = params->ssids[j].ssid_len;
+ memcpy(ssids[i].ssid, params->ssids[j].ssid,
+ ssids[i].len);
+ tmp_bitmap |= BIT(i);
+ } else {
+ tmp_bitmap |= BIT(index);
+ }
+ }
+
+ if (ssid_bitmap)
+ *ssid_bitmap = tmp_bitmap;
+}
+
+static void
+iwl_mld_scan_fill_6g_chan_list(struct iwl_mld_scan_params *params,
+ struct iwl_scan_probe_params_v4 *pp)
+{
+ int j, idex_s = 0, idex_b = 0;
+ struct cfg80211_scan_6ghz_params *scan_6ghz_params =
+ params->scan_6ghz_params;
+
+ for (j = 0;
+ j < params->n_ssids && idex_s < SCAN_SHORT_SSID_MAX_SIZE;
+ j++) {
+ if (!params->ssids[j].ssid_len)
+ continue;
+
+ pp->short_ssid[idex_s] =
+ cpu_to_le32(~crc32_le(~0, params->ssids[j].ssid,
+ params->ssids[j].ssid_len));
+
+ /* hidden 6ghz scan */
+ pp->direct_scan[idex_s].id = WLAN_EID_SSID;
+ pp->direct_scan[idex_s].len = params->ssids[j].ssid_len;
+ memcpy(pp->direct_scan[idex_s].ssid, params->ssids[j].ssid,
+ params->ssids[j].ssid_len);
+ idex_s++;
+ }
+
+ /* Populate the arrays of the short SSIDs and the BSSIDs using the 6GHz
+ * collocated parameters. This might not be optimal, as this processing
+ * does not (yet) correspond to the actual channels, so it is possible
+ * that some entries would be left out.
+ */
+ for (j = 0; j < params->n_6ghz_params; j++) {
+ int k;
+
+ /* First, try to place the short SSID */
+ if (scan_6ghz_params[j].short_ssid_valid) {
+ for (k = 0; k < idex_s; k++) {
+ if (pp->short_ssid[k] ==
+ cpu_to_le32(scan_6ghz_params[j].short_ssid))
+ break;
+ }
+
+ if (k == idex_s && idex_s < SCAN_SHORT_SSID_MAX_SIZE) {
+ pp->short_ssid[idex_s++] =
+ cpu_to_le32(scan_6ghz_params[j].short_ssid);
+ }
+ }
+
+ /* try to place BSSID for the same entry */
+ for (k = 0; k < idex_b; k++) {
+ if (!memcmp(&pp->bssid_array[k],
+ scan_6ghz_params[j].bssid, ETH_ALEN))
+ break;
+ }
+
+ if (k == idex_b && idex_b < SCAN_BSSID_MAX_SIZE &&
+ !WARN_ONCE(!is_valid_ether_addr(scan_6ghz_params[j].bssid),
+ "scan: invalid BSSID at index %u, index_b=%u\n",
+ j, idex_b)) {
+ memcpy(&pp->bssid_array[idex_b++],
+ scan_6ghz_params[j].bssid, ETH_ALEN);
+ }
+ }
+
+ pp->short_ssid_num = idex_s;
+ pp->bssid_num = idex_b;
+}
+
+static void
+iwl_mld_scan_cmd_set_probe_params(struct iwl_mld_scan_params *params,
+ struct iwl_scan_probe_params_v4 *pp,
+ u32 *bitmap_ssid)
+{
+ pp->preq = params->preq;
+
+ if (params->scan_6ghz) {
+ iwl_mld_scan_fill_6g_chan_list(params, pp);
+ return;
+ }
+
+ /* relevant only for 2.4 GHz /5 GHz scan */
+ iwl_mld_scan_cmd_build_ssids(params, pp->direct_scan, bitmap_ssid);
+}
+
+static bool
+iwl_mld_scan_use_ebs(struct iwl_mld *mld, struct ieee80211_vif *vif,
+ bool low_latency)
+{
+ const struct iwl_ucode_capabilities *capa = &mld->fw->ucode_capa;
+
+ /* We can only use EBS if:
+ * 1. the feature is supported.
+ * 2. the last EBS was successful.
+ * 3. it's not a p2p find operation.
+ * 4. we are not in low latency mode,
+ * or if fragmented ebs is supported by the FW
+ * 5. the VIF is not an AP interface (scan wants survey results)
+ */
+ return ((capa->flags & IWL_UCODE_TLV_FLAGS_EBS_SUPPORT) &&
+ !mld->scan.last_ebs_failed &&
+ vif->type != NL80211_IFTYPE_P2P_DEVICE &&
+ (!low_latency || fw_has_api(capa, IWL_UCODE_TLV_API_FRAG_EBS)) &&
+ ieee80211_vif_type_p2p(vif) != NL80211_IFTYPE_AP);
+}
+
+static u8
+iwl_mld_scan_cmd_set_chan_flags(struct iwl_mld *mld,
+ struct iwl_mld_scan_params *params,
+ struct ieee80211_vif *vif,
+ bool low_latency)
+{
+ u8 flags = 0;
+
+ flags |= IWL_SCAN_CHANNEL_FLAG_ENABLE_CHAN_ORDER;
+
+ if (iwl_mld_scan_use_ebs(mld, vif, low_latency))
+ flags |= IWL_SCAN_CHANNEL_FLAG_EBS |
+ IWL_SCAN_CHANNEL_FLAG_EBS_ACCURATE |
+ IWL_SCAN_CHANNEL_FLAG_CACHE_ADD;
+
+ /* set fragmented ebs for fragmented scan */
+ if (iwl_mld_scan_is_fragmented(params->type))
+ flags |= IWL_SCAN_CHANNEL_FLAG_EBS_FRAG;
+
+ /* Force EBS in case the scan is a fragmented and there is a need
+ * to take P2P GO operation into consideration during scan operation.
+ */
+ /* TODO: CDB */
+ if (iwl_mld_scan_is_fragmented(params->type) &&
+ params->respect_p2p_go) {
+ IWL_DEBUG_SCAN(mld, "Respect P2P GO. Force EBS\n");
+ flags |= IWL_SCAN_CHANNEL_FLAG_FORCE_EBS;
+ }
+
+ return flags;
+}
+
+static const u8 p2p_go_friendly_chs[] = {
+ 36, 40, 44, 48, 149, 153, 157, 161, 165,
+};
+
+static const u8 social_chs[] = {
+ 1, 6, 11
+};
+
+static u32 iwl_mld_scan_ch_n_aps_flag(enum nl80211_iftype vif_type, u8 ch_id)
+{
+ if (vif_type != NL80211_IFTYPE_P2P_DEVICE)
+ return 0;
+
+ for (int i = 0; i < ARRAY_SIZE(p2p_go_friendly_chs); i++) {
+ if (ch_id == p2p_go_friendly_chs[i])
+ return IWL_SCAN_ADWELL_N_APS_GO_FRIENDLY_BIT;
+ }
+
+ for (int i = 0; i < ARRAY_SIZE(social_chs); i++) {
+ if (ch_id == social_chs[i])
+ return IWL_SCAN_ADWELL_N_APS_SOCIAL_CHS_BIT;
+ }
+
+ return 0;
+}
+
+static void
+iwl_mld_scan_cmd_set_channels(struct iwl_mld *mld,
+ struct ieee80211_channel **channels,
+ struct iwl_scan_channel_params_v7 *cp,
+ int n_channels, u32 flags,
+ enum nl80211_iftype vif_type)
+{
+ for (int i = 0; i < n_channels; i++) {
+ enum nl80211_band band = channels[i]->band;
+ struct iwl_scan_channel_cfg_umac *cfg = &cp->channel_config[i];
+ u8 iwl_band = iwl_mld_nl80211_band_to_fw(band);
+ u32 n_aps_flag =
+ iwl_mld_scan_ch_n_aps_flag(vif_type,
+ channels[i]->hw_value);
+
+ if (IWL_MLD_ADAPTIVE_DWELL_NUM_APS_OVERRIDE)
+ n_aps_flag = IWL_SCAN_ADWELL_N_APS_GO_FRIENDLY_BIT;
+
+ cfg->flags = cpu_to_le32(flags | n_aps_flag);
+ cfg->channel_num = channels[i]->hw_value;
+ if (cfg80211_channel_is_psc(channels[i]))
+ cfg->flags = 0;
+
+ if (band == NL80211_BAND_6GHZ) {
+ /* 6 GHz channels should only appear in a scan request
+ * that has scan_6ghz set. The only exception is MLO
+ * scan, which has to be passive.
+ */
+ WARN_ON_ONCE(cfg->flags != 0);
+ cfg->flags =
+ cpu_to_le32(IWL_UHB_CHAN_CFG_FLAG_FORCE_PASSIVE);
+ }
+
+ cfg->v2.iter_count = 1;
+ cfg->v2.iter_interval = 0;
+ cfg->flags |= cpu_to_le32(iwl_band <<
+ IWL_CHAN_CFG_FLAGS_BAND_POS);
+ }
+}
+
+static u8
+iwl_mld_scan_cfg_channels_6g(struct iwl_mld *mld,
+ struct iwl_mld_scan_params *params,
+ u32 n_channels,
+ struct iwl_scan_probe_params_v4 *pp,
+ struct iwl_scan_channel_params_v7 *cp,
+ enum nl80211_iftype vif_type)
+{
+ struct cfg80211_scan_6ghz_params *scan_6ghz_params =
+ params->scan_6ghz_params;
+ u32 i;
+ u8 ch_cnt;
+
+ for (i = 0, ch_cnt = 0; i < params->n_channels; i++) {
+ struct iwl_scan_channel_cfg_umac *cfg =
+ &cp->channel_config[ch_cnt];
+
+ u32 s_ssid_bitmap = 0, bssid_bitmap = 0, flags = 0;
+ u8 k, n_s_ssids = 0, n_bssids = 0;
+ u8 max_s_ssids, max_bssids;
+ bool force_passive = false, found = false, allow_passive = true,
+ unsolicited_probe_on_chan = false, psc_no_listen = false;
+ s8 psd_20 = IEEE80211_RNR_TBTT_PARAMS_PSD_RESERVED;
+
+ /* Avoid performing passive scan on non PSC channels unless the
+ * scan is specifically a passive scan, i.e., no SSIDs
+ * configured in the scan command.
+ */
+ if (!cfg80211_channel_is_psc(params->channels[i]) &&
+ !params->n_6ghz_params && params->n_ssids)
+ continue;
+
+ cfg->channel_num = params->channels[i]->hw_value;
+ cfg->flags |=
+ cpu_to_le32(PHY_BAND_6 << IWL_CHAN_CFG_FLAGS_BAND_POS);
+
+ cfg->v5.iter_count = 1;
+ cfg->v5.iter_interval = 0;
+
+ for (u32 j = 0; j < params->n_6ghz_params; j++) {
+ s8 tmp_psd_20;
+
+ if (!(scan_6ghz_params[j].channel_idx == i))
+ continue;
+
+ unsolicited_probe_on_chan |=
+ scan_6ghz_params[j].unsolicited_probe;
+
+ /* Use the highest PSD value allowed as advertised by
+ * APs for this channel
+ */
+ tmp_psd_20 = scan_6ghz_params[j].psd_20;
+ if (tmp_psd_20 !=
+ IEEE80211_RNR_TBTT_PARAMS_PSD_RESERVED &&
+ (psd_20 ==
+ IEEE80211_RNR_TBTT_PARAMS_PSD_RESERVED ||
+ psd_20 < tmp_psd_20))
+ psd_20 = tmp_psd_20;
+
+ psc_no_listen |= scan_6ghz_params[j].psc_no_listen;
+ }
+
+ /* In the following cases apply passive scan:
+ * 1. Non fragmented scan:
+ * - PSC channel with NO_LISTEN_FLAG on should be treated
+ * like non PSC channel
+ * - Non PSC channel with more than 3 short SSIDs or more
+ * than 9 BSSIDs.
+ * - Non PSC Channel with unsolicited probe response and
+ * more than 2 short SSIDs or more than 6 BSSIDs.
+ * - PSC channel with more than 2 short SSIDs or more than
+ * 6 BSSIDs.
+ * 2. Fragmented scan:
+ * - PSC channel with more than 1 SSID or 3 BSSIDs.
+ * - Non PSC channel with more than 2 SSIDs or 6 BSSIDs.
+ * - Non PSC channel with unsolicited probe response and
+ * more than 1 SSID or more than 3 BSSIDs.
+ */
+ if (!iwl_mld_scan_is_fragmented(params->type)) {
+ if (!cfg80211_channel_is_psc(params->channels[i]) ||
+ psc_no_listen) {
+ if (unsolicited_probe_on_chan) {
+ max_s_ssids = 2;
+ max_bssids = 6;
+ } else {
+ max_s_ssids = 3;
+ max_bssids = 9;
+ }
+ } else {
+ max_s_ssids = 2;
+ max_bssids = 6;
+ }
+ } else if (cfg80211_channel_is_psc(params->channels[i])) {
+ max_s_ssids = 1;
+ max_bssids = 3;
+ } else {
+ if (unsolicited_probe_on_chan) {
+ max_s_ssids = 1;
+ max_bssids = 3;
+ } else {
+ max_s_ssids = 2;
+ max_bssids = 6;
+ }
+ }
+
+ /* To optimize the scan time, i.e., reduce the scan dwell time
+ * on each channel, the below logic tries to set 3 direct BSSID
+ * probe requests for each broadcast probe request with a short
+ * SSID.
+ */
+ for (u32 j = 0; j < params->n_6ghz_params; j++) {
+ if (!(scan_6ghz_params[j].channel_idx == i))
+ continue;
+
+ found = false;
+
+ for (k = 0;
+ k < pp->short_ssid_num && n_s_ssids < max_s_ssids;
+ k++) {
+ if (!scan_6ghz_params[j].unsolicited_probe &&
+ le32_to_cpu(pp->short_ssid[k]) ==
+ scan_6ghz_params[j].short_ssid) {
+ /* Relevant short SSID bit set */
+ if (s_ssid_bitmap & BIT(k)) {
+ found = true;
+ break;
+ }
+
+ /* Prefer creating BSSID entries unless
+ * the short SSID probe can be done in
+ * the same channel dwell iteration.
+ *
+ * We also need to create a short SSID
+ * entry for any hidden AP.
+ */
+ if (3 * n_s_ssids > n_bssids &&
+ !pp->direct_scan[k].len)
+ break;
+
+ /* Hidden AP, cannot do passive scan */
+ if (pp->direct_scan[k].len)
+ allow_passive = false;
+
+ s_ssid_bitmap |= BIT(k);
+ n_s_ssids++;
+ found = true;
+ break;
+ }
+ }
+
+ if (found)
+ continue;
+
+ for (k = 0; k < pp->bssid_num; k++) {
+ if (memcmp(&pp->bssid_array[k],
+ scan_6ghz_params[j].bssid,
+ ETH_ALEN))
+ continue;
+
+ if (bssid_bitmap & BIT(k))
+ break;
+
+ if (n_bssids < max_bssids) {
+ bssid_bitmap |= BIT(k);
+ n_bssids++;
+ } else {
+ force_passive = TRUE;
+ }
+
+ break;
+ }
+ }
+
+ if (cfg80211_channel_is_psc(params->channels[i]) &&
+ psc_no_listen)
+ flags |= IWL_UHB_CHAN_CFG_FLAG_PSC_CHAN_NO_LISTEN;
+
+ if (unsolicited_probe_on_chan)
+ flags |= IWL_UHB_CHAN_CFG_FLAG_UNSOLICITED_PROBE_RES;
+
+ if ((allow_passive && force_passive) ||
+ (!(bssid_bitmap | s_ssid_bitmap) &&
+ !cfg80211_channel_is_psc(params->channels[i])))
+ flags |= IWL_UHB_CHAN_CFG_FLAG_FORCE_PASSIVE;
+ else
+ flags |= bssid_bitmap | (s_ssid_bitmap << 16);
+
+ cfg->flags |= cpu_to_le32(flags);
+ cfg->v5.psd_20 = psd_20;
+
+ ch_cnt++;
+ }
+
+ if (params->n_channels > ch_cnt)
+ IWL_DEBUG_SCAN(mld,
+ "6GHz: reducing number channels: (%u->%u)\n",
+ params->n_channels, ch_cnt);
+
+ return ch_cnt;
+}
+
+static int
+iwl_mld_scan_cmd_set_6ghz_chan_params(struct iwl_mld *mld,
+ struct iwl_mld_scan_params *params,
+ struct ieee80211_vif *vif,
+ struct iwl_scan_req_params_v17 *scan_p,
+ enum iwl_mld_scan_status scan_status)
+{
+ struct iwl_scan_channel_params_v7 *chan_p = &scan_p->channel_params;
+ struct iwl_scan_probe_params_v4 *probe_p = &scan_p->probe_params;
+
+ chan_p->flags = iwl_mld_scan_get_cmd_gen_flags(mld, params, vif,
+ scan_status);
+ chan_p->count = iwl_mld_scan_cfg_channels_6g(mld, params,
+ params->n_channels,
+ probe_p, chan_p,
+ vif->type);
+ if (!chan_p->count)
+ return -EINVAL;
+
+ if (!params->n_ssids ||
+ (params->n_ssids == 1 && !params->ssids[0].ssid_len))
+ chan_p->flags |= IWL_SCAN_CHANNEL_FLAG_6G_PSC_NO_FILTER;
+
+ return 0;
+}
+
+static int
+iwl_mld_scan_cmd_set_chan_params(struct iwl_mld *mld,
+ struct iwl_mld_scan_params *params,
+ struct ieee80211_vif *vif,
+ struct iwl_scan_req_params_v17 *scan_p,
+ bool low_latency,
+ enum iwl_mld_scan_status scan_status,
+ u32 channel_cfg_flags)
+{
+ struct iwl_scan_channel_params_v7 *cp = &scan_p->channel_params;
+ struct ieee80211_supported_band *sband =
+ &mld->nvm_data->bands[NL80211_BAND_6GHZ];
+
+ cp->n_aps_override[0] = IWL_SCAN_ADWELL_N_APS_GO_FRIENDLY;
+ cp->n_aps_override[1] = IWL_SCAN_ADWELL_N_APS_SOCIAL_CHS;
+
+ if (IWL_MLD_ADAPTIVE_DWELL_NUM_APS_OVERRIDE)
+ cp->n_aps_override[0] = IWL_MLD_ADAPTIVE_DWELL_NUM_APS_OVERRIDE;
+
+ if (params->scan_6ghz)
+ return iwl_mld_scan_cmd_set_6ghz_chan_params(mld, params,
+ vif, scan_p,
+ scan_status);
+
+ /* relevant only for 2.4 GHz/5 GHz scan */
+ cp->flags = iwl_mld_scan_cmd_set_chan_flags(mld, params, vif,
+ low_latency);
+ cp->count = params->n_channels;
+
+ iwl_mld_scan_cmd_set_channels(mld, params->channels, cp,
+ params->n_channels, channel_cfg_flags,
+ vif->type);
+
+ if (!params->enable_6ghz_passive)
+ return 0;
+
+ /* fill 6 GHz passive scan cfg */
+ for (int i = 0; i < sband->n_channels; i++) {
+ struct ieee80211_channel *channel =
+ &sband->channels[i];
+ struct iwl_scan_channel_cfg_umac *cfg =
+ &cp->channel_config[cp->count];
+
+ if (!cfg80211_channel_is_psc(channel))
+ continue;
+
+ cfg->channel_num = channel->hw_value;
+ cfg->v5.iter_count = 1;
+ cfg->v5.iter_interval = 0;
+ cfg->v5.psd_20 =
+ IEEE80211_RNR_TBTT_PARAMS_PSD_RESERVED;
+ cfg->flags = cpu_to_le32(PHY_BAND_6 <<
+ IWL_CHAN_CFG_FLAGS_BAND_POS);
+ cp->count++;
+ }
+
+ return 0;
+}
+
+static int
+iwl_mld_scan_build_cmd(struct iwl_mld *mld, struct ieee80211_vif *vif,
+ struct iwl_mld_scan_params *params,
+ enum iwl_mld_scan_status scan_status,
+ bool low_latency)
+{
+ struct iwl_scan_req_umac_v17 *cmd = mld->scan.cmd;
+ struct iwl_scan_req_params_v17 *scan_p = &cmd->scan_params;
+ u32 bitmap_ssid = 0;
+ int uid, ret;
+
+ memset(mld->scan.cmd, 0, mld->scan.cmd_size);
+
+ /* find a free UID entry */
+ uid = iwl_mld_scan_uid_by_status(mld, IWL_MLD_SCAN_NONE);
+ if (uid < 0)
+ return uid;
+
+ cmd->uid = cpu_to_le32(uid);
+ cmd->ooc_priority =
+ cpu_to_le32(iwl_mld_scan_ooc_priority(scan_status));
+
+ iwl_mld_scan_cmd_set_gen_params(mld, params, vif,
+ &scan_p->general_params, scan_status);
+
+ ret = iwl_mld_scan_cmd_set_sched_params(params,
+ scan_p->periodic_params.schedule,
+ &scan_p->periodic_params.delay);
+ if (ret)
+ return ret;
+
+ iwl_mld_scan_cmd_set_probe_params(params, &scan_p->probe_params,
+ &bitmap_ssid);
+
+ ret = iwl_mld_scan_cmd_set_chan_params(mld, params, vif, scan_p,
+ low_latency, scan_status,
+ bitmap_ssid);
+ if (ret)
+ return ret;
+
+ return uid;
+}
+
+static bool
+iwl_mld_scan_pass_all(struct iwl_mld *mld,
+ struct cfg80211_sched_scan_request *req)
+{
+ if (req->n_match_sets && req->match_sets[0].ssid.ssid_len) {
+ IWL_DEBUG_SCAN(mld,
+ "Sending scheduled scan with filtering, n_match_sets %d\n",
+ req->n_match_sets);
+ mld->scan.pass_all_sched_res = SCHED_SCAN_PASS_ALL_STATE_DISABLED;
+ return false;
+ }
+
+ IWL_DEBUG_SCAN(mld, "Sending Scheduled scan without filtering\n");
+ mld->scan.pass_all_sched_res = SCHED_SCAN_PASS_ALL_STATE_ENABLED;
+
+ return true;
+}
+
+static int
+iwl_mld_config_sched_scan_profiles(struct iwl_mld *mld,
+ struct cfg80211_sched_scan_request *req)
+{
+ struct iwl_host_cmd hcmd = {
+ .id = SCAN_OFFLOAD_UPDATE_PROFILES_CMD,
+ .dataflags[0] = IWL_HCMD_DFL_NOCOPY,
+ };
+ struct iwl_scan_offload_profile *profile;
+ struct iwl_scan_offload_profile_cfg_data *cfg_data;
+ struct iwl_scan_offload_profile_cfg *profile_cfg;
+ struct iwl_scan_offload_blocklist *blocklist;
+ u32 blocklist_size = IWL_SCAN_MAX_BLACKLIST_LEN * sizeof(*blocklist);
+ u32 cmd_size = blocklist_size + sizeof(*profile_cfg);
+ u8 *cmd;
+ int ret;
+
+ if (WARN_ON(req->n_match_sets > IWL_SCAN_MAX_PROFILES_V2))
+ return -EIO;
+
+ cmd = kzalloc(cmd_size, GFP_KERNEL);
+ if (!cmd)
+ return -ENOMEM;
+
+ hcmd.data[0] = cmd;
+ hcmd.len[0] = cmd_size;
+
+ blocklist = (struct iwl_scan_offload_blocklist *)cmd;
+ profile_cfg = (struct iwl_scan_offload_profile_cfg *)(cmd + blocklist_size);
+
+ /* No blocklist configuration */
+ cfg_data = &profile_cfg->data;
+ cfg_data->num_profiles = req->n_match_sets;
+ cfg_data->active_clients = SCAN_CLIENT_SCHED_SCAN;
+ cfg_data->pass_match = SCAN_CLIENT_SCHED_SCAN;
+ cfg_data->match_notify = SCAN_CLIENT_SCHED_SCAN;
+
+ if (!req->n_match_sets || !req->match_sets[0].ssid.ssid_len)
+ cfg_data->any_beacon_notify = SCAN_CLIENT_SCHED_SCAN;
+
+ for (int i = 0; i < req->n_match_sets; i++) {
+ profile = &profile_cfg->profiles[i];
+
+ /* Support any cipher and auth algorithm */
+ profile->unicast_cipher = 0xff;
+ profile->auth_alg = IWL_AUTH_ALGO_UNSUPPORTED |
+ IWL_AUTH_ALGO_NONE | IWL_AUTH_ALGO_PSK |
+ IWL_AUTH_ALGO_8021X | IWL_AUTH_ALGO_SAE |
+ IWL_AUTH_ALGO_8021X_SHA384 | IWL_AUTH_ALGO_OWE;
+ profile->network_type = IWL_NETWORK_TYPE_ANY;
+ profile->band_selection = IWL_SCAN_OFFLOAD_SELECT_ANY;
+ profile->client_bitmap = SCAN_CLIENT_SCHED_SCAN;
+ profile->ssid_index = i;
+ }
+
+ IWL_DEBUG_SCAN(mld,
+ "Sending scheduled scan profile config (n_match_sets=%u)\n",
+ req->n_match_sets);
+
+ ret = iwl_mld_send_cmd(mld, &hcmd);
+
+ kfree(cmd);
+
+ return ret;
+}
+
+static int
+iwl_mld_sched_scan_handle_non_psc_channels(struct iwl_mld_scan_params *params,
+ bool *non_psc_included)
+{
+ int i, j;
+
+ *non_psc_included = false;
+ /* for 6 GHZ band only PSC channels need to be added */
+ for (i = 0; i < params->n_channels; i++) {
+ struct ieee80211_channel *channel = params->channels[i];
+
+ if (channel->band == NL80211_BAND_6GHZ &&
+ !cfg80211_channel_is_psc(channel)) {
+ *non_psc_included = true;
+ break;
+ }
+ }
+
+ if (!*non_psc_included)
+ return 0;
+
+ params->channels =
+ kmemdup(params->channels,
+ sizeof(params->channels[0]) * params->n_channels,
+ GFP_KERNEL);
+ if (!params->channels)
+ return -ENOMEM;
+
+ for (i = j = 0; i < params->n_channels; i++) {
+ if (params->channels[i]->band == NL80211_BAND_6GHZ &&
+ !cfg80211_channel_is_psc(params->channels[i]))
+ continue;
+ params->channels[j++] = params->channels[i];
+ }
+
+ params->n_channels = j;
+
+ return 0;
+}
+
+static void
+iwl_mld_scan_6ghz_passive_scan(struct iwl_mld *mld,
+ struct iwl_mld_scan_params *params,
+ struct ieee80211_vif *vif)
+{
+ struct ieee80211_supported_band *sband =
+ &mld->nvm_data->bands[NL80211_BAND_6GHZ];
+ u32 n_disabled, i;
+
+ params->enable_6ghz_passive = false;
+
+ /* 6 GHz passive scan may be enabled in the first 2.4 GHz/5 GHz scan
+ * phase to discover geo location if no AP's are found. Skip it when
+ * we're in the 6 GHz scan phase.
+ */
+ if (params->scan_6ghz)
+ return;
+
+ /* 6 GHz passive scan allowed only on station interface */
+ if (vif->type != NL80211_IFTYPE_STATION) {
+ IWL_DEBUG_SCAN(mld,
+ "6GHz passive scan: not station interface\n");
+ return;
+ }
+
+ /* 6 GHz passive scan is allowed in a defined time interval following
+ * HW reset or resume flow, or while not associated and a large
+ * interval has passed since the last 6 GHz passive scan.
+ */
+ if ((vif->cfg.assoc ||
+ time_after(mld->scan.last_6ghz_passive_jiffies +
+ (IWL_MLD_6GHZ_PASSIVE_SCAN_TIMEOUT * HZ), jiffies)) &&
+ (time_before(mld->scan.last_start_time_jiffies +
+ (IWL_MLD_6GHZ_PASSIVE_SCAN_ASSOC_TIMEOUT * HZ),
+ jiffies))) {
+ IWL_DEBUG_SCAN(mld, "6GHz passive scan: %s\n",
+ vif->cfg.assoc ? "associated" :
+ "timeout did not expire");
+ return;
+ }
+
+ /* not enough channels in the regular scan request */
+ if (params->n_channels < IWL_MLD_6GHZ_PASSIVE_SCAN_MIN_CHANS) {
+ IWL_DEBUG_SCAN(mld,
+ "6GHz passive scan: not enough channels %d\n",
+ params->n_channels);
+ return;
+ }
+
+ for (i = 0; i < params->n_ssids; i++) {
+ if (!params->ssids[i].ssid_len)
+ break;
+ }
+
+ /* not a wildcard scan, so cannot enable passive 6 GHz scan */
+ if (i == params->n_ssids) {
+ IWL_DEBUG_SCAN(mld,
+ "6GHz passive scan: no wildcard SSID\n");
+ return;
+ }
+
+ if (!sband || !sband->n_channels) {
+ IWL_DEBUG_SCAN(mld,
+ "6GHz passive scan: no 6GHz channels\n");
+ return;
+ }
+
+ for (i = 0, n_disabled = 0; i < sband->n_channels; i++) {
+ if (sband->channels[i].flags & (IEEE80211_CHAN_DISABLED))
+ n_disabled++;
+ }
+
+ /* Not all the 6 GHz channels are disabled, so no need for 6 GHz
+ * passive scan
+ */
+ if (n_disabled != sband->n_channels) {
+ IWL_DEBUG_SCAN(mld,
+ "6GHz passive scan: 6GHz channels enabled\n");
+ return;
+ }
+
+ /* all conditions to enable 6 GHz passive scan are satisfied */
+ IWL_DEBUG_SCAN(mld, "6GHz passive scan: can be enabled\n");
+ params->enable_6ghz_passive = true;
+}
+
+static void
+iwl_mld_scan_set_link_id(struct iwl_mld *mld, struct ieee80211_vif *vif,
+ struct iwl_mld_scan_params *params,
+ s8 tsf_report_link_id,
+ enum iwl_mld_scan_status scan_status)
+{
+ struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
+ struct iwl_mld_link *link;
+
+ if (tsf_report_link_id < 0) {
+ if (vif->active_links)
+ tsf_report_link_id = __ffs(vif->active_links);
+ else
+ tsf_report_link_id = 0;
+ }
+
+ link = iwl_mld_link_dereference_check(mld_vif, tsf_report_link_id);
+ if (!WARN_ON(!link)) {
+ params->fw_link_id = link->fw_id;
+ /* we to store fw_link_id only for regular scan,
+ * and use it in scan complete notif
+ */
+ if (scan_status == IWL_MLD_SCAN_REGULAR)
+ mld->scan.fw_link_id = link->fw_id;
+ } else {
+ mld->scan.fw_link_id = IWL_MLD_INVALID_FW_ID;
+ params->fw_link_id = IWL_MLD_INVALID_FW_ID;
+ }
+}
+
+static int
+_iwl_mld_single_scan_start(struct iwl_mld *mld, struct ieee80211_vif *vif,
+ struct cfg80211_scan_request *req,
+ struct ieee80211_scan_ies *ies,
+ enum iwl_mld_scan_status scan_status)
+{
+ struct iwl_host_cmd hcmd = {
+ .id = WIDE_ID(LONG_GROUP, SCAN_REQ_UMAC),
+ .len = { mld->scan.cmd_size, },
+ .data = { mld->scan.cmd, },
+ .dataflags = { IWL_HCMD_DFL_NOCOPY, },
+ };
+ struct iwl_mld_scan_iter_data scan_iter_data = {
+ .current_vif = vif,
+ };
+ struct cfg80211_sched_scan_plan scan_plan = {.iterations = 1};
+ struct iwl_mld_scan_params params = {};
+ int ret, uid;
+
+ /* we should have failed registration if scan_cmd was NULL */
+ if (WARN_ON(!mld->scan.cmd))
+ return -ENOMEM;
+
+ if (!iwl_mld_scan_fits(mld, req->n_ssids, ies, req->n_channels))
+ return -ENOBUFS;
+
+ ieee80211_iterate_active_interfaces_mtx(mld->hw,
+ IEEE80211_IFACE_ITER_NORMAL,
+ iwl_mld_scan_iterator,
+ &scan_iter_data);
+
+ params.type = iwl_mld_get_scan_type(mld, vif, &scan_iter_data);
+ params.n_ssids = req->n_ssids;
+ params.flags = req->flags;
+ params.n_channels = req->n_channels;
+ params.delay = 0;
+ params.ssids = req->ssids;
+ params.channels = req->channels;
+ params.mac_addr = req->mac_addr;
+ params.mac_addr_mask = req->mac_addr_mask;
+ params.no_cck = req->no_cck;
+ params.pass_all = true;
+ params.n_match_sets = 0;
+ params.match_sets = NULL;
+ params.scan_plans = &scan_plan;
+ params.n_scan_plans = 1;
+
+ params.n_6ghz_params = req->n_6ghz_params;
+ params.scan_6ghz_params = req->scan_6ghz_params;
+ params.scan_6ghz = req->scan_6ghz;
+
+ ether_addr_copy(params.bssid, req->bssid);
+ /* TODO: CDB - per-band flag */
+ params.respect_p2p_go =
+ iwl_mld_get_respect_p2p_go(mld, vif,
+ scan_iter_data.global_low_latency);
+
+ if (req->duration)
+ params.iter_notif = true;
+
+ iwl_mld_scan_set_link_id(mld, vif, &params, req->tsf_report_link_id,
+ scan_status);
+
+ iwl_mld_scan_build_probe_req(mld, vif, ies, &params);
+
+ iwl_mld_scan_6ghz_passive_scan(mld, &params, vif);
+
+ uid = iwl_mld_scan_build_cmd(mld, vif, &params, scan_status,
+ scan_iter_data.global_low_latency);
+ if (uid < 0)
+ return uid;
+
+ ret = iwl_mld_send_cmd(mld, &hcmd);
+ if (ret) {
+ IWL_ERR(mld, "Scan failed! ret %d\n", ret);
+ return ret;
+ }
+
+ IWL_DEBUG_SCAN(mld, "Scan request send success: status=%u, uid=%u\n",
+ scan_status, uid);
+
+ mld->scan.uid_status[uid] = scan_status;
+ mld->scan.status |= scan_status;
+
+ if (params.enable_6ghz_passive)
+ mld->scan.last_6ghz_passive_jiffies = jiffies;
+
+ return 0;
+}
+
+static int
+iwl_mld_scan_send_abort_cmd_status(struct iwl_mld *mld, int uid, u32 *status)
+{
+ struct iwl_umac_scan_abort abort_cmd = {
+ .uid = cpu_to_le32(uid),
+ };
+ struct iwl_host_cmd cmd = {
+ .id = WIDE_ID(LONG_GROUP, SCAN_ABORT_UMAC),
+ .flags = CMD_WANT_SKB,
+ .data = { &abort_cmd },
+ .len[0] = sizeof(abort_cmd),
+ };
+ struct iwl_rx_packet *pkt;
+ struct iwl_cmd_response *resp;
+ u32 resp_len;
+ int ret;
+
+ ret = iwl_mld_send_cmd(mld, &cmd);
+ if (ret)
+ return ret;
+
+ pkt = cmd.resp_pkt;
+
+ resp_len = iwl_rx_packet_payload_len(pkt);
+ if (IWL_FW_CHECK(mld, resp_len != sizeof(*resp),
+ "Scan Abort: unexpected response length %d\n",
+ resp_len)) {
+ ret = -EIO;
+ goto out;
+ }
+
+ resp = (void *)pkt->data;
+ *status = le32_to_cpu(resp->status);
+
+out:
+ iwl_free_resp(&cmd);
+ return ret;
+}
+
+static int
+iwl_mld_scan_abort(struct iwl_mld *mld, int type, int uid, bool *wait)
+{
+ enum iwl_umac_scan_abort_status status;
+ int ret;
+
+ *wait = true;
+
+ IWL_DEBUG_SCAN(mld, "Sending scan abort, uid %u\n", uid);
+
+ ret = iwl_mld_scan_send_abort_cmd_status(mld, uid, &status);
+
+ IWL_DEBUG_SCAN(mld, "Scan abort: ret=%d status=%u\n", ret, status);
+
+ /* We don't need to wait to scan complete in the following cases:
+ * 1. Driver failed to send the scan abort cmd.
+ * 2. The FW is no longer familiar with the scan that needs to be
+ * stopped. It is expected that the scan complete notification was
+ * already received but not yet processed.
+ *
+ * In both cases the flow should continue similar to the case that the
+ * scan was really aborted.
+ */
+ if (ret || status == IWL_UMAC_SCAN_ABORT_STATUS_NOT_FOUND)
+ *wait = false;
+
+ return ret;
+}
+
+static int
+iwl_mld_scan_stop_wait(struct iwl_mld *mld, int type, int uid)
+{
+ struct iwl_notification_wait wait_scan_done;
+ static const u16 scan_comp_notif[] = { SCAN_COMPLETE_UMAC };
+ bool wait = true;
+ int ret;
+
+ iwl_init_notification_wait(&mld->notif_wait, &wait_scan_done,
+ scan_comp_notif,
+ ARRAY_SIZE(scan_comp_notif),
+ NULL, NULL);
+
+ IWL_DEBUG_SCAN(mld, "Preparing to stop scan, type=%x\n", type);
+
+ ret = iwl_mld_scan_abort(mld, type, uid, &wait);
+ if (ret) {
+ IWL_DEBUG_SCAN(mld, "couldn't stop scan type=%d\n", type);
+ goto return_no_wait;
+ }
+
+ if (!wait) {
+ IWL_DEBUG_SCAN(mld, "no need to wait for scan type=%d\n", type);
+ goto return_no_wait;
+ }
+
+ return iwl_wait_notification(&mld->notif_wait, &wait_scan_done, HZ);
+
+return_no_wait:
+ iwl_remove_notification(&mld->notif_wait, &wait_scan_done);
+ return ret;
+}
+
+int iwl_mld_sched_scan_start(struct iwl_mld *mld,
+ struct ieee80211_vif *vif,
+ struct cfg80211_sched_scan_request *req,
+ struct ieee80211_scan_ies *ies,
+ int type)
+{
+ struct iwl_host_cmd hcmd = {
+ .id = WIDE_ID(LONG_GROUP, SCAN_REQ_UMAC),
+ .len = { mld->scan.cmd_size, },
+ .data = { mld->scan.cmd, },
+ .dataflags = { IWL_HCMD_DFL_NOCOPY, },
+ };
+ struct iwl_mld_scan_params params = {};
+ struct iwl_mld_scan_iter_data scan_iter_data = {
+ .current_vif = vif,
+ };
+ bool non_psc_included = false;
+ int ret, uid;
+
+ /* we should have failed registration if scan_cmd was NULL */
+ if (WARN_ON(!mld->scan.cmd))
+ return -ENOMEM;
+
+ /* FW supports only a single periodic scan */
+ if (mld->scan.status & (IWL_MLD_SCAN_SCHED | IWL_MLD_SCAN_NETDETECT))
+ return -EBUSY;
+
+ ieee80211_iterate_active_interfaces_mtx(mld->hw,
+ IEEE80211_IFACE_ITER_NORMAL,
+ iwl_mld_scan_iterator,
+ &scan_iter_data);
+
+ params.type = iwl_mld_get_scan_type(mld, vif, &scan_iter_data);
+ params.flags = req->flags;
+ params.n_ssids = req->n_ssids;
+ params.ssids = req->ssids;
+ params.n_channels = req->n_channels;
+ params.channels = req->channels;
+ params.mac_addr = req->mac_addr;
+ params.mac_addr_mask = req->mac_addr_mask;
+ params.no_cck = false;
+ params.pass_all = iwl_mld_scan_pass_all(mld, req);
+ params.n_match_sets = req->n_match_sets;
+ params.match_sets = req->match_sets;
+ params.n_scan_plans = req->n_scan_plans;
+ params.scan_plans = req->scan_plans;
+ /* TODO: CDB - per-band flag */
+ params.respect_p2p_go =
+ iwl_mld_get_respect_p2p_go(mld, vif,
+ scan_iter_data.global_low_latency);
+
+ /* UMAC scan supports up to 16-bit delays, trim it down to 16-bits */
+ params.delay = req->delay > U16_MAX ? U16_MAX : req->delay;
+
+ eth_broadcast_addr(params.bssid);
+
+ ret = iwl_mld_config_sched_scan_profiles(mld, req);
+ if (ret)
+ return ret;
+
+ iwl_mld_scan_build_probe_req(mld, vif, ies, &params);
+
+ ret = iwl_mld_sched_scan_handle_non_psc_channels(&params,
+ &non_psc_included);
+ if (ret)
+ goto out;
+
+ if (!iwl_mld_scan_fits(mld, req->n_ssids, ies, params.n_channels)) {
+ ret = -ENOBUFS;
+ goto out;
+ }
+
+ uid = iwl_mld_scan_build_cmd(mld, vif, &params, type,
+ scan_iter_data.global_low_latency);
+ if (uid < 0) {
+ ret = uid;
+ goto out;
+ }
+
+ ret = iwl_mld_send_cmd(mld, &hcmd);
+ if (!ret) {
+ IWL_DEBUG_SCAN(mld,
+ "Sched scan request send success: type=%u, uid=%u\n",
+ type, uid);
+ mld->scan.uid_status[uid] = type;
+ mld->scan.status |= type;
+ } else {
+ IWL_ERR(mld, "Sched scan failed! ret %d\n", ret);
+ mld->scan.pass_all_sched_res = SCHED_SCAN_PASS_ALL_STATE_DISABLED;
+ }
+
+out:
+ if (non_psc_included)
+ kfree(params.channels);
+ return ret;
+}
+
+int iwl_mld_scan_stop(struct iwl_mld *mld, int type, bool notify)
+{
+ int uid, ret;
+
+ IWL_DEBUG_SCAN(mld,
+ "Request to stop scan: type=0x%x, status=0x%x\n",
+ type, mld->scan.status);
+
+ if (!(mld->scan.status & type))
+ return 0;
+
+ uid = iwl_mld_scan_uid_by_status(mld, type);
+ /* must be valid, we just checked it's running */
+ if (WARN_ON_ONCE(uid < 0))
+ return uid;
+
+ ret = iwl_mld_scan_stop_wait(mld, type, uid);
+ if (ret)
+ IWL_DEBUG_SCAN(mld, "Failed to stop scan\n");
+
+ /* Clear the scan status so the next scan requests will
+ * succeed and mark the scan as stopping, so that the Rx
+ * handler doesn't do anything, as the scan was stopped from
+ * above. Also remove the handler to not notify mac80211
+ * erroneously after a new scan starts, for example.
+ */
+ mld->scan.status &= ~type;
+ mld->scan.uid_status[uid] = IWL_MLD_SCAN_NONE;
+ iwl_mld_cancel_notifications_of_object(mld, IWL_MLD_OBJECT_TYPE_SCAN,
+ uid);
+
+ if (type == IWL_MLD_SCAN_REGULAR) {
+ if (notify) {
+ struct cfg80211_scan_info info = {
+ .aborted = true,
+ };
+
+ ieee80211_scan_completed(mld->hw, &info);
+ }
+ } else if (notify) {
+ ieee80211_sched_scan_stopped(mld->hw);
+ mld->scan.pass_all_sched_res = SCHED_SCAN_PASS_ALL_STATE_DISABLED;
+ }
+
+ return ret;
+}
+
+int iwl_mld_regular_scan_start(struct iwl_mld *mld, struct ieee80211_vif *vif,
+ struct cfg80211_scan_request *req,
+ struct ieee80211_scan_ies *ies)
+{
+ return _iwl_mld_single_scan_start(mld, vif, req, ies,
+ IWL_MLD_SCAN_REGULAR);
+}
+
+static void iwl_mld_int_mlo_scan_start(struct iwl_mld *mld,
+ struct ieee80211_vif *vif,
+ struct ieee80211_channel **channels,
+ size_t n_channels)
+{
+ struct cfg80211_scan_request *req __free(kfree) = NULL;
+ struct ieee80211_scan_ies ies = {};
+ size_t size;
+ int ret;
+
+ IWL_DEBUG_SCAN(mld, "Starting Internal MLO scan: n_channels=%zu\n",
+ n_channels);
+
+ size = struct_size(req, channels, n_channels);
+ req = kzalloc(size, GFP_KERNEL);
+ if (!req)
+ return;
+
+ /* set the requested channels */
+ for (int i = 0; i < n_channels; i++)
+ req->channels[i] = channels[i];
+
+ req->n_channels = n_channels;
+
+ /* set the rates */
+ for (int i = 0; i < NUM_NL80211_BANDS; i++)
+ if (mld->wiphy->bands[i])
+ req->rates[i] =
+ (1 << mld->wiphy->bands[i]->n_bitrates) - 1;
+
+ req->wdev = ieee80211_vif_to_wdev(vif);
+ req->wiphy = mld->wiphy;
+ req->scan_start = jiffies;
+ req->tsf_report_link_id = -1;
+
+ ret = _iwl_mld_single_scan_start(mld, vif, req, &ies,
+ IWL_MLD_SCAN_INT_MLO);
+
+ if (!ret)
+ mld->scan.last_mlo_scan_time = ktime_get_boottime_ns();
+
+ IWL_DEBUG_SCAN(mld, "Internal MLO scan: ret=%d\n", ret);
+}
+
+void iwl_mld_int_mlo_scan(struct iwl_mld *mld, struct ieee80211_vif *vif)
+{
+ struct ieee80211_channel *channels[IEEE80211_MLD_MAX_NUM_LINKS];
+ unsigned long usable_links = ieee80211_vif_usable_links(vif);
+ size_t n_channels = 0;
+ u8 link_id;
+
+ lockdep_assert_wiphy(mld->wiphy);
+
+ if (!vif->cfg.assoc || !ieee80211_vif_is_mld(vif) ||
+ hweight16(vif->valid_links) == 1)
+ return;
+
+ if (mld->scan.status & IWL_MLD_SCAN_INT_MLO) {
+ IWL_DEBUG_SCAN(mld, "Internal MLO scan is already running\n");
+ return;
+ }
+
+ for_each_set_bit(link_id, &usable_links, IEEE80211_MLD_MAX_NUM_LINKS) {
+ struct ieee80211_bss_conf *link_conf =
+ link_conf_dereference_check(vif, link_id);
+
+ if (WARN_ON_ONCE(!link_conf))
+ continue;
+
+ channels[n_channels++] = link_conf->chanreq.oper.chan;
+ }
+
+ if (!n_channels)
+ return;
+
+ iwl_mld_int_mlo_scan_start(mld, vif, channels, n_channels);
+}
+
+void iwl_mld_handle_scan_iter_complete_notif(struct iwl_mld *mld,
+ struct iwl_rx_packet *pkt)
+{
+ struct iwl_umac_scan_iter_complete_notif *notif = (void *)pkt->data;
+ u32 uid = __le32_to_cpu(notif->uid);
+
+ if (IWL_FW_CHECK(mld, uid >= ARRAY_SIZE(mld->scan.uid_status),
+ "FW reports out-of-range scan UID %d\n", uid))
+ return;
+
+ if (mld->scan.uid_status[uid] == IWL_MLD_SCAN_REGULAR)
+ mld->scan.start_tsf = le64_to_cpu(notif->start_tsf);
+
+ IWL_DEBUG_SCAN(mld,
+ "UMAC Scan iteration complete: status=0x%x scanned_channels=%d\n",
+ notif->status, notif->scanned_channels);
+
+ if (mld->scan.pass_all_sched_res == SCHED_SCAN_PASS_ALL_STATE_FOUND) {
+ IWL_DEBUG_SCAN(mld, "Pass all scheduled scan results found\n");
+ ieee80211_sched_scan_results(mld->hw);
+ mld->scan.pass_all_sched_res = SCHED_SCAN_PASS_ALL_STATE_ENABLED;
+ }
+
+ IWL_DEBUG_SCAN(mld,
+ "UMAC Scan iteration complete: scan started at %llu (TSF)\n",
+ le64_to_cpu(notif->start_tsf));
+}
+
+void iwl_mld_handle_match_found_notif(struct iwl_mld *mld,
+ struct iwl_rx_packet *pkt)
+{
+ IWL_DEBUG_SCAN(mld, "Scheduled scan results\n");
+ ieee80211_sched_scan_results(mld->hw);
+}
+
+void iwl_mld_handle_scan_complete_notif(struct iwl_mld *mld,
+ struct iwl_rx_packet *pkt)
+{
+ struct iwl_umac_scan_complete *notif = (void *)pkt->data;
+ bool aborted = (notif->status == IWL_SCAN_OFFLOAD_ABORTED);
+ u32 uid = __le32_to_cpu(notif->uid);
+
+ if (IWL_FW_CHECK(mld, uid >= ARRAY_SIZE(mld->scan.uid_status),
+ "FW reports out-of-range scan UID %d\n", uid))
+ return;
+
+ IWL_DEBUG_SCAN(mld,
+ "Scan completed: uid=%u type=%u, status=%s, EBS=%s\n",
+ uid, mld->scan.uid_status[uid],
+ notif->status == IWL_SCAN_OFFLOAD_COMPLETED ?
+ "completed" : "aborted",
+ iwl_mld_scan_ebs_status_str(notif->ebs_status));
+ IWL_DEBUG_SCAN(mld, "Scan completed: scan_status=0x%x\n",
+ mld->scan.status);
+ IWL_DEBUG_SCAN(mld,
+ "Scan completed: line=%u, iter=%u, elapsed time=%u\n",
+ notif->last_schedule, notif->last_iter,
+ __le32_to_cpu(notif->time_from_last_iter));
+
+ if (IWL_FW_CHECK(mld, !(mld->scan.uid_status[uid] & mld->scan.status),
+ "FW reports scan UID %d we didn't trigger\n", uid))
+ return;
+
+ /* if the scan is already stopping, we don't need to notify mac80211 */
+ if (mld->scan.uid_status[uid] == IWL_MLD_SCAN_REGULAR) {
+ struct cfg80211_scan_info info = {
+ .aborted = aborted,
+ .scan_start_tsf = mld->scan.start_tsf,
+ };
+ int fw_link_id = mld->scan.fw_link_id;
+ struct ieee80211_bss_conf *link_conf = NULL;
+
+ if (fw_link_id != IWL_MLD_INVALID_FW_ID)
+ link_conf =
+ wiphy_dereference(mld->wiphy,
+ mld->fw_id_to_bss_conf[fw_link_id]);
+
+ /* It is possible that by the time the scan is complete the
+ * link was already removed and is not valid.
+ */
+ if (link_conf)
+ ether_addr_copy(info.tsf_bssid, link_conf->bssid);
+ else
+ IWL_DEBUG_SCAN(mld, "Scan link is no longer valid\n");
+
+ ieee80211_scan_completed(mld->hw, &info);
+
+ /* Scan is over, we can check again the tpt counters */
+ iwl_mld_stop_ignoring_tpt_updates(mld);
+ } else if (mld->scan.uid_status[uid] == IWL_MLD_SCAN_SCHED) {
+ ieee80211_sched_scan_stopped(mld->hw);
+ mld->scan.pass_all_sched_res = SCHED_SCAN_PASS_ALL_STATE_DISABLED;
+ } else if (mld->scan.uid_status[uid] == IWL_MLD_SCAN_INT_MLO) {
+ IWL_DEBUG_SCAN(mld, "Internal MLO scan completed\n");
+
+ /*
+ * We limit link selection to internal MLO scans as otherwise
+ * we do not know whether all channels were covered.
+ */
+ iwl_mld_select_links(mld);
+ }
+
+ mld->scan.status &= ~mld->scan.uid_status[uid];
+
+ IWL_DEBUG_SCAN(mld, "Scan completed: after update: scan_status=0x%x\n",
+ mld->scan.status);
+
+ mld->scan.uid_status[uid] = IWL_MLD_SCAN_NONE;
+
+ if (notif->ebs_status != IWL_SCAN_EBS_SUCCESS &&
+ notif->ebs_status != IWL_SCAN_EBS_INACTIVE)
+ mld->scan.last_ebs_failed = true;
+}
+
+/* This function is used in nic restart flow, to inform mac80211 about scans
+ * that were aborted by restart flow or by an assert.
+ */
+void iwl_mld_report_scan_aborted(struct iwl_mld *mld)
+{
+ int uid;
+
+ uid = iwl_mld_scan_uid_by_status(mld, IWL_MLD_SCAN_REGULAR);
+ if (uid >= 0) {
+ struct cfg80211_scan_info info = {
+ .aborted = true,
+ };
+
+ ieee80211_scan_completed(mld->hw, &info);
+ mld->scan.uid_status[uid] = IWL_MLD_SCAN_NONE;
+ }
+
+ uid = iwl_mld_scan_uid_by_status(mld, IWL_MLD_SCAN_SCHED);
+ if (uid >= 0) {
+ mld->scan.pass_all_sched_res = SCHED_SCAN_PASS_ALL_STATE_DISABLED;
+ mld->scan.uid_status[uid] = IWL_MLD_SCAN_NONE;
+
+ /* sched scan will be restarted by mac80211 in reconfig.
+ * report to mac80211 that sched scan stopped only if we won't
+ * restart the firmware.
+ */
+ if (!iwlwifi_mod_params.fw_restart)
+ ieee80211_sched_scan_stopped(mld->hw);
+ }
+
+ uid = iwl_mld_scan_uid_by_status(mld, IWL_MLD_SCAN_INT_MLO);
+ if (uid >= 0) {
+ IWL_DEBUG_SCAN(mld, "Internal MLO scan aborted\n");
+ mld->scan.uid_status[uid] = IWL_MLD_SCAN_NONE;
+ }
+
+ BUILD_BUG_ON(IWL_MLD_SCAN_NONE != 0);
+ memset(mld->scan.uid_status, 0, sizeof(mld->scan.uid_status));
+}
+
+int iwl_mld_alloc_scan_cmd(struct iwl_mld *mld)
+{
+ u8 scan_cmd_ver = iwl_fw_lookup_cmd_ver(mld->fw, SCAN_REQ_UMAC,
+ IWL_FW_CMD_VER_UNKNOWN);
+ size_t scan_cmd_size;
+
+ if (scan_cmd_ver == 17) {
+ scan_cmd_size = sizeof(struct iwl_scan_req_umac_v17);
+ } else {
+ IWL_ERR(mld, "Unexpected scan cmd version %d\n", scan_cmd_ver);
+ return -EINVAL;
+ }
+
+ mld->scan.cmd = kmalloc(scan_cmd_size, GFP_KERNEL);
+ if (!mld->scan.cmd)
+ return -ENOMEM;
+
+ mld->scan.cmd_size = scan_cmd_size;
+
+ return 0;
+}
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/scan.h b/drivers/net/wireless/intel/iwlwifi/mld/scan.h
new file mode 100644
index 000000000000..3ae940d55065
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/mld/scan.h
@@ -0,0 +1,136 @@
+/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
+/*
+ * Copyright (C) 2024-2025 Intel Corporation
+ */
+#ifndef __iwl_mld_scan_h__
+#define __iwl_mld_scan_h__
+
+int iwl_mld_alloc_scan_cmd(struct iwl_mld *mld);
+
+int iwl_mld_regular_scan_start(struct iwl_mld *mld, struct ieee80211_vif *vif,
+ struct cfg80211_scan_request *req,
+ struct ieee80211_scan_ies *ies);
+
+void iwl_mld_int_mlo_scan(struct iwl_mld *mld, struct ieee80211_vif *vif);
+
+void iwl_mld_handle_scan_iter_complete_notif(struct iwl_mld *mld,
+ struct iwl_rx_packet *pkt);
+
+int iwl_mld_scan_stop(struct iwl_mld *mld, int type, bool notify);
+
+int iwl_mld_sched_scan_start(struct iwl_mld *mld,
+ struct ieee80211_vif *vif,
+ struct cfg80211_sched_scan_request *req,
+ struct ieee80211_scan_ies *ies,
+ int type);
+
+void iwl_mld_handle_match_found_notif(struct iwl_mld *mld,
+ struct iwl_rx_packet *pkt);
+
+void iwl_mld_handle_scan_complete_notif(struct iwl_mld *mld,
+ struct iwl_rx_packet *pkt);
+
+#define WFA_TPC_IE_LEN 9
+
+static inline int iwl_mld_scan_max_template_size(void)
+{
+#define MAC_HDR_LEN 24
+#define DS_IE_LEN 3
+#define SSID_IE_LEN 2
+
+/* driver create the 802.11 header, WFA TPC IE, DS parameter and SSID IE */
+#define DRIVER_TOTAL_IES_LEN \
+ (MAC_HDR_LEN + WFA_TPC_IE_LEN + DS_IE_LEN + SSID_IE_LEN)
+
+ BUILD_BUG_ON(SCAN_OFFLOAD_PROBE_REQ_SIZE < DRIVER_TOTAL_IES_LEN);
+
+ return SCAN_OFFLOAD_PROBE_REQ_SIZE - DRIVER_TOTAL_IES_LEN;
+}
+
+void iwl_mld_report_scan_aborted(struct iwl_mld *mld);
+
+enum iwl_mld_scan_status {
+ IWL_MLD_SCAN_NONE = 0,
+ IWL_MLD_SCAN_REGULAR = BIT(0),
+ IWL_MLD_SCAN_SCHED = BIT(1),
+ IWL_MLD_SCAN_NETDETECT = BIT(2),
+ IWL_MLD_SCAN_INT_MLO = BIT(3),
+};
+
+/* enum iwl_mld_pass_all_sched_results_states - Defines the states for
+ * handling/passing scheduled scan results to mac80211
+ * @SCHED_SCAN_PASS_ALL_STATE_DISABLED: Don't pass all scan results, only when
+ * a match found.
+ * @SCHED_SCAN_PASS_ALL_STATE_ENABLED: Pass all scan results is enabled
+ * (no filtering).
+ * @SCHED_SCAN_PASS_ALL_STATE_FOUND: A scan result is found, pass it on the
+ * next scan iteration complete notification.
+ */
+enum iwl_mld_pass_all_sched_results_states {
+ SCHED_SCAN_PASS_ALL_STATE_DISABLED,
+ SCHED_SCAN_PASS_ALL_STATE_ENABLED,
+ SCHED_SCAN_PASS_ALL_STATE_FOUND,
+};
+
+/**
+ * enum iwl_mld_traffic_load - Levels of traffic load
+ *
+ * @IWL_MLD_TRAFFIC_LOW: low traffic load
+ * @IWL_MLD_TRAFFIC_MEDIUM: medium traffic load
+ * @IWL_MLD_TRAFFIC_HIGH: high traffic load
+ */
+enum iwl_mld_traffic_load {
+ IWL_MLD_TRAFFIC_LOW,
+ IWL_MLD_TRAFFIC_MEDIUM,
+ IWL_MLD_TRAFFIC_HIGH,
+};
+
+/**
+ * struct iwl_mld_scan - Scan data
+ * @status: scan status, a combination of %enum iwl_mld_scan_status,
+ * reflects the %scan.uid_status array.
+ * @uid_status: array to track the scan status per uid.
+ * @start_tsf: start time of last scan in TSF of the link that requested
+ * the scan.
+ * @last_ebs_failed: true if the last EBS (Energy Based Scan) failed.
+ * @pass_all_sched_res: see %enum iwl_mld_pass_all_sched_results_states.
+ * @fw_link_id: the current (regular) scan fw link id, used by scan
+ * complete notif.
+ * @traffic_load: traffic load related data
+ * @traffic_load.last_stats_ts_usec: The timestamp of the last statistics
+ * notification, used to calculate the elapsed time between two
+ * notifications and determine the traffic load
+ * @traffic_load.status: The current traffic load status, see
+ * &enum iwl_mld_traffic_load
+ * @cmd_size: size of %cmd.
+ * @cmd: pointer to scan cmd buffer (allocated once in op mode start).
+ * @last_6ghz_passive_jiffies: stores the last 6GHz passive scan time
+ * in jiffies.
+ * @last_start_time_jiffies: stores the last start time in jiffies
+ * (interface up/reset/resume).
+ * @last_mlo_scan_time: start time of the last MLO scan in nanoseconds since
+ * boot.
+ */
+struct iwl_mld_scan {
+ /* Add here fields that need clean up on restart */
+ struct_group(zeroed_on_hw_restart,
+ unsigned int status;
+ u32 uid_status[IWL_MAX_UMAC_SCANS];
+ u64 start_tsf;
+ bool last_ebs_failed;
+ enum iwl_mld_pass_all_sched_results_states pass_all_sched_res;
+ u8 fw_link_id;
+ struct {
+ u32 last_stats_ts_usec;
+ enum iwl_mld_traffic_load status;
+ } traffic_load;
+ );
+ /* And here fields that survive a fw restart */
+ size_t cmd_size;
+ void *cmd;
+ unsigned long last_6ghz_passive_jiffies;
+ unsigned long last_start_time_jiffies;
+ unsigned long last_mlo_scan_time;
+};
+
+#endif /* __iwl_mld_scan_h__ */
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/session-protect.c b/drivers/net/wireless/intel/iwlwifi/mld/session-protect.c
new file mode 100644
index 000000000000..dbb5615dc3f6
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/mld/session-protect.c
@@ -0,0 +1,222 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
+/*
+ * Copyright (C) 2024 Intel Corporation
+ */
+#include "session-protect.h"
+#include "fw/api/time-event.h"
+#include "fw/api/context.h"
+#include "iface.h"
+#include <net/mac80211.h>
+
+void iwl_mld_handle_session_prot_notif(struct iwl_mld *mld,
+ struct iwl_rx_packet *pkt)
+{
+ struct iwl_session_prot_notif *notif = (void *)pkt->data;
+ int fw_link_id = le32_to_cpu(notif->mac_link_id);
+ struct ieee80211_bss_conf *link_conf =
+ iwl_mld_fw_id_to_link_conf(mld, fw_link_id);
+ struct ieee80211_vif *vif;
+ struct iwl_mld_vif *mld_vif;
+ struct iwl_mld_session_protect *session_protect;
+
+ if (WARN_ON(!link_conf))
+ return;
+
+ vif = link_conf->vif;
+ mld_vif = iwl_mld_vif_from_mac80211(vif);
+ session_protect = &mld_vif->session_protect;
+
+ if (!le32_to_cpu(notif->status)) {
+ memset(session_protect, 0, sizeof(*session_protect));
+ } else if (le32_to_cpu(notif->start)) {
+ /* End_jiffies indicates an active session */
+ session_protect->session_requested = false;
+ session_protect->end_jiffies =
+ TU_TO_EXP_TIME(session_protect->duration);
+ /* !session_protect->end_jiffies means inactive session */
+ if (!session_protect->end_jiffies)
+ session_protect->end_jiffies = 1;
+ } else {
+ memset(session_protect, 0, sizeof(*session_protect));
+ }
+}
+
+static int _iwl_mld_schedule_session_protection(struct iwl_mld *mld,
+ struct ieee80211_vif *vif,
+ u32 duration, u32 min_duration,
+ int link_id)
+{
+ struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
+ struct iwl_mld_link *link =
+ iwl_mld_link_dereference_check(mld_vif, link_id);
+ struct iwl_mld_session_protect *session_protect =
+ &mld_vif->session_protect;
+ struct iwl_session_prot_cmd cmd = {
+ .id_and_color = cpu_to_le32(link->fw_id),
+ .action = cpu_to_le32(FW_CTXT_ACTION_ADD),
+ .conf_id = cpu_to_le32(SESSION_PROTECT_CONF_ASSOC),
+ .duration_tu = cpu_to_le32(MSEC_TO_TU(duration)),
+ };
+ int ret;
+
+ lockdep_assert_wiphy(mld->wiphy);
+
+ WARN(hweight16(vif->active_links) > 1,
+ "Session protection isn't allowed with more than one active link");
+
+ if (session_protect->end_jiffies &&
+ time_after(session_protect->end_jiffies,
+ TU_TO_EXP_TIME(min_duration))) {
+ IWL_DEBUG_TE(mld, "We have ample in the current session: %u\n",
+ jiffies_to_msecs(session_protect->end_jiffies -
+ jiffies));
+ return -EALREADY;
+ }
+
+ IWL_DEBUG_TE(mld, "Add a new session protection, duration %d TU\n",
+ le32_to_cpu(cmd.duration_tu));
+
+ ret = iwl_mld_send_cmd_pdu(mld, WIDE_ID(MAC_CONF_GROUP,
+ SESSION_PROTECTION_CMD), &cmd);
+
+ if (ret)
+ return ret;
+
+ /* end_jiffies will be updated when handling session_prot_notif */
+ session_protect->end_jiffies = 0;
+ session_protect->duration = duration;
+ session_protect->session_requested = true;
+
+ return 0;
+}
+
+void iwl_mld_schedule_session_protection(struct iwl_mld *mld,
+ struct ieee80211_vif *vif,
+ u32 duration, u32 min_duration,
+ int link_id)
+{
+ int ret;
+
+ ret = _iwl_mld_schedule_session_protection(mld, vif, duration,
+ min_duration, link_id);
+ if (ret && ret != -EALREADY)
+ IWL_ERR(mld,
+ "Couldn't send the SESSION_PROTECTION_CMD (%d)\n",
+ ret);
+}
+
+struct iwl_mld_session_start_data {
+ struct iwl_mld *mld;
+ struct ieee80211_bss_conf *link_conf;
+ bool success;
+};
+
+static bool iwl_mld_session_start_fn(struct iwl_notif_wait_data *notif_wait,
+ struct iwl_rx_packet *pkt, void *_data)
+{
+ struct iwl_session_prot_notif *notif = (void *)pkt->data;
+ unsigned int pkt_len = iwl_rx_packet_payload_len(pkt);
+ struct iwl_mld_session_start_data *data = _data;
+ struct ieee80211_bss_conf *link_conf;
+ struct iwl_mld *mld = data->mld;
+ int fw_link_id;
+
+ if (IWL_FW_CHECK(mld, pkt_len < sizeof(*notif),
+ "short session prot notif (%d)\n",
+ pkt_len))
+ return false;
+
+ fw_link_id = le32_to_cpu(notif->mac_link_id);
+ link_conf = iwl_mld_fw_id_to_link_conf(mld, fw_link_id);
+
+ if (link_conf != data->link_conf)
+ return false;
+
+ if (!le32_to_cpu(notif->status))
+ return true;
+
+ if (notif->start) {
+ data->success = true;
+ return true;
+ }
+
+ return false;
+}
+
+int iwl_mld_start_session_protection(struct iwl_mld *mld,
+ struct ieee80211_vif *vif,
+ u32 duration, u32 min_duration,
+ int link_id, unsigned long timeout)
+{
+ static const u16 start_notif[] = { SESSION_PROTECTION_NOTIF };
+ struct iwl_notification_wait start_wait;
+ struct iwl_mld_session_start_data data = {
+ .mld = mld,
+ .link_conf = wiphy_dereference(mld->wiphy,
+ vif->link_conf[link_id]),
+ };
+ int ret;
+
+ if (WARN_ON(!data.link_conf))
+ return -EINVAL;
+
+ iwl_init_notification_wait(&mld->notif_wait, &start_wait,
+ start_notif, ARRAY_SIZE(start_notif),
+ iwl_mld_session_start_fn, &data);
+
+ ret = _iwl_mld_schedule_session_protection(mld, vif, duration,
+ min_duration, link_id);
+
+ if (ret) {
+ iwl_remove_notification(&mld->notif_wait, &start_wait);
+ return ret == -EALREADY ? 0 : ret;
+ }
+
+ ret = iwl_wait_notification(&mld->notif_wait, &start_wait, timeout);
+ if (ret)
+ return ret;
+ return data.success ? 0 : -EIO;
+}
+
+int iwl_mld_cancel_session_protection(struct iwl_mld *mld,
+ struct ieee80211_vif *vif,
+ int link_id)
+{
+ struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
+ struct iwl_mld_link *link =
+ iwl_mld_link_dereference_check(mld_vif, link_id);
+ struct iwl_mld_session_protect *session_protect =
+ &mld_vif->session_protect;
+ struct iwl_session_prot_cmd cmd = {
+ .action = cpu_to_le32(FW_CTXT_ACTION_REMOVE),
+ .conf_id = cpu_to_le32(SESSION_PROTECT_CONF_ASSOC),
+ };
+ int ret;
+
+ lockdep_assert_wiphy(mld->wiphy);
+
+ /* If there isn't an active session or a requested one for this
+ * link do nothing
+ */
+ if (!session_protect->session_requested &&
+ !session_protect->end_jiffies)
+ return 0;
+
+ if (WARN_ON(!link))
+ return -EINVAL;
+
+ cmd.id_and_color = cpu_to_le32(link->fw_id);
+
+ ret = iwl_mld_send_cmd_pdu(mld,
+ WIDE_ID(MAC_CONF_GROUP,
+ SESSION_PROTECTION_CMD), &cmd);
+ if (ret) {
+ IWL_ERR(mld,
+ "Couldn't send the SESSION_PROTECTION_CMD\n");
+ return ret;
+ }
+
+ memset(session_protect, 0, sizeof(*session_protect));
+
+ return 0;
+}
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/session-protect.h b/drivers/net/wireless/intel/iwlwifi/mld/session-protect.h
new file mode 100644
index 000000000000..642bec8451a1
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/mld/session-protect.h
@@ -0,0 +1,102 @@
+/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
+/*
+ * Copyright (C) 2024-2025 Intel Corporation
+ */
+
+#ifndef __session_protect_h__
+#define __session_protect_h__
+
+#include "mld.h"
+#include "hcmd.h"
+#include <net/mac80211.h>
+#include "fw/api/mac-cfg.h"
+
+/**
+ * DOC: session protection
+ *
+ * Session protection is an API from the firmware that allows the driver to
+ * request time on medium. This is needed before the association when we need
+ * to be on medium for the association frame exchange. Once we configure the
+ * firmware as 'associated', the firmware will allocate time on medium without
+ * needed a session protection.
+ *
+ * TDLS discover uses this API as well even after association to ensure that
+ * other activities internal to the firmware will not interrupt our presence
+ * on medium.
+ */
+
+/**
+ * struct iwl_mld_session_protect - session protection parameters
+ * @end_jiffies: expected end_jiffies of current session protection.
+ * 0 if not active
+ * @duration: the duration in tu of current session
+ * @session_requested: A session protection command was sent and wasn't yet
+ * answered
+ */
+struct iwl_mld_session_protect {
+ unsigned long end_jiffies;
+ u32 duration;
+ bool session_requested;
+};
+
+#define IWL_MLD_SESSION_PROTECTION_ASSOC_TIME_MS 900
+#define IWL_MLD_SESSION_PROTECTION_MIN_TIME_MS 400
+
+/**
+ * iwl_mld_handle_session_prot_notif - handles %SESSION_PROTECTION_NOTIF
+ * @mld: the mld component
+ * @pkt: the RX packet containing the notification
+ */
+void iwl_mld_handle_session_prot_notif(struct iwl_mld *mld,
+ struct iwl_rx_packet *pkt);
+
+/**
+ * iwl_mld_schedule_session_protection - schedule a session protection
+ * @mld: the mld component
+ * @vif: the virtual interface for which the protection issued
+ * @duration: the requested duration of the protection
+ * @min_duration: the minimum duration of the protection
+ * @link_id: The link to schedule a session protection for
+ */
+void iwl_mld_schedule_session_protection(struct iwl_mld *mld,
+ struct ieee80211_vif *vif,
+ u32 duration, u32 min_duration,
+ int link_id);
+
+/**
+ * iwl_mld_start_session_protection - start a session protection
+ * @mld: the mld component
+ * @vif: the virtual interface for which the protection issued
+ * @duration: the requested duration of the protection
+ * @min_duration: the minimum duration of the protection
+ * @link_id: The link to schedule a session protection for
+ * @timeout: timeout for waiting
+ *
+ * This schedules the session protection, and waits for it to start
+ * (with timeout)
+ *
+ * Returns: 0 if successful, error code otherwise
+ */
+int iwl_mld_start_session_protection(struct iwl_mld *mld,
+ struct ieee80211_vif *vif,
+ u32 duration, u32 min_duration,
+ int link_id, unsigned long timeout);
+
+/**
+ * iwl_mld_cancel_session_protection - cancel the session protection.
+ * @mld: the mld component
+ * @vif: the virtual interface for which the session is issued
+ * @link_id: cancel the session protection for given link
+ *
+ * This functions cancels the session protection which is an act of good
+ * citizenship. If it is not needed any more it should be canceled because
+ * the other mac contexts wait for the medium during that time.
+ *
+ * Returns: 0 if successful, error code otherwise
+ *
+ */
+int iwl_mld_cancel_session_protection(struct iwl_mld *mld,
+ struct ieee80211_vif *vif,
+ int link_id);
+
+#endif /* __session_protect_h__ */
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/sta.c b/drivers/net/wireless/intel/iwlwifi/mld/sta.c
new file mode 100644
index 000000000000..8fb51209b4a6
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/mld/sta.c
@@ -0,0 +1,1321 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
+/*
+ * Copyright (C) 2024-2025 Intel Corporation
+ */
+
+#include <linux/ieee80211.h>
+#include <kunit/static_stub.h>
+
+#include "sta.h"
+#include "hcmd.h"
+#include "iface.h"
+#include "mlo.h"
+#include "key.h"
+#include "agg.h"
+#include "tlc.h"
+#include "fw/api/sta.h"
+#include "fw/api/mac.h"
+#include "fw/api/rx.h"
+
+int iwl_mld_fw_sta_id_from_link_sta(struct iwl_mld *mld,
+ struct ieee80211_link_sta *link_sta)
+{
+ struct iwl_mld_link_sta *mld_link_sta;
+
+ /* This function should only be used with the wiphy lock held,
+ * In other cases, it is not guaranteed that the link_sta will exist
+ * in the driver too, and it is checked here.
+ */
+ lockdep_assert_wiphy(mld->wiphy);
+
+ /* This is not meant to be called with a NULL pointer */
+ if (WARN_ON(!link_sta))
+ return -ENOENT;
+
+ mld_link_sta = iwl_mld_link_sta_from_mac80211(link_sta);
+ if (!mld_link_sta) {
+ WARN_ON(!iwl_mld_error_before_recovery(mld));
+ return -ENOENT;
+ }
+
+ return mld_link_sta->fw_id;
+}
+
+static void
+iwl_mld_fill_ampdu_size_and_dens(struct ieee80211_link_sta *link_sta,
+ struct ieee80211_bss_conf *link,
+ __le32 *tx_ampdu_max_size,
+ __le32 *tx_ampdu_spacing)
+{
+ u32 agg_size = 0, mpdu_dens = 0;
+
+ if (WARN_ON(!link_sta || !link))
+ return;
+
+ /* Note that we always use only legacy & highest supported PPDUs, so
+ * of Draft P802.11be D.30 Table 10-12a--Fields used for calculating
+ * the maximum A-MPDU size of various PPDU types in different bands,
+ * we only need to worry about the highest supported PPDU type here.
+ */
+
+ if (link_sta->ht_cap.ht_supported) {
+ agg_size = link_sta->ht_cap.ampdu_factor;
+ mpdu_dens = link_sta->ht_cap.ampdu_density;
+ }
+
+ if (link->chanreq.oper.chan->band == NL80211_BAND_6GHZ) {
+ /* overwrite HT values on 6 GHz */
+ mpdu_dens =
+ le16_get_bits(link_sta->he_6ghz_capa.capa,
+ IEEE80211_HE_6GHZ_CAP_MIN_MPDU_START);
+ agg_size =
+ le16_get_bits(link_sta->he_6ghz_capa.capa,
+ IEEE80211_HE_6GHZ_CAP_MAX_AMPDU_LEN_EXP);
+ } else if (link_sta->vht_cap.vht_supported) {
+ /* if VHT supported overwrite HT value */
+ agg_size =
+ u32_get_bits(link_sta->vht_cap.cap,
+ IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK);
+ }
+
+ /* D6.0 10.12.2 A-MPDU length limit rules
+ * A STA indicates the maximum length of the A-MPDU preEOF padding
+ * that it can receive in an HE PPDU in the Maximum A-MPDU Length
+ * Exponent field in its HT Capabilities, VHT Capabilities,
+ * and HE 6 GHz Band Capabilities elements (if present) and the
+ * Maximum AMPDU Length Exponent Extension field in its HE
+ * Capabilities element
+ */
+ if (link_sta->he_cap.has_he)
+ agg_size +=
+ u8_get_bits(link_sta->he_cap.he_cap_elem.mac_cap_info[3],
+ IEEE80211_HE_MAC_CAP3_MAX_AMPDU_LEN_EXP_MASK);
+
+ if (link_sta->eht_cap.has_eht)
+ agg_size +=
+ u8_get_bits(link_sta->eht_cap.eht_cap_elem.mac_cap_info[1],
+ IEEE80211_EHT_MAC_CAP1_MAX_AMPDU_LEN_MASK);
+
+ /* Limit to max A-MPDU supported by FW */
+ agg_size = min_t(u32, agg_size,
+ STA_FLG_MAX_AGG_SIZE_4M >> STA_FLG_MAX_AGG_SIZE_SHIFT);
+
+ *tx_ampdu_max_size = cpu_to_le32(agg_size);
+ *tx_ampdu_spacing = cpu_to_le32(mpdu_dens);
+}
+
+static u8 iwl_mld_get_uapsd_acs(struct ieee80211_sta *sta)
+{
+ u8 uapsd_acs = 0;
+
+ if (sta->uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_BK)
+ uapsd_acs |= BIT(AC_BK);
+ if (sta->uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_BE)
+ uapsd_acs |= BIT(AC_BE);
+ if (sta->uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_VI)
+ uapsd_acs |= BIT(AC_VI);
+ if (sta->uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_VO)
+ uapsd_acs |= BIT(AC_VO);
+
+ return uapsd_acs | uapsd_acs << 4;
+}
+
+static u8 iwl_mld_he_get_ppe_val(u8 *ppe, u8 ppe_pos_bit)
+{
+ u8 byte_num = ppe_pos_bit / 8;
+ u8 bit_num = ppe_pos_bit % 8;
+ u8 residue_bits;
+ u8 res;
+
+ if (bit_num <= 5)
+ return (ppe[byte_num] >> bit_num) &
+ (BIT(IEEE80211_PPE_THRES_INFO_PPET_SIZE) - 1);
+
+ /* If bit_num > 5, we have to combine bits with next byte.
+ * Calculate how many bits we need to take from current byte (called
+ * here "residue_bits"), and add them to bits from next byte.
+ */
+
+ residue_bits = 8 - bit_num;
+
+ res = (ppe[byte_num + 1] &
+ (BIT(IEEE80211_PPE_THRES_INFO_PPET_SIZE - residue_bits) - 1)) <<
+ residue_bits;
+ res += (ppe[byte_num] >> bit_num) & (BIT(residue_bits) - 1);
+
+ return res;
+}
+
+static void iwl_mld_parse_ppe(struct iwl_mld *mld,
+ struct iwl_he_pkt_ext_v2 *pkt_ext, u8 nss,
+ u8 ru_index_bitmap, u8 *ppe, u8 ppe_pos_bit,
+ bool inheritance)
+{
+ /* FW currently supports only nss == MAX_HE_SUPP_NSS
+ *
+ * If nss > MAX: we can ignore values we don't support
+ * If nss < MAX: we can set zeros in other streams
+ */
+ if (nss > MAX_HE_SUPP_NSS) {
+ IWL_DEBUG_INFO(mld, "Got NSS = %d - trimming to %d\n", nss,
+ MAX_HE_SUPP_NSS);
+ nss = MAX_HE_SUPP_NSS;
+ }
+
+ for (int i = 0; i < nss; i++) {
+ u8 ru_index_tmp = ru_index_bitmap << 1;
+ u8 low_th = IWL_HE_PKT_EXT_NONE, high_th = IWL_HE_PKT_EXT_NONE;
+
+ for (u8 bw = 0;
+ bw < ARRAY_SIZE(pkt_ext->pkt_ext_qam_th[i]);
+ bw++) {
+ ru_index_tmp >>= 1;
+
+ /* According to the 11be spec, if for a specific BW the PPE Thresholds
+ * isn't present - it should inherit the thresholds from the last
+ * BW for which we had PPE Thresholds. In 11ax though, we don't have
+ * this inheritance - continue in this case
+ */
+ if (!(ru_index_tmp & 1)) {
+ if (inheritance)
+ goto set_thresholds;
+ else
+ continue;
+ }
+
+ high_th = iwl_mld_he_get_ppe_val(ppe, ppe_pos_bit);
+ ppe_pos_bit += IEEE80211_PPE_THRES_INFO_PPET_SIZE;
+ low_th = iwl_mld_he_get_ppe_val(ppe, ppe_pos_bit);
+ ppe_pos_bit += IEEE80211_PPE_THRES_INFO_PPET_SIZE;
+
+set_thresholds:
+ pkt_ext->pkt_ext_qam_th[i][bw][0] = low_th;
+ pkt_ext->pkt_ext_qam_th[i][bw][1] = high_th;
+ }
+ }
+}
+
+static void iwl_mld_set_pkt_ext_from_he_ppe(struct iwl_mld *mld,
+ struct ieee80211_link_sta *link_sta,
+ struct iwl_he_pkt_ext_v2 *pkt_ext,
+ bool inheritance)
+{
+ u8 nss = (link_sta->he_cap.ppe_thres[0] &
+ IEEE80211_PPE_THRES_NSS_MASK) + 1;
+ u8 *ppe = &link_sta->he_cap.ppe_thres[0];
+ u8 ru_index_bitmap =
+ u8_get_bits(*ppe,
+ IEEE80211_PPE_THRES_RU_INDEX_BITMASK_MASK);
+ /* Starting after PPE header */
+ u8 ppe_pos_bit = IEEE80211_HE_PPE_THRES_INFO_HEADER_SIZE;
+
+ iwl_mld_parse_ppe(mld, pkt_ext, nss, ru_index_bitmap, ppe, ppe_pos_bit,
+ inheritance);
+}
+
+static int
+iwl_mld_set_pkt_ext_from_nominal_padding(struct iwl_he_pkt_ext_v2 *pkt_ext,
+ u8 nominal_padding)
+{
+ int low_th = -1;
+ int high_th = -1;
+
+ /* all the macros are the same for EHT and HE */
+ switch (nominal_padding) {
+ case IEEE80211_EHT_PHY_CAP5_COMMON_NOMINAL_PKT_PAD_0US:
+ low_th = IWL_HE_PKT_EXT_NONE;
+ high_th = IWL_HE_PKT_EXT_NONE;
+ break;
+ case IEEE80211_EHT_PHY_CAP5_COMMON_NOMINAL_PKT_PAD_8US:
+ low_th = IWL_HE_PKT_EXT_BPSK;
+ high_th = IWL_HE_PKT_EXT_NONE;
+ break;
+ case IEEE80211_EHT_PHY_CAP5_COMMON_NOMINAL_PKT_PAD_16US:
+ case IEEE80211_EHT_PHY_CAP5_COMMON_NOMINAL_PKT_PAD_20US:
+ low_th = IWL_HE_PKT_EXT_NONE;
+ high_th = IWL_HE_PKT_EXT_BPSK;
+ break;
+ }
+
+ if (low_th < 0 || high_th < 0)
+ return -EINVAL;
+
+ /* Set the PPE thresholds accordingly */
+ for (int i = 0; i < MAX_HE_SUPP_NSS; i++) {
+ for (u8 bw = 0;
+ bw < ARRAY_SIZE(pkt_ext->pkt_ext_qam_th[i]);
+ bw++) {
+ pkt_ext->pkt_ext_qam_th[i][bw][0] = low_th;
+ pkt_ext->pkt_ext_qam_th[i][bw][1] = high_th;
+ }
+ }
+
+ return 0;
+}
+
+static void iwl_mld_get_optimal_ppe_info(struct iwl_he_pkt_ext_v2 *pkt_ext,
+ u8 nominal_padding)
+{
+ for (int i = 0; i < MAX_HE_SUPP_NSS; i++) {
+ for (u8 bw = 0; bw < ARRAY_SIZE(pkt_ext->pkt_ext_qam_th[i]);
+ bw++) {
+ u8 *qam_th = &pkt_ext->pkt_ext_qam_th[i][bw][0];
+
+ if (nominal_padding >
+ IEEE80211_EHT_PHY_CAP5_COMMON_NOMINAL_PKT_PAD_8US &&
+ qam_th[1] == IWL_HE_PKT_EXT_NONE)
+ qam_th[1] = IWL_HE_PKT_EXT_4096QAM;
+ else if (nominal_padding ==
+ IEEE80211_EHT_PHY_CAP5_COMMON_NOMINAL_PKT_PAD_8US &&
+ qam_th[0] == IWL_HE_PKT_EXT_NONE &&
+ qam_th[1] == IWL_HE_PKT_EXT_NONE)
+ qam_th[0] = IWL_HE_PKT_EXT_4096QAM;
+ }
+ }
+}
+
+static void iwl_mld_fill_pkt_ext(struct iwl_mld *mld,
+ struct ieee80211_link_sta *link_sta,
+ struct iwl_he_pkt_ext_v2 *pkt_ext)
+{
+ if (WARN_ON(!link_sta))
+ return;
+
+ /* Initialize the PPE thresholds to "None" (7), as described in Table
+ * 9-262ac of 80211.ax/D3.0.
+ */
+ memset(pkt_ext, IWL_HE_PKT_EXT_NONE, sizeof(*pkt_ext));
+
+ if (link_sta->eht_cap.has_eht) {
+ u8 nominal_padding =
+ u8_get_bits(link_sta->eht_cap.eht_cap_elem.phy_cap_info[5],
+ IEEE80211_EHT_PHY_CAP5_COMMON_NOMINAL_PKT_PAD_MASK);
+
+ /* If PPE Thresholds exists, parse them into a FW-familiar
+ * format.
+ */
+ if (link_sta->eht_cap.eht_cap_elem.phy_cap_info[5] &
+ IEEE80211_EHT_PHY_CAP5_PPE_THRESHOLD_PRESENT) {
+ u8 nss = (link_sta->eht_cap.eht_ppe_thres[0] &
+ IEEE80211_EHT_PPE_THRES_NSS_MASK) + 1;
+ u8 *ppe = &link_sta->eht_cap.eht_ppe_thres[0];
+ u8 ru_index_bitmap =
+ u16_get_bits(*ppe,
+ IEEE80211_EHT_PPE_THRES_RU_INDEX_BITMASK_MASK);
+ /* Starting after PPE header */
+ u8 ppe_pos_bit = IEEE80211_EHT_PPE_THRES_INFO_HEADER_SIZE;
+
+ iwl_mld_parse_ppe(mld, pkt_ext, nss, ru_index_bitmap,
+ ppe, ppe_pos_bit, true);
+ /* EHT PPE Thresholds doesn't exist - set the API according to
+ * HE PPE Tresholds
+ */
+ } else if (link_sta->he_cap.he_cap_elem.phy_cap_info[6] &
+ IEEE80211_HE_PHY_CAP6_PPE_THRESHOLD_PRESENT) {
+ /* Even though HE Capabilities IE doesn't contain PPE
+ * Thresholds for BW 320Mhz, thresholds for this BW will
+ * be filled in with the same values as 160Mhz, due to
+ * the inheritance, as required.
+ */
+ iwl_mld_set_pkt_ext_from_he_ppe(mld, link_sta, pkt_ext,
+ true);
+
+ /* According to the requirements, for MCSs 12-13 the
+ * maximum value between HE PPE Threshold and Common
+ * Nominal Packet Padding needs to be taken
+ */
+ iwl_mld_get_optimal_ppe_info(pkt_ext, nominal_padding);
+
+ /* if PPE Thresholds doesn't present in both EHT IE and HE IE -
+ * take the Thresholds from Common Nominal Packet Padding field
+ */
+ } else {
+ iwl_mld_set_pkt_ext_from_nominal_padding(pkt_ext,
+ nominal_padding);
+ }
+ } else if (link_sta->he_cap.has_he) {
+ /* If PPE Thresholds exist, parse them into a FW-familiar format. */
+ if (link_sta->he_cap.he_cap_elem.phy_cap_info[6] &
+ IEEE80211_HE_PHY_CAP6_PPE_THRESHOLD_PRESENT) {
+ iwl_mld_set_pkt_ext_from_he_ppe(mld, link_sta, pkt_ext,
+ false);
+ /* PPE Thresholds doesn't exist - set the API PPE values
+ * according to Common Nominal Packet Padding field.
+ */
+ } else {
+ u8 nominal_padding =
+ u8_get_bits(link_sta->he_cap.he_cap_elem.phy_cap_info[9],
+ IEEE80211_HE_PHY_CAP9_NOMINAL_PKT_PADDING_MASK);
+ if (nominal_padding != IEEE80211_HE_PHY_CAP9_NOMINAL_PKT_PADDING_RESERVED)
+ iwl_mld_set_pkt_ext_from_nominal_padding(pkt_ext,
+ nominal_padding);
+ }
+ }
+
+ for (int i = 0; i < MAX_HE_SUPP_NSS; i++) {
+ for (int bw = 0;
+ bw < ARRAY_SIZE(*pkt_ext->pkt_ext_qam_th[i]);
+ bw++) {
+ u8 *qam_th =
+ &pkt_ext->pkt_ext_qam_th[i][bw][0];
+
+ IWL_DEBUG_HT(mld,
+ "PPE table: nss[%d] bw[%d] PPET8 = %d, PPET16 = %d\n",
+ i, bw, qam_th[0], qam_th[1]);
+ }
+ }
+}
+
+static u32 iwl_mld_get_htc_flags(struct ieee80211_link_sta *link_sta)
+{
+ u8 *mac_cap_info =
+ &link_sta->he_cap.he_cap_elem.mac_cap_info[0];
+ u32 htc_flags = 0;
+
+ if (mac_cap_info[0] & IEEE80211_HE_MAC_CAP0_HTC_HE)
+ htc_flags |= IWL_HE_HTC_SUPPORT;
+ if ((mac_cap_info[1] & IEEE80211_HE_MAC_CAP1_LINK_ADAPTATION) ||
+ (mac_cap_info[2] & IEEE80211_HE_MAC_CAP2_LINK_ADAPTATION)) {
+ u8 link_adap =
+ ((mac_cap_info[2] &
+ IEEE80211_HE_MAC_CAP2_LINK_ADAPTATION) << 1) +
+ (mac_cap_info[1] &
+ IEEE80211_HE_MAC_CAP1_LINK_ADAPTATION);
+
+ if (link_adap == 2)
+ htc_flags |=
+ IWL_HE_HTC_LINK_ADAP_UNSOLICITED;
+ else if (link_adap == 3)
+ htc_flags |= IWL_HE_HTC_LINK_ADAP_BOTH;
+ }
+ if (mac_cap_info[2] & IEEE80211_HE_MAC_CAP2_BSR)
+ htc_flags |= IWL_HE_HTC_BSR_SUPP;
+ if (mac_cap_info[3] & IEEE80211_HE_MAC_CAP3_OMI_CONTROL)
+ htc_flags |= IWL_HE_HTC_OMI_SUPP;
+ if (mac_cap_info[4] & IEEE80211_HE_MAC_CAP4_BQR)
+ htc_flags |= IWL_HE_HTC_BQR_SUPP;
+
+ return htc_flags;
+}
+
+static int iwl_mld_send_sta_cmd(struct iwl_mld *mld,
+ const struct iwl_sta_cfg_cmd *cmd)
+{
+ u32 cmd_id = WIDE_ID(MAC_CONF_GROUP, STA_CONFIG_CMD);
+ int cmd_len = iwl_fw_lookup_cmd_ver(mld->fw, cmd_id, 0) > 1 ?
+ sizeof(*cmd) :
+ sizeof(struct iwl_sta_cfg_cmd_v1);
+ int ret = iwl_mld_send_cmd_pdu(mld, cmd_id, cmd, cmd_len);
+ if (ret)
+ IWL_ERR(mld, "STA_CONFIG_CMD send failed, ret=0x%x\n", ret);
+ return ret;
+}
+
+static int
+iwl_mld_add_modify_sta_cmd(struct iwl_mld *mld,
+ struct ieee80211_link_sta *link_sta)
+{
+ struct ieee80211_sta *sta = link_sta->sta;
+ struct iwl_mld_sta *mld_sta = iwl_mld_sta_from_mac80211(sta);
+ struct ieee80211_bss_conf *link;
+ struct iwl_mld_link *mld_link;
+ struct iwl_sta_cfg_cmd cmd = {};
+ int fw_id = iwl_mld_fw_sta_id_from_link_sta(mld, link_sta);
+
+ lockdep_assert_wiphy(mld->wiphy);
+
+ link = link_conf_dereference_protected(mld_sta->vif,
+ link_sta->link_id);
+
+ mld_link = iwl_mld_link_from_mac80211(link);
+
+ if (WARN_ON(!link || !mld_link) || fw_id < 0)
+ return -EINVAL;
+
+ cmd.sta_id = cpu_to_le32(fw_id);
+ cmd.station_type = cpu_to_le32(mld_sta->sta_type);
+ cmd.link_id = cpu_to_le32(mld_link->fw_id);
+
+ memcpy(&cmd.peer_mld_address, sta->addr, ETH_ALEN);
+ memcpy(&cmd.peer_link_address, link_sta->addr, ETH_ALEN);
+
+ if (mld_sta->sta_state >= IEEE80211_STA_ASSOC)
+ cmd.assoc_id = cpu_to_le32(sta->aid);
+
+ if (sta->mfp || mld_sta->sta_state < IEEE80211_STA_AUTHORIZED)
+ cmd.mfp = cpu_to_le32(1);
+
+ switch (link_sta->rx_nss) {
+ case 1:
+ cmd.mimo = cpu_to_le32(0);
+ break;
+ case 2 ... 8:
+ cmd.mimo = cpu_to_le32(1);
+ break;
+ }
+
+ switch (link_sta->smps_mode) {
+ case IEEE80211_SMPS_AUTOMATIC:
+ case IEEE80211_SMPS_NUM_MODES:
+ WARN_ON(1);
+ break;
+ case IEEE80211_SMPS_STATIC:
+ /* override NSS */
+ cmd.mimo = cpu_to_le32(0);
+ break;
+ case IEEE80211_SMPS_DYNAMIC:
+ cmd.mimo_protection = cpu_to_le32(1);
+ break;
+ case IEEE80211_SMPS_OFF:
+ /* nothing */
+ break;
+ }
+
+ iwl_mld_fill_ampdu_size_and_dens(link_sta, link,
+ &cmd.tx_ampdu_max_size,
+ &cmd.tx_ampdu_spacing);
+
+ if (sta->wme) {
+ cmd.sp_length =
+ cpu_to_le32(sta->max_sp ? sta->max_sp * 2 : 128);
+ cmd.uapsd_acs = cpu_to_le32(iwl_mld_get_uapsd_acs(sta));
+ }
+
+ if (link_sta->he_cap.has_he) {
+ cmd.trig_rnd_alloc =
+ cpu_to_le32(link->uora_exists ? 1 : 0);
+
+ /* PPE Thresholds */
+ iwl_mld_fill_pkt_ext(mld, link_sta, &cmd.pkt_ext);
+
+ /* HTC flags */
+ cmd.htc_flags =
+ cpu_to_le32(iwl_mld_get_htc_flags(link_sta));
+
+ if (link_sta->he_cap.he_cap_elem.mac_cap_info[2] &
+ IEEE80211_HE_MAC_CAP2_ACK_EN)
+ cmd.ack_enabled = cpu_to_le32(1);
+ }
+
+ return iwl_mld_send_sta_cmd(mld, &cmd);
+}
+
+IWL_MLD_ALLOC_FN(link_sta, link_sta)
+
+static int
+iwl_mld_add_link_sta(struct iwl_mld *mld, struct ieee80211_link_sta *link_sta)
+{
+ struct iwl_mld_sta *mld_sta = iwl_mld_sta_from_mac80211(link_sta->sta);
+ struct iwl_mld_link_sta *mld_link_sta;
+ int ret;
+ u8 fw_id;
+
+ lockdep_assert_wiphy(mld->wiphy);
+
+ /* We will fail to add it to the FW anyway */
+ if (iwl_mld_error_before_recovery(mld))
+ return -ENODEV;
+
+ mld_link_sta = iwl_mld_link_sta_from_mac80211(link_sta);
+
+ /* We need to preserve the fw sta ids during a restart, since the fw
+ * will recover SN/PN for them, this is why the mld_link_sta exists.
+ */
+ if (mld_link_sta) {
+ /* But if we are not restarting, this is not OK */
+ WARN_ON(!mld->fw_status.in_hw_restart);
+
+ /* Avoid adding a STA that is already in FW to avoid an assert */
+ if (WARN_ON(mld_link_sta->in_fw))
+ return -EINVAL;
+
+ fw_id = mld_link_sta->fw_id;
+ goto add_to_fw;
+ }
+
+ /* Allocate a fw id and map it to the link_sta */
+ ret = iwl_mld_allocate_link_sta_fw_id(mld, &fw_id, link_sta);
+ if (ret)
+ return ret;
+
+ if (link_sta == &link_sta->sta->deflink) {
+ mld_link_sta = &mld_sta->deflink;
+ } else {
+ mld_link_sta = kzalloc(sizeof(*mld_link_sta), GFP_KERNEL);
+ if (!mld_link_sta)
+ return -ENOMEM;
+ }
+
+ mld_link_sta->fw_id = fw_id;
+ rcu_assign_pointer(mld_sta->link[link_sta->link_id], mld_link_sta);
+
+add_to_fw:
+ ret = iwl_mld_add_modify_sta_cmd(mld, link_sta);
+ if (ret) {
+ RCU_INIT_POINTER(mld->fw_id_to_link_sta[fw_id], NULL);
+ RCU_INIT_POINTER(mld_sta->link[link_sta->link_id], NULL);
+ if (link_sta != &link_sta->sta->deflink)
+ kfree(mld_link_sta);
+ return ret;
+ }
+ mld_link_sta->in_fw = true;
+
+ return 0;
+}
+
+static int iwl_mld_rm_sta_from_fw(struct iwl_mld *mld, u8 fw_sta_id)
+{
+ struct iwl_remove_sta_cmd cmd = {
+ .sta_id = cpu_to_le32(fw_sta_id),
+ };
+ int ret;
+
+ ret = iwl_mld_send_cmd_pdu(mld,
+ WIDE_ID(MAC_CONF_GROUP, STA_REMOVE_CMD),
+ &cmd);
+ if (ret)
+ IWL_ERR(mld, "Failed to remove station. Id=%d\n", fw_sta_id);
+
+ return ret;
+}
+
+static void
+iwl_mld_remove_link_sta(struct iwl_mld *mld,
+ struct ieee80211_link_sta *link_sta)
+{
+ struct iwl_mld_sta *mld_sta = iwl_mld_sta_from_mac80211(link_sta->sta);
+ struct iwl_mld_link_sta *mld_link_sta =
+ iwl_mld_link_sta_from_mac80211(link_sta);
+
+ if (WARN_ON(!mld_link_sta))
+ return;
+
+ iwl_mld_rm_sta_from_fw(mld, mld_link_sta->fw_id);
+ mld_link_sta->in_fw = false;
+
+ /* Now that the STA doesn't exist in FW, we don't expect any new
+ * notifications for it. Cancel the ones that are already pending
+ */
+ iwl_mld_cancel_notifications_of_object(mld, IWL_MLD_OBJECT_TYPE_STA,
+ mld_link_sta->fw_id);
+
+ /* This will not be done upon reconfig, so do it also when
+ * failed to remove from fw
+ */
+ RCU_INIT_POINTER(mld->fw_id_to_link_sta[mld_link_sta->fw_id], NULL);
+ RCU_INIT_POINTER(mld_sta->link[link_sta->link_id], NULL);
+ if (mld_link_sta != &mld_sta->deflink)
+ kfree_rcu(mld_link_sta, rcu_head);
+}
+
+static void iwl_mld_set_max_amsdu_len(struct iwl_mld *mld,
+ struct ieee80211_link_sta *link_sta)
+{
+ const struct ieee80211_sta_ht_cap *ht_cap = &link_sta->ht_cap;
+
+ /* For EHT, HE and VHT we can use the value as it was calculated by
+ * mac80211. For HT, mac80211 doesn't enforce to 4095, so force it
+ * here
+ */
+ if (link_sta->eht_cap.has_eht || link_sta->he_cap.has_he ||
+ link_sta->vht_cap.vht_supported ||
+ !ht_cap->ht_supported ||
+ !(ht_cap->cap & IEEE80211_HT_CAP_MAX_AMSDU))
+ return;
+
+ link_sta->agg.max_amsdu_len = IEEE80211_MAX_MPDU_LEN_HT_BA;
+ ieee80211_sta_recalc_aggregates(link_sta->sta);
+}
+
+int iwl_mld_update_all_link_stations(struct iwl_mld *mld,
+ struct ieee80211_sta *sta)
+{
+ struct iwl_mld_sta *mld_sta = iwl_mld_sta_from_mac80211(sta);
+ struct ieee80211_link_sta *link_sta;
+ int link_id;
+
+ for_each_sta_active_link(mld_sta->vif, sta, link_sta, link_id) {
+ int ret = iwl_mld_add_modify_sta_cmd(mld, link_sta);
+
+ if (ret)
+ return ret;
+
+ if (mld_sta->sta_state == IEEE80211_STA_ASSOC)
+ iwl_mld_set_max_amsdu_len(mld, link_sta);
+ }
+ return 0;
+}
+
+static void iwl_mld_destroy_sta(struct ieee80211_sta *sta)
+{
+ struct iwl_mld_sta *mld_sta = iwl_mld_sta_from_mac80211(sta);
+
+ kfree(mld_sta->dup_data);
+ kfree(mld_sta->mpdu_counters);
+}
+
+static int
+iwl_mld_alloc_dup_data(struct iwl_mld *mld, struct iwl_mld_sta *mld_sta)
+{
+ struct iwl_mld_rxq_dup_data *dup_data;
+
+ if (mld->fw_status.in_hw_restart)
+ return 0;
+
+ dup_data = kcalloc(mld->trans->info.num_rxqs, sizeof(*dup_data),
+ GFP_KERNEL);
+ if (!dup_data)
+ return -ENOMEM;
+
+ /* Initialize all the last_seq values to 0xffff which can never
+ * compare equal to the frame's seq_ctrl in the check in
+ * iwl_mld_is_dup() since the lower 4 bits are the fragment
+ * number and fragmented packets don't reach that function.
+ *
+ * This thus allows receiving a packet with seqno 0 and the
+ * retry bit set as the very first packet on a new TID.
+ */
+ for (int q = 0; q < mld->trans->info.num_rxqs; q++)
+ memset(dup_data[q].last_seq, 0xff,
+ sizeof(dup_data[q].last_seq));
+ mld_sta->dup_data = dup_data;
+
+ return 0;
+}
+
+static void iwl_mld_alloc_mpdu_counters(struct iwl_mld *mld,
+ struct ieee80211_sta *sta)
+{
+ struct iwl_mld_sta *mld_sta = iwl_mld_sta_from_mac80211(sta);
+ struct ieee80211_vif *vif = mld_sta->vif;
+
+ if (mld->fw_status.in_hw_restart)
+ return;
+
+ /* MPDUs are counted only when EMLSR is possible */
+ if (ieee80211_vif_type_p2p(vif) != NL80211_IFTYPE_STATION ||
+ sta->tdls || !ieee80211_vif_is_mld(vif))
+ return;
+
+ mld_sta->mpdu_counters = kcalloc(mld->trans->info.num_rxqs,
+ sizeof(*mld_sta->mpdu_counters),
+ GFP_KERNEL);
+ if (!mld_sta->mpdu_counters)
+ return;
+
+ for (int q = 0; q < mld->trans->info.num_rxqs; q++)
+ spin_lock_init(&mld_sta->mpdu_counters[q].lock);
+}
+
+static int
+iwl_mld_init_sta(struct iwl_mld *mld, struct ieee80211_sta *sta,
+ struct ieee80211_vif *vif, enum iwl_fw_sta_type type)
+{
+ struct iwl_mld_sta *mld_sta = iwl_mld_sta_from_mac80211(sta);
+
+ mld_sta->vif = vif;
+ mld_sta->sta_type = type;
+ mld_sta->mld = mld;
+
+ if (!mld->fw_status.in_hw_restart)
+ for (int i = 0; i < ARRAY_SIZE(sta->txq); i++)
+ iwl_mld_init_txq(iwl_mld_txq_from_mac80211(sta->txq[i]));
+
+ iwl_mld_alloc_mpdu_counters(mld, sta);
+
+ iwl_mld_toggle_tx_ant(mld, &mld_sta->data_tx_ant);
+
+ return iwl_mld_alloc_dup_data(mld, mld_sta);
+}
+
+int iwl_mld_add_sta(struct iwl_mld *mld, struct ieee80211_sta *sta,
+ struct ieee80211_vif *vif, enum iwl_fw_sta_type type)
+{
+ struct iwl_mld_sta *mld_sta = iwl_mld_sta_from_mac80211(sta);
+ struct ieee80211_link_sta *link_sta;
+ int link_id;
+ int ret;
+
+ ret = iwl_mld_init_sta(mld, sta, vif, type);
+ if (ret)
+ return ret;
+
+ /* We could have add only the deflink link_sta, but it will not work
+ * in the restart case if the single link that is active during
+ * reconfig is not the deflink one.
+ */
+ for_each_sta_active_link(mld_sta->vif, sta, link_sta, link_id) {
+ ret = iwl_mld_add_link_sta(mld, link_sta);
+ if (ret)
+ goto destroy_sta;
+ }
+
+ return 0;
+
+destroy_sta:
+ iwl_mld_destroy_sta(sta);
+
+ return ret;
+}
+
+void iwl_mld_flush_sta_txqs(struct iwl_mld *mld, struct ieee80211_sta *sta)
+{
+ struct iwl_mld_sta *mld_sta = iwl_mld_sta_from_mac80211(sta);
+ struct ieee80211_link_sta *link_sta;
+ int link_id;
+
+ for_each_sta_active_link(mld_sta->vif, sta, link_sta, link_id) {
+ int fw_sta_id = iwl_mld_fw_sta_id_from_link_sta(mld, link_sta);
+
+ if (fw_sta_id < 0)
+ continue;
+
+ iwl_mld_flush_link_sta_txqs(mld, fw_sta_id);
+ }
+}
+
+void iwl_mld_wait_sta_txqs_empty(struct iwl_mld *mld, struct ieee80211_sta *sta)
+{
+ /* Avoid a warning in iwl_trans_wait_txq_empty if are anyway on the way
+ * to a restart.
+ */
+ if (iwl_mld_error_before_recovery(mld))
+ return;
+
+ for (int i = 0; i < ARRAY_SIZE(sta->txq); i++) {
+ struct iwl_mld_txq *mld_txq =
+ iwl_mld_txq_from_mac80211(sta->txq[i]);
+
+ if (!mld_txq->status.allocated)
+ continue;
+
+ iwl_trans_wait_txq_empty(mld->trans, mld_txq->fw_id);
+ }
+}
+
+void iwl_mld_remove_sta(struct iwl_mld *mld, struct ieee80211_sta *sta)
+{
+ struct iwl_mld_sta *mld_sta = iwl_mld_sta_from_mac80211(sta);
+ struct ieee80211_vif *vif = mld_sta->vif;
+ struct ieee80211_link_sta *link_sta;
+ u8 link_id;
+
+ lockdep_assert_wiphy(mld->wiphy);
+
+ /* Tell the HW to flush the queues */
+ iwl_mld_flush_sta_txqs(mld, sta);
+
+ /* Wait for trans to empty its queues */
+ iwl_mld_wait_sta_txqs_empty(mld, sta);
+
+ /* Now we can remove the queues */
+ for (int i = 0; i < ARRAY_SIZE(sta->txq); i++)
+ iwl_mld_remove_txq(mld, sta->txq[i]);
+
+ for_each_sta_active_link(vif, sta, link_sta, link_id) {
+ /* Mac8011 will remove the groupwise keys after the sta is
+ * removed, but FW expects all the keys to be removed before
+ * the STA is, so remove them all here.
+ */
+ if (vif->type == NL80211_IFTYPE_STATION && !sta->tdls)
+ iwl_mld_remove_ap_keys(mld, vif, sta, link_id);
+
+ /* Remove the link_sta */
+ iwl_mld_remove_link_sta(mld, link_sta);
+ }
+
+ iwl_mld_destroy_sta(sta);
+}
+
+u32 iwl_mld_fw_sta_id_mask(struct iwl_mld *mld, struct ieee80211_sta *sta)
+{
+ struct ieee80211_vif *vif = iwl_mld_sta_from_mac80211(sta)->vif;
+ struct ieee80211_link_sta *link_sta;
+ unsigned int link_id;
+ u32 result = 0;
+
+ KUNIT_STATIC_STUB_REDIRECT(iwl_mld_fw_sta_id_mask, mld, sta);
+
+ /* This function should only be used with the wiphy lock held,
+ * In other cases, it is not guaranteed that the link_sta will exist
+ * in the driver too, and it is checked in
+ * iwl_mld_fw_sta_id_from_link_sta.
+ */
+ lockdep_assert_wiphy(mld->wiphy);
+
+ for_each_sta_active_link(vif, sta, link_sta, link_id) {
+ int fw_id = iwl_mld_fw_sta_id_from_link_sta(mld, link_sta);
+
+ if (!(fw_id < 0))
+ result |= BIT(fw_id);
+ }
+
+ return result;
+}
+EXPORT_SYMBOL_IF_IWLWIFI_KUNIT(iwl_mld_fw_sta_id_mask);
+
+static void iwl_mld_count_mpdu(struct ieee80211_link_sta *link_sta, int queue,
+ u32 count, bool tx)
+{
+ struct iwl_mld_per_q_mpdu_counter *queue_counter;
+ struct iwl_mld_per_link_mpdu_counter *link_counter;
+ struct iwl_mld_vif *mld_vif;
+ struct iwl_mld_sta *mld_sta;
+ struct iwl_mld_link *mld_link;
+ struct iwl_mld *mld;
+ int total_mpdus = 0;
+
+ if (WARN_ON(!link_sta))
+ return;
+
+ mld_sta = iwl_mld_sta_from_mac80211(link_sta->sta);
+ if (!mld_sta->mpdu_counters)
+ return;
+
+ mld_vif = iwl_mld_vif_from_mac80211(mld_sta->vif);
+ mld_link = iwl_mld_link_dereference_check(mld_vif, link_sta->link_id);
+
+ if (WARN_ON_ONCE(!mld_link))
+ return;
+
+ queue_counter = &mld_sta->mpdu_counters[queue];
+
+ mld = mld_vif->mld;
+
+ /* If it the window is over, first clear the counters.
+ * When we are not blocked by TPT, the window is managed by check_tpt_wk
+ */
+ if ((mld_vif->emlsr.blocked_reasons & IWL_MLD_EMLSR_BLOCKED_TPT) &&
+ time_is_before_jiffies(queue_counter->window_start_time +
+ IWL_MLD_TPT_COUNT_WINDOW)) {
+ memset(queue_counter->per_link, 0,
+ sizeof(queue_counter->per_link));
+ queue_counter->window_start_time = jiffies;
+
+ IWL_DEBUG_INFO(mld, "MPDU counters are cleared\n");
+ }
+
+ link_counter = &queue_counter->per_link[mld_link->fw_id];
+
+ spin_lock_bh(&queue_counter->lock);
+
+ /* Update the statistics for this TPT measurement window */
+ if (tx)
+ link_counter->tx += count;
+ else
+ link_counter->rx += count;
+
+ /*
+ * Next, evaluate whether we should queue an unblock,
+ * skip this if we are not blocked due to low throughput.
+ */
+ if (!(mld_vif->emlsr.blocked_reasons & IWL_MLD_EMLSR_BLOCKED_TPT))
+ goto unlock;
+
+ for (int i = 0; i <= IWL_FW_MAX_LINK_ID; i++)
+ total_mpdus += tx ? queue_counter->per_link[i].tx :
+ queue_counter->per_link[i].rx;
+
+ /* Unblock is already queued if the threshold was reached before */
+ if (total_mpdus - count >= IWL_MLD_ENTER_EMLSR_TPT_THRESH)
+ goto unlock;
+
+ if (total_mpdus >= IWL_MLD_ENTER_EMLSR_TPT_THRESH)
+ wiphy_work_queue(mld->wiphy, &mld_vif->emlsr.unblock_tpt_wk);
+
+unlock:
+ spin_unlock_bh(&queue_counter->lock);
+}
+
+/* must be called under rcu_read_lock() */
+void iwl_mld_count_mpdu_rx(struct ieee80211_link_sta *link_sta, int queue,
+ u32 count)
+{
+ iwl_mld_count_mpdu(link_sta, queue, count, false);
+}
+
+/* must be called under rcu_read_lock() */
+void iwl_mld_count_mpdu_tx(struct ieee80211_link_sta *link_sta, u32 count)
+{
+ /* use queue 0 for all TX */
+ iwl_mld_count_mpdu(link_sta, 0, count, true);
+}
+
+static int iwl_mld_allocate_internal_txq(struct iwl_mld *mld,
+ struct iwl_mld_int_sta *internal_sta,
+ u8 tid)
+{
+ u32 sta_mask = BIT(internal_sta->sta_id);
+ int queue, size;
+
+ size = max_t(u32, IWL_MGMT_QUEUE_SIZE,
+ mld->trans->mac_cfg->base->min_txq_size);
+
+ queue = iwl_trans_txq_alloc(mld->trans, 0, sta_mask, tid, size,
+ IWL_WATCHDOG_DISABLED);
+
+ if (queue >= 0)
+ IWL_DEBUG_TX_QUEUES(mld,
+ "Enabling TXQ #%d for sta mask 0x%x tid %d\n",
+ queue, sta_mask, tid);
+ return queue;
+}
+
+static int iwl_mld_send_aux_sta_cmd(struct iwl_mld *mld,
+ const struct iwl_mld_int_sta *internal_sta)
+{
+ struct iwl_aux_sta_cmd cmd = {
+ .sta_id = cpu_to_le32(internal_sta->sta_id),
+ /* TODO: CDB - properly set the lmac_id */
+ .lmac_id = cpu_to_le32(IWL_LMAC_24G_INDEX),
+ };
+
+ return iwl_mld_send_cmd_pdu(mld, WIDE_ID(MAC_CONF_GROUP, AUX_STA_CMD),
+ &cmd);
+}
+
+static int
+iwl_mld_add_internal_sta_to_fw(struct iwl_mld *mld,
+ const struct iwl_mld_int_sta *internal_sta,
+ u8 fw_link_id,
+ const u8 *addr)
+{
+ struct iwl_sta_cfg_cmd cmd = {};
+
+ if (internal_sta->sta_type == STATION_TYPE_AUX)
+ return iwl_mld_send_aux_sta_cmd(mld, internal_sta);
+
+ cmd.sta_id = cpu_to_le32((u8)internal_sta->sta_id);
+ cmd.link_id = cpu_to_le32(fw_link_id);
+ cmd.station_type = cpu_to_le32(internal_sta->sta_type);
+
+ /* FW doesn't allow to add a IGTK/BIGTK if the sta isn't marked as MFP.
+ * On the other hand, FW will never check this flag during RX since
+ * an AP/GO doesn't receive protected broadcast management frames.
+ * So, we can set it unconditionally.
+ */
+ if (internal_sta->sta_type == STATION_TYPE_BCAST_MGMT)
+ cmd.mfp = cpu_to_le32(1);
+
+ if (addr) {
+ memcpy(cmd.peer_mld_address, addr, ETH_ALEN);
+ memcpy(cmd.peer_link_address, addr, ETH_ALEN);
+ }
+
+ return iwl_mld_send_sta_cmd(mld, &cmd);
+}
+
+static int iwl_mld_add_internal_sta(struct iwl_mld *mld,
+ struct iwl_mld_int_sta *internal_sta,
+ enum iwl_fw_sta_type sta_type,
+ u8 fw_link_id, const u8 *addr, u8 tid)
+{
+ int ret, queue_id;
+
+ ret = iwl_mld_allocate_link_sta_fw_id(mld,
+ &internal_sta->sta_id,
+ ERR_PTR(-EINVAL));
+ if (ret)
+ return ret;
+
+ internal_sta->sta_type = sta_type;
+
+ ret = iwl_mld_add_internal_sta_to_fw(mld, internal_sta, fw_link_id,
+ addr);
+ if (ret)
+ goto err;
+
+ queue_id = iwl_mld_allocate_internal_txq(mld, internal_sta, tid);
+ if (queue_id < 0) {
+ iwl_mld_rm_sta_from_fw(mld, internal_sta->sta_id);
+ ret = queue_id;
+ goto err;
+ }
+
+ internal_sta->queue_id = queue_id;
+
+ return 0;
+err:
+ iwl_mld_free_internal_sta(mld, internal_sta);
+ return ret;
+}
+
+int iwl_mld_add_bcast_sta(struct iwl_mld *mld,
+ struct ieee80211_vif *vif,
+ struct ieee80211_bss_conf *link)
+{
+ struct iwl_mld_link *mld_link = iwl_mld_link_from_mac80211(link);
+ const u8 bcast_addr[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
+ const u8 *addr;
+
+ if (WARN_ON(!mld_link))
+ return -EINVAL;
+
+ if (WARN_ON(vif->type != NL80211_IFTYPE_AP &&
+ vif->type != NL80211_IFTYPE_ADHOC))
+ return -EINVAL;
+
+ addr = vif->type == NL80211_IFTYPE_ADHOC ? link->bssid : bcast_addr;
+
+ return iwl_mld_add_internal_sta(mld, &mld_link->bcast_sta,
+ STATION_TYPE_BCAST_MGMT,
+ mld_link->fw_id, addr,
+ IWL_MGMT_TID);
+}
+
+int iwl_mld_add_mcast_sta(struct iwl_mld *mld,
+ struct ieee80211_vif *vif,
+ struct ieee80211_bss_conf *link)
+{
+ struct iwl_mld_link *mld_link = iwl_mld_link_from_mac80211(link);
+ const u8 mcast_addr[] = {0x03, 0x00, 0x00, 0x00, 0x00, 0x00};
+
+ if (WARN_ON(!mld_link))
+ return -EINVAL;
+
+ if (WARN_ON(vif->type != NL80211_IFTYPE_AP &&
+ vif->type != NL80211_IFTYPE_ADHOC))
+ return -EINVAL;
+
+ return iwl_mld_add_internal_sta(mld, &mld_link->mcast_sta,
+ STATION_TYPE_MCAST,
+ mld_link->fw_id, mcast_addr, 0);
+}
+
+int iwl_mld_add_aux_sta(struct iwl_mld *mld,
+ struct iwl_mld_int_sta *internal_sta)
+{
+ return iwl_mld_add_internal_sta(mld, internal_sta, STATION_TYPE_AUX,
+ 0, NULL, IWL_MAX_TID_COUNT);
+}
+
+int iwl_mld_add_mon_sta(struct iwl_mld *mld,
+ struct ieee80211_vif *vif,
+ struct ieee80211_bss_conf *link)
+{
+ struct iwl_mld_link *mld_link = iwl_mld_link_from_mac80211(link);
+
+ if (WARN_ON(!mld_link))
+ return -EINVAL;
+
+ if (WARN_ON(vif->type != NL80211_IFTYPE_MONITOR))
+ return -EINVAL;
+
+ return iwl_mld_add_internal_sta(mld, &mld_link->mon_sta,
+ STATION_TYPE_BCAST_MGMT,
+ mld_link->fw_id, NULL,
+ IWL_MAX_TID_COUNT);
+}
+
+static void iwl_mld_remove_internal_sta(struct iwl_mld *mld,
+ struct iwl_mld_int_sta *internal_sta,
+ bool flush, u8 tid)
+{
+ if (WARN_ON_ONCE(internal_sta->sta_id == IWL_INVALID_STA ||
+ internal_sta->queue_id == IWL_MLD_INVALID_QUEUE))
+ return;
+
+ if (flush)
+ iwl_mld_flush_link_sta_txqs(mld, internal_sta->sta_id);
+
+ iwl_mld_free_txq(mld, BIT(internal_sta->sta_id),
+ tid, internal_sta->queue_id);
+
+ iwl_mld_rm_sta_from_fw(mld, internal_sta->sta_id);
+
+ iwl_mld_free_internal_sta(mld, internal_sta);
+}
+
+void iwl_mld_remove_bcast_sta(struct iwl_mld *mld,
+ struct ieee80211_vif *vif,
+ struct ieee80211_bss_conf *link)
+{
+ struct iwl_mld_link *mld_link = iwl_mld_link_from_mac80211(link);
+
+ if (WARN_ON(!mld_link))
+ return;
+
+ if (WARN_ON(vif->type != NL80211_IFTYPE_AP &&
+ vif->type != NL80211_IFTYPE_ADHOC))
+ return;
+
+ iwl_mld_remove_internal_sta(mld, &mld_link->bcast_sta, true,
+ IWL_MGMT_TID);
+}
+
+void iwl_mld_remove_mcast_sta(struct iwl_mld *mld,
+ struct ieee80211_vif *vif,
+ struct ieee80211_bss_conf *link)
+{
+ struct iwl_mld_link *mld_link = iwl_mld_link_from_mac80211(link);
+
+ if (WARN_ON(!mld_link))
+ return;
+
+ if (WARN_ON(vif->type != NL80211_IFTYPE_AP &&
+ vif->type != NL80211_IFTYPE_ADHOC))
+ return;
+
+ iwl_mld_remove_internal_sta(mld, &mld_link->mcast_sta, true, 0);
+}
+
+void iwl_mld_remove_aux_sta(struct iwl_mld *mld,
+ struct ieee80211_vif *vif)
+{
+ struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
+
+ if (WARN_ON(vif->type != NL80211_IFTYPE_P2P_DEVICE &&
+ vif->type != NL80211_IFTYPE_STATION))
+ return;
+
+ iwl_mld_remove_internal_sta(mld, &mld_vif->aux_sta, false,
+ IWL_MAX_TID_COUNT);
+}
+
+void iwl_mld_remove_mon_sta(struct iwl_mld *mld,
+ struct ieee80211_vif *vif,
+ struct ieee80211_bss_conf *link)
+{
+ struct iwl_mld_link *mld_link = iwl_mld_link_from_mac80211(link);
+
+ if (WARN_ON(!mld_link))
+ return;
+
+ if (WARN_ON(vif->type != NL80211_IFTYPE_MONITOR))
+ return;
+
+ iwl_mld_remove_internal_sta(mld, &mld_link->mon_sta, false,
+ IWL_MAX_TID_COUNT);
+}
+
+static int iwl_mld_update_sta_resources(struct iwl_mld *mld,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta,
+ u32 old_sta_mask,
+ u32 new_sta_mask)
+{
+ int ret;
+
+ ret = iwl_mld_update_sta_txqs(mld, sta, old_sta_mask, new_sta_mask);
+ if (ret)
+ return ret;
+
+ ret = iwl_mld_update_sta_keys(mld, vif, sta, old_sta_mask, new_sta_mask);
+ if (ret)
+ return ret;
+
+ return iwl_mld_update_sta_baids(mld, old_sta_mask, new_sta_mask);
+}
+
+int iwl_mld_update_link_stas(struct iwl_mld *mld,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta,
+ u16 old_links, u16 new_links)
+{
+ struct iwl_mld_sta *mld_sta = iwl_mld_sta_from_mac80211(sta);
+ struct iwl_mld_link_sta *mld_link_sta;
+ unsigned long links_to_add = ~old_links & new_links;
+ unsigned long links_to_rem = old_links & ~new_links;
+ unsigned long old_links_long = old_links;
+ unsigned long sta_mask_added = 0;
+ u32 current_sta_mask = 0, sta_mask_to_rem = 0;
+ unsigned int link_id, sta_id;
+ int ret;
+
+ lockdep_assert_wiphy(mld->wiphy);
+
+ for_each_set_bit(link_id, &old_links_long,
+ IEEE80211_MLD_MAX_NUM_LINKS) {
+ mld_link_sta =
+ iwl_mld_link_sta_dereference_check(mld_sta, link_id);
+
+ if (WARN_ON(!mld_link_sta))
+ return -EINVAL;
+
+ current_sta_mask |= BIT(mld_link_sta->fw_id);
+ if (links_to_rem & BIT(link_id))
+ sta_mask_to_rem |= BIT(mld_link_sta->fw_id);
+ }
+
+ if (sta_mask_to_rem) {
+ ret = iwl_mld_update_sta_resources(mld, vif, sta,
+ current_sta_mask,
+ current_sta_mask &
+ ~sta_mask_to_rem);
+ if (ret)
+ return ret;
+
+ current_sta_mask &= ~sta_mask_to_rem;
+ }
+
+ for_each_set_bit(link_id, &links_to_rem, IEEE80211_MLD_MAX_NUM_LINKS) {
+ struct ieee80211_link_sta *link_sta =
+ link_sta_dereference_protected(sta, link_id);
+
+ if (WARN_ON(!link_sta))
+ return -EINVAL;
+
+ iwl_mld_remove_link_sta(mld, link_sta);
+ }
+
+ for_each_set_bit(link_id, &links_to_add, IEEE80211_MLD_MAX_NUM_LINKS) {
+ struct ieee80211_link_sta *link_sta =
+ link_sta_dereference_protected(sta, link_id);
+ struct ieee80211_bss_conf *link;
+
+ if (WARN_ON(!link_sta))
+ return -EINVAL;
+
+ ret = iwl_mld_add_link_sta(mld, link_sta);
+ if (ret)
+ goto remove_added_link_stas;
+
+ mld_link_sta =
+ iwl_mld_link_sta_dereference_check(mld_sta,
+ link_id);
+
+ link = link_conf_dereference_protected(mld_sta->vif,
+ link_sta->link_id);
+
+ iwl_mld_set_max_amsdu_len(mld, link_sta);
+ iwl_mld_config_tlc_link(mld, vif, link, link_sta);
+
+ sta_mask_added |= BIT(mld_link_sta->fw_id);
+ }
+
+ if (sta_mask_added) {
+ ret = iwl_mld_update_sta_resources(mld, vif, sta,
+ current_sta_mask,
+ current_sta_mask |
+ sta_mask_added);
+ if (ret)
+ goto remove_added_link_stas;
+ }
+
+ /* We couldn't activate the links before it has a STA. Now we can */
+ for_each_set_bit(link_id, &links_to_add, IEEE80211_MLD_MAX_NUM_LINKS) {
+ struct ieee80211_bss_conf *link =
+ link_conf_dereference_protected(mld_sta->vif, link_id);
+
+ if (WARN_ON(!link))
+ continue;
+
+ iwl_mld_activate_link(mld, link);
+ }
+
+ return 0;
+
+remove_added_link_stas:
+ for_each_set_bit(sta_id, &sta_mask_added, mld->fw->ucode_capa.num_stations) {
+ struct ieee80211_link_sta *link_sta =
+ wiphy_dereference(mld->wiphy,
+ mld->fw_id_to_link_sta[sta_id]);
+
+ if (WARN_ON(!link_sta))
+ continue;
+
+ iwl_mld_remove_link_sta(mld, link_sta);
+ }
+
+ return ret;
+}
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/sta.h b/drivers/net/wireless/intel/iwlwifi/mld/sta.h
new file mode 100644
index 000000000000..1897b121aae2
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/mld/sta.h
@@ -0,0 +1,273 @@
+/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
+/*
+ * Copyright (C) 2024-2025 Intel Corporation
+ */
+
+#ifndef __iwl_mld_sta_h__
+#define __iwl_mld_sta_h__
+
+#include <net/mac80211.h>
+
+#include "mld.h"
+#include "tx.h"
+
+/**
+ * struct iwl_mld_rxq_dup_data - Duplication detection data, per STA & Rx queue
+ * @last_seq: last sequence per tid.
+ * @last_sub_frame_idx: the index of the last subframe in an A-MSDU. This value
+ * will be zero if the packet is not part of an A-MSDU.
+ */
+struct iwl_mld_rxq_dup_data {
+ __le16 last_seq[IWL_MAX_TID_COUNT + 1];
+ u8 last_sub_frame_idx[IWL_MAX_TID_COUNT + 1];
+} ____cacheline_aligned_in_smp;
+
+/**
+ * struct iwl_mld_link_sta - link-level station
+ *
+ * This represents the link-level sta - the driver level equivalent to the
+ * ieee80211_link_sta
+ *
+ * @last_rate_n_flags: rate_n_flags from the last &iwl_tlc_update_notif
+ * @signal_avg: the signal average coming from the firmware
+ * @in_fw: whether the link STA is uploaded to the FW (false during restart)
+ * @rcu_head: RCU head for freeing this object
+ * @fw_id: the FW id of this link sta.
+ */
+struct iwl_mld_link_sta {
+ /* Add here fields that need clean up on restart */
+ struct_group(zeroed_on_hw_restart,
+ u32 last_rate_n_flags;
+ bool in_fw;
+ s8 signal_avg;
+ );
+ /* And here fields that survive a fw restart */
+ struct rcu_head rcu_head;
+ u32 fw_id;
+};
+
+#define iwl_mld_link_sta_dereference_check(mld_sta, link_id) \
+ rcu_dereference_check((mld_sta)->link[link_id], \
+ lockdep_is_held(&mld_sta->mld->wiphy->mtx))
+
+#define for_each_mld_link_sta(mld_sta, link_sta, link_id) \
+ for (link_id = 0; link_id < ARRAY_SIZE((mld_sta)->link); \
+ link_id++) \
+ if ((link_sta = \
+ iwl_mld_link_sta_dereference_check(mld_sta, link_id)))
+
+#define IWL_NUM_DEFAULT_KEYS 4
+
+/* struct iwl_mld_ptk_pn - Holds Packet Number (PN) per TID.
+ * @rcu_head: RCU head for freeing this data.
+ * @pn: Array storing PN for each TID.
+ */
+struct iwl_mld_ptk_pn {
+ struct rcu_head rcu_head;
+ struct {
+ u8 pn[IWL_MAX_TID_COUNT][IEEE80211_CCMP_PN_LEN];
+ } ____cacheline_aligned_in_smp q[];
+};
+
+/**
+ * struct iwl_mld_per_link_mpdu_counter - per-link TX/RX MPDU counters
+ *
+ * @tx: Number of TX MPDUs.
+ * @rx: Number of RX MPDUs.
+ */
+struct iwl_mld_per_link_mpdu_counter {
+ u32 tx;
+ u32 rx;
+};
+
+/**
+ * struct iwl_mld_per_q_mpdu_counter - per-queue MPDU counter
+ *
+ * @lock: Needed to protect the counters when modified from statistics.
+ * @per_link: per-link counters.
+ * @window_start_time: timestamp of the counting-window start
+ */
+struct iwl_mld_per_q_mpdu_counter {
+ spinlock_t lock;
+ struct iwl_mld_per_link_mpdu_counter per_link[IWL_FW_MAX_LINK_ID + 1];
+ unsigned long window_start_time;
+} ____cacheline_aligned_in_smp;
+
+/**
+ * struct iwl_mld_sta - representation of a station in the driver.
+ *
+ * This represent the MLD-level sta, and will not be added to the FW.
+ * Embedded in ieee80211_sta.
+ *
+ * @vif: pointer the vif object.
+ * @sta_state: station state according to enum %ieee80211_sta_state
+ * @sta_type: type of this station. See &enum iwl_fw_sta_type
+ * @mld: a pointer to the iwl_mld object
+ * @dup_data: per queue duplicate packet detection data
+ * @data_tx_ant: stores the last TX antenna index; used for setting
+ * TX rate_n_flags for injected data frames (toggles on every TX failure).
+ * @tid_to_baid: a simple map of TID to Block-Ack fw id
+ * @deflink: This holds the default link STA information, for non MLO STA all
+ * link specific STA information is accessed through @deflink or through
+ * link[0] which points to address of @deflink. For MLO Link STA
+ * the first added link STA will point to deflink.
+ * @link: reference to Link Sta entries. For Non MLO STA, except 1st link,
+ * i.e link[0] all links would be assigned to NULL by default and
+ * would access link information via @deflink or link[0]. For MLO
+ * STA, first link STA being added will point its link pointer to
+ * @deflink address and remaining would be allocated and the address
+ * would be assigned to link[link_id] where link_id is the id assigned
+ * by the AP.
+ * @ptk_pn: Array of pointers to PTK PN data, used to track the Packet Number
+ * per key index and per queue (TID).
+ * @mpdu_counters: RX/TX MPDUs counters for each queue.
+ */
+struct iwl_mld_sta {
+ /* Add here fields that need clean up on restart */
+ struct_group(zeroed_on_hw_restart,
+ enum ieee80211_sta_state sta_state;
+ enum iwl_fw_sta_type sta_type;
+ );
+ /* And here fields that survive a fw restart */
+ struct iwl_mld *mld;
+ struct ieee80211_vif *vif;
+ struct iwl_mld_rxq_dup_data *dup_data;
+ u8 tid_to_baid[IWL_MAX_TID_COUNT];
+ u8 data_tx_ant;
+
+ struct iwl_mld_link_sta deflink;
+ struct iwl_mld_link_sta __rcu *link[IEEE80211_MLD_MAX_NUM_LINKS];
+ struct iwl_mld_ptk_pn __rcu *ptk_pn[IWL_NUM_DEFAULT_KEYS];
+ struct iwl_mld_per_q_mpdu_counter *mpdu_counters;
+};
+
+static inline struct iwl_mld_sta *
+iwl_mld_sta_from_mac80211(struct ieee80211_sta *sta)
+{
+ return (void *)sta->drv_priv;
+}
+
+static inline void
+iwl_mld_cleanup_sta(void *data, struct ieee80211_sta *sta)
+{
+ struct iwl_mld_sta *mld_sta = iwl_mld_sta_from_mac80211(sta);
+ struct iwl_mld_link_sta *mld_link_sta;
+ u8 link_id;
+
+ for (int i = 0; i < ARRAY_SIZE(sta->txq); i++)
+ CLEANUP_STRUCT(iwl_mld_txq_from_mac80211(sta->txq[i]));
+
+ for_each_mld_link_sta(mld_sta, mld_link_sta, link_id) {
+ CLEANUP_STRUCT(mld_link_sta);
+
+ if (!ieee80211_vif_is_mld(mld_sta->vif)) {
+ /* not an MLD STA; only has the deflink with ID zero */
+ WARN_ON(link_id);
+ continue;
+ }
+
+ if (mld_sta->vif->active_links & BIT(link_id))
+ continue;
+
+ /* Should not happen as link removal should always succeed */
+ WARN_ON(1);
+ RCU_INIT_POINTER(mld_sta->link[link_id], NULL);
+ RCU_INIT_POINTER(mld_sta->mld->fw_id_to_link_sta[mld_link_sta->fw_id],
+ NULL);
+ if (mld_link_sta != &mld_sta->deflink)
+ kfree_rcu(mld_link_sta, rcu_head);
+ }
+
+ CLEANUP_STRUCT(mld_sta);
+}
+
+static inline struct iwl_mld_link_sta *
+iwl_mld_link_sta_from_mac80211(struct ieee80211_link_sta *link_sta)
+{
+ struct iwl_mld_sta *mld_sta = iwl_mld_sta_from_mac80211(link_sta->sta);
+
+ return iwl_mld_link_sta_dereference_check(mld_sta, link_sta->link_id);
+}
+
+int iwl_mld_add_sta(struct iwl_mld *mld, struct ieee80211_sta *sta,
+ struct ieee80211_vif *vif, enum iwl_fw_sta_type type);
+void iwl_mld_remove_sta(struct iwl_mld *mld, struct ieee80211_sta *sta);
+int iwl_mld_fw_sta_id_from_link_sta(struct iwl_mld *mld,
+ struct ieee80211_link_sta *link_sta);
+u32 iwl_mld_fw_sta_id_mask(struct iwl_mld *mld, struct ieee80211_sta *sta);
+int iwl_mld_update_all_link_stations(struct iwl_mld *mld,
+ struct ieee80211_sta *sta);
+void iwl_mld_flush_sta_txqs(struct iwl_mld *mld, struct ieee80211_sta *sta);
+void iwl_mld_wait_sta_txqs_empty(struct iwl_mld *mld,
+ struct ieee80211_sta *sta);
+void iwl_mld_count_mpdu_rx(struct ieee80211_link_sta *link_sta, int queue,
+ u32 count);
+void iwl_mld_count_mpdu_tx(struct ieee80211_link_sta *link_sta, u32 count);
+
+/**
+ * struct iwl_mld_int_sta - representation of an internal station
+ * (a station that exist in FW and in driver, but not in mac80211)
+ *
+ * @sta_id: the index of the station in the fw
+ * @queue_id: the if of the queue used by the station
+ * @sta_type: station type. One of &iwl_fw_sta_type
+ */
+struct iwl_mld_int_sta {
+ u8 sta_id;
+ u32 queue_id;
+ enum iwl_fw_sta_type sta_type;
+};
+
+static inline void
+iwl_mld_init_internal_sta(struct iwl_mld_int_sta *internal_sta)
+{
+ internal_sta->sta_id = IWL_INVALID_STA;
+ internal_sta->queue_id = IWL_MLD_INVALID_QUEUE;
+}
+
+static inline void
+iwl_mld_free_internal_sta(struct iwl_mld *mld,
+ struct iwl_mld_int_sta *internal_sta)
+{
+ if (WARN_ON(internal_sta->sta_id == IWL_INVALID_STA))
+ return;
+
+ RCU_INIT_POINTER(mld->fw_id_to_link_sta[internal_sta->sta_id], NULL);
+ iwl_mld_init_internal_sta(internal_sta);
+}
+
+int iwl_mld_add_bcast_sta(struct iwl_mld *mld,
+ struct ieee80211_vif *vif,
+ struct ieee80211_bss_conf *link);
+
+int iwl_mld_add_mcast_sta(struct iwl_mld *mld,
+ struct ieee80211_vif *vif,
+ struct ieee80211_bss_conf *link);
+
+int iwl_mld_add_aux_sta(struct iwl_mld *mld,
+ struct iwl_mld_int_sta *internal_sta);
+
+int iwl_mld_add_mon_sta(struct iwl_mld *mld,
+ struct ieee80211_vif *vif,
+ struct ieee80211_bss_conf *link);
+
+void iwl_mld_remove_bcast_sta(struct iwl_mld *mld,
+ struct ieee80211_vif *vif,
+ struct ieee80211_bss_conf *link);
+
+void iwl_mld_remove_mcast_sta(struct iwl_mld *mld,
+ struct ieee80211_vif *vif,
+ struct ieee80211_bss_conf *link);
+
+void iwl_mld_remove_aux_sta(struct iwl_mld *mld,
+ struct ieee80211_vif *vif);
+
+void iwl_mld_remove_mon_sta(struct iwl_mld *mld,
+ struct ieee80211_vif *vif,
+ struct ieee80211_bss_conf *link);
+
+int iwl_mld_update_link_stas(struct iwl_mld *mld,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta,
+ u16 old_links, u16 new_links);
+#endif /* __iwl_mld_sta_h__ */
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/stats.c b/drivers/net/wireless/intel/iwlwifi/mld/stats.c
new file mode 100644
index 000000000000..f633cb1cf510
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/mld/stats.c
@@ -0,0 +1,520 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
+/*
+ * Copyright (C) 2024-2025 Intel Corporation
+ */
+
+#include "mld.h"
+#include "stats.h"
+#include "sta.h"
+#include "mlo.h"
+#include "hcmd.h"
+#include "iface.h"
+#include "scan.h"
+#include "phy.h"
+#include "fw/api/stats.h"
+
+static int iwl_mld_send_fw_stats_cmd(struct iwl_mld *mld, u32 cfg_mask,
+ u32 cfg_time, u32 type_mask)
+{
+ u32 cmd_id = WIDE_ID(SYSTEM_GROUP, SYSTEM_STATISTICS_CMD);
+ struct iwl_system_statistics_cmd stats_cmd = {
+ .cfg_mask = cpu_to_le32(cfg_mask),
+ .config_time_sec = cpu_to_le32(cfg_time),
+ .type_id_mask = cpu_to_le32(type_mask),
+ };
+
+ return iwl_mld_send_cmd_pdu(mld, cmd_id, &stats_cmd);
+}
+
+int iwl_mld_clear_stats_in_fw(struct iwl_mld *mld)
+{
+ u32 cfg_mask = IWL_STATS_CFG_FLG_ON_DEMAND_NTFY_MSK;
+ u32 type_mask = IWL_STATS_NTFY_TYPE_ID_OPER |
+ IWL_STATS_NTFY_TYPE_ID_OPER_PART1;
+
+ return iwl_mld_send_fw_stats_cmd(mld, cfg_mask, 0, type_mask);
+}
+
+static void
+iwl_mld_fill_stats_from_oper_notif(struct iwl_mld *mld,
+ struct iwl_rx_packet *pkt,
+ u8 fw_sta_id, struct station_info *sinfo)
+{
+ const struct iwl_system_statistics_notif_oper *notif =
+ (void *)&pkt->data;
+ const struct iwl_stats_ntfy_per_sta *per_sta =
+ &notif->per_sta[fw_sta_id];
+ struct ieee80211_link_sta *link_sta;
+ struct iwl_mld_link_sta *mld_link_sta;
+
+ /* 0 isn't a valid value, but FW might send 0.
+ * In that case, set the latest non-zero value we stored
+ */
+ rcu_read_lock();
+
+ link_sta = rcu_dereference(mld->fw_id_to_link_sta[fw_sta_id]);
+ if (IS_ERR_OR_NULL(link_sta))
+ goto unlock;
+
+ mld_link_sta = iwl_mld_link_sta_from_mac80211(link_sta);
+ if (WARN_ON(!mld_link_sta))
+ goto unlock;
+
+ if (per_sta->average_energy)
+ mld_link_sta->signal_avg =
+ -(s8)le32_to_cpu(per_sta->average_energy);
+
+ sinfo->signal_avg = mld_link_sta->signal_avg;
+ sinfo->filled |= BIT_ULL(NL80211_STA_INFO_SIGNAL_AVG);
+
+unlock:
+ rcu_read_unlock();
+}
+
+struct iwl_mld_stats_data {
+ u8 fw_sta_id;
+ struct station_info *sinfo;
+ struct iwl_mld *mld;
+};
+
+static bool iwl_mld_wait_stats_handler(struct iwl_notif_wait_data *notif_data,
+ struct iwl_rx_packet *pkt, void *data)
+{
+ struct iwl_mld_stats_data *stats_data = data;
+ u16 cmd = WIDE_ID(pkt->hdr.group_id, pkt->hdr.cmd);
+
+ switch (cmd) {
+ case WIDE_ID(STATISTICS_GROUP, STATISTICS_OPER_NOTIF):
+ iwl_mld_fill_stats_from_oper_notif(stats_data->mld, pkt,
+ stats_data->fw_sta_id,
+ stats_data->sinfo);
+ break;
+ case WIDE_ID(STATISTICS_GROUP, STATISTICS_OPER_PART1_NOTIF):
+ break;
+ case WIDE_ID(SYSTEM_GROUP, SYSTEM_STATISTICS_END_NOTIF):
+ return true;
+ }
+
+ return false;
+}
+
+static int
+iwl_mld_fw_stats_to_mac80211(struct iwl_mld *mld, struct iwl_mld_sta *mld_sta,
+ struct station_info *sinfo)
+{
+ u32 cfg_mask = IWL_STATS_CFG_FLG_ON_DEMAND_NTFY_MSK |
+ IWL_STATS_CFG_FLG_RESET_MSK;
+ u32 type_mask = IWL_STATS_NTFY_TYPE_ID_OPER |
+ IWL_STATS_NTFY_TYPE_ID_OPER_PART1;
+ static const u16 notifications[] = {
+ WIDE_ID(STATISTICS_GROUP, STATISTICS_OPER_NOTIF),
+ WIDE_ID(STATISTICS_GROUP, STATISTICS_OPER_PART1_NOTIF),
+ WIDE_ID(SYSTEM_GROUP, SYSTEM_STATISTICS_END_NOTIF),
+ };
+ struct iwl_mld_stats_data wait_stats_data = {
+ /* We don't support drv_sta_statistics in EMLSR */
+ .fw_sta_id = mld_sta->deflink.fw_id,
+ .sinfo = sinfo,
+ .mld = mld,
+ };
+ struct iwl_notification_wait stats_wait;
+ int ret;
+
+ iwl_init_notification_wait(&mld->notif_wait, &stats_wait,
+ notifications, ARRAY_SIZE(notifications),
+ iwl_mld_wait_stats_handler,
+ &wait_stats_data);
+
+ ret = iwl_mld_send_fw_stats_cmd(mld, cfg_mask, 0, type_mask);
+ if (ret) {
+ iwl_remove_notification(&mld->notif_wait, &stats_wait);
+ return ret;
+ }
+
+ /* Wait 500ms for OPERATIONAL, PART1, and END notifications,
+ * which should be sufficient for the firmware to gather data
+ * from all LMACs and send notifications to the host.
+ */
+ ret = iwl_wait_notification(&mld->notif_wait, &stats_wait, HZ / 2);
+ if (ret)
+ return ret;
+
+ /* When periodic statistics are sent, FW will clear its statistics DB.
+ * If the statistics request here happens shortly afterwards,
+ * the response will contain data collected over a short time
+ * interval. The response we got here shouldn't be processed by
+ * the general statistics processing because it's incomplete.
+ * So, we delete it from the list so it won't be processed.
+ */
+ iwl_mld_delete_handlers(mld, notifications, ARRAY_SIZE(notifications));
+
+ return 0;
+}
+
+#define PERIODIC_STATS_SECONDS 5
+
+int iwl_mld_request_periodic_fw_stats(struct iwl_mld *mld, bool enable)
+{
+ u32 cfg_mask = enable ? 0 : IWL_STATS_CFG_FLG_DISABLE_NTFY_MSK;
+ u32 type_mask = enable ? (IWL_STATS_NTFY_TYPE_ID_OPER |
+ IWL_STATS_NTFY_TYPE_ID_OPER_PART1) : 0;
+ u32 cfg_time = enable ? PERIODIC_STATS_SECONDS : 0;
+
+ return iwl_mld_send_fw_stats_cmd(mld, cfg_mask, cfg_time, type_mask);
+}
+
+static void iwl_mld_sta_stats_fill_txrate(struct iwl_mld_sta *mld_sta,
+ struct station_info *sinfo)
+{
+ struct rate_info *rinfo = &sinfo->txrate;
+ u32 rate_n_flags = mld_sta->deflink.last_rate_n_flags;
+ u32 format = rate_n_flags & RATE_MCS_MOD_TYPE_MSK;
+ u32 gi_ltf;
+
+ sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_BITRATE);
+
+ switch (rate_n_flags & RATE_MCS_CHAN_WIDTH_MSK) {
+ case RATE_MCS_CHAN_WIDTH_20:
+ rinfo->bw = RATE_INFO_BW_20;
+ break;
+ case RATE_MCS_CHAN_WIDTH_40:
+ rinfo->bw = RATE_INFO_BW_40;
+ break;
+ case RATE_MCS_CHAN_WIDTH_80:
+ rinfo->bw = RATE_INFO_BW_80;
+ break;
+ case RATE_MCS_CHAN_WIDTH_160:
+ rinfo->bw = RATE_INFO_BW_160;
+ break;
+ case RATE_MCS_CHAN_WIDTH_320:
+ rinfo->bw = RATE_INFO_BW_320;
+ break;
+ }
+
+ if (format == RATE_MCS_MOD_TYPE_CCK ||
+ format == RATE_MCS_MOD_TYPE_LEGACY_OFDM) {
+ int rate = u32_get_bits(rate_n_flags, RATE_LEGACY_RATE_MSK);
+
+ /* add the offset needed to get to the legacy ofdm indices */
+ if (format == RATE_MCS_MOD_TYPE_LEGACY_OFDM)
+ rate += IWL_FIRST_OFDM_RATE;
+
+ switch (rate) {
+ case IWL_RATE_1M_INDEX:
+ rinfo->legacy = 10;
+ break;
+ case IWL_RATE_2M_INDEX:
+ rinfo->legacy = 20;
+ break;
+ case IWL_RATE_5M_INDEX:
+ rinfo->legacy = 55;
+ break;
+ case IWL_RATE_11M_INDEX:
+ rinfo->legacy = 110;
+ break;
+ case IWL_RATE_6M_INDEX:
+ rinfo->legacy = 60;
+ break;
+ case IWL_RATE_9M_INDEX:
+ rinfo->legacy = 90;
+ break;
+ case IWL_RATE_12M_INDEX:
+ rinfo->legacy = 120;
+ break;
+ case IWL_RATE_18M_INDEX:
+ rinfo->legacy = 180;
+ break;
+ case IWL_RATE_24M_INDEX:
+ rinfo->legacy = 240;
+ break;
+ case IWL_RATE_36M_INDEX:
+ rinfo->legacy = 360;
+ break;
+ case IWL_RATE_48M_INDEX:
+ rinfo->legacy = 480;
+ break;
+ case IWL_RATE_54M_INDEX:
+ rinfo->legacy = 540;
+ }
+ return;
+ }
+
+ rinfo->nss = u32_get_bits(rate_n_flags, RATE_MCS_NSS_MSK) + 1;
+
+ if (format == RATE_MCS_MOD_TYPE_HT)
+ rinfo->mcs = RATE_HT_MCS_INDEX(rate_n_flags);
+ else
+ rinfo->mcs = u32_get_bits(rate_n_flags, RATE_MCS_CODE_MSK);
+
+ if (rate_n_flags & RATE_MCS_SGI_MSK)
+ rinfo->flags |= RATE_INFO_FLAGS_SHORT_GI;
+
+ switch (format) {
+ case RATE_MCS_MOD_TYPE_EHT:
+ rinfo->flags |= RATE_INFO_FLAGS_EHT_MCS;
+ break;
+ case RATE_MCS_MOD_TYPE_HE:
+ gi_ltf = u32_get_bits(rate_n_flags, RATE_MCS_HE_GI_LTF_MSK);
+
+ rinfo->flags |= RATE_INFO_FLAGS_HE_MCS;
+
+ if (rate_n_flags & RATE_MCS_HE_106T_MSK) {
+ rinfo->bw = RATE_INFO_BW_HE_RU;
+ rinfo->he_ru_alloc = NL80211_RATE_INFO_HE_RU_ALLOC_106;
+ }
+
+ switch (rate_n_flags & RATE_MCS_HE_TYPE_MSK) {
+ case RATE_MCS_HE_TYPE_SU:
+ case RATE_MCS_HE_TYPE_EXT_SU:
+ if (gi_ltf == 0 || gi_ltf == 1)
+ rinfo->he_gi = NL80211_RATE_INFO_HE_GI_0_8;
+ else if (gi_ltf == 2)
+ rinfo->he_gi = NL80211_RATE_INFO_HE_GI_1_6;
+ else if (gi_ltf == 3)
+ rinfo->he_gi = NL80211_RATE_INFO_HE_GI_3_2;
+ else
+ rinfo->he_gi = NL80211_RATE_INFO_HE_GI_0_8;
+ break;
+ case RATE_MCS_HE_TYPE_MU:
+ if (gi_ltf == 0 || gi_ltf == 1)
+ rinfo->he_gi = NL80211_RATE_INFO_HE_GI_0_8;
+ else if (gi_ltf == 2)
+ rinfo->he_gi = NL80211_RATE_INFO_HE_GI_1_6;
+ else
+ rinfo->he_gi = NL80211_RATE_INFO_HE_GI_3_2;
+ break;
+ case RATE_MCS_HE_TYPE_TRIG:
+ if (gi_ltf == 0 || gi_ltf == 1)
+ rinfo->he_gi = NL80211_RATE_INFO_HE_GI_1_6;
+ else
+ rinfo->he_gi = NL80211_RATE_INFO_HE_GI_3_2;
+ break;
+ }
+
+ if (rate_n_flags & RATE_HE_DUAL_CARRIER_MODE_MSK)
+ rinfo->he_dcm = 1;
+ break;
+ case RATE_MCS_MOD_TYPE_HT:
+ rinfo->flags |= RATE_INFO_FLAGS_MCS;
+ break;
+ case RATE_MCS_MOD_TYPE_VHT:
+ rinfo->flags |= RATE_INFO_FLAGS_VHT_MCS;
+ break;
+ }
+}
+
+void iwl_mld_mac80211_sta_statistics(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta,
+ struct station_info *sinfo)
+{
+ struct iwl_mld_sta *mld_sta = iwl_mld_sta_from_mac80211(sta);
+
+ /* This API is not EMLSR ready, so we cannot provide complete
+ * information if EMLSR is active
+ */
+ if (hweight16(vif->active_links) > 1)
+ return;
+
+ if (iwl_mld_fw_stats_to_mac80211(mld_sta->mld, mld_sta, sinfo))
+ return;
+
+ iwl_mld_sta_stats_fill_txrate(mld_sta, sinfo);
+
+ /* TODO: NL80211_STA_INFO_BEACON_RX */
+
+ /* TODO: NL80211_STA_INFO_BEACON_SIGNAL_AVG */
+}
+
+#define IWL_MLD_TRAFFIC_LOAD_MEDIUM_THRESH 10 /* percentage */
+#define IWL_MLD_TRAFFIC_LOAD_HIGH_THRESH 50 /* percentage */
+#define IWL_MLD_TRAFFIC_LOAD_MIN_WINDOW_USEC (500 * 1000)
+
+static u8 iwl_mld_stats_load_percentage(u32 last_ts_usec, u32 curr_ts_usec,
+ u32 total_airtime_usec)
+{
+ u32 elapsed_usec = curr_ts_usec - last_ts_usec;
+
+ if (elapsed_usec < IWL_MLD_TRAFFIC_LOAD_MIN_WINDOW_USEC)
+ return 0;
+
+ return (100 * total_airtime_usec / elapsed_usec);
+}
+
+static void iwl_mld_stats_recalc_traffic_load(struct iwl_mld *mld,
+ u32 total_airtime_usec,
+ u32 curr_ts_usec)
+{
+ u32 last_ts_usec = mld->scan.traffic_load.last_stats_ts_usec;
+ u8 load_prec;
+
+ /* Skip the calculation as this is the first notification received */
+ if (!last_ts_usec)
+ goto out;
+
+ load_prec = iwl_mld_stats_load_percentage(last_ts_usec, curr_ts_usec,
+ total_airtime_usec);
+
+ if (load_prec > IWL_MLD_TRAFFIC_LOAD_HIGH_THRESH)
+ mld->scan.traffic_load.status = IWL_MLD_TRAFFIC_HIGH;
+ else if (load_prec > IWL_MLD_TRAFFIC_LOAD_MEDIUM_THRESH)
+ mld->scan.traffic_load.status = IWL_MLD_TRAFFIC_MEDIUM;
+ else
+ mld->scan.traffic_load.status = IWL_MLD_TRAFFIC_LOW;
+
+out:
+ mld->scan.traffic_load.last_stats_ts_usec = curr_ts_usec;
+}
+
+static void iwl_mld_update_link_sig(struct ieee80211_vif *vif, int sig,
+ struct ieee80211_bss_conf *bss_conf)
+{
+ struct iwl_mld *mld = iwl_mld_vif_from_mac80211(vif)->mld;
+ int exit_emlsr_thresh;
+
+ if (sig == 0) {
+ IWL_DEBUG_RX(mld, "RSSI is 0 - skip signal based decision\n");
+ return;
+ }
+
+ /* TODO: task=statistics handle CQM notifications */
+
+ if (sig < IWL_MLD_LOW_RSSI_MLO_SCAN_THRESH)
+ iwl_mld_int_mlo_scan(mld, vif);
+
+ if (!iwl_mld_emlsr_active(vif))
+ return;
+
+ /* We are in EMLSR, check if we need to exit */
+ exit_emlsr_thresh =
+ iwl_mld_get_emlsr_rssi_thresh(mld, &bss_conf->chanreq.oper,
+ true);
+
+ if (sig < exit_emlsr_thresh)
+ iwl_mld_exit_emlsr(mld, vif, IWL_MLD_EMLSR_EXIT_LOW_RSSI,
+ iwl_mld_get_other_link(vif,
+ bss_conf->link_id));
+}
+
+static void
+iwl_mld_process_per_link_stats(struct iwl_mld *mld,
+ const struct iwl_stats_ntfy_per_link *per_link,
+ u32 curr_ts_usec)
+{
+ u32 total_airtime_usec = 0;
+
+ for (u32 fw_id = 0;
+ fw_id < ARRAY_SIZE(mld->fw_id_to_bss_conf);
+ fw_id++) {
+ const struct iwl_stats_ntfy_per_link *link_stats;
+ struct ieee80211_bss_conf *bss_conf;
+ int sig;
+
+ bss_conf = wiphy_dereference(mld->wiphy,
+ mld->fw_id_to_bss_conf[fw_id]);
+ if (!bss_conf || bss_conf->vif->type != NL80211_IFTYPE_STATION)
+ continue;
+
+ link_stats = &per_link[fw_id];
+
+ total_airtime_usec += le32_to_cpu(link_stats->air_time);
+
+ sig = -le32_to_cpu(link_stats->beacon_filter_average_energy);
+ iwl_mld_update_link_sig(bss_conf->vif, sig, bss_conf);
+
+ /* TODO: parse more fields here (task=statistics)*/
+ }
+
+ iwl_mld_stats_recalc_traffic_load(mld, total_airtime_usec,
+ curr_ts_usec);
+}
+
+static void
+iwl_mld_process_per_sta_stats(struct iwl_mld *mld,
+ const struct iwl_stats_ntfy_per_sta *per_sta)
+{
+ for (int i = 0; i < mld->fw->ucode_capa.num_stations; i++) {
+ struct ieee80211_link_sta *link_sta =
+ wiphy_dereference(mld->wiphy,
+ mld->fw_id_to_link_sta[i]);
+ struct iwl_mld_link_sta *mld_link_sta;
+ s8 avg_energy =
+ -(s8)le32_to_cpu(per_sta[i].average_energy);
+
+ if (IS_ERR_OR_NULL(link_sta) || !avg_energy)
+ continue;
+
+ mld_link_sta = iwl_mld_link_sta_from_mac80211(link_sta);
+ if (WARN_ON(!mld_link_sta))
+ continue;
+
+ mld_link_sta->signal_avg = avg_energy;
+ }
+}
+
+static void iwl_mld_fill_chanctx_stats(struct ieee80211_hw *hw,
+ struct ieee80211_chanctx_conf *ctx,
+ void *data)
+{
+ struct iwl_mld_phy *phy = iwl_mld_phy_from_mac80211(ctx);
+ const struct iwl_stats_ntfy_per_phy *per_phy = data;
+ u32 new_load, old_load;
+
+ if (WARN_ON(phy->fw_id >= IWL_STATS_MAX_PHY_OPERATIONAL))
+ return;
+
+ phy->channel_load_by_us =
+ le32_to_cpu(per_phy[phy->fw_id].channel_load_by_us);
+
+ old_load = phy->avg_channel_load_not_by_us;
+ new_load = le32_to_cpu(per_phy[phy->fw_id].channel_load_not_by_us);
+
+ if (IWL_FW_CHECK(phy->mld,
+ new_load != IWL_STATS_UNKNOWN_CHANNEL_LOAD &&
+ new_load > 100,
+ "Invalid channel load %u\n", new_load))
+ return;
+
+ if (new_load != IWL_STATS_UNKNOWN_CHANNEL_LOAD) {
+ /* update giving a weight of 0.5 for the old value */
+ phy->avg_channel_load_not_by_us = (new_load >> 1) +
+ (old_load >> 1);
+ }
+
+ iwl_mld_emlsr_check_chan_load(hw, phy, old_load);
+}
+
+static void
+iwl_mld_process_per_phy_stats(struct iwl_mld *mld,
+ const struct iwl_stats_ntfy_per_phy *per_phy)
+{
+ ieee80211_iter_chan_contexts_mtx(mld->hw,
+ iwl_mld_fill_chanctx_stats,
+ (void *)(uintptr_t)per_phy);
+
+}
+
+void iwl_mld_handle_stats_oper_notif(struct iwl_mld *mld,
+ struct iwl_rx_packet *pkt)
+{
+ const struct iwl_system_statistics_notif_oper *stats =
+ (void *)&pkt->data;
+ u32 curr_ts_usec = le32_to_cpu(stats->time_stamp);
+
+ BUILD_BUG_ON(ARRAY_SIZE(stats->per_sta) != IWL_STATION_COUNT_MAX);
+ BUILD_BUG_ON(ARRAY_SIZE(stats->per_link) <
+ ARRAY_SIZE(mld->fw_id_to_bss_conf));
+
+ iwl_mld_process_per_link_stats(mld, stats->per_link, curr_ts_usec);
+ iwl_mld_process_per_sta_stats(mld, stats->per_sta);
+ iwl_mld_process_per_phy_stats(mld, stats->per_phy);
+
+ iwl_mld_check_omi_bw_reduction(mld);
+}
+
+void iwl_mld_handle_stats_oper_part1_notif(struct iwl_mld *mld,
+ struct iwl_rx_packet *pkt)
+{
+ /* TODO */
+}
+
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/stats.h b/drivers/net/wireless/intel/iwlwifi/mld/stats.h
new file mode 100644
index 000000000000..de434e66d555
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/mld/stats.h
@@ -0,0 +1,22 @@
+/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
+/*
+ * Copyright (C) 2024 Intel Corporation
+ */
+#ifndef __iwl_mld_stats_h__
+#define __iwl_mld_stats_h__
+
+int iwl_mld_request_periodic_fw_stats(struct iwl_mld *mld, bool enable);
+
+void iwl_mld_mac80211_sta_statistics(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta,
+ struct station_info *sinfo);
+
+void iwl_mld_handle_stats_oper_notif(struct iwl_mld *mld,
+ struct iwl_rx_packet *pkt);
+void iwl_mld_handle_stats_oper_part1_notif(struct iwl_mld *mld,
+ struct iwl_rx_packet *pkt);
+
+int iwl_mld_clear_stats_in_fw(struct iwl_mld *mld);
+
+#endif /* __iwl_mld_stats_h__ */
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/tests/Makefile b/drivers/net/wireless/intel/iwlwifi/mld/tests/Makefile
new file mode 100644
index 000000000000..3e2ae6020613
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/mld/tests/Makefile
@@ -0,0 +1,5 @@
+# SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
+iwlmld-tests-y += module.o hcmd.o utils.o link.o rx.o agg.o link-selection.o emlsr_with_bt.o
+
+ccflags-y += -I$(src)/../
+obj-$(CONFIG_IWLWIFI_KUNIT_TESTS) += iwlmld-tests.o
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/tests/agg.c b/drivers/net/wireless/intel/iwlwifi/mld/tests/agg.c
new file mode 100644
index 000000000000..29b0248cec3d
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/mld/tests/agg.c
@@ -0,0 +1,663 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
+/*
+ * KUnit tests for channel helper functions
+ *
+ * Copyright (C) 2024-2025 Intel Corporation
+ */
+#include <kunit/test.h>
+#include <kunit/static_stub.h>
+#include <kunit/skbuff.h>
+
+#include "utils.h"
+#include "mld.h"
+#include "sta.h"
+#include "agg.h"
+#include "rx.h"
+
+#define FC_QOS_DATA (IEEE80211_FTYPE_DATA | IEEE80211_STYPE_QOS_DATA)
+#define BA_WINDOW_SIZE 64
+#define QUEUE 0
+
+static const struct reorder_buffer_case {
+ const char *desc;
+ struct {
+ /* ieee80211_hdr fields */
+ u16 fc;
+ u8 tid;
+ bool multicast;
+ /* iwl_rx_mpdu_desc fields */
+ u16 nssn;
+ /* used also for setting hdr::seq_ctrl */
+ u16 sn;
+ u8 baid;
+ bool amsdu;
+ bool last_subframe;
+ bool old_sn;
+ bool dup;
+ } rx_pkt;
+ struct {
+ bool valid;
+ u16 head_sn;
+ u8 baid;
+ u16 num_entries;
+ /* The test prepares the reorder buffer with fake skbs based
+ * on the sequence numbers provided in @entries array.
+ */
+ struct {
+ u16 sn;
+ /* Set add_subframes > 0 to simulate an A-MSDU by
+ * queueing additional @add_subframes skbs in the
+ * appropriate reorder buffer entry (based on the @sn)
+ */
+ u8 add_subframes;
+ } entries[BA_WINDOW_SIZE];
+ } reorder_buf_state;
+ struct {
+ enum iwl_mld_reorder_result reorder_res;
+ u16 head_sn;
+ u16 num_stored;
+ u16 skb_release_order[BA_WINDOW_SIZE];
+ u16 skb_release_order_count;
+ } expected;
+} reorder_buffer_cases[] = {
+ {
+ .desc = "RX packet with invalid BAID",
+ .rx_pkt = {
+ .fc = FC_QOS_DATA,
+ .baid = IWL_RX_REORDER_DATA_INVALID_BAID,
+ },
+ .reorder_buf_state = {
+ .valid = true,
+ },
+ .expected = {
+ /* Invalid BAID should not be buffered. The frame is
+ * passed to the network stack immediately.
+ */
+ .reorder_res = IWL_MLD_PASS_SKB,
+ .num_stored = 0,
+ },
+ },
+ {
+ .desc = "RX Multicast packet",
+ .rx_pkt = {
+ .fc = FC_QOS_DATA,
+ .multicast = true,
+ },
+ .reorder_buf_state = {
+ .valid = true,
+ },
+ .expected = {
+ /* Multicast packets are not buffered. The packet is
+ * passed to the network stack immediately.
+ */
+ .reorder_res = IWL_MLD_PASS_SKB,
+ .num_stored = 0,
+ },
+ },
+ {
+ .desc = "RX non-QoS data",
+ .rx_pkt = {
+ .fc = IEEE80211_FTYPE_DATA,
+ },
+ .reorder_buf_state = {
+ .valid = true,
+ },
+ .expected = {
+ /* non-QoS data frames do not require reordering.
+ * The packet is passed to the network stack
+ * immediately.
+ */
+ .reorder_res = IWL_MLD_PASS_SKB,
+ },
+ },
+ {
+ .desc = "RX old SN packet, reorder buffer is not yet valid",
+ .rx_pkt = {
+ .fc = FC_QOS_DATA,
+ .old_sn = true,
+ },
+ .reorder_buf_state = {
+ .valid = false,
+ },
+ .expected = {
+ /* The buffer is invalid and the RX packet has an old
+ * SN. The packet is passed to the network stack
+ * immediately.
+ */
+ .reorder_res = IWL_MLD_PASS_SKB,
+ },
+ },
+ {
+ .desc = "RX old SN packet, reorder buffer valid",
+ .rx_pkt = {
+ .fc = FC_QOS_DATA,
+ .old_sn = true,
+ },
+ .reorder_buf_state = {
+ .valid = true,
+ .head_sn = 100,
+ },
+ .expected = {
+ /* The buffer is valid and the RX packet has an old SN.
+ * The packet should be dropped.
+ */
+ .reorder_res = IWL_MLD_DROP_SKB,
+ .num_stored = 0,
+ .head_sn = 100,
+ },
+ },
+ {
+ .desc = "RX duplicate packet",
+ .rx_pkt = {
+ .fc = FC_QOS_DATA,
+ .dup = true,
+ },
+ .reorder_buf_state = {
+ .valid = true,
+ .head_sn = 100,
+ },
+ .expected = {
+ /* Duplicate packets should be dropped */
+ .reorder_res = IWL_MLD_DROP_SKB,
+ .num_stored = 0,
+ .head_sn = 100,
+ },
+ },
+ {
+ .desc = "RX In-order packet, sn < nssn",
+ .rx_pkt = {
+ .fc = FC_QOS_DATA,
+ .sn = 100,
+ .nssn = 101,
+ },
+ .reorder_buf_state = {
+ .valid = true,
+ .head_sn = 100,
+ },
+ .expected = {
+ /* 1. Reorder buffer is empty.
+ * 2. RX packet SN is in order and less than NSSN.
+ * Packet is released to the network stack immediately
+ * and buffer->head_sn is updated to NSSN.
+ */
+ .reorder_res = IWL_MLD_PASS_SKB,
+ .num_stored = 0,
+ .head_sn = 101,
+ },
+ },
+ {
+ .desc = "RX In-order packet, sn == head_sn",
+ .rx_pkt = {
+ .fc = FC_QOS_DATA,
+ .sn = 101,
+ .nssn = 100,
+ },
+ .reorder_buf_state = {
+ .valid = true,
+ .head_sn = 101,
+ },
+ .expected = {
+ /* 1. Reorder buffer is empty.
+ * 2. RX packet SN is equal to buffer->head_sn.
+ * Packet is released to the network stack immediately
+ * and buffer->head_sn is incremented.
+ */
+ .reorder_res = IWL_MLD_PASS_SKB,
+ .num_stored = 0,
+ .head_sn = 102,
+ },
+ },
+ {
+ .desc = "RX In-order packet, IEEE80211_MAX_SN wrap around",
+ .rx_pkt = {
+ .fc = FC_QOS_DATA,
+ .sn = IEEE80211_MAX_SN,
+ .nssn = IEEE80211_MAX_SN - 1,
+ },
+ .reorder_buf_state = {
+ .valid = true,
+ .head_sn = IEEE80211_MAX_SN,
+ },
+ .expected = {
+ /* 1. Reorder buffer is empty.
+ * 2. RX SN == buffer->head_sn == IEEE80211_MAX_SN
+ * Packet is released to the network stack immediately
+ * and buffer->head_sn is incremented correctly (wraps
+ * around to 0).
+ */
+ .reorder_res = IWL_MLD_PASS_SKB,
+ .num_stored = 0,
+ .head_sn = 0,
+ },
+ },
+ {
+ .desc = "RX Out-of-order packet, pending packet in buffer",
+ .rx_pkt = {
+ .fc = FC_QOS_DATA,
+ .sn = 100,
+ .nssn = 102,
+ },
+ .reorder_buf_state = {
+ .valid = true,
+ .head_sn = 100,
+ .num_entries = 1,
+ .entries[0].sn = 101,
+ },
+ .expected = {
+ /* 1. Reorder buffer contains one packet with SN=101.
+ * 2. RX packet SN = buffer->head_sn.
+ * Both packets are released (in order) to the network
+ * stack as there are no gaps.
+ */
+ .reorder_res = IWL_MLD_BUFFERED_SKB,
+ .num_stored = 0,
+ .head_sn = 102,
+ .skb_release_order = {100, 101},
+ .skb_release_order_count = 2,
+ },
+ },
+ {
+ .desc = "RX Out-of-order packet, pending packet in buffer (wrap around)",
+ .rx_pkt = {
+ .fc = FC_QOS_DATA,
+ .sn = 0,
+ .nssn = 1,
+ },
+ .reorder_buf_state = {
+ .valid = true,
+ .head_sn = IEEE80211_MAX_SN - 1,
+ .num_entries = 1,
+ .entries[0].sn = IEEE80211_MAX_SN,
+ },
+ .expected = {
+ /* 1. Reorder buffer contains one packet with
+ * SN=IEEE80211_MAX_SN.
+ * 2. RX Packet SN = 0 (after wrap around)
+ * Both packets are released (in order) to the network
+ * stack as there are no gaps.
+ */
+ .reorder_res = IWL_MLD_BUFFERED_SKB,
+ .num_stored = 0,
+ .head_sn = 1,
+ .skb_release_order = { 4095, 0 },
+ .skb_release_order_count = 2,
+ },
+ },
+ {
+ .desc = "RX Out-of-order packet, filling 1/2 holes in buffer, release RX packet",
+ .rx_pkt = {
+ .fc = FC_QOS_DATA,
+ .sn = 100,
+ .nssn = 101,
+ },
+ .reorder_buf_state = {
+ .valid = true,
+ .head_sn = 100,
+ .num_entries = 1,
+ .entries[0].sn = 102,
+ },
+ .expected = {
+ /* 1. Reorder buffer contains one packet with SN=102.
+ * 2. There are 2 holes at SN={100, 101}.
+ * Only the RX packet (SN=100) is released, there is
+ * still a hole at 101.
+ */
+ .reorder_res = IWL_MLD_BUFFERED_SKB,
+ .num_stored = 1,
+ .head_sn = 101,
+ .skb_release_order = {100},
+ .skb_release_order_count = 1,
+ },
+ },
+ {
+ .desc = "RX Out-of-order packet, filling 1/2 holes, release 2 packets",
+ .rx_pkt = {
+ .fc = FC_QOS_DATA,
+ .sn = 102,
+ .nssn = 103,
+ },
+ .reorder_buf_state = {
+ .valid = true,
+ .head_sn = 100,
+ .num_entries = 3,
+ .entries[0].sn = 101,
+ .entries[1].sn = 104,
+ .entries[2].sn = 105,
+ },
+ .expected = {
+ /* 1. Reorder buffer contains three packets.
+ * 2. RX packet fills one of two holes (at SN=102).
+ * Two packets are released (until the next hole at
+ * SN=103).
+ */
+ .reorder_res = IWL_MLD_BUFFERED_SKB,
+ .num_stored = 2,
+ .head_sn = 103,
+ .skb_release_order = {101, 102},
+ .skb_release_order_count = 2,
+ },
+ },
+ {
+ .desc = "RX Out-of-order packet, filling 1/1 holes, no packets released",
+ .rx_pkt = {
+ .fc = FC_QOS_DATA,
+ .sn = 102,
+ .nssn = 100,
+ },
+ .reorder_buf_state = {
+ .valid = true,
+ .head_sn = 100,
+ .num_entries = 3,
+ .entries[0].sn = 101,
+ .entries[1].sn = 103,
+ .entries[2].sn = 104,
+ },
+ .expected = {
+ /* 1. Reorder buffer contains three packets:
+ * SN={101, 103, 104}.
+ * 2. RX packet fills a hole (SN=102), but NSSN is
+ * smaller than buffered frames.
+ * No packets can be released yet and buffer->head_sn
+ * is not updated.
+ */
+ .reorder_res = IWL_MLD_BUFFERED_SKB,
+ .num_stored = 4,
+ .head_sn = 100,
+ },
+ },
+ {
+ .desc = "RX In-order A-MSDU, last subframe",
+ .rx_pkt = {
+ .fc = FC_QOS_DATA,
+ .sn = 100,
+ .nssn = 101,
+ .amsdu = true,
+ .last_subframe = true,
+ },
+ .reorder_buf_state = {
+ .valid = true,
+ .head_sn = 100,
+ .num_entries = 1,
+ .entries[0] = {
+ .sn = 100,
+ .add_subframes = 1,
+ },
+ },
+ .expected = {
+ /* 1. Reorder buffer contains a 2-sub frames A-MSDU
+ * at SN=100.
+ * 2. RX packet is the last SN=100 A-MSDU subframe
+ * All packets are released in order (3 x SN=100).
+ */
+ .reorder_res = IWL_MLD_BUFFERED_SKB,
+ .num_stored = 0,
+ .head_sn = 101,
+ .skb_release_order = {100, 100, 100},
+ .skb_release_order_count = 3,
+ },
+ },
+ {
+ .desc = "RX In-order A-MSDU, not the last subframe",
+ .rx_pkt = {
+ .fc = FC_QOS_DATA,
+ .sn = 100,
+ .nssn = 101,
+ .amsdu = true,
+ .last_subframe = false,
+ },
+ .reorder_buf_state = {
+ .valid = true,
+ .head_sn = 100,
+ .num_entries = 1,
+ .entries[0] = {
+ .sn = 100,
+ .add_subframes = 1,
+ },
+ },
+ .expected = {
+ /* 1. Reorder buffer contains a 2-sub frames A-MSDU
+ * at SN=100.
+ * 2. RX packet additional SN=100 A-MSDU subframe,
+ * but not the last one
+ * No packets are released and head_sn is not updated.
+ */
+ .reorder_res = IWL_MLD_BUFFERED_SKB,
+ .num_stored = 3,
+ .head_sn = 100,
+ },
+ },
+};
+
+KUNIT_ARRAY_PARAM_DESC(test_reorder_buffer, reorder_buffer_cases, desc);
+
+static struct sk_buff_head g_released_skbs;
+static u16 g_num_released_skbs;
+
+/* Add released skbs from reorder buffer to a global list; This allows us
+ * to verify the correct release order of packets after they pass through the
+ * simulated reorder logic.
+ */
+static void
+fake_iwl_mld_pass_packet_to_mac80211(struct iwl_mld *mld,
+ struct napi_struct *napi,
+ struct sk_buff *skb, int queue,
+ struct ieee80211_sta *sta)
+{
+ __skb_queue_tail(&g_released_skbs, skb);
+ g_num_released_skbs++;
+}
+
+static u32
+fake_iwl_mld_fw_sta_id_mask(struct iwl_mld *mld, struct ieee80211_sta *sta)
+{
+ struct iwl_mld_sta *mld_sta = iwl_mld_sta_from_mac80211(sta);
+ struct iwl_mld_link_sta *mld_link_sta;
+ u8 link_id;
+ u32 sta_mask = 0;
+
+ /* This is the expectation in the real function */
+ lockdep_assert_wiphy(mld->wiphy);
+
+ /* We can't use for_each_sta_active_link */
+ for_each_mld_link_sta(mld_sta, mld_link_sta, link_id)
+ sta_mask |= BIT(mld_link_sta->fw_id);
+ return sta_mask;
+}
+
+static struct iwl_rx_mpdu_desc *setup_mpdu_desc(void)
+{
+ struct kunit *test = kunit_get_current_test();
+ const struct reorder_buffer_case *param =
+ (const void *)(test->param_value);
+ struct iwl_rx_mpdu_desc *mpdu_desc;
+
+ KUNIT_ALLOC_AND_ASSERT(test, mpdu_desc);
+
+ mpdu_desc->reorder_data |=
+ le32_encode_bits(param->rx_pkt.baid,
+ IWL_RX_MPDU_REORDER_BAID_MASK);
+ mpdu_desc->reorder_data |=
+ le32_encode_bits(param->rx_pkt.sn,
+ IWL_RX_MPDU_REORDER_SN_MASK);
+ mpdu_desc->reorder_data |=
+ le32_encode_bits(param->rx_pkt.nssn,
+ IWL_RX_MPDU_REORDER_NSSN_MASK);
+ if (param->rx_pkt.old_sn)
+ mpdu_desc->reorder_data |=
+ cpu_to_le32(IWL_RX_MPDU_REORDER_BA_OLD_SN);
+
+ if (param->rx_pkt.dup)
+ mpdu_desc->status |= cpu_to_le32(IWL_RX_MPDU_STATUS_DUPLICATE);
+
+ if (param->rx_pkt.amsdu) {
+ mpdu_desc->mac_flags2 |= IWL_RX_MPDU_MFLG2_AMSDU;
+ if (param->rx_pkt.last_subframe)
+ mpdu_desc->amsdu_info |=
+ IWL_RX_MPDU_AMSDU_LAST_SUBFRAME;
+ }
+
+ return mpdu_desc;
+}
+
+static struct sk_buff *alloc_and_setup_skb(u16 fc, u16 seq_ctrl, u8 tid,
+ bool mcast)
+{
+ struct kunit *test = kunit_get_current_test();
+ struct ieee80211_hdr hdr = {
+ .frame_control = cpu_to_le16(fc),
+ .seq_ctrl = cpu_to_le16(seq_ctrl),
+ };
+ struct sk_buff *skb;
+
+ skb = kunit_zalloc_skb(test, 128, GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, skb);
+
+ if (ieee80211_is_data_qos(hdr.frame_control)) {
+ u8 *qc = ieee80211_get_qos_ctl(&hdr);
+
+ qc[0] = tid & IEEE80211_QOS_CTL_TID_MASK;
+ }
+
+ if (mcast)
+ hdr.addr1[0] = 0x1;
+
+ skb_set_mac_header(skb, skb->len);
+ skb_put_data(skb, &hdr, ieee80211_hdrlen(hdr.frame_control));
+
+ return skb;
+}
+
+static struct iwl_mld_reorder_buffer *
+setup_reorder_buffer(struct iwl_mld_baid_data *baid_data)
+{
+ struct kunit *test = kunit_get_current_test();
+ const struct reorder_buffer_case *param =
+ (const void *)(test->param_value);
+ struct iwl_mld_reorder_buffer *buffer = baid_data->reorder_buf;
+ struct iwl_mld_reorder_buf_entry *entries = baid_data->entries;
+ struct sk_buff *fake_skb;
+
+ buffer->valid = param->reorder_buf_state.valid;
+ buffer->head_sn = param->reorder_buf_state.head_sn;
+ buffer->queue = QUEUE;
+
+ for (int i = 0; i < baid_data->buf_size; i++)
+ __skb_queue_head_init(&entries[i].frames);
+
+ for (int i = 0; i < param->reorder_buf_state.num_entries; i++) {
+ u16 sn = param->reorder_buf_state.entries[i].sn;
+ int index = sn % baid_data->buf_size;
+ u8 add_subframes =
+ param->reorder_buf_state.entries[i].add_subframes;
+ /* create 1 skb per entry + additional skbs per num of
+ * requested subframes
+ */
+ u8 num_skbs = 1 + add_subframes;
+
+ for (int j = 0; j < num_skbs; j++) {
+ fake_skb = alloc_and_setup_skb(FC_QOS_DATA, sn, 0,
+ false);
+ __skb_queue_tail(&entries[index].frames, fake_skb);
+ buffer->num_stored++;
+ }
+ }
+
+ return buffer;
+}
+
+static struct iwl_mld_reorder_buffer *setup_ba_data(struct ieee80211_sta *sta)
+{
+ struct kunit *test = kunit_get_current_test();
+ struct iwl_mld *mld = test->priv;
+ const struct reorder_buffer_case *param =
+ (const void *)(test->param_value);
+ struct iwl_mld_baid_data *baid_data = NULL;
+ struct iwl_mld_reorder_buffer *buffer;
+ u32 reorder_buf_size = BA_WINDOW_SIZE * sizeof(baid_data->entries[0]);
+ u8 baid = param->reorder_buf_state.baid;
+
+ /* Assuming only 1 RXQ */
+ KUNIT_ALLOC_AND_ASSERT_SIZE(test, baid_data,
+ sizeof(*baid_data) + reorder_buf_size);
+
+ baid_data->baid = baid;
+ baid_data->tid = param->rx_pkt.tid;
+ baid_data->buf_size = BA_WINDOW_SIZE;
+
+ wiphy_lock(mld->wiphy);
+ baid_data->sta_mask = iwl_mld_fw_sta_id_mask(mld, sta);
+ wiphy_unlock(mld->wiphy);
+
+ baid_data->entries_per_queue = BA_WINDOW_SIZE;
+
+ buffer = setup_reorder_buffer(baid_data);
+
+ KUNIT_EXPECT_NULL(test, rcu_access_pointer(mld->fw_id_to_ba[baid]));
+ rcu_assign_pointer(mld->fw_id_to_ba[baid], baid_data);
+
+ return buffer;
+}
+
+static void test_reorder_buffer(struct kunit *test)
+{
+ struct iwl_mld *mld = test->priv;
+ const struct reorder_buffer_case *param =
+ (const void *)(test->param_value);
+ struct iwl_rx_mpdu_desc *mpdu_desc;
+ struct ieee80211_vif *vif;
+ struct ieee80211_sta *sta;
+ struct sk_buff *skb;
+ struct iwl_mld_reorder_buffer *buffer;
+ enum iwl_mld_reorder_result reorder_res;
+ u16 skb_release_order_count = param->expected.skb_release_order_count;
+ u16 skb_idx = 0;
+
+ /* Init globals and activate stubs */
+ __skb_queue_head_init(&g_released_skbs);
+ g_num_released_skbs = 0;
+ kunit_activate_static_stub(test, iwl_mld_fw_sta_id_mask,
+ fake_iwl_mld_fw_sta_id_mask);
+ kunit_activate_static_stub(test, iwl_mld_pass_packet_to_mac80211,
+ fake_iwl_mld_pass_packet_to_mac80211);
+
+ vif = iwlmld_kunit_add_vif(false, NL80211_IFTYPE_STATION);
+ sta = iwlmld_kunit_setup_sta(vif, IEEE80211_STA_AUTHORIZED, -1);
+
+ /* Prepare skb, mpdu_desc, BA data and the reorder buffer */
+ skb = alloc_and_setup_skb(param->rx_pkt.fc, param->rx_pkt.sn,
+ param->rx_pkt.tid, param->rx_pkt.multicast);
+ buffer = setup_ba_data(sta);
+ mpdu_desc = setup_mpdu_desc();
+
+ rcu_read_lock();
+ reorder_res = iwl_mld_reorder(mld, NULL, QUEUE, sta, skb, mpdu_desc);
+ rcu_read_unlock();
+
+ KUNIT_ASSERT_EQ(test, reorder_res, param->expected.reorder_res);
+ KUNIT_ASSERT_EQ(test, buffer->num_stored, param->expected.num_stored);
+ KUNIT_ASSERT_EQ(test, buffer->head_sn, param->expected.head_sn);
+
+ /* Verify skbs release order */
+ KUNIT_ASSERT_EQ(test, skb_release_order_count, g_num_released_skbs);
+ while ((skb = __skb_dequeue(&g_released_skbs))) {
+ struct ieee80211_hdr *hdr = (void *)skb_mac_header(skb);
+
+ KUNIT_ASSERT_EQ(test, le16_to_cpu(hdr->seq_ctrl),
+ param->expected.skb_release_order[skb_idx]);
+ skb_idx++;
+ }
+ KUNIT_ASSERT_EQ(test, skb_idx, skb_release_order_count);
+}
+
+static struct kunit_case reorder_buffer_test_cases[] = {
+ KUNIT_CASE_PARAM(test_reorder_buffer, test_reorder_buffer_gen_params),
+ {},
+};
+
+static struct kunit_suite reorder_buffer = {
+ .name = "iwlmld-reorder-buffer",
+ .test_cases = reorder_buffer_test_cases,
+ .init = iwlmld_kunit_test_init,
+};
+
+kunit_test_suite(reorder_buffer);
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/tests/emlsr_with_bt.c b/drivers/net/wireless/intel/iwlwifi/mld/tests/emlsr_with_bt.c
new file mode 100644
index 000000000000..91556ee5c142
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/mld/tests/emlsr_with_bt.c
@@ -0,0 +1,140 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
+/*
+ * KUnit tests for link selection functions
+ *
+ * Copyright (C) 2025 Intel Corporation
+ */
+#include <kunit/static_stub.h>
+
+#include "utils.h"
+#include "mld.h"
+#include "mlo.h"
+
+static const struct emlsr_with_bt_test_case {
+ const char *desc;
+ struct {
+ struct iwl_bt_coex_profile_notif notif;
+ s32 signal;
+ bool check_entry;
+ } input;
+ bool emlsr_allowed;
+} emlsr_with_bt_cases[] = {
+ {
+ .desc = "BT penalty(exit) with low rssi 4.5: emlsr allowed",
+ .input = {
+ .notif.wifi_loss_low_rssi[1] = {4, 5},
+ .notif.wifi_loss_mid_high_rssi[1] = {7, 9},
+ .signal = -69,
+ .check_entry = false,
+ },
+ .emlsr_allowed = true,
+ },
+ {
+ .desc = "BT penalty(exit) from high rssi 5: emlsr allowed",
+ .input = {
+ .notif.wifi_loss_low_rssi[1] = {7, 9},
+ .notif.wifi_loss_mid_high_rssi[1] = {5, 5},
+ .signal = -68,
+ .check_entry = false,
+ },
+ .emlsr_allowed = true,
+ },
+ {
+ .desc = "BT penalty(exit) with low rssi 8: emlsr not allowed",
+ .input = {
+ .notif.wifi_loss_low_rssi[1] = {7, 9},
+ .notif.wifi_loss_mid_high_rssi[1] = {4, 5},
+ .signal = -69,
+ .check_entry = false,
+ },
+ .emlsr_allowed = false,
+ },
+ {
+ .desc = "BT penalty(exit) from high rssi 9: emlsr not allowed",
+ .input = {
+ .notif.wifi_loss_low_rssi[1] = {4, 5},
+ .notif.wifi_loss_mid_high_rssi[1] = {9, 9},
+ .signal = -68,
+ .check_entry = false,
+ },
+ .emlsr_allowed = false,
+ },
+ {
+ .desc = "BT penalty(entry) with low rssi 4.5: emlsr allowed",
+ .input = {
+ .notif.wifi_loss_low_rssi[1] = {4, 5},
+ .notif.wifi_loss_mid_high_rssi[1] = {7, 9},
+ .signal = -63,
+ .check_entry = true,
+ },
+ .emlsr_allowed = true,
+ },
+ {
+ .desc = "BT penalty(entry) from high rssi 5: emlsr allowed",
+ .input = {
+ .notif.wifi_loss_low_rssi[1] = {7, 9},
+ .notif.wifi_loss_mid_high_rssi[1] = {5, 5},
+ .signal = -62,
+ .check_entry = false,
+ },
+ .emlsr_allowed = true,
+ },
+ {
+ .desc = "BT penalty(entry) with low rssi 8: emlsr not allowed",
+ .input = {
+ .notif.wifi_loss_low_rssi[1] = {7, 9},
+ .notif.wifi_loss_mid_high_rssi[1] = {4, 5},
+ .signal = -63,
+ .check_entry = false,
+ },
+ .emlsr_allowed = true,
+ },
+ {
+ .desc = "BT penalty(entry) from high rssi 9: emlsr not allowed",
+ .input = {
+ .notif.wifi_loss_low_rssi[1] = {4, 5},
+ .notif.wifi_loss_mid_high_rssi[1] = {9, 9},
+ .signal = -62,
+ .check_entry = true,
+ },
+ .emlsr_allowed = false,
+ },
+};
+
+KUNIT_ARRAY_PARAM_DESC(emlsr_with_bt, emlsr_with_bt_cases, desc);
+
+static void test_emlsr_with_bt(struct kunit *test)
+{
+ struct iwl_mld *mld = test->priv;
+ const struct emlsr_with_bt_test_case *test_param =
+ (const void *)(test->param_value);
+ struct ieee80211_vif *vif =
+ iwlmld_kunit_add_vif(true, NL80211_IFTYPE_STATION);
+ struct ieee80211_bss_conf *link = iwlmld_kunit_add_link(vif, 1);
+ bool actual_value = false;
+
+ KUNIT_ALLOC_AND_ASSERT(test, link->bss);
+
+ /* Extract test case parameters */
+ link->bss->signal = DBM_TO_MBM(test_param->input.signal);
+ memcpy(&mld->last_bt_notif, &test_param->input.notif,
+ sizeof(struct iwl_bt_coex_profile_notif));
+
+ actual_value = iwl_mld_bt_allows_emlsr(mld, link,
+ test_param->input.check_entry);
+ /* Assert that the returned value matches the expected emlsr_allowed */
+ KUNIT_EXPECT_EQ(test, actual_value, test_param->emlsr_allowed);
+}
+
+static struct kunit_case emlsr_with_bt_test_cases[] = {
+ KUNIT_CASE_PARAM(test_emlsr_with_bt, emlsr_with_bt_gen_params),
+ {},
+};
+
+static struct kunit_suite emlsr_with_bt = {
+ .name = "iwlmld-emlsr-with-bt-tests",
+ .test_cases = emlsr_with_bt_test_cases,
+ .init = iwlmld_kunit_test_init,
+};
+
+kunit_test_suite(emlsr_with_bt);
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/tests/hcmd.c b/drivers/net/wireless/intel/iwlwifi/mld/tests/hcmd.c
new file mode 100644
index 000000000000..0e3b9417dd63
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/mld/tests/hcmd.c
@@ -0,0 +1,62 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
+/*
+ * KUnit tests for channel helper functions
+ *
+ * Copyright (C) 2024-2025 Intel Corporation
+ */
+#include <kunit/test.h>
+
+#include <iwl-trans.h>
+#include "mld.h"
+
+MODULE_IMPORT_NS("EXPORTED_FOR_KUNIT_TESTING");
+
+static void test_hcmd_names_sorted(struct kunit *test)
+{
+ int i;
+
+ for (i = 0; i < global_iwl_mld_goups_size; i++) {
+ const struct iwl_hcmd_arr *arr = &iwl_mld_groups[i];
+ int j;
+
+ if (!arr->arr)
+ continue;
+ for (j = 0; j < arr->size - 1; j++)
+ KUNIT_EXPECT_LE(test, arr->arr[j].cmd_id,
+ arr->arr[j + 1].cmd_id);
+ }
+}
+
+static void test_hcmd_names_for_rx(struct kunit *test)
+{
+ static struct iwl_trans t = {
+ .conf.command_groups = iwl_mld_groups,
+ };
+
+ t.conf.command_groups_size = global_iwl_mld_goups_size;
+
+ for (unsigned int i = 0; i < iwl_mld_rx_handlers_num; i++) {
+ const struct iwl_rx_handler *rxh;
+ const char *name;
+
+ rxh = &iwl_mld_rx_handlers[i];
+
+ name = iwl_get_cmd_string(&t, rxh->cmd_id);
+ KUNIT_EXPECT_NOT_NULL(test, name);
+ KUNIT_EXPECT_NE_MSG(test, strcmp(name, "UNKNOWN"), 0,
+ "ID 0x%04x is UNKNOWN", rxh->cmd_id);
+ }
+}
+
+static struct kunit_case hcmd_names_cases[] = {
+ KUNIT_CASE(test_hcmd_names_sorted),
+ KUNIT_CASE(test_hcmd_names_for_rx),
+ {},
+};
+
+static struct kunit_suite hcmd_names = {
+ .name = "iwlmld-hcmd-names",
+ .test_cases = hcmd_names_cases,
+};
+
+kunit_test_suite(hcmd_names);
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/tests/link-selection.c b/drivers/net/wireless/intel/iwlwifi/mld/tests/link-selection.c
new file mode 100644
index 000000000000..94a037bec1fa
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/mld/tests/link-selection.c
@@ -0,0 +1,345 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
+/*
+ * KUnit tests for link selection functions
+ *
+ * Copyright (C) 2025 Intel Corporation
+ */
+#include <kunit/static_stub.h>
+
+#include "utils.h"
+#include "mld.h"
+#include "link.h"
+#include "iface.h"
+#include "phy.h"
+#include "mlo.h"
+
+static const struct link_grading_test_case {
+ const char *desc;
+ struct {
+ struct {
+ u8 link_id;
+ const struct cfg80211_chan_def *chandef;
+ bool active;
+ s32 signal;
+ bool has_chan_util_elem;
+ u8 chan_util; /* 0-255 , used only if has_chan_util_elem is true */
+ u8 chan_load_by_us; /* 0-100, used only if active is true */;
+ } link;
+ } input;
+ unsigned int expected_grade;
+} link_grading_cases[] = {
+ {
+ .desc = "channel util of 128 (50%)",
+ .input.link = {
+ .link_id = 0,
+ .chandef = &chandef_2ghz_20mhz,
+ .active = false,
+ .has_chan_util_elem = true,
+ .chan_util = 128,
+ },
+ .expected_grade = 86,
+ },
+ {
+ .desc = "channel util of 180 (70%)",
+ .input.link = {
+ .link_id = 0,
+ .chandef = &chandef_2ghz_20mhz,
+ .active = false,
+ .has_chan_util_elem = true,
+ .chan_util = 180,
+ },
+ .expected_grade = 51,
+ },
+ {
+ .desc = "channel util of 180 (70%), channel load by us of 10%",
+ .input.link = {
+ .link_id = 0,
+ .chandef = &chandef_2ghz_20mhz,
+ .has_chan_util_elem = true,
+ .chan_util = 180,
+ .active = true,
+ .chan_load_by_us = 10,
+ },
+ .expected_grade = 67,
+ },
+ {
+ .desc = "no channel util element",
+ .input.link = {
+ .link_id = 0,
+ .chandef = &chandef_2ghz_20mhz,
+ .active = true,
+ },
+ .expected_grade = 120,
+ },
+};
+
+KUNIT_ARRAY_PARAM_DESC(link_grading, link_grading_cases, desc);
+
+static void setup_link(struct ieee80211_bss_conf *link)
+{
+ struct kunit *test = kunit_get_current_test();
+ struct iwl_mld *mld = test->priv;
+ const struct link_grading_test_case *test_param =
+ (const void *)(test->param_value);
+
+ KUNIT_ALLOC_AND_ASSERT(test, link->bss);
+
+ link->bss->signal = DBM_TO_MBM(test_param->input.link.signal);
+
+ link->chanreq.oper = *test_param->input.link.chandef;
+
+ if (test_param->input.link.has_chan_util_elem) {
+ struct cfg80211_bss_ies *ies;
+ struct ieee80211_bss_load_elem bss_load = {
+ .channel_util = test_param->input.link.chan_util,
+ };
+ struct element *elem =
+ iwlmld_kunit_gen_element(WLAN_EID_QBSS_LOAD,
+ &bss_load,
+ sizeof(bss_load));
+ unsigned int elem_len = sizeof(*elem) + sizeof(bss_load);
+
+ KUNIT_ALLOC_AND_ASSERT_SIZE(test, ies, sizeof(*ies) + elem_len);
+ memcpy(ies->data, elem, elem_len);
+ ies->len = elem_len;
+ rcu_assign_pointer(link->bss->beacon_ies, ies);
+ rcu_assign_pointer(link->bss->ies, ies);
+ }
+
+ if (test_param->input.link.active) {
+ struct ieee80211_chanctx_conf *chan_ctx =
+ wiphy_dereference(mld->wiphy, link->chanctx_conf);
+ struct iwl_mld_phy *phy;
+
+ KUNIT_ASSERT_NOT_NULL(test, chan_ctx);
+
+ phy = iwl_mld_phy_from_mac80211(chan_ctx);
+
+ phy->channel_load_by_us = test_param->input.link.chan_load_by_us;
+ }
+}
+
+static void test_link_grading(struct kunit *test)
+{
+ struct iwl_mld *mld = test->priv;
+ const struct link_grading_test_case *test_param =
+ (const void *)(test->param_value);
+ struct ieee80211_vif *vif;
+ struct ieee80211_bss_conf *link;
+ unsigned int actual_grade;
+ /* Extract test case parameters */
+ u8 link_id = test_param->input.link.link_id;
+ bool active = test_param->input.link.active;
+ u16 valid_links;
+ struct iwl_mld_kunit_link assoc_link = {
+ .chandef = test_param->input.link.chandef,
+ };
+
+ /* If the link is not active, use a different link as the assoc link */
+ if (active) {
+ assoc_link.id = link_id;
+ valid_links = BIT(link_id);
+ } else {
+ assoc_link.id = BIT(ffz(BIT(link_id)));
+ valid_links = BIT(assoc_link.id) | BIT(link_id);
+ }
+
+ vif = iwlmld_kunit_setup_mlo_assoc(valid_links, &assoc_link);
+
+ wiphy_lock(mld->wiphy);
+ link = wiphy_dereference(mld->wiphy, vif->link_conf[link_id]);
+ KUNIT_ASSERT_NOT_NULL(test, link);
+
+ setup_link(link);
+
+ actual_grade = iwl_mld_get_link_grade(mld, link);
+ wiphy_unlock(mld->wiphy);
+
+ /* Assert that the returned grade matches the expected grade */
+ KUNIT_EXPECT_EQ(test, actual_grade, test_param->expected_grade);
+}
+
+static struct kunit_case link_selection_cases[] = {
+ KUNIT_CASE_PARAM(test_link_grading, link_grading_gen_params),
+ {},
+};
+
+static struct kunit_suite link_selection = {
+ .name = "iwlmld-link-selection-tests",
+ .test_cases = link_selection_cases,
+ .init = iwlmld_kunit_test_init,
+};
+
+kunit_test_suite(link_selection);
+
+static const struct link_pair_case {
+ const char *desc;
+ const struct cfg80211_chan_def *chandef_a, *chandef_b;
+ bool low_latency_vif;
+ u32 chan_load_not_by_us;
+ bool primary_link_active;
+ u32 expected_result;
+} link_pair_cases[] = {
+ {
+ .desc = "Unequal bandwidth, primary link inactive, EMLSR not allowed",
+ .low_latency_vif = false,
+ .primary_link_active = false,
+ .chandef_a = &chandef_5ghz_40mhz,
+ .chandef_b = &chandef_6ghz_20mhz,
+ .expected_result = IWL_MLD_EMLSR_EXIT_CHAN_LOAD,
+ },
+ {
+ .desc = "Equal bandwidths, sufficient channel load, EMLSR allowed",
+ .low_latency_vif = false,
+ .primary_link_active = true,
+ .chan_load_not_by_us = 11,
+ .chandef_a = &chandef_5ghz_40mhz,
+ .chandef_b = &chandef_6ghz_40mhz,
+ .expected_result = 0,
+ },
+ {
+ .desc = "Equal bandwidths, insufficient channel load, EMLSR not allowed",
+ .low_latency_vif = false,
+ .primary_link_active = true,
+ .chan_load_not_by_us = 6,
+ .chandef_a = &chandef_5ghz_80mhz,
+ .chandef_b = &chandef_6ghz_80mhz,
+ .expected_result = IWL_MLD_EMLSR_EXIT_CHAN_LOAD,
+ },
+ {
+ .desc = "Low latency VIF, sufficient channel load, EMLSR allowed",
+ .low_latency_vif = true,
+ .primary_link_active = true,
+ .chan_load_not_by_us = 6,
+ .chandef_a = &chandef_5ghz_160mhz,
+ .chandef_b = &chandef_6ghz_160mhz,
+ .expected_result = 0,
+ },
+ {
+ .desc = "Different bandwidths (2x ratio), primary link load permits EMLSR",
+ .low_latency_vif = false,
+ .primary_link_active = true,
+ .chan_load_not_by_us = 30,
+ .chandef_a = &chandef_5ghz_40mhz,
+ .chandef_b = &chandef_6ghz_20mhz,
+ .expected_result = 0,
+ },
+ {
+ .desc = "Different bandwidths (4x ratio), primary link load permits EMLSR",
+ .low_latency_vif = false,
+ .primary_link_active = true,
+ .chan_load_not_by_us = 45,
+ .chandef_a = &chandef_5ghz_80mhz,
+ .chandef_b = &chandef_6ghz_20mhz,
+ .expected_result = 0,
+ },
+ {
+ .desc = "Different bandwidths (16x ratio), primary link load insufficient",
+ .low_latency_vif = false,
+ .primary_link_active = true,
+ .chan_load_not_by_us = 45,
+ .chandef_a = &chandef_6ghz_320mhz,
+ .chandef_b = &chandef_5ghz_20mhz,
+ .expected_result = IWL_MLD_EMLSR_EXIT_CHAN_LOAD,
+ },
+ {
+ .desc = "Same band not allowed (2.4 GHz)",
+ .low_latency_vif = false,
+ .primary_link_active = true,
+ .chan_load_not_by_us = 30,
+ .chandef_a = &chandef_2ghz_20mhz,
+ .chandef_b = &chandef_2ghz_11_20mhz,
+ .expected_result = IWL_MLD_EMLSR_EXIT_EQUAL_BAND,
+ },
+ {
+ .desc = "Same band not allowed (5 GHz)",
+ .low_latency_vif = false,
+ .primary_link_active = true,
+ .chan_load_not_by_us = 30,
+ .chandef_a = &chandef_5ghz_40mhz,
+ .chandef_b = &chandef_5ghz_40mhz,
+ .expected_result = IWL_MLD_EMLSR_EXIT_EQUAL_BAND,
+ },
+ {
+ .desc = "Same band allowed (5 GHz separated)",
+ .low_latency_vif = false,
+ .primary_link_active = true,
+ .chan_load_not_by_us = 30,
+ .chandef_a = &chandef_5ghz_40mhz,
+ .chandef_b = &chandef_5ghz_120_40mhz,
+ .expected_result = 0,
+ },
+ {
+ .desc = "Same band not allowed (6 GHz)",
+ .low_latency_vif = false,
+ .primary_link_active = true,
+ .chan_load_not_by_us = 30,
+ .chandef_a = &chandef_6ghz_160mhz,
+ .chandef_b = &chandef_6ghz_221_160mhz,
+ .expected_result = IWL_MLD_EMLSR_EXIT_EQUAL_BAND,
+ },
+};
+
+KUNIT_ARRAY_PARAM_DESC(link_pair, link_pair_cases, desc);
+
+static void test_iwl_mld_link_pair_allows_emlsr(struct kunit *test)
+{
+ const struct link_pair_case *params = test->param_value;
+ struct iwl_mld *mld = test->priv;
+ struct ieee80211_vif *vif;
+ struct ieee80211_bss_conf *link;
+ /* link A is the primary and link B is the secondary */
+ struct iwl_mld_link_sel_data a = {
+ .chandef = params->chandef_a,
+ .link_id = 4,
+ };
+ struct iwl_mld_link_sel_data b = {
+ .chandef = params->chandef_b,
+ .link_id = 5,
+ };
+ struct iwl_mld_kunit_link assoc_link = {
+ .chandef = params->primary_link_active ? a.chandef : b.chandef,
+ .id = params->primary_link_active ? a.link_id : b.link_id,
+ };
+ u32 result;
+
+ vif = iwlmld_kunit_setup_mlo_assoc(BIT(a.link_id) | BIT(b.link_id),
+ &assoc_link);
+
+ if (params->low_latency_vif)
+ iwl_mld_vif_from_mac80211(vif)->low_latency_causes = 1;
+
+ wiphy_lock(mld->wiphy);
+
+ link = wiphy_dereference(mld->wiphy, vif->link_conf[a.link_id]);
+ KUNIT_ALLOC_AND_ASSERT(test, link->bss);
+ link = wiphy_dereference(mld->wiphy, vif->link_conf[b.link_id]);
+ KUNIT_ALLOC_AND_ASSERT(test, link->bss);
+
+ /* Simulate channel load */
+ if (params->primary_link_active) {
+ struct iwl_mld_phy *phy =
+ iwlmld_kunit_get_phy_of_link(vif, a.link_id);
+
+ phy->avg_channel_load_not_by_us = params->chan_load_not_by_us;
+ }
+
+ result = iwl_mld_emlsr_pair_state(vif, &a, &b);
+
+ wiphy_unlock(mld->wiphy);
+
+ KUNIT_EXPECT_EQ(test, result, params->expected_result);
+}
+
+static struct kunit_case link_pair_criteria_test_cases[] = {
+ KUNIT_CASE_PARAM(test_iwl_mld_link_pair_allows_emlsr, link_pair_gen_params),
+ {}
+};
+
+static struct kunit_suite link_pair_criteria_tests = {
+ .name = "iwlmld_link_pair_allows_emlsr",
+ .test_cases = link_pair_criteria_test_cases,
+ .init = iwlmld_kunit_test_init,
+};
+
+kunit_test_suite(link_pair_criteria_tests);
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/tests/link.c b/drivers/net/wireless/intel/iwlwifi/mld/tests/link.c
new file mode 100644
index 000000000000..69a0d67858bf
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/mld/tests/link.c
@@ -0,0 +1,110 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
+/*
+ * KUnit tests for channel helper functions
+ *
+ * Copyright (C) 2024-2025 Intel Corporation
+ */
+#include <kunit/static_stub.h>
+
+#include "utils.h"
+#include "mld.h"
+#include "link.h"
+#include "iface.h"
+#include "fw/api/mac-cfg.h"
+
+static const struct missed_beacon_test_case {
+ const char *desc;
+ struct {
+ struct iwl_missed_beacons_notif notif;
+ bool emlsr;
+ } input;
+ struct {
+ bool disconnected;
+ bool emlsr;
+ } output;
+} missed_beacon_cases[] = {
+ {
+ .desc = "no EMLSR, no disconnect",
+ .input.notif = {
+ .consec_missed_beacons = cpu_to_le32(4),
+ },
+ },
+ {
+ .desc = "no EMLSR, no beacon loss since Rx, no disconnect",
+ .input.notif = {
+ .consec_missed_beacons = cpu_to_le32(20),
+ },
+ },
+ {
+ .desc = "no EMLSR, beacon loss since Rx, disconnect",
+ .input.notif = {
+ .consec_missed_beacons = cpu_to_le32(20),
+ .consec_missed_beacons_since_last_rx =
+ cpu_to_le32(10),
+ },
+ .output.disconnected = true,
+ },
+};
+
+KUNIT_ARRAY_PARAM_DESC(test_missed_beacon, missed_beacon_cases, desc);
+
+static void fake_ieee80211_connection_loss(struct ieee80211_vif *vif)
+{
+ vif->cfg.assoc = false;
+}
+
+static void test_missed_beacon(struct kunit *test)
+{
+ struct iwl_mld *mld = test->priv;
+ struct iwl_missed_beacons_notif *notif;
+ const struct missed_beacon_test_case *test_param =
+ (const void *)(test->param_value);
+ struct ieee80211_vif *vif;
+ struct iwl_rx_packet *pkt;
+ struct iwl_mld_kunit_link link1 = {
+ .id = 0,
+ .chandef = &chandef_6ghz_160mhz,
+ };
+ struct iwl_mld_kunit_link link2 = {
+ .id = 1,
+ .chandef = &chandef_5ghz_80mhz,
+ };
+
+ kunit_activate_static_stub(test, ieee80211_connection_loss,
+ fake_ieee80211_connection_loss);
+ pkt = iwl_mld_kunit_create_pkt(test_param->input.notif);
+ notif = (void *)pkt->data;
+
+ if (test_param->input.emlsr) {
+ vif = iwlmld_kunit_assoc_emlsr(&link1, &link2);
+ } else {
+ struct iwl_mld_vif *mld_vif;
+
+ vif = iwlmld_kunit_setup_non_mlo_assoc(&link1);
+ mld_vif = iwl_mld_vif_from_mac80211(vif);
+ notif->link_id = cpu_to_le32(mld_vif->deflink.fw_id);
+ }
+
+ wiphy_lock(mld->wiphy);
+
+ iwl_mld_handle_missed_beacon_notif(mld, pkt);
+
+ wiphy_unlock(mld->wiphy);
+
+ KUNIT_ASSERT_NE(test, vif->cfg.assoc, test_param->output.disconnected);
+
+ /* TODO: add test cases for esr and check */
+}
+
+static struct kunit_case link_cases[] = {
+ KUNIT_CASE_PARAM(test_missed_beacon, test_missed_beacon_gen_params),
+ {},
+};
+
+static struct kunit_suite link = {
+ .name = "iwlmld-link",
+ .test_cases = link_cases,
+ .init = iwlmld_kunit_test_init,
+};
+
+kunit_test_suite(link);
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/tests/module.c b/drivers/net/wireless/intel/iwlwifi/mld/tests/module.c
new file mode 100644
index 000000000000..5d9818587b23
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/mld/tests/module.c
@@ -0,0 +1,11 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
+/*
+ * This is just module boilerplate for the iwlmld kunit module.
+ *
+ * Copyright (C) 2024 Intel Corporation
+ */
+#include <linux/module.h>
+
+MODULE_IMPORT_NS("IWLWIFI");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("kunit tests for iwlmld");
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/tests/rx.c b/drivers/net/wireless/intel/iwlwifi/mld/tests/rx.c
new file mode 100644
index 000000000000..20cb4e03ab41
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/mld/tests/rx.c
@@ -0,0 +1,353 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
+/*
+ * KUnit tests for channel helper functions
+ *
+ * Copyright (C) 2024 Intel Corporation
+ */
+#include <kunit/test.h>
+#include "utils.h"
+#include "iwl-trans.h"
+#include "mld.h"
+#include "sta.h"
+
+static const struct is_dup_case {
+ const char *desc;
+ struct {
+ /* ieee80211_hdr fields */
+ __le16 fc;
+ __le16 seq;
+ u8 tid;
+ bool multicast;
+ /* iwl_rx_mpdu_desc fields */
+ bool is_amsdu;
+ u8 sub_frame_idx;
+ } rx_pkt;
+ struct {
+ __le16 last_seq;
+ u8 last_sub_frame_idx;
+ u8 tid;
+ } dup_data_state;
+ struct {
+ bool is_dup;
+ u32 rx_status_flag;
+ } result;
+} is_dup_cases[] = {
+ {
+ .desc = "Control frame",
+ .rx_pkt = {
+ .fc = __cpu_to_le16(IEEE80211_FTYPE_CTL),
+ },
+ .result = {
+ .is_dup = false,
+ .rx_status_flag = 0,
+ }
+ },
+ {
+ .desc = "Null func frame",
+ .rx_pkt = {
+ .fc = __cpu_to_le16(IEEE80211_FTYPE_DATA |
+ IEEE80211_STYPE_NULLFUNC),
+ },
+ .result = {
+ .is_dup = false,
+ .rx_status_flag = 0,
+ }
+ },
+ {
+ .desc = "Multicast data",
+ .rx_pkt = {
+ .fc = __cpu_to_le16(IEEE80211_FTYPE_DATA),
+ .multicast = true,
+ },
+ .result = {
+ .is_dup = false,
+ .rx_status_flag = 0,
+ }
+ },
+ {
+ .desc = "QoS null func frame",
+ .rx_pkt = {
+ .fc = __cpu_to_le16(IEEE80211_FTYPE_DATA |
+ IEEE80211_STYPE_QOS_NULLFUNC),
+ },
+ .result = {
+ .is_dup = false,
+ .rx_status_flag = 0,
+ }
+ },
+ {
+ .desc = "QoS data new sequence",
+ .rx_pkt = {
+ .fc = __cpu_to_le16(IEEE80211_FTYPE_DATA |
+ IEEE80211_STYPE_QOS_DATA),
+ .seq = __cpu_to_le16(0x101),
+ },
+ .dup_data_state = {
+ .last_seq = __cpu_to_le16(0x100),
+ .last_sub_frame_idx = 0,
+ },
+ .result = {
+ .is_dup = false,
+ .rx_status_flag = RX_FLAG_DUP_VALIDATED,
+ },
+ },
+ {
+ .desc = "QoS data same sequence, no retry",
+ .rx_pkt = {
+ .fc = __cpu_to_le16(IEEE80211_FTYPE_DATA |
+ IEEE80211_STYPE_QOS_DATA),
+ .seq = __cpu_to_le16(0x100),
+ },
+ .dup_data_state = {
+ .last_seq = __cpu_to_le16(0x100),
+ .last_sub_frame_idx = 0,
+ },
+ .result = {
+ .is_dup = false,
+ .rx_status_flag = RX_FLAG_DUP_VALIDATED,
+ },
+ },
+ {
+ .desc = "QoS data same sequence, has retry",
+ .rx_pkt = {
+ .fc = __cpu_to_le16(IEEE80211_FTYPE_DATA |
+ IEEE80211_STYPE_QOS_DATA |
+ IEEE80211_FCTL_RETRY),
+ .seq = __cpu_to_le16(0x100),
+ },
+ .dup_data_state = {
+ .last_seq = __cpu_to_le16(0x100),
+ .last_sub_frame_idx = 0,
+ },
+ .result = {
+ .is_dup = true,
+ .rx_status_flag = 0,
+ },
+ },
+ {
+ .desc = "QoS data invalid tid",
+ .rx_pkt = {
+ .fc = __cpu_to_le16(IEEE80211_FTYPE_DATA |
+ IEEE80211_STYPE_QOS_DATA),
+ .seq = __cpu_to_le16(0x100),
+ .tid = IWL_MAX_TID_COUNT + 1,
+ },
+ .result = {
+ .is_dup = true,
+ .rx_status_flag = 0,
+ },
+ },
+ {
+ .desc = "non-QoS data, same sequence, same tid, no retry",
+ .rx_pkt = {
+ /* Driver will use tid = IWL_MAX_TID_COUNT */
+ .fc = __cpu_to_le16(IEEE80211_FTYPE_DATA),
+ .seq = __cpu_to_le16(0x100),
+ },
+ .dup_data_state = {
+ .tid = IWL_MAX_TID_COUNT,
+ .last_seq = __cpu_to_le16(0x100),
+ .last_sub_frame_idx = 0,
+ },
+ .result = {
+ .is_dup = false,
+ .rx_status_flag = RX_FLAG_DUP_VALIDATED,
+ },
+ },
+ {
+ .desc = "non-QoS data, same sequence, same tid, has retry",
+ .rx_pkt = {
+ /* Driver will use tid = IWL_MAX_TID_COUNT */
+ .fc = __cpu_to_le16(IEEE80211_FTYPE_DATA |
+ IEEE80211_FCTL_RETRY),
+ .seq = __cpu_to_le16(0x100),
+ },
+ .dup_data_state = {
+ .tid = IWL_MAX_TID_COUNT,
+ .last_seq = __cpu_to_le16(0x100),
+ .last_sub_frame_idx = 0,
+ },
+ .result = {
+ .is_dup = true,
+ .rx_status_flag = 0,
+ },
+ },
+ {
+ .desc = "non-QoS data, same sequence on different tid's",
+ .rx_pkt = {
+ /* Driver will use tid = IWL_MAX_TID_COUNT */
+ .fc = __cpu_to_le16(IEEE80211_FTYPE_DATA),
+ .seq = __cpu_to_le16(0x100),
+ },
+ .dup_data_state = {
+ .tid = 7,
+ .last_seq = __cpu_to_le16(0x100),
+ .last_sub_frame_idx = 0,
+ },
+ .result = {
+ .is_dup = false,
+ .rx_status_flag = RX_FLAG_DUP_VALIDATED,
+ },
+ },
+ {
+ .desc = "A-MSDU new subframe, allow same PN",
+ .rx_pkt = {
+ .fc = __cpu_to_le16(IEEE80211_FTYPE_DATA |
+ IEEE80211_STYPE_QOS_DATA),
+ .seq = __cpu_to_le16(0x100),
+ .is_amsdu = true,
+ .sub_frame_idx = 1,
+ },
+ .dup_data_state = {
+ .last_seq = __cpu_to_le16(0x100),
+ .last_sub_frame_idx = 0,
+ },
+ .result = {
+ .is_dup = false,
+ .rx_status_flag = RX_FLAG_ALLOW_SAME_PN |
+ RX_FLAG_DUP_VALIDATED,
+ },
+ },
+ {
+ .desc = "A-MSDU subframe with smaller idx, disallow same PN",
+ .rx_pkt = {
+ .fc = __cpu_to_le16(IEEE80211_FTYPE_DATA |
+ IEEE80211_STYPE_QOS_DATA),
+ .seq = __cpu_to_le16(0x100),
+ .is_amsdu = true,
+ .sub_frame_idx = 1,
+ },
+ .dup_data_state = {
+ .last_seq = __cpu_to_le16(0x100),
+ .last_sub_frame_idx = 2,
+ },
+ .result = {
+ .is_dup = false,
+ .rx_status_flag = RX_FLAG_DUP_VALIDATED,
+ },
+ },
+ {
+ .desc = "A-MSDU same subframe, no retry, disallow same PN",
+ .rx_pkt = {
+ .fc = __cpu_to_le16(IEEE80211_FTYPE_DATA |
+ IEEE80211_STYPE_QOS_DATA),
+ .seq = __cpu_to_le16(0x100),
+ .is_amsdu = true,
+ .sub_frame_idx = 0,
+ },
+ .dup_data_state = {
+ .last_seq = __cpu_to_le16(0x100),
+ .last_sub_frame_idx = 0,
+ },
+ .result = {
+ .is_dup = false,
+ .rx_status_flag = RX_FLAG_DUP_VALIDATED,
+ },
+ },
+ {
+ .desc = "A-MSDU same subframe, has retry",
+ .rx_pkt = {
+ .fc = __cpu_to_le16(IEEE80211_FTYPE_DATA |
+ IEEE80211_STYPE_QOS_DATA |
+ IEEE80211_FCTL_RETRY),
+ .seq = __cpu_to_le16(0x100),
+ .is_amsdu = true,
+ .sub_frame_idx = 0,
+ },
+ .dup_data_state = {
+ .last_seq = __cpu_to_le16(0x100),
+ .last_sub_frame_idx = 0,
+ },
+ .result = {
+ .is_dup = true,
+ .rx_status_flag = 0,
+ },
+ },
+};
+
+KUNIT_ARRAY_PARAM_DESC(test_is_dup, is_dup_cases, desc);
+
+static void
+setup_dup_data_state(struct ieee80211_sta *sta)
+{
+ struct kunit *test = kunit_get_current_test();
+ const struct is_dup_case *param = (const void *)(test->param_value);
+ struct iwl_mld_sta *mld_sta = iwl_mld_sta_from_mac80211(sta);
+ u8 tid = param->dup_data_state.tid;
+ struct iwl_mld_rxq_dup_data *dup_data;
+
+ /* Allocate dup_data only for 1 queue */
+ KUNIT_ALLOC_AND_ASSERT(test, dup_data);
+
+ /* Initialize dup data, see iwl_mld_alloc_dup_data */
+ memset(dup_data->last_seq, 0xff, sizeof(dup_data->last_seq));
+
+ dup_data->last_seq[tid] = param->dup_data_state.last_seq;
+ dup_data->last_sub_frame_idx[tid] =
+ param->dup_data_state.last_sub_frame_idx;
+
+ mld_sta->dup_data = dup_data;
+}
+
+static void setup_rx_pkt(const struct is_dup_case *param,
+ struct ieee80211_hdr *hdr,
+ struct iwl_rx_mpdu_desc *mpdu_desc)
+{
+ u8 tid = param->rx_pkt.tid;
+
+ /* Set "new rx packet" header */
+ hdr->frame_control = param->rx_pkt.fc;
+ hdr->seq_ctrl = param->rx_pkt.seq;
+
+ if (ieee80211_is_data_qos(hdr->frame_control)) {
+ u8 *qc = ieee80211_get_qos_ctl(hdr);
+
+ qc[0] = tid & IEEE80211_QOS_CTL_TID_MASK;
+ }
+
+ if (param->rx_pkt.multicast)
+ hdr->addr1[0] = 0x1;
+
+ /* Set mpdu_desc */
+ mpdu_desc->amsdu_info = param->rx_pkt.sub_frame_idx &
+ IWL_RX_MPDU_AMSDU_SUBFRAME_IDX_MASK;
+ if (param->rx_pkt.is_amsdu)
+ mpdu_desc->mac_flags2 |= IWL_RX_MPDU_MFLG2_AMSDU;
+}
+
+static void test_is_dup(struct kunit *test)
+{
+ const struct is_dup_case *param = (const void *)(test->param_value);
+ struct iwl_mld *mld = test->priv;
+ struct iwl_rx_mpdu_desc mpdu_desc = { };
+ struct ieee80211_rx_status rx_status = { };
+ struct ieee80211_vif *vif;
+ struct ieee80211_sta *sta;
+ struct ieee80211_hdr hdr;
+
+ vif = iwlmld_kunit_add_vif(false, NL80211_IFTYPE_STATION);
+ sta = iwlmld_kunit_setup_sta(vif, IEEE80211_STA_AUTHORIZED, -1);
+
+ /* Prepare test case state */
+ setup_dup_data_state(sta);
+ setup_rx_pkt(param, &hdr, &mpdu_desc);
+
+ KUNIT_EXPECT_EQ(test,
+ iwl_mld_is_dup(mld, sta, &hdr, &mpdu_desc, &rx_status,
+ 0), /* assuming only 1 queue */
+ param->result.is_dup);
+ KUNIT_EXPECT_EQ(test, rx_status.flag, param->result.rx_status_flag);
+}
+
+static struct kunit_case is_dup_test_cases[] = {
+ KUNIT_CASE_PARAM(test_is_dup, test_is_dup_gen_params),
+ {},
+};
+
+static struct kunit_suite is_dup = {
+ .name = "iwlmld-rx-is-dup",
+ .test_cases = is_dup_test_cases,
+ .init = iwlmld_kunit_test_init,
+};
+
+kunit_test_suite(is_dup);
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/tests/utils.c b/drivers/net/wireless/intel/iwlwifi/mld/tests/utils.c
new file mode 100644
index 000000000000..26cf27be762d
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/mld/tests/utils.c
@@ -0,0 +1,503 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
+/*
+ * KUnit tests for channel helper functions
+ *
+ * Copyright (C) 2024-2025 Intel Corporation
+ */
+#include <kunit/test.h>
+#include <kunit/test-bug.h>
+
+#include "utils.h"
+
+#include <linux/device.h>
+
+#include "fw/api/scan.h"
+#include "fw/api/mac-cfg.h"
+#include "iwl-trans.h"
+#include "mld.h"
+#include "iface.h"
+#include "link.h"
+#include "phy.h"
+#include "sta.h"
+
+int iwlmld_kunit_test_init(struct kunit *test)
+{
+ struct iwl_mld *mld;
+ struct iwl_trans *trans;
+ const struct iwl_rf_cfg *cfg;
+ struct iwl_fw *fw;
+ struct ieee80211_hw *hw;
+
+ KUNIT_ALLOC_AND_ASSERT(test, trans);
+ KUNIT_ALLOC_AND_ASSERT(test, trans->dev);
+ KUNIT_ALLOC_AND_ASSERT(test, cfg);
+ KUNIT_ALLOC_AND_ASSERT(test, fw);
+ KUNIT_ALLOC_AND_ASSERT(test, hw);
+ KUNIT_ALLOC_AND_ASSERT(test, hw->wiphy);
+
+ mutex_init(&hw->wiphy->mtx);
+
+ /* Allocate and initialize the mld structure */
+ KUNIT_ALLOC_AND_ASSERT(test, mld);
+ iwl_construct_mld(mld, trans, cfg, fw, hw, NULL);
+
+ fw->ucode_capa.num_stations = IWL_STATION_COUNT_MAX;
+ fw->ucode_capa.num_links = IWL_FW_MAX_LINK_ID + 1;
+
+ mld->fwrt.trans = trans;
+ mld->fwrt.fw = fw;
+ mld->fwrt.dev = trans->dev;
+
+ /* TODO: add priv_size to hw allocation and setup hw->priv to enable
+ * testing mac80211 callbacks
+ */
+
+ KUNIT_ALLOC_AND_ASSERT(test, mld->nvm_data);
+ KUNIT_ALLOC_AND_ASSERT_SIZE(test, mld->scan.cmd,
+ sizeof(struct iwl_scan_req_umac_v17));
+ mld->scan.cmd_size = sizeof(struct iwl_scan_req_umac_v17);
+
+ /* This is not the state at the end of the regular opmode_start,
+ * but it is more common to need it. Explicitly undo this if needed.
+ */
+ mld->trans->state = IWL_TRANS_FW_ALIVE;
+ mld->fw_status.running = true;
+
+ /* Avoid passing mld struct around */
+ test->priv = mld;
+ return 0;
+}
+
+IWL_MLD_ALLOC_FN(link, bss_conf)
+
+static void iwlmld_kunit_init_link(struct ieee80211_vif *vif,
+ struct ieee80211_bss_conf *link,
+ struct iwl_mld_link *mld_link, int link_id)
+{
+ struct kunit *test = kunit_get_current_test();
+ struct iwl_mld *mld = test->priv;
+ struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
+ int ret;
+
+ /* setup mac80211 link */
+ rcu_assign_pointer(vif->link_conf[link_id], link);
+ link->link_id = link_id;
+ link->vif = vif;
+ link->beacon_int = 100;
+ link->dtim_period = 3;
+ link->qos = true;
+
+ /* and mld_link */
+ ret = iwl_mld_allocate_link_fw_id(mld, &mld_link->fw_id, link);
+ KUNIT_ASSERT_EQ(test, ret, 0);
+ rcu_assign_pointer(mld_vif->link[link_id], mld_link);
+ rcu_assign_pointer(vif->link_conf[link_id], link);
+}
+
+IWL_MLD_ALLOC_FN(vif, vif)
+
+/* Helper function to add and initialize a VIF for KUnit tests */
+struct ieee80211_vif *iwlmld_kunit_add_vif(bool mlo, enum nl80211_iftype type)
+{
+ struct kunit *test = kunit_get_current_test();
+ struct iwl_mld *mld = test->priv;
+ struct ieee80211_vif *vif;
+ struct iwl_mld_vif *mld_vif;
+ int ret;
+
+ /* TODO: support more types */
+ KUNIT_ASSERT_EQ(test, type, NL80211_IFTYPE_STATION);
+
+ KUNIT_ALLOC_AND_ASSERT_SIZE(test, vif,
+ sizeof(*vif) + sizeof(*mld_vif));
+
+ vif->type = type;
+ mld_vif = iwl_mld_vif_from_mac80211(vif);
+ mld_vif->mld = mld;
+
+ ret = iwl_mld_allocate_vif_fw_id(mld, &mld_vif->fw_id, vif);
+ KUNIT_ASSERT_EQ(test, ret, 0);
+
+ /* TODO: revisit (task=EHT) */
+ if (mlo)
+ return vif;
+
+ /* Initialize the default link */
+ iwlmld_kunit_init_link(vif, &vif->bss_conf, &mld_vif->deflink, 0);
+
+ return vif;
+}
+
+/* Use only for MLO vif */
+struct ieee80211_bss_conf *
+iwlmld_kunit_add_link(struct ieee80211_vif *vif, int link_id)
+{
+ struct kunit *test = kunit_get_current_test();
+ struct ieee80211_bss_conf *link;
+ struct iwl_mld_link *mld_link;
+
+ KUNIT_ALLOC_AND_ASSERT(test, link);
+ KUNIT_ALLOC_AND_ASSERT(test, mld_link);
+
+ iwlmld_kunit_init_link(vif, link, mld_link, link_id);
+ vif->valid_links |= BIT(link_id);
+
+ return link;
+}
+
+struct ieee80211_chanctx_conf *
+iwlmld_kunit_add_chanctx(const struct cfg80211_chan_def *def)
+{
+ struct kunit *test = kunit_get_current_test();
+ struct iwl_mld *mld = test->priv;
+ struct ieee80211_chanctx_conf *ctx;
+ struct iwl_mld_phy *phy;
+ int fw_id;
+
+ KUNIT_ALLOC_AND_ASSERT_SIZE(test, ctx, sizeof(*ctx) + sizeof(*phy));
+
+ /* Setup the chanctx conf */
+ ctx->def = *def;
+ ctx->min_def = *def;
+ ctx->ap = *def;
+
+ /* and the iwl_mld_phy */
+ phy = iwl_mld_phy_from_mac80211(ctx);
+
+ fw_id = iwl_mld_allocate_fw_phy_id(mld);
+ KUNIT_ASSERT_GE(test, fw_id, 0);
+
+ phy->fw_id = fw_id;
+ phy->mld = mld;
+ phy->chandef = *def;
+
+ return ctx;
+}
+
+void iwlmld_kunit_assign_chanctx_to_link(struct ieee80211_vif *vif,
+ struct ieee80211_bss_conf *link,
+ struct ieee80211_chanctx_conf *ctx)
+{
+ struct kunit *test = kunit_get_current_test();
+ struct iwl_mld *mld = test->priv;
+ struct iwl_mld_link *mld_link;
+
+ KUNIT_EXPECT_NULL(test, rcu_access_pointer(link->chanctx_conf));
+ rcu_assign_pointer(link->chanctx_conf, ctx);
+
+ lockdep_assert_wiphy(mld->wiphy);
+
+ mld_link = iwl_mld_link_from_mac80211(link);
+
+ KUNIT_EXPECT_NULL(test, rcu_access_pointer(mld_link->chan_ctx));
+ KUNIT_EXPECT_FALSE(test, mld_link->active);
+
+ rcu_assign_pointer(mld_link->chan_ctx, ctx);
+ mld_link->active = true;
+
+ if (ieee80211_vif_is_mld(vif))
+ vif->active_links |= BIT(link->link_id);
+}
+
+IWL_MLD_ALLOC_FN(link_sta, link_sta)
+
+static void iwlmld_kunit_add_link_sta(struct ieee80211_sta *sta,
+ struct ieee80211_link_sta *link_sta,
+ struct iwl_mld_link_sta *mld_link_sta,
+ u8 link_id)
+{
+ struct kunit *test = kunit_get_current_test();
+ struct iwl_mld_sta *mld_sta = iwl_mld_sta_from_mac80211(sta);
+ struct iwl_mld *mld = test->priv;
+ u8 fw_id;
+ int ret;
+
+ /* initialize mac80211's link_sta */
+ link_sta->link_id = link_id;
+ rcu_assign_pointer(sta->link[link_id], link_sta);
+
+ link_sta->sta = sta;
+
+ /* and the iwl_mld_link_sta */
+ ret = iwl_mld_allocate_link_sta_fw_id(mld, &fw_id, link_sta);
+ KUNIT_ASSERT_EQ(test, ret, 0);
+ mld_link_sta->fw_id = fw_id;
+
+ rcu_assign_pointer(mld_sta->link[link_id], mld_link_sta);
+}
+
+static struct ieee80211_link_sta *
+iwlmld_kunit_alloc_link_sta(struct ieee80211_sta *sta, int link_id)
+{
+ struct kunit *test = kunit_get_current_test();
+ struct ieee80211_link_sta *link_sta;
+ struct iwl_mld_link_sta *mld_link_sta;
+
+ /* Only valid for MLO */
+ KUNIT_ASSERT_TRUE(test, sta->valid_links);
+
+ KUNIT_ALLOC_AND_ASSERT(test, link_sta);
+ KUNIT_ALLOC_AND_ASSERT(test, mld_link_sta);
+
+ iwlmld_kunit_add_link_sta(sta, link_sta, mld_link_sta, link_id);
+
+ sta->valid_links |= BIT(link_id);
+
+ return link_sta;
+}
+
+/* Allocate and initialize a STA with the first link_sta */
+static struct ieee80211_sta *
+iwlmld_kunit_add_sta(struct ieee80211_vif *vif, int link_id)
+{
+ struct kunit *test = kunit_get_current_test();
+ struct ieee80211_sta *sta;
+ struct iwl_mld_sta *mld_sta;
+
+ /* Allocate memory for ieee80211_sta with embedded iwl_mld_sta */
+ KUNIT_ALLOC_AND_ASSERT_SIZE(test, sta, sizeof(*sta) + sizeof(*mld_sta));
+
+ /* TODO: allocate and initialize the TXQs ? */
+
+ mld_sta = iwl_mld_sta_from_mac80211(sta);
+ mld_sta->vif = vif;
+ mld_sta->mld = test->priv;
+
+ /* TODO: adjust for internal stations */
+ mld_sta->sta_type = STATION_TYPE_PEER;
+
+ if (link_id >= 0) {
+ iwlmld_kunit_add_link_sta(sta, &sta->deflink,
+ &mld_sta->deflink, link_id);
+ sta->valid_links = BIT(link_id);
+ } else {
+ iwlmld_kunit_add_link_sta(sta, &sta->deflink,
+ &mld_sta->deflink, 0);
+ }
+ return sta;
+}
+
+/* Move s STA to a state */
+static void iwlmld_kunit_move_sta_state(struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta,
+ enum ieee80211_sta_state state)
+{
+ struct kunit *test = kunit_get_current_test();
+ struct iwl_mld_sta *mld_sta;
+ struct iwl_mld_vif *mld_vif;
+
+ /* The sta will be removed automatically at the end of the test */
+ KUNIT_ASSERT_NE(test, state, IEEE80211_STA_NOTEXIST);
+
+ mld_sta = iwl_mld_sta_from_mac80211(sta);
+ mld_sta->sta_state = state;
+
+ mld_vif = iwl_mld_vif_from_mac80211(mld_sta->vif);
+ mld_vif->authorized = state == IEEE80211_STA_AUTHORIZED;
+
+ if (vif->type == NL80211_IFTYPE_STATION && !sta->tdls)
+ mld_vif->ap_sta = sta;
+}
+
+struct ieee80211_sta *iwlmld_kunit_setup_sta(struct ieee80211_vif *vif,
+ enum ieee80211_sta_state state,
+ int link_id)
+{
+ struct kunit *test = kunit_get_current_test();
+ struct ieee80211_sta *sta;
+
+ /* The sta will be removed automatically at the end of the test */
+ KUNIT_ASSERT_NE(test, state, IEEE80211_STA_NOTEXIST);
+
+ /* First - allocate and init the STA */
+ sta = iwlmld_kunit_add_sta(vif, link_id);
+
+ /* Now move it all the way to the wanted state */
+ for (enum ieee80211_sta_state _state = IEEE80211_STA_NONE;
+ _state <= state; _state++)
+ iwlmld_kunit_move_sta_state(vif, sta, state);
+
+ return sta;
+}
+
+static void iwlmld_kunit_set_vif_associated(struct ieee80211_vif *vif)
+{
+ /* TODO: setup chanreq */
+ /* TODO setup capabilities */
+
+ vif->cfg.assoc = 1;
+}
+
+static struct ieee80211_vif *
+iwlmld_kunit_setup_assoc(bool mlo, struct iwl_mld_kunit_link *assoc_link)
+{
+ struct kunit *test = kunit_get_current_test();
+ struct iwl_mld *mld = test->priv;
+ struct ieee80211_vif *vif;
+ struct ieee80211_bss_conf *link;
+ struct ieee80211_chanctx_conf *chan_ctx;
+
+ KUNIT_ASSERT_TRUE(test, mlo || assoc_link->id == 0);
+
+ vif = iwlmld_kunit_add_vif(mlo, NL80211_IFTYPE_STATION);
+
+ if (mlo)
+ link = iwlmld_kunit_add_link(vif, assoc_link->id);
+ else
+ link = &vif->bss_conf;
+
+ chan_ctx = iwlmld_kunit_add_chanctx(assoc_link->chandef);
+
+ wiphy_lock(mld->wiphy);
+ iwlmld_kunit_assign_chanctx_to_link(vif, link, chan_ctx);
+ wiphy_unlock(mld->wiphy);
+
+ /* The AP sta will now be pointer to by mld_vif->ap_sta */
+ iwlmld_kunit_setup_sta(vif, IEEE80211_STA_AUTHORIZED, assoc_link->id);
+
+ iwlmld_kunit_set_vif_associated(vif);
+
+ return vif;
+}
+
+struct ieee80211_vif *
+iwlmld_kunit_setup_mlo_assoc(u16 valid_links,
+ struct iwl_mld_kunit_link *assoc_link)
+{
+ struct kunit *test = kunit_get_current_test();
+ struct ieee80211_vif *vif;
+
+ KUNIT_ASSERT_TRUE(test,
+ hweight16(valid_links) == 1 ||
+ hweight16(valid_links) == 2);
+ KUNIT_ASSERT_TRUE(test, valid_links & BIT(assoc_link->id));
+
+ vif = iwlmld_kunit_setup_assoc(true, assoc_link);
+
+ /* Add the other link, if applicable */
+ if (hweight16(valid_links) > 1) {
+ u8 other_link_id = ffs(valid_links & ~BIT(assoc_link->id)) - 1;
+
+ iwlmld_kunit_add_link(vif, other_link_id);
+ }
+
+ return vif;
+}
+
+struct ieee80211_vif *
+iwlmld_kunit_setup_non_mlo_assoc(struct iwl_mld_kunit_link *assoc_link)
+{
+ return iwlmld_kunit_setup_assoc(false, assoc_link);
+}
+
+struct iwl_rx_packet *
+_iwl_mld_kunit_create_pkt(const void *notif, size_t notif_sz)
+{
+ struct kunit *test = kunit_get_current_test();
+ struct iwl_rx_packet *pkt;
+
+ KUNIT_ALLOC_AND_ASSERT_SIZE(test, pkt, sizeof(pkt) + notif_sz);
+
+ memcpy(pkt->data, notif, notif_sz);
+ pkt->len_n_flags = cpu_to_le32(sizeof(pkt->hdr) + notif_sz);
+
+ return pkt;
+}
+
+struct ieee80211_vif *iwlmld_kunit_assoc_emlsr(struct iwl_mld_kunit_link *link1,
+ struct iwl_mld_kunit_link *link2)
+{
+ struct kunit *test = kunit_get_current_test();
+ struct iwl_mld *mld = test->priv;
+ struct ieee80211_vif *vif;
+ struct ieee80211_bss_conf *link;
+ struct ieee80211_chanctx_conf *chan_ctx;
+ struct ieee80211_sta *sta;
+ struct iwl_mld_vif *mld_vif;
+ u16 valid_links = BIT(link1->id) | BIT(link2->id);
+
+ KUNIT_ASSERT_TRUE(test, hweight16(valid_links) == 2);
+
+ vif = iwlmld_kunit_setup_mlo_assoc(valid_links, link1);
+ mld_vif = iwl_mld_vif_from_mac80211(vif);
+
+ /* Activate second link */
+ wiphy_lock(mld->wiphy);
+
+ link = wiphy_dereference(mld->wiphy, vif->link_conf[link2->id]);
+ KUNIT_EXPECT_NOT_NULL(test, link);
+
+ chan_ctx = iwlmld_kunit_add_chanctx(link2->chandef);
+ iwlmld_kunit_assign_chanctx_to_link(vif, link, chan_ctx);
+
+ wiphy_unlock(mld->wiphy);
+
+ /* And other link sta */
+ sta = mld_vif->ap_sta;
+ KUNIT_EXPECT_NOT_NULL(test, sta);
+
+ iwlmld_kunit_alloc_link_sta(sta, link2->id);
+
+ return vif;
+}
+
+struct element *iwlmld_kunit_gen_element(u8 id, const void *data, size_t len)
+{
+ struct kunit *test = kunit_get_current_test();
+ struct element *elem;
+
+ KUNIT_ALLOC_AND_ASSERT_SIZE(test, elem, sizeof(*elem) + len);
+
+ elem->id = id;
+ elem->datalen = len;
+ memcpy(elem->data, data, len);
+
+ return elem;
+}
+
+struct iwl_mld_phy *iwlmld_kunit_get_phy_of_link(struct ieee80211_vif *vif,
+ u8 link_id)
+{
+ struct kunit *test = kunit_get_current_test();
+ struct iwl_mld *mld = test->priv;
+ struct ieee80211_chanctx_conf *chanctx;
+ struct ieee80211_bss_conf *link =
+ wiphy_dereference(mld->wiphy, vif->link_conf[link_id]);
+
+ KUNIT_EXPECT_NOT_NULL(test, link);
+
+ chanctx = wiphy_dereference(mld->wiphy, link->chanctx_conf);
+ KUNIT_EXPECT_NOT_NULL(test, chanctx);
+
+ return iwl_mld_phy_from_mac80211(chanctx);
+}
+
+static const struct chandef_case {
+ const char *desc;
+ const struct cfg80211_chan_def *chandef;
+} chandef_cases[] = {
+#define CHANDEF(c, ...) { .desc = "chandef " #c " valid", .chandef = &c, },
+ CHANDEF_LIST
+#undef CHANDEF
+};
+
+KUNIT_ARRAY_PARAM_DESC(chandef, chandef_cases, desc);
+
+static void test_iwl_mld_chandef_valid(struct kunit *test)
+{
+ const struct chandef_case *params = test->param_value;
+
+ KUNIT_EXPECT_EQ(test, true, cfg80211_chandef_valid(params->chandef));
+}
+
+static struct kunit_case chandef_test_cases[] = {
+ KUNIT_CASE_PARAM(test_iwl_mld_chandef_valid, chandef_gen_params),
+ {}
+};
+
+static struct kunit_suite chandef_tests = {
+ .name = "iwlmld_valid_test_chandefs",
+ .test_cases = chandef_test_cases,
+};
+
+kunit_test_suite(chandef_tests);
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/tests/utils.h b/drivers/net/wireless/intel/iwlwifi/mld/tests/utils.h
new file mode 100644
index 000000000000..edf8eef4e81a
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/mld/tests/utils.h
@@ -0,0 +1,140 @@
+/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
+/*
+ * Copyright (C) 2024-2025 Intel Corporation
+ */
+
+#ifndef __iwl_mld_kunit_utils_h__
+#define __iwl_mld_kunit_utils_h__
+
+#include <net/mac80211.h>
+#include <kunit/test-bug.h>
+
+struct iwl_mld;
+
+int iwlmld_kunit_test_init(struct kunit *test);
+
+struct iwl_mld_kunit_link {
+ const struct cfg80211_chan_def *chandef;
+ u8 id;
+};
+
+enum nl80211_iftype;
+
+struct ieee80211_vif *iwlmld_kunit_add_vif(bool mlo, enum nl80211_iftype type);
+
+struct ieee80211_bss_conf *
+iwlmld_kunit_add_link(struct ieee80211_vif *vif, int link_id);
+
+#define KUNIT_ALLOC_AND_ASSERT_SIZE(test, ptr, size) \
+do { \
+ (ptr) = kunit_kzalloc((test), (size), GFP_KERNEL); \
+ KUNIT_ASSERT_NOT_NULL((test), (ptr)); \
+} while (0)
+
+#define KUNIT_ALLOC_AND_ASSERT(test, ptr) \
+ KUNIT_ALLOC_AND_ASSERT_SIZE(test, ptr, sizeof(*(ptr)))
+
+#define CHANNEL(_name, _band, _freq) \
+static struct ieee80211_channel _name = { \
+ .band = (_band), \
+ .center_freq = (_freq), \
+ .hw_value = (_freq), \
+}
+
+CHANNEL(chan_2ghz, NL80211_BAND_2GHZ, 2412);
+CHANNEL(chan_2ghz_11, NL80211_BAND_2GHZ, 2462);
+CHANNEL(chan_5ghz, NL80211_BAND_5GHZ, 5200);
+CHANNEL(chan_5ghz_120, NL80211_BAND_5GHZ, 5600);
+CHANNEL(chan_6ghz, NL80211_BAND_6GHZ, 6115);
+CHANNEL(chan_6ghz_221, NL80211_BAND_6GHZ, 7055);
+/* Feel free to add more */
+#undef CHANNEL
+
+#define CHANDEF_LIST \
+ CHANDEF(chandef_2ghz_20mhz, chan_2ghz, 2412, \
+ NL80211_CHAN_WIDTH_20) \
+ CHANDEF(chandef_2ghz_40mhz, chan_2ghz, 2422, \
+ NL80211_CHAN_WIDTH_40) \
+ CHANDEF(chandef_2ghz_11_20mhz, chan_2ghz_11, 2462, \
+ NL80211_CHAN_WIDTH_20) \
+ CHANDEF(chandef_5ghz_20mhz, chan_5ghz, 5200, \
+ NL80211_CHAN_WIDTH_20) \
+ CHANDEF(chandef_5ghz_40mhz, chan_5ghz, 5210, \
+ NL80211_CHAN_WIDTH_40) \
+ CHANDEF(chandef_5ghz_80mhz, chan_5ghz, 5210, \
+ NL80211_CHAN_WIDTH_80) \
+ CHANDEF(chandef_5ghz_160mhz, chan_5ghz, 5250, \
+ NL80211_CHAN_WIDTH_160) \
+ CHANDEF(chandef_5ghz_120_40mhz, chan_5ghz_120, 5610, \
+ NL80211_CHAN_WIDTH_40) \
+ CHANDEF(chandef_6ghz_20mhz, chan_6ghz, 6115, \
+ NL80211_CHAN_WIDTH_20) \
+ CHANDEF(chandef_6ghz_40mhz, chan_6ghz, 6125, \
+ NL80211_CHAN_WIDTH_40) \
+ CHANDEF(chandef_6ghz_80mhz, chan_6ghz, 6145, \
+ NL80211_CHAN_WIDTH_80) \
+ CHANDEF(chandef_6ghz_160mhz, chan_6ghz, 6185, \
+ NL80211_CHAN_WIDTH_160) \
+ CHANDEF(chandef_6ghz_320mhz, chan_6ghz, 6105, \
+ NL80211_CHAN_WIDTH_320) \
+ CHANDEF(chandef_6ghz_221_160mhz, chan_6ghz_221, 6985, \
+ NL80211_CHAN_WIDTH_160) \
+ /* Feel free to add more */
+
+#define CHANDEF(_name, _channel, _freq1, _width) \
+__maybe_unused static const struct cfg80211_chan_def _name = { \
+ .chan = &(_channel), \
+ .center_freq1 = (_freq1), \
+ .width = (_width), \
+};
+CHANDEF_LIST
+#undef CHANDEF
+
+struct ieee80211_chanctx_conf *
+iwlmld_kunit_add_chanctx(const struct cfg80211_chan_def *def);
+
+void iwlmld_kunit_assign_chanctx_to_link(struct ieee80211_vif *vif,
+ struct ieee80211_bss_conf *link,
+ struct ieee80211_chanctx_conf *ctx);
+
+/* Allocate a sta, initialize it and move it to the wanted state */
+struct ieee80211_sta *iwlmld_kunit_setup_sta(struct ieee80211_vif *vif,
+ enum ieee80211_sta_state state,
+ int link_id);
+
+struct ieee80211_vif *
+iwlmld_kunit_setup_mlo_assoc(u16 valid_links,
+ struct iwl_mld_kunit_link *assoc_link);
+
+struct ieee80211_vif *
+iwlmld_kunit_setup_non_mlo_assoc(struct iwl_mld_kunit_link *assoc_link);
+
+struct iwl_rx_packet *
+_iwl_mld_kunit_create_pkt(const void *notif, size_t notif_sz);
+
+#define iwl_mld_kunit_create_pkt(_notif) \
+ _iwl_mld_kunit_create_pkt(&(_notif), sizeof(_notif))
+
+struct ieee80211_vif *
+iwlmld_kunit_assoc_emlsr(struct iwl_mld_kunit_link *link1,
+ struct iwl_mld_kunit_link *link2);
+
+struct element *iwlmld_kunit_gen_element(u8 id, const void *data, size_t len);
+
+/**
+ * iwlmld_kunit_get_phy_of_link - Get the phy of a link
+ *
+ * @vif: The vif to get the phy from.
+ * @link_id: The id of the link to get the phy for.
+ *
+ * given a vif and link id, return the phy pointer of that link.
+ * This assumes that the link exists, and that it had a chanctx
+ * assigned.
+ * If this is not the case, the test will fail.
+ *
+ * Return: phy pointer.
+ */
+struct iwl_mld_phy *iwlmld_kunit_get_phy_of_link(struct ieee80211_vif *vif,
+ u8 link_id);
+
+#endif /* __iwl_mld_kunit_utils_h__ */
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/thermal.c b/drivers/net/wireless/intel/iwlwifi/mld/thermal.c
new file mode 100644
index 000000000000..f8a8c35066be
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/mld/thermal.c
@@ -0,0 +1,467 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
+/*
+ * Copyright (C) 2024-2025 Intel Corporation
+ */
+#ifdef CONFIG_THERMAL
+#include <linux/sort.h>
+#include <linux/thermal.h>
+#endif
+
+#include "fw/api/phy.h"
+
+#include "thermal.h"
+#include "mld.h"
+#include "hcmd.h"
+
+#define IWL_MLD_NUM_CTDP_STEPS 20
+#define IWL_MLD_MIN_CTDP_BUDGET_MW 150
+
+#define IWL_MLD_CT_KILL_DURATION (5 * HZ)
+
+void iwl_mld_handle_ct_kill_notif(struct iwl_mld *mld,
+ struct iwl_rx_packet *pkt)
+{
+ const struct ct_kill_notif *notif = (const void *)pkt->data;
+
+ IWL_ERR(mld,
+ "CT Kill notification: temp = %d, DTS = 0x%x, Scheme 0x%x - Enter CT Kill\n",
+ le16_to_cpu(notif->temperature), notif->dts,
+ notif->scheme);
+
+ iwl_mld_set_ctkill(mld, true);
+
+ wiphy_delayed_work_queue(mld->wiphy, &mld->ct_kill_exit_wk,
+ round_jiffies_relative(IWL_MLD_CT_KILL_DURATION));
+}
+
+static void iwl_mld_exit_ctkill(struct wiphy *wiphy, struct wiphy_work *wk)
+{
+ struct iwl_mld *mld;
+
+ mld = container_of(wk, struct iwl_mld, ct_kill_exit_wk.work);
+
+ IWL_ERR(mld, "Exit CT Kill\n");
+ iwl_mld_set_ctkill(mld, false);
+}
+
+void iwl_mld_handle_temp_notif(struct iwl_mld *mld, struct iwl_rx_packet *pkt)
+{
+ const struct iwl_dts_measurement_notif *notif =
+ (const void *)pkt->data;
+ int temp;
+ u32 ths_crossed;
+
+ temp = le32_to_cpu(notif->temp);
+
+ /* shouldn't be negative, but since it's s32, make sure it isn't */
+ if (IWL_FW_CHECK(mld, temp < 0, "negative temperature %d\n", temp))
+ return;
+
+ ths_crossed = le32_to_cpu(notif->threshold_idx);
+
+ /* 0xFF in ths_crossed means the notification is not related
+ * to a trip, so we can ignore it here.
+ */
+ if (ths_crossed == 0xFF)
+ return;
+
+ IWL_DEBUG_TEMP(mld, "Temp = %d Threshold crossed = %d\n",
+ temp, ths_crossed);
+
+ if (IWL_FW_CHECK(mld, ths_crossed >= IWL_MAX_DTS_TRIPS,
+ "bad threshold: %d\n", ths_crossed))
+ return;
+
+#ifdef CONFIG_THERMAL
+ if (mld->tzone)
+ thermal_zone_device_update(mld->tzone, THERMAL_TRIP_VIOLATED);
+#endif /* CONFIG_THERMAL */
+}
+
+#ifdef CONFIG_THERMAL
+static int iwl_mld_get_temp(struct iwl_mld *mld, s32 *temp)
+{
+ struct iwl_host_cmd cmd = {
+ .id = WIDE_ID(PHY_OPS_GROUP, CMD_DTS_MEASUREMENT_TRIGGER_WIDE),
+ .flags = CMD_WANT_SKB,
+ };
+ const struct iwl_dts_measurement_resp *resp;
+ int ret;
+
+ lockdep_assert_wiphy(mld->wiphy);
+
+ ret = iwl_mld_send_cmd(mld, &cmd);
+ if (ret) {
+ IWL_ERR(mld,
+ "Failed to send the temperature measurement command (err=%d)\n",
+ ret);
+ return ret;
+ }
+
+ if (iwl_rx_packet_payload_len(cmd.resp_pkt) < sizeof(*resp)) {
+ IWL_ERR(mld,
+ "Failed to get a valid response to DTS measurement\n");
+ ret = -EIO;
+ goto free_resp;
+ }
+
+ resp = (const void *)cmd.resp_pkt->data;
+ *temp = le32_to_cpu(resp->temp);
+
+ IWL_DEBUG_TEMP(mld,
+ "Got temperature measurement response: temp=%d\n",
+ *temp);
+
+free_resp:
+ iwl_free_resp(&cmd);
+ return ret;
+}
+
+static int compare_temps(const void *a, const void *b)
+{
+ return ((s16)le16_to_cpu(*(const __le16 *)a) -
+ (s16)le16_to_cpu(*(const __le16 *)b));
+}
+
+struct iwl_trip_walk_data {
+ __le16 *thresholds;
+ int count;
+};
+
+static int iwl_trip_temp_iter(struct thermal_trip *trip, void *arg)
+{
+ struct iwl_trip_walk_data *twd = arg;
+
+ if (trip->temperature == THERMAL_TEMP_INVALID)
+ return 0;
+
+ twd->thresholds[twd->count++] =
+ cpu_to_le16((s16)(trip->temperature / 1000));
+ return 0;
+}
+#endif
+
+int iwl_mld_config_temp_report_ths(struct iwl_mld *mld)
+{
+ struct temp_report_ths_cmd cmd = {0};
+ int ret;
+#ifdef CONFIG_THERMAL
+ struct iwl_trip_walk_data twd = {
+ .thresholds = cmd.thresholds,
+ .count = 0
+ };
+
+ if (!mld->tzone)
+ goto send;
+
+ /* The thermal core holds an array of temperature trips that are
+ * unsorted and uncompressed, the FW should get it compressed and
+ * sorted.
+ */
+
+ /* compress trips to cmd array, remove uninitialized values*/
+ for_each_thermal_trip(mld->tzone, iwl_trip_temp_iter, &twd);
+
+ cmd.num_temps = cpu_to_le32(twd.count);
+ if (twd.count)
+ sort(cmd.thresholds, twd.count, sizeof(s16),
+ compare_temps, NULL);
+
+send:
+#endif
+ lockdep_assert_wiphy(mld->wiphy);
+
+ ret = iwl_mld_send_cmd_pdu(mld, WIDE_ID(PHY_OPS_GROUP,
+ TEMP_REPORTING_THRESHOLDS_CMD),
+ &cmd);
+ if (ret)
+ IWL_ERR(mld, "TEMP_REPORT_THS_CMD command failed (err=%d)\n",
+ ret);
+
+ return ret;
+}
+
+#ifdef CONFIG_THERMAL
+static int iwl_mld_tzone_get_temp(struct thermal_zone_device *device,
+ int *temperature)
+{
+ struct iwl_mld *mld = thermal_zone_device_priv(device);
+ int temp;
+ int ret = 0;
+
+ wiphy_lock(mld->wiphy);
+
+ if (!mld->fw_status.running) {
+ /* Tell the core that there is no valid temperature value to
+ * return, but it need not worry about this.
+ */
+ *temperature = THERMAL_TEMP_INVALID;
+ goto unlock;
+ }
+
+ ret = iwl_mld_get_temp(mld, &temp);
+ if (ret)
+ goto unlock;
+
+ *temperature = temp * 1000;
+unlock:
+ wiphy_unlock(mld->wiphy);
+ return ret;
+}
+
+static int iwl_mld_tzone_set_trip_temp(struct thermal_zone_device *device,
+ const struct thermal_trip *trip,
+ int temp)
+{
+ struct iwl_mld *mld = thermal_zone_device_priv(device);
+ int ret;
+
+ wiphy_lock(mld->wiphy);
+
+ if (!mld->fw_status.running) {
+ ret = -EIO;
+ goto unlock;
+ }
+
+ if ((temp / 1000) > S16_MAX) {
+ ret = -EINVAL;
+ goto unlock;
+ }
+
+ ret = iwl_mld_config_temp_report_ths(mld);
+unlock:
+ wiphy_unlock(mld->wiphy);
+ return ret;
+}
+
+static struct thermal_zone_device_ops tzone_ops = {
+ .get_temp = iwl_mld_tzone_get_temp,
+ .set_trip_temp = iwl_mld_tzone_set_trip_temp,
+};
+
+static void iwl_mld_thermal_zone_register(struct iwl_mld *mld)
+{
+ int ret;
+ char name[16];
+ static atomic_t counter = ATOMIC_INIT(0);
+ struct thermal_trip trips[IWL_MAX_DTS_TRIPS] = {
+ [0 ... IWL_MAX_DTS_TRIPS - 1] = {
+ .temperature = THERMAL_TEMP_INVALID,
+ .type = THERMAL_TRIP_PASSIVE,
+ .flags = THERMAL_TRIP_FLAG_RW_TEMP,
+ },
+ };
+
+ BUILD_BUG_ON(ARRAY_SIZE(name) >= THERMAL_NAME_LENGTH);
+
+ sprintf(name, "iwlwifi_%u", atomic_inc_return(&counter) & 0xFF);
+ mld->tzone =
+ thermal_zone_device_register_with_trips(name, trips,
+ IWL_MAX_DTS_TRIPS,
+ mld, &tzone_ops,
+ NULL, 0, 0);
+ if (IS_ERR(mld->tzone)) {
+ IWL_DEBUG_TEMP(mld,
+ "Failed to register to thermal zone (err = %ld)\n",
+ PTR_ERR(mld->tzone));
+ mld->tzone = NULL;
+ return;
+ }
+
+ ret = thermal_zone_device_enable(mld->tzone);
+ if (ret) {
+ IWL_DEBUG_TEMP(mld, "Failed to enable thermal zone\n");
+ thermal_zone_device_unregister(mld->tzone);
+ }
+}
+
+int iwl_mld_config_ctdp(struct iwl_mld *mld, u32 state,
+ enum iwl_ctdp_cmd_operation op)
+{
+ struct iwl_ctdp_cmd cmd = {
+ .operation = cpu_to_le32(op),
+ .window_size = 0,
+ };
+ u32 budget;
+ int ret;
+
+ lockdep_assert_wiphy(mld->wiphy);
+
+ /* Do a linear scale from IWL_MLD_MIN_CTDP_BUDGET_MW to the configured
+ * maximum in the predefined number of steps.
+ */
+ budget = ((mld->power_budget_mw - IWL_MLD_MIN_CTDP_BUDGET_MW) *
+ (IWL_MLD_NUM_CTDP_STEPS - 1 - state)) /
+ (IWL_MLD_NUM_CTDP_STEPS - 1) +
+ IWL_MLD_MIN_CTDP_BUDGET_MW;
+ cmd.budget = cpu_to_le32(budget);
+
+ ret = iwl_mld_send_cmd_pdu(mld, WIDE_ID(PHY_OPS_GROUP, CTDP_CONFIG_CMD),
+ &cmd);
+
+ if (ret) {
+ IWL_ERR(mld, "cTDP command failed (err=%d)\n", ret);
+ return ret;
+ }
+
+ if (op == CTDP_CMD_OPERATION_START)
+ mld->cooling_dev.cur_state = state;
+
+ return 0;
+}
+
+static int iwl_mld_tcool_get_max_state(struct thermal_cooling_device *cdev,
+ unsigned long *state)
+{
+ *state = IWL_MLD_NUM_CTDP_STEPS - 1;
+
+ return 0;
+}
+
+static int iwl_mld_tcool_get_cur_state(struct thermal_cooling_device *cdev,
+ unsigned long *state)
+{
+ struct iwl_mld *mld = (struct iwl_mld *)(cdev->devdata);
+
+ *state = mld->cooling_dev.cur_state;
+
+ return 0;
+}
+
+static int iwl_mld_tcool_set_cur_state(struct thermal_cooling_device *cdev,
+ unsigned long new_state)
+{
+ struct iwl_mld *mld = (struct iwl_mld *)(cdev->devdata);
+ int ret;
+
+ wiphy_lock(mld->wiphy);
+
+ if (!mld->fw_status.running) {
+ ret = -EIO;
+ goto unlock;
+ }
+
+ if (new_state >= IWL_MLD_NUM_CTDP_STEPS) {
+ ret = -EINVAL;
+ goto unlock;
+ }
+
+ ret = iwl_mld_config_ctdp(mld, new_state, CTDP_CMD_OPERATION_START);
+
+unlock:
+ wiphy_unlock(mld->wiphy);
+ return ret;
+}
+
+static const struct thermal_cooling_device_ops tcooling_ops = {
+ .get_max_state = iwl_mld_tcool_get_max_state,
+ .get_cur_state = iwl_mld_tcool_get_cur_state,
+ .set_cur_state = iwl_mld_tcool_set_cur_state,
+};
+
+static void iwl_mld_cooling_device_register(struct iwl_mld *mld)
+{
+ char name[] = "iwlwifi";
+
+ BUILD_BUG_ON(ARRAY_SIZE(name) >= THERMAL_NAME_LENGTH);
+
+ mld->cooling_dev.cdev =
+ thermal_cooling_device_register(name,
+ mld,
+ &tcooling_ops);
+
+ if (IS_ERR(mld->cooling_dev.cdev)) {
+ IWL_DEBUG_TEMP(mld,
+ "Failed to register to cooling device (err = %ld)\n",
+ PTR_ERR(mld->cooling_dev.cdev));
+ mld->cooling_dev.cdev = NULL;
+ return;
+ }
+}
+
+static void iwl_mld_thermal_zone_unregister(struct iwl_mld *mld)
+{
+ if (!mld->tzone)
+ return;
+
+ IWL_DEBUG_TEMP(mld, "Thermal zone device unregister\n");
+ if (mld->tzone) {
+ thermal_zone_device_unregister(mld->tzone);
+ mld->tzone = NULL;
+ }
+}
+
+static void iwl_mld_cooling_device_unregister(struct iwl_mld *mld)
+{
+ if (!mld->cooling_dev.cdev)
+ return;
+
+ IWL_DEBUG_TEMP(mld, "Cooling device unregister\n");
+ if (mld->cooling_dev.cdev) {
+ thermal_cooling_device_unregister(mld->cooling_dev.cdev);
+ mld->cooling_dev.cdev = NULL;
+ }
+}
+#endif /* CONFIG_THERMAL */
+
+static u32 iwl_mld_ctdp_get_max_budget(struct iwl_mld *mld)
+{
+ u64 bios_power_budget = 0;
+ u32 default_power_budget;
+
+ switch (CSR_HW_RFID_TYPE(mld->trans->info.hw_rf_id)) {
+ case IWL_CFG_RF_TYPE_GF:
+ /* dual-radio devices have a higher budget */
+ if (CSR_HW_RFID_IS_CDB(mld->trans->info.hw_rf_id))
+ default_power_budget = 5200;
+ else
+ default_power_budget = 2880;
+ break;
+ case IWL_CFG_RF_TYPE_FM:
+ default_power_budget = 3450;
+ break;
+ case IWL_CFG_RF_TYPE_WH:
+ case IWL_CFG_RF_TYPE_PE:
+ default:
+ default_power_budget = 5550;
+ break;
+ }
+
+ iwl_bios_get_pwr_limit(&mld->fwrt, &bios_power_budget);
+
+ /* 32bit in UEFI, 16bit in ACPI; use BIOS value if it is in range */
+ if (bios_power_budget &&
+ bios_power_budget != 0xffff && bios_power_budget != 0xffffffff &&
+ bios_power_budget >= IWL_MLD_MIN_CTDP_BUDGET_MW &&
+ bios_power_budget <= default_power_budget)
+ return (u32)bios_power_budget;
+
+ return default_power_budget;
+}
+
+void iwl_mld_thermal_initialize(struct iwl_mld *mld)
+{
+ lockdep_assert_not_held(&mld->wiphy->mtx);
+
+ wiphy_delayed_work_init(&mld->ct_kill_exit_wk, iwl_mld_exit_ctkill);
+
+ mld->power_budget_mw = iwl_mld_ctdp_get_max_budget(mld);
+ IWL_DEBUG_TEMP(mld, "cTDP power budget: %d mW\n", mld->power_budget_mw);
+
+#ifdef CONFIG_THERMAL
+ iwl_mld_cooling_device_register(mld);
+ iwl_mld_thermal_zone_register(mld);
+#endif
+}
+
+void iwl_mld_thermal_exit(struct iwl_mld *mld)
+{
+ wiphy_lock(mld->wiphy);
+ wiphy_delayed_work_cancel(mld->wiphy, &mld->ct_kill_exit_wk);
+ wiphy_unlock(mld->wiphy);
+
+#ifdef CONFIG_THERMAL
+ iwl_mld_cooling_device_unregister(mld);
+ iwl_mld_thermal_zone_unregister(mld);
+#endif
+}
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/thermal.h b/drivers/net/wireless/intel/iwlwifi/mld/thermal.h
new file mode 100644
index 000000000000..8c8893331b05
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/mld/thermal.h
@@ -0,0 +1,36 @@
+/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
+/*
+ * Copyright (C) 2024 Intel Corporation
+ */
+#ifndef __iwl_mld_thermal_h__
+#define __iwl_mld_thermal_h__
+
+#include "iwl-trans.h"
+
+struct iwl_mld;
+
+#ifdef CONFIG_THERMAL
+#include <linux/thermal.h>
+
+/*
+ * struct iwl_mld_cooling_device
+ * @cur_state: current state
+ * @cdev: struct thermal cooling device
+ */
+struct iwl_mld_cooling_device {
+ u32 cur_state;
+ struct thermal_cooling_device *cdev;
+};
+
+int iwl_mld_config_ctdp(struct iwl_mld *mld, u32 state,
+ enum iwl_ctdp_cmd_operation op);
+#endif
+
+void iwl_mld_handle_temp_notif(struct iwl_mld *mld, struct iwl_rx_packet *pkt);
+void iwl_mld_handle_ct_kill_notif(struct iwl_mld *mld,
+ struct iwl_rx_packet *pkt);
+int iwl_mld_config_temp_report_ths(struct iwl_mld *mld);
+void iwl_mld_thermal_initialize(struct iwl_mld *mld);
+void iwl_mld_thermal_exit(struct iwl_mld *mld);
+
+#endif /* __iwl_mld_thermal_h__ */
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/time_sync.c b/drivers/net/wireless/intel/iwlwifi/mld/time_sync.c
new file mode 100644
index 000000000000..50799f9bfccb
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/mld/time_sync.c
@@ -0,0 +1,240 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
+/*
+ * Copyright (C) 2025 Intel Corporation
+ */
+
+#include "mld.h"
+#include "hcmd.h"
+#include "ptp.h"
+#include "time_sync.h"
+#include <linux/ieee80211.h>
+
+static int iwl_mld_init_time_sync(struct iwl_mld *mld, u32 protocols,
+ const u8 *addr)
+{
+ struct iwl_mld_time_sync_data *time_sync = kzalloc(sizeof(*time_sync),
+ GFP_KERNEL);
+
+ if (!time_sync)
+ return -ENOMEM;
+
+ time_sync->active_protocols = protocols;
+ ether_addr_copy(time_sync->peer_addr, addr);
+ skb_queue_head_init(&time_sync->frame_list);
+ rcu_assign_pointer(mld->time_sync, time_sync);
+
+ return 0;
+}
+
+int iwl_mld_time_sync_fw_config(struct iwl_mld *mld)
+{
+ struct iwl_time_sync_cfg_cmd cmd = {};
+ struct iwl_mld_time_sync_data *time_sync;
+ int err;
+
+ time_sync = wiphy_dereference(mld->wiphy, mld->time_sync);
+ if (!time_sync)
+ return -EINVAL;
+
+ cmd.protocols = cpu_to_le32(time_sync->active_protocols);
+ ether_addr_copy(cmd.peer_addr, time_sync->peer_addr);
+
+ err = iwl_mld_send_cmd_pdu(mld,
+ WIDE_ID(DATA_PATH_GROUP,
+ WNM_80211V_TIMING_MEASUREMENT_CONFIG_CMD),
+ &cmd);
+ if (err)
+ IWL_ERR(mld, "Failed to send time sync cfg cmd: %d\n", err);
+
+ return err;
+}
+
+int iwl_mld_time_sync_config(struct iwl_mld *mld, const u8 *addr, u32 protocols)
+{
+ struct iwl_mld_time_sync_data *time_sync;
+ int err;
+
+ time_sync = wiphy_dereference(mld->wiphy, mld->time_sync);
+
+ /* The fw only supports one peer. We do allow reconfiguration of the
+ * same peer for cases of fw reset etc.
+ */
+ if (time_sync && time_sync->active_protocols &&
+ !ether_addr_equal(addr, time_sync->peer_addr)) {
+ IWL_DEBUG_INFO(mld, "Time sync: reject config for peer: %pM\n",
+ addr);
+ return -ENOBUFS;
+ }
+
+ if (protocols & ~(IWL_TIME_SYNC_PROTOCOL_TM |
+ IWL_TIME_SYNC_PROTOCOL_FTM))
+ return -EINVAL;
+
+ IWL_DEBUG_INFO(mld, "Time sync: set peer addr=%pM\n", addr);
+
+ iwl_mld_deinit_time_sync(mld);
+ err = iwl_mld_init_time_sync(mld, protocols, addr);
+ if (err)
+ return err;
+
+ err = iwl_mld_time_sync_fw_config(mld);
+ return err;
+}
+
+void iwl_mld_deinit_time_sync(struct iwl_mld *mld)
+{
+ struct iwl_mld_time_sync_data *time_sync =
+ wiphy_dereference(mld->wiphy, mld->time_sync);
+
+ if (!time_sync)
+ return;
+
+ RCU_INIT_POINTER(mld->time_sync, NULL);
+ skb_queue_purge(&time_sync->frame_list);
+ kfree_rcu(time_sync, rcu_head);
+}
+
+bool iwl_mld_time_sync_frame(struct iwl_mld *mld, struct sk_buff *skb, u8 *addr)
+{
+ struct iwl_mld_time_sync_data *time_sync;
+
+ rcu_read_lock();
+ time_sync = rcu_dereference(mld->time_sync);
+ if (time_sync && ether_addr_equal(time_sync->peer_addr, addr) &&
+ (ieee80211_is_timing_measurement(skb) || ieee80211_is_ftm(skb))) {
+ skb_queue_tail(&time_sync->frame_list, skb);
+ rcu_read_unlock();
+ return true;
+ }
+ rcu_read_unlock();
+
+ return false;
+}
+
+static bool iwl_mld_is_skb_match(struct sk_buff *skb, u8 *addr, u8 dialog_token)
+{
+ struct ieee80211_mgmt *mgmt = (void *)skb->data;
+ u8 skb_dialog_token;
+
+ if (ieee80211_is_timing_measurement(skb))
+ skb_dialog_token = mgmt->u.action.u.wnm_timing_msr.dialog_token;
+ else
+ skb_dialog_token = mgmt->u.action.u.ftm.dialog_token;
+
+ if ((ether_addr_equal(mgmt->sa, addr) ||
+ ether_addr_equal(mgmt->da, addr)) &&
+ skb_dialog_token == dialog_token)
+ return true;
+
+ return false;
+}
+
+static struct sk_buff *iwl_mld_time_sync_find_skb(struct iwl_mld *mld, u8 *addr,
+ u8 dialog_token)
+{
+ struct iwl_mld_time_sync_data *time_sync;
+ struct sk_buff *skb;
+
+ rcu_read_lock();
+
+ time_sync = rcu_dereference(mld->time_sync);
+ if (IWL_FW_CHECK(mld, !time_sync,
+ "Time sync notification but time sync is not initialized\n")) {
+ rcu_read_unlock();
+ return NULL;
+ }
+
+ /* The notifications are expected to arrive in the same order of the
+ * frames. If the incoming notification doesn't match the first SKB
+ * in the queue, it means there was no time sync notification for this
+ * SKB and it can be dropped.
+ */
+ while ((skb = skb_dequeue(&time_sync->frame_list))) {
+ if (iwl_mld_is_skb_match(skb, addr, dialog_token))
+ break;
+
+ kfree_skb(skb);
+ skb = NULL;
+ IWL_DEBUG_DROP(mld,
+ "Time sync: drop SKB without matching notification\n");
+ }
+ rcu_read_unlock();
+
+ return skb;
+}
+
+static u64 iwl_mld_get_64_bit(__le32 high, __le32 low)
+{
+ return ((u64)le32_to_cpu(high) << 32) | le32_to_cpu(low);
+}
+
+void iwl_mld_handle_time_msmt_notif(struct iwl_mld *mld,
+ struct iwl_rx_packet *pkt)
+{
+ struct ptp_data *data = &mld->ptp_data;
+ struct iwl_time_msmt_notify *notif = (void *)pkt->data;
+ struct ieee80211_rx_status *rx_status;
+ struct skb_shared_hwtstamps *shwt;
+ u64 ts_10ns;
+ struct sk_buff *skb =
+ iwl_mld_time_sync_find_skb(mld, notif->peer_addr,
+ le32_to_cpu(notif->dialog_token));
+ u64 adj_time;
+
+ if (IWL_FW_CHECK(mld, !skb, "Time sync event but no pending skb\n"))
+ return;
+
+ spin_lock_bh(&data->lock);
+ ts_10ns = iwl_mld_get_64_bit(notif->t2_hi, notif->t2_lo);
+ adj_time = iwl_mld_ptp_get_adj_time(mld, ts_10ns * 10);
+ shwt = skb_hwtstamps(skb);
+ shwt->hwtstamp = ktime_set(0, adj_time);
+
+ ts_10ns = iwl_mld_get_64_bit(notif->t3_hi, notif->t3_lo);
+ adj_time = iwl_mld_ptp_get_adj_time(mld, ts_10ns * 10);
+ rx_status = IEEE80211_SKB_RXCB(skb);
+ rx_status->ack_tx_hwtstamp = ktime_set(0, adj_time);
+ spin_unlock_bh(&data->lock);
+
+ IWL_DEBUG_INFO(mld,
+ "Time sync: RX event - report frame t2=%llu t3=%llu\n",
+ ktime_to_ns(shwt->hwtstamp),
+ ktime_to_ns(rx_status->ack_tx_hwtstamp));
+ ieee80211_rx_napi(mld->hw, NULL, skb, NULL);
+}
+
+void iwl_mld_handle_time_sync_confirm_notif(struct iwl_mld *mld,
+ struct iwl_rx_packet *pkt)
+{
+ struct ptp_data *data = &mld->ptp_data;
+ struct iwl_time_msmt_cfm_notify *notif = (void *)pkt->data;
+ struct ieee80211_tx_status status = {};
+ struct skb_shared_hwtstamps *shwt;
+ u64 ts_10ns, adj_time;
+
+ status.skb =
+ iwl_mld_time_sync_find_skb(mld, notif->peer_addr,
+ le32_to_cpu(notif->dialog_token));
+
+ if (IWL_FW_CHECK(mld, !status.skb,
+ "Time sync confirm but no pending skb\n"))
+ return;
+
+ spin_lock_bh(&data->lock);
+ ts_10ns = iwl_mld_get_64_bit(notif->t1_hi, notif->t1_lo);
+ adj_time = iwl_mld_ptp_get_adj_time(mld, ts_10ns * 10);
+ shwt = skb_hwtstamps(status.skb);
+ shwt->hwtstamp = ktime_set(0, adj_time);
+
+ ts_10ns = iwl_mld_get_64_bit(notif->t4_hi, notif->t4_lo);
+ adj_time = iwl_mld_ptp_get_adj_time(mld, ts_10ns * 10);
+ status.info = IEEE80211_SKB_CB(status.skb);
+ status.ack_hwtstamp = ktime_set(0, adj_time);
+ spin_unlock_bh(&data->lock);
+
+ IWL_DEBUG_INFO(mld,
+ "Time sync: TX event - report frame t1=%llu t4=%llu\n",
+ ktime_to_ns(shwt->hwtstamp),
+ ktime_to_ns(status.ack_hwtstamp));
+ ieee80211_tx_status_ext(mld->hw, &status);
+}
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/time_sync.h b/drivers/net/wireless/intel/iwlwifi/mld/time_sync.h
new file mode 100644
index 000000000000..2d4c5512e961
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/mld/time_sync.h
@@ -0,0 +1,26 @@
+/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
+/*
+ * Copyright (C) 2025 Intel Corporation
+ */
+#ifndef __iwl_mld_time_sync_h__
+#define __iwl_mld_time_sync_h__
+
+struct iwl_mld_time_sync_data {
+ struct rcu_head rcu_head;
+ u8 peer_addr[ETH_ALEN];
+ u32 active_protocols;
+ struct sk_buff_head frame_list;
+};
+
+int iwl_mld_time_sync_config(struct iwl_mld *mld, const u8 *addr,
+ u32 protocols);
+int iwl_mld_time_sync_fw_config(struct iwl_mld *mld);
+void iwl_mld_deinit_time_sync(struct iwl_mld *mld);
+void iwl_mld_handle_time_msmt_notif(struct iwl_mld *mld,
+ struct iwl_rx_packet *pkt);
+bool iwl_mld_time_sync_frame(struct iwl_mld *mld, struct sk_buff *skb,
+ u8 *addr);
+void iwl_mld_handle_time_sync_confirm_notif(struct iwl_mld *mld,
+ struct iwl_rx_packet *pkt);
+
+#endif /* __iwl_mld_time_sync_h__ */
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/tlc.c b/drivers/net/wireless/intel/iwlwifi/mld/tlc.c
new file mode 100644
index 000000000000..a9ca92c0455e
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/mld/tlc.c
@@ -0,0 +1,702 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
+/*
+ * Copyright (C) 2024-2025 Intel Corporation
+ */
+
+#include <net/mac80211.h>
+
+#include "tlc.h"
+#include "hcmd.h"
+#include "sta.h"
+
+#include "fw/api/rs.h"
+#include "fw/api/context.h"
+#include "fw/api/dhc.h"
+
+static u8 iwl_mld_fw_bw_from_sta_bw(const struct ieee80211_link_sta *link_sta)
+{
+ switch (link_sta->bandwidth) {
+ case IEEE80211_STA_RX_BW_320:
+ return IWL_TLC_MNG_CH_WIDTH_320MHZ;
+ case IEEE80211_STA_RX_BW_160:
+ return IWL_TLC_MNG_CH_WIDTH_160MHZ;
+ case IEEE80211_STA_RX_BW_80:
+ return IWL_TLC_MNG_CH_WIDTH_80MHZ;
+ case IEEE80211_STA_RX_BW_40:
+ return IWL_TLC_MNG_CH_WIDTH_40MHZ;
+ case IEEE80211_STA_RX_BW_20:
+ default:
+ return IWL_TLC_MNG_CH_WIDTH_20MHZ;
+ }
+}
+
+static __le16
+iwl_mld_get_tlc_cmd_flags(struct iwl_mld *mld,
+ struct ieee80211_vif *vif,
+ struct ieee80211_link_sta *link_sta,
+ const struct ieee80211_sta_he_cap *own_he_cap,
+ const struct ieee80211_sta_eht_cap *own_eht_cap)
+{
+ struct ieee80211_sta_ht_cap *ht_cap = &link_sta->ht_cap;
+ struct ieee80211_sta_vht_cap *vht_cap = &link_sta->vht_cap;
+ struct ieee80211_sta_he_cap *he_cap = &link_sta->he_cap;
+ bool has_vht = vht_cap->vht_supported;
+ u16 flags = 0;
+
+ /* STBC flags */
+ if (mld->cfg->ht_params.stbc &&
+ (hweight8(iwl_mld_get_valid_tx_ant(mld)) > 1)) {
+ if (he_cap->has_he && he_cap->he_cap_elem.phy_cap_info[2] &
+ IEEE80211_HE_PHY_CAP2_STBC_RX_UNDER_80MHZ)
+ flags |= IWL_TLC_MNG_CFG_FLAGS_STBC_MSK;
+ else if (vht_cap->cap & IEEE80211_VHT_CAP_RXSTBC_MASK)
+ flags |= IWL_TLC_MNG_CFG_FLAGS_STBC_MSK;
+ else if (ht_cap->cap & IEEE80211_HT_CAP_RX_STBC)
+ flags |= IWL_TLC_MNG_CFG_FLAGS_STBC_MSK;
+ }
+
+ /* LDPC */
+ if (mld->cfg->ht_params.ldpc &&
+ ((ht_cap->cap & IEEE80211_HT_CAP_LDPC_CODING) ||
+ (has_vht && (vht_cap->cap & IEEE80211_VHT_CAP_RXLDPC))))
+ flags |= IWL_TLC_MNG_CFG_FLAGS_LDPC_MSK;
+
+ if (he_cap->has_he && (he_cap->he_cap_elem.phy_cap_info[1] &
+ IEEE80211_HE_PHY_CAP1_LDPC_CODING_IN_PAYLOAD))
+ flags |= IWL_TLC_MNG_CFG_FLAGS_LDPC_MSK;
+
+ if (own_he_cap &&
+ !(own_he_cap->he_cap_elem.phy_cap_info[1] &
+ IEEE80211_HE_PHY_CAP1_LDPC_CODING_IN_PAYLOAD))
+ flags &= ~IWL_TLC_MNG_CFG_FLAGS_LDPC_MSK;
+
+ /* DCM */
+ if (he_cap->has_he &&
+ (he_cap->he_cap_elem.phy_cap_info[3] &
+ IEEE80211_HE_PHY_CAP3_DCM_MAX_CONST_RX_MASK &&
+ own_he_cap &&
+ own_he_cap->he_cap_elem.phy_cap_info[3] &
+ IEEE80211_HE_PHY_CAP3_DCM_MAX_CONST_TX_MASK))
+ flags |= IWL_TLC_MNG_CFG_FLAGS_HE_DCM_NSS_1_MSK;
+
+ /* Extra EHT LTF */
+ if (own_eht_cap &&
+ own_eht_cap->eht_cap_elem.phy_cap_info[5] &
+ IEEE80211_EHT_PHY_CAP5_SUPP_EXTRA_EHT_LTF &&
+ link_sta->eht_cap.has_eht &&
+ link_sta->eht_cap.eht_cap_elem.phy_cap_info[5] &
+ IEEE80211_EHT_PHY_CAP5_SUPP_EXTRA_EHT_LTF) {
+ flags |= IWL_TLC_MNG_CFG_FLAGS_EHT_EXTRA_LTF_MSK;
+ }
+
+ return cpu_to_le16(flags);
+}
+
+static u8 iwl_mld_get_fw_chains(struct iwl_mld *mld)
+{
+ u8 chains = iwl_mld_get_valid_tx_ant(mld);
+ u8 fw_chains = 0;
+
+ if (chains & ANT_A)
+ fw_chains |= IWL_TLC_MNG_CHAIN_A_MSK;
+ if (chains & ANT_B)
+ fw_chains |= IWL_TLC_MNG_CHAIN_B_MSK;
+
+ return fw_chains;
+}
+
+static u8 iwl_mld_get_fw_sgi(struct ieee80211_link_sta *link_sta)
+{
+ struct ieee80211_sta_ht_cap *ht_cap = &link_sta->ht_cap;
+ struct ieee80211_sta_vht_cap *vht_cap = &link_sta->vht_cap;
+ struct ieee80211_sta_he_cap *he_cap = &link_sta->he_cap;
+ u8 sgi_chwidths = 0;
+
+ /* If the association supports HE, HT/VHT rates will never be used for
+ * Tx and therefor there's no need to set the
+ * sgi-per-channel-width-support bits
+ */
+ if (he_cap->has_he)
+ return 0;
+
+ if (ht_cap->cap & IEEE80211_HT_CAP_SGI_20)
+ sgi_chwidths |= BIT(IWL_TLC_MNG_CH_WIDTH_20MHZ);
+ if (ht_cap->cap & IEEE80211_HT_CAP_SGI_40)
+ sgi_chwidths |= BIT(IWL_TLC_MNG_CH_WIDTH_40MHZ);
+ if (vht_cap->cap & IEEE80211_VHT_CAP_SHORT_GI_80)
+ sgi_chwidths |= BIT(IWL_TLC_MNG_CH_WIDTH_80MHZ);
+ if (vht_cap->cap & IEEE80211_VHT_CAP_SHORT_GI_160)
+ sgi_chwidths |= BIT(IWL_TLC_MNG_CH_WIDTH_160MHZ);
+
+ return sgi_chwidths;
+}
+
+static int
+iwl_mld_get_highest_fw_mcs(const struct ieee80211_sta_vht_cap *vht_cap,
+ int nss)
+{
+ u16 rx_mcs = le16_to_cpu(vht_cap->vht_mcs.rx_mcs_map) &
+ (0x3 << (2 * (nss - 1)));
+ rx_mcs >>= (2 * (nss - 1));
+
+ switch (rx_mcs) {
+ case IEEE80211_VHT_MCS_SUPPORT_0_7:
+ return IWL_TLC_MNG_HT_RATE_MCS7;
+ case IEEE80211_VHT_MCS_SUPPORT_0_8:
+ return IWL_TLC_MNG_HT_RATE_MCS8;
+ case IEEE80211_VHT_MCS_SUPPORT_0_9:
+ return IWL_TLC_MNG_HT_RATE_MCS9;
+ default:
+ WARN_ON_ONCE(1);
+ break;
+ }
+
+ return 0;
+}
+
+static void
+iwl_mld_fill_vht_rates(const struct ieee80211_link_sta *link_sta,
+ const struct ieee80211_sta_vht_cap *vht_cap,
+ struct iwl_tlc_config_cmd_v4 *cmd)
+{
+ u16 supp;
+ int i, highest_mcs;
+ u8 max_nss = link_sta->rx_nss;
+ struct ieee80211_vht_cap ieee_vht_cap = {
+ .vht_cap_info = cpu_to_le32(vht_cap->cap),
+ .supp_mcs = vht_cap->vht_mcs,
+ };
+
+ /* the station support only a single receive chain */
+ if (link_sta->smps_mode == IEEE80211_SMPS_STATIC)
+ max_nss = 1;
+
+ for (i = 0; i < max_nss && i < IWL_TLC_NSS_MAX; i++) {
+ int nss = i + 1;
+
+ highest_mcs = iwl_mld_get_highest_fw_mcs(vht_cap, nss);
+ if (!highest_mcs)
+ continue;
+
+ supp = BIT(highest_mcs + 1) - 1;
+ if (link_sta->bandwidth == IEEE80211_STA_RX_BW_20)
+ supp &= ~BIT(IWL_TLC_MNG_HT_RATE_MCS9);
+
+ cmd->ht_rates[i][IWL_TLC_MCS_PER_BW_80] = cpu_to_le16(supp);
+ /* Check if VHT extended NSS indicates that the bandwidth/NSS
+ * configuration is supported - only for MCS 0 since we already
+ * decoded the MCS bits anyway ourselves.
+ */
+ if (link_sta->bandwidth == IEEE80211_STA_RX_BW_160 &&
+ ieee80211_get_vht_max_nss(&ieee_vht_cap,
+ IEEE80211_VHT_CHANWIDTH_160MHZ,
+ 0, true, nss) >= nss)
+ cmd->ht_rates[i][IWL_TLC_MCS_PER_BW_160] =
+ cmd->ht_rates[i][IWL_TLC_MCS_PER_BW_80];
+ }
+}
+
+static u16 iwl_mld_he_mac80211_mcs_to_fw_mcs(u16 mcs)
+{
+ switch (mcs) {
+ case IEEE80211_HE_MCS_SUPPORT_0_7:
+ return BIT(IWL_TLC_MNG_HT_RATE_MCS7 + 1) - 1;
+ case IEEE80211_HE_MCS_SUPPORT_0_9:
+ return BIT(IWL_TLC_MNG_HT_RATE_MCS9 + 1) - 1;
+ case IEEE80211_HE_MCS_SUPPORT_0_11:
+ return BIT(IWL_TLC_MNG_HT_RATE_MCS11 + 1) - 1;
+ case IEEE80211_HE_MCS_NOT_SUPPORTED:
+ return 0;
+ }
+
+ WARN(1, "invalid HE MCS %d\n", mcs);
+ return 0;
+}
+
+static void
+iwl_mld_fill_he_rates(const struct ieee80211_link_sta *link_sta,
+ const struct ieee80211_sta_he_cap *own_he_cap,
+ struct iwl_tlc_config_cmd_v4 *cmd)
+{
+ const struct ieee80211_sta_he_cap *he_cap = &link_sta->he_cap;
+ u16 mcs_160 = le16_to_cpu(he_cap->he_mcs_nss_supp.rx_mcs_160);
+ u16 mcs_80 = le16_to_cpu(he_cap->he_mcs_nss_supp.rx_mcs_80);
+ u16 tx_mcs_80 = le16_to_cpu(own_he_cap->he_mcs_nss_supp.tx_mcs_80);
+ u16 tx_mcs_160 = le16_to_cpu(own_he_cap->he_mcs_nss_supp.tx_mcs_160);
+ int i;
+ u8 nss = link_sta->rx_nss;
+
+ /* the station support only a single receive chain */
+ if (link_sta->smps_mode == IEEE80211_SMPS_STATIC)
+ nss = 1;
+
+ for (i = 0; i < nss && i < IWL_TLC_NSS_MAX; i++) {
+ u16 _mcs_160 = (mcs_160 >> (2 * i)) & 0x3;
+ u16 _mcs_80 = (mcs_80 >> (2 * i)) & 0x3;
+ u16 _tx_mcs_160 = (tx_mcs_160 >> (2 * i)) & 0x3;
+ u16 _tx_mcs_80 = (tx_mcs_80 >> (2 * i)) & 0x3;
+
+ /* If one side doesn't support - mark both as not supporting */
+ if (_mcs_80 == IEEE80211_HE_MCS_NOT_SUPPORTED ||
+ _tx_mcs_80 == IEEE80211_HE_MCS_NOT_SUPPORTED) {
+ _mcs_80 = IEEE80211_HE_MCS_NOT_SUPPORTED;
+ _tx_mcs_80 = IEEE80211_HE_MCS_NOT_SUPPORTED;
+ }
+ if (_mcs_80 > _tx_mcs_80)
+ _mcs_80 = _tx_mcs_80;
+ cmd->ht_rates[i][IWL_TLC_MCS_PER_BW_80] =
+ cpu_to_le16(iwl_mld_he_mac80211_mcs_to_fw_mcs(_mcs_80));
+
+ /* If one side doesn't support - mark both as not supporting */
+ if (_mcs_160 == IEEE80211_HE_MCS_NOT_SUPPORTED ||
+ _tx_mcs_160 == IEEE80211_HE_MCS_NOT_SUPPORTED) {
+ _mcs_160 = IEEE80211_HE_MCS_NOT_SUPPORTED;
+ _tx_mcs_160 = IEEE80211_HE_MCS_NOT_SUPPORTED;
+ }
+ if (_mcs_160 > _tx_mcs_160)
+ _mcs_160 = _tx_mcs_160;
+ cmd->ht_rates[i][IWL_TLC_MCS_PER_BW_160] =
+ cpu_to_le16(iwl_mld_he_mac80211_mcs_to_fw_mcs(_mcs_160));
+ }
+}
+
+static void iwl_mld_set_eht_mcs(__le16 ht_rates[][3],
+ enum IWL_TLC_MCS_PER_BW bw,
+ u8 max_nss, u16 mcs_msk)
+{
+ if (max_nss >= 2)
+ ht_rates[IWL_TLC_NSS_2][bw] |= cpu_to_le16(mcs_msk);
+
+ if (max_nss >= 1)
+ ht_rates[IWL_TLC_NSS_1][bw] |= cpu_to_le16(mcs_msk);
+}
+
+static const
+struct ieee80211_eht_mcs_nss_supp_bw *
+iwl_mld_get_eht_mcs_of_bw(enum IWL_TLC_MCS_PER_BW bw,
+ const struct ieee80211_eht_mcs_nss_supp *eht_mcs)
+{
+ switch (bw) {
+ case IWL_TLC_MCS_PER_BW_80:
+ return &eht_mcs->bw._80;
+ case IWL_TLC_MCS_PER_BW_160:
+ return &eht_mcs->bw._160;
+ case IWL_TLC_MCS_PER_BW_320:
+ return &eht_mcs->bw._320;
+ default:
+ return NULL;
+ }
+}
+
+static u8 iwl_mld_get_eht_max_nss(u8 rx_nss, u8 tx_nss)
+{
+ u8 tx = u8_get_bits(tx_nss, IEEE80211_EHT_MCS_NSS_TX);
+ u8 rx = u8_get_bits(rx_nss, IEEE80211_EHT_MCS_NSS_RX);
+ /* the max nss that can be used,
+ * is the min with our tx capa and the peer rx capa.
+ */
+ return min(tx, rx);
+}
+
+#define MAX_NSS_MCS(mcs_num, rx, tx) \
+ iwl_mld_get_eht_max_nss((rx)->rx_tx_mcs ##mcs_num## _max_nss, \
+ (tx)->rx_tx_mcs ##mcs_num## _max_nss)
+
+static void
+iwl_mld_fill_eht_rates(struct ieee80211_vif *vif,
+ const struct ieee80211_link_sta *link_sta,
+ const struct ieee80211_sta_he_cap *own_he_cap,
+ const struct ieee80211_sta_eht_cap *own_eht_cap,
+ struct iwl_tlc_config_cmd_v4 *cmd)
+{
+ /* peer RX mcs capa */
+ const struct ieee80211_eht_mcs_nss_supp *eht_rx_mcs =
+ &link_sta->eht_cap.eht_mcs_nss_supp;
+ /* our TX mcs capa */
+ const struct ieee80211_eht_mcs_nss_supp *eht_tx_mcs =
+ &own_eht_cap->eht_mcs_nss_supp;
+
+ enum IWL_TLC_MCS_PER_BW bw;
+ struct ieee80211_eht_mcs_nss_supp_20mhz_only mcs_rx_20;
+ struct ieee80211_eht_mcs_nss_supp_20mhz_only mcs_tx_20;
+
+ /* peer is 20 MHz only */
+ if (vif->type == NL80211_IFTYPE_AP &&
+ !(link_sta->he_cap.he_cap_elem.phy_cap_info[0] &
+ IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_MASK_ALL)) {
+ mcs_rx_20 = eht_rx_mcs->only_20mhz;
+ } else {
+ mcs_rx_20.rx_tx_mcs7_max_nss =
+ eht_rx_mcs->bw._80.rx_tx_mcs9_max_nss;
+ mcs_rx_20.rx_tx_mcs9_max_nss =
+ eht_rx_mcs->bw._80.rx_tx_mcs9_max_nss;
+ mcs_rx_20.rx_tx_mcs11_max_nss =
+ eht_rx_mcs->bw._80.rx_tx_mcs11_max_nss;
+ mcs_rx_20.rx_tx_mcs13_max_nss =
+ eht_rx_mcs->bw._80.rx_tx_mcs13_max_nss;
+ }
+
+ /* NIC is capable of 20 MHz only */
+ if (!(own_he_cap->he_cap_elem.phy_cap_info[0] &
+ IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_MASK_ALL)) {
+ mcs_tx_20 = eht_tx_mcs->only_20mhz;
+ } else {
+ mcs_tx_20.rx_tx_mcs7_max_nss =
+ eht_tx_mcs->bw._80.rx_tx_mcs9_max_nss;
+ mcs_tx_20.rx_tx_mcs9_max_nss =
+ eht_tx_mcs->bw._80.rx_tx_mcs9_max_nss;
+ mcs_tx_20.rx_tx_mcs11_max_nss =
+ eht_tx_mcs->bw._80.rx_tx_mcs11_max_nss;
+ mcs_tx_20.rx_tx_mcs13_max_nss =
+ eht_tx_mcs->bw._80.rx_tx_mcs13_max_nss;
+ }
+
+ /* rates for 20/40/80 MHz */
+ bw = IWL_TLC_MCS_PER_BW_80;
+ iwl_mld_set_eht_mcs(cmd->ht_rates, bw,
+ MAX_NSS_MCS(7, &mcs_rx_20, &mcs_tx_20),
+ GENMASK(7, 0));
+ iwl_mld_set_eht_mcs(cmd->ht_rates, bw,
+ MAX_NSS_MCS(9, &mcs_rx_20, &mcs_tx_20),
+ GENMASK(9, 8));
+ iwl_mld_set_eht_mcs(cmd->ht_rates, bw,
+ MAX_NSS_MCS(11, &mcs_rx_20, &mcs_tx_20),
+ GENMASK(11, 10));
+ iwl_mld_set_eht_mcs(cmd->ht_rates, bw,
+ MAX_NSS_MCS(13, &mcs_rx_20, &mcs_tx_20),
+ GENMASK(13, 12));
+
+ /* rates for 160/320 MHz */
+ for (bw = IWL_TLC_MCS_PER_BW_160; bw <= IWL_TLC_MCS_PER_BW_320; bw++) {
+ const struct ieee80211_eht_mcs_nss_supp_bw *mcs_rx =
+ iwl_mld_get_eht_mcs_of_bw(bw, eht_rx_mcs);
+ const struct ieee80211_eht_mcs_nss_supp_bw *mcs_tx =
+ iwl_mld_get_eht_mcs_of_bw(bw, eht_tx_mcs);
+
+ /* got unsupported index for bw */
+ if (!mcs_rx || !mcs_tx)
+ continue;
+
+ /* break out if we don't support the bandwidth */
+ if (cmd->max_ch_width < (bw + IWL_TLC_MNG_CH_WIDTH_80MHZ))
+ break;
+
+ iwl_mld_set_eht_mcs(cmd->ht_rates, bw,
+ MAX_NSS_MCS(9, mcs_rx, mcs_tx),
+ GENMASK(9, 0));
+ iwl_mld_set_eht_mcs(cmd->ht_rates, bw,
+ MAX_NSS_MCS(11, mcs_rx, mcs_tx),
+ GENMASK(11, 10));
+ iwl_mld_set_eht_mcs(cmd->ht_rates, bw,
+ MAX_NSS_MCS(13, mcs_rx, mcs_tx),
+ GENMASK(13, 12));
+ }
+
+ /* the station support only a single receive chain */
+ if (link_sta->smps_mode == IEEE80211_SMPS_STATIC ||
+ link_sta->rx_nss < 2)
+ memset(cmd->ht_rates[IWL_TLC_NSS_2], 0,
+ sizeof(cmd->ht_rates[IWL_TLC_NSS_2]));
+}
+
+static void
+iwl_mld_fill_supp_rates(struct iwl_mld *mld, struct ieee80211_vif *vif,
+ struct ieee80211_link_sta *link_sta,
+ struct ieee80211_supported_band *sband,
+ const struct ieee80211_sta_he_cap *own_he_cap,
+ const struct ieee80211_sta_eht_cap *own_eht_cap,
+ struct iwl_tlc_config_cmd_v4 *cmd)
+{
+ int i;
+ u16 non_ht_rates = 0;
+ unsigned long rates_bitmap;
+ const struct ieee80211_sta_ht_cap *ht_cap = &link_sta->ht_cap;
+ const struct ieee80211_sta_vht_cap *vht_cap = &link_sta->vht_cap;
+ const struct ieee80211_sta_he_cap *he_cap = &link_sta->he_cap;
+
+ /* non HT rates */
+ rates_bitmap = link_sta->supp_rates[sband->band];
+ for_each_set_bit(i, &rates_bitmap, BITS_PER_LONG)
+ non_ht_rates |= BIT(sband->bitrates[i].hw_value);
+
+ cmd->non_ht_rates = cpu_to_le16(non_ht_rates);
+ cmd->mode = IWL_TLC_MNG_MODE_NON_HT;
+
+ if (link_sta->eht_cap.has_eht && own_he_cap && own_eht_cap) {
+ cmd->mode = IWL_TLC_MNG_MODE_EHT;
+ iwl_mld_fill_eht_rates(vif, link_sta, own_he_cap,
+ own_eht_cap, cmd);
+ } else if (he_cap->has_he && own_he_cap) {
+ cmd->mode = IWL_TLC_MNG_MODE_HE;
+ iwl_mld_fill_he_rates(link_sta, own_he_cap, cmd);
+ } else if (vht_cap->vht_supported) {
+ cmd->mode = IWL_TLC_MNG_MODE_VHT;
+ iwl_mld_fill_vht_rates(link_sta, vht_cap, cmd);
+ } else if (ht_cap->ht_supported) {
+ cmd->mode = IWL_TLC_MNG_MODE_HT;
+ cmd->ht_rates[IWL_TLC_NSS_1][IWL_TLC_MCS_PER_BW_80] =
+ cpu_to_le16(ht_cap->mcs.rx_mask[0]);
+
+ /* the station support only a single receive chain */
+ if (link_sta->smps_mode == IEEE80211_SMPS_STATIC)
+ cmd->ht_rates[IWL_TLC_NSS_2][IWL_TLC_MCS_PER_BW_80] =
+ 0;
+ else
+ cmd->ht_rates[IWL_TLC_NSS_2][IWL_TLC_MCS_PER_BW_80] =
+ cpu_to_le16(ht_cap->mcs.rx_mask[1]);
+ }
+}
+
+static void iwl_mld_send_tlc_cmd(struct iwl_mld *mld,
+ struct ieee80211_vif *vif,
+ struct ieee80211_link_sta *link_sta,
+ enum nl80211_band band)
+{
+ struct iwl_mld_sta *mld_sta = iwl_mld_sta_from_mac80211(link_sta->sta);
+ struct ieee80211_supported_band *sband = mld->hw->wiphy->bands[band];
+ const struct ieee80211_sta_he_cap *own_he_cap =
+ ieee80211_get_he_iftype_cap_vif(sband, vif);
+ const struct ieee80211_sta_eht_cap *own_eht_cap =
+ ieee80211_get_eht_iftype_cap_vif(sband, vif);
+ struct iwl_tlc_config_cmd_v4 cmd = {
+ /* For AP mode, use 20 MHz until the STA is authorized */
+ .max_ch_width = mld_sta->sta_state > IEEE80211_STA_ASSOC ?
+ iwl_mld_fw_bw_from_sta_bw(link_sta) :
+ IWL_TLC_MNG_CH_WIDTH_20MHZ,
+ .flags = iwl_mld_get_tlc_cmd_flags(mld, vif, link_sta,
+ own_he_cap, own_eht_cap),
+ .chains = iwl_mld_get_fw_chains(mld),
+ .sgi_ch_width_supp = iwl_mld_get_fw_sgi(link_sta),
+ .max_mpdu_len = cpu_to_le16(link_sta->agg.max_amsdu_len),
+ };
+ int fw_sta_id = iwl_mld_fw_sta_id_from_link_sta(mld, link_sta);
+ int ret;
+
+ if (fw_sta_id < 0)
+ return;
+
+ cmd.sta_id = fw_sta_id;
+
+ iwl_mld_fill_supp_rates(mld, vif, link_sta, sband,
+ own_he_cap, own_eht_cap,
+ &cmd);
+
+ IWL_DEBUG_RATE(mld,
+ "TLC CONFIG CMD, sta_id=%d, max_ch_width=%d, mode=%d\n",
+ cmd.sta_id, cmd.max_ch_width, cmd.mode);
+
+ /* Send async since this can be called within a RCU-read section */
+ ret = iwl_mld_send_cmd_with_flags_pdu(mld, WIDE_ID(DATA_PATH_GROUP,
+ TLC_MNG_CONFIG_CMD),
+ CMD_ASYNC, &cmd);
+ if (ret)
+ IWL_ERR(mld, "Failed to send TLC cmd (%d)\n", ret);
+}
+
+int iwl_mld_send_tlc_dhc(struct iwl_mld *mld, u8 sta_id, u32 type, u32 data)
+{
+ struct {
+ struct iwl_dhc_cmd dhc;
+ struct iwl_dhc_tlc_cmd tlc;
+ } __packed cmd = {
+ .tlc.sta_id = sta_id,
+ .tlc.type = cpu_to_le32(type),
+ .tlc.data[0] = cpu_to_le32(data),
+ .dhc.length = cpu_to_le32(sizeof(cmd.tlc) >> 2),
+ .dhc.index_and_mask =
+ cpu_to_le32(DHC_TABLE_INTEGRATION | DHC_TARGET_UMAC |
+ DHC_INTEGRATION_TLC_DEBUG_CONFIG),
+ };
+ int ret;
+
+ ret = iwl_mld_send_cmd_with_flags_pdu(mld,
+ WIDE_ID(IWL_ALWAYS_LONG_GROUP,
+ DEBUG_HOST_COMMAND),
+ CMD_ASYNC, &cmd);
+ IWL_DEBUG_RATE(mld, "sta_id %d, type: 0x%X, value: 0x%X, ret%d\n",
+ sta_id, type, data, ret);
+ return ret;
+}
+
+void iwl_mld_config_tlc_link(struct iwl_mld *mld,
+ struct ieee80211_vif *vif,
+ struct ieee80211_bss_conf *link_conf,
+ struct ieee80211_link_sta *link_sta)
+{
+ struct iwl_mld_sta *mld_sta = iwl_mld_sta_from_mac80211(link_sta->sta);
+ enum nl80211_band band;
+
+ if (WARN_ON_ONCE(!link_conf->chanreq.oper.chan))
+ return;
+
+ /* Before we have information about a station, configure the A-MSDU RC
+ * limit such that iwlmd and mac80211 would not be allowed to build
+ * A-MSDUs.
+ */
+ if (mld_sta->sta_state < IEEE80211_STA_ASSOC) {
+ link_sta->agg.max_rc_amsdu_len = 1;
+ ieee80211_sta_recalc_aggregates(link_sta->sta);
+ }
+
+ band = link_conf->chanreq.oper.chan->band;
+ iwl_mld_send_tlc_cmd(mld, vif, link_sta, band);
+}
+
+void iwl_mld_config_tlc(struct iwl_mld *mld, struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta)
+{
+ struct ieee80211_bss_conf *link;
+ int link_id;
+
+ lockdep_assert_wiphy(mld->wiphy);
+
+ for_each_vif_active_link(vif, link, link_id) {
+ struct ieee80211_link_sta *link_sta =
+ link_sta_dereference_check(sta, link_id);
+
+ if (!link || !link_sta)
+ continue;
+
+ iwl_mld_config_tlc_link(mld, vif, link, link_sta);
+ }
+}
+
+static u16
+iwl_mld_get_amsdu_size_of_tid(struct iwl_mld *mld,
+ struct ieee80211_link_sta *link_sta,
+ unsigned int tid)
+{
+ struct ieee80211_sta *sta = link_sta->sta;
+ struct ieee80211_vif *vif = iwl_mld_sta_from_mac80211(sta)->vif;
+ const u8 tid_to_mac80211_ac[] = {
+ IEEE80211_AC_BE,
+ IEEE80211_AC_BK,
+ IEEE80211_AC_BK,
+ IEEE80211_AC_BE,
+ IEEE80211_AC_VI,
+ IEEE80211_AC_VI,
+ IEEE80211_AC_VO,
+ IEEE80211_AC_VO,
+ };
+ unsigned int result = link_sta->agg.max_rc_amsdu_len;
+ u8 ac, txf, lmac;
+
+ lockdep_assert_wiphy(mld->wiphy);
+
+ /* Don't send an AMSDU that will be longer than the TXF.
+ * Add a security margin of 256 for the TX command + headers.
+ * We also want to have the start of the next packet inside the
+ * fifo to be able to send bursts.
+ */
+
+ if (WARN_ON(tid >= ARRAY_SIZE(tid_to_mac80211_ac)))
+ return 0;
+
+ ac = tid_to_mac80211_ac[tid];
+
+ /* For HE redirect to trigger based fifos */
+ if (link_sta->he_cap.has_he)
+ ac += 4;
+
+ txf = iwl_mld_mac80211_ac_to_fw_tx_fifo(ac);
+
+ /* Only one link: take the lmac according to the band */
+ if (hweight16(sta->valid_links) <= 1) {
+ enum nl80211_band band;
+ struct ieee80211_bss_conf *link =
+ wiphy_dereference(mld->wiphy,
+ vif->link_conf[link_sta->link_id]);
+
+ if (WARN_ON(!link || !link->chanreq.oper.chan))
+ band = NL80211_BAND_2GHZ;
+ else
+ band = link->chanreq.oper.chan->band;
+ lmac = iwl_mld_get_lmac_id(mld, band);
+
+ /* More than one link but with 2 lmacs: take the minimum */
+ } else if (fw_has_capa(&mld->fw->ucode_capa,
+ IWL_UCODE_TLV_CAPA_CDB_SUPPORT)) {
+ lmac = IWL_LMAC_5G_INDEX;
+ result = min_t(unsigned int, result,
+ mld->fwrt.smem_cfg.lmac[lmac].txfifo_size[txf] - 256);
+ lmac = IWL_LMAC_24G_INDEX;
+ /* More than one link but only one lmac */
+ } else {
+ lmac = IWL_LMAC_24G_INDEX;
+ }
+
+ return min_t(unsigned int, result,
+ mld->fwrt.smem_cfg.lmac[lmac].txfifo_size[txf] - 256);
+}
+
+void iwl_mld_handle_tlc_notif(struct iwl_mld *mld,
+ struct iwl_rx_packet *pkt)
+{
+ struct iwl_tlc_update_notif *notif = (void *)pkt->data;
+ struct ieee80211_link_sta *link_sta;
+ u32 flags = le32_to_cpu(notif->flags);
+ u32 enabled;
+ u16 size;
+
+ if (IWL_FW_CHECK(mld, notif->sta_id >= mld->fw->ucode_capa.num_stations,
+ "Invalid sta id (%d) in TLC notification\n",
+ notif->sta_id))
+ return;
+
+ link_sta = wiphy_dereference(mld->wiphy,
+ mld->fw_id_to_link_sta[notif->sta_id]);
+
+ if (WARN(IS_ERR_OR_NULL(link_sta),
+ "link_sta of sta id (%d) doesn't exist\n", notif->sta_id))
+ return;
+
+ if (flags & IWL_TLC_NOTIF_FLAG_RATE) {
+ struct iwl_mld_link_sta *mld_link_sta =
+ iwl_mld_link_sta_from_mac80211(link_sta);
+ char pretty_rate[100];
+
+ if (WARN_ON(!mld_link_sta))
+ return;
+
+ mld_link_sta->last_rate_n_flags =
+ iwl_v3_rate_from_v2_v3(notif->rate,
+ mld->fw_rates_ver_3);
+
+ rs_pretty_print_rate(pretty_rate, sizeof(pretty_rate),
+ mld_link_sta->last_rate_n_flags);
+ IWL_DEBUG_RATE(mld, "TLC notif: new rate = %s\n", pretty_rate);
+ }
+
+ /* We are done processing the notif */
+ if (!(flags & IWL_TLC_NOTIF_FLAG_AMSDU))
+ return;
+
+ enabled = le32_to_cpu(notif->amsdu_enabled);
+ size = le32_to_cpu(notif->amsdu_size);
+
+ if (size < 2000) {
+ size = 0;
+ enabled = 0;
+ }
+
+ if (IWL_FW_CHECK(mld, size > link_sta->agg.max_amsdu_len,
+ "Invalid AMSDU len in TLC notif: %d (Max AMSDU len: %d)\n",
+ size, link_sta->agg.max_amsdu_len))
+ return;
+
+ link_sta->agg.max_rc_amsdu_len = size;
+
+ for (int i = 0; i < IWL_MAX_TID_COUNT; i++) {
+ if (enabled & BIT(i))
+ link_sta->agg.max_tid_amsdu_len[i] =
+ iwl_mld_get_amsdu_size_of_tid(mld, link_sta, i);
+ else
+ link_sta->agg.max_tid_amsdu_len[i] = 1;
+ }
+
+ ieee80211_sta_recalc_aggregates(link_sta->sta);
+
+ IWL_DEBUG_RATE(mld,
+ "AMSDU update. AMSDU size: %d, AMSDU selected size: %d, AMSDU TID bitmap 0x%X\n",
+ le32_to_cpu(notif->amsdu_size), size, enabled);
+}
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/tlc.h b/drivers/net/wireless/intel/iwlwifi/mld/tlc.h
new file mode 100644
index 000000000000..c32f42e8840b
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/mld/tlc.h
@@ -0,0 +1,23 @@
+/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
+/*
+ * Copyright (C) 2024 Intel Corporation
+ */
+#ifndef __iwl_mld_tlc_h__
+#define __iwl_mld_tlc_h__
+
+#include "mld.h"
+
+void iwl_mld_config_tlc_link(struct iwl_mld *mld,
+ struct ieee80211_vif *vif,
+ struct ieee80211_bss_conf *link_conf,
+ struct ieee80211_link_sta *link_sta);
+
+void iwl_mld_config_tlc(struct iwl_mld *mld, struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta);
+
+void iwl_mld_handle_tlc_notif(struct iwl_mld *mld,
+ struct iwl_rx_packet *pkt);
+
+int iwl_mld_send_tlc_dhc(struct iwl_mld *mld, u8 sta_id, u32 type, u32 data);
+
+#endif /* __iwl_mld_tlc_h__ */
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/tx.c b/drivers/net/wireless/intel/iwlwifi/mld/tx.c
new file mode 100644
index 000000000000..3b4b575aadaa
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/mld/tx.c
@@ -0,0 +1,1394 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
+/*
+ * Copyright (C) 2024 - 2025 Intel Corporation
+ */
+#include <net/ip.h>
+
+#include "tx.h"
+#include "sta.h"
+#include "hcmd.h"
+#include "iwl-utils.h"
+#include "iface.h"
+
+#include "fw/dbg.h"
+
+#include "fw/api/tx.h"
+#include "fw/api/rs.h"
+#include "fw/api/txq.h"
+#include "fw/api/datapath.h"
+#include "fw/api/time-event.h"
+
+#define MAX_ANT_NUM 2
+
+/* Toggles between TX antennas. Receives the bitmask of valid TX antennas and
+ * the *index* used for the last TX, and returns the next valid *index* to use.
+ * In order to set it in the tx_cmd, must do BIT(idx).
+ */
+static u8 iwl_mld_next_ant(u8 valid, u8 last_idx)
+{
+ u8 index = last_idx;
+
+ for (int i = 0; i < MAX_ANT_NUM; i++) {
+ index = (index + 1) % MAX_ANT_NUM;
+ if (valid & BIT(index))
+ return index;
+ }
+
+ WARN_ONCE(1, "Failed to toggle between antennas 0x%x", valid);
+
+ return last_idx;
+}
+
+void iwl_mld_toggle_tx_ant(struct iwl_mld *mld, u8 *ant)
+{
+ *ant = iwl_mld_next_ant(iwl_mld_get_valid_tx_ant(mld), *ant);
+}
+
+static int
+iwl_mld_get_queue_size(struct iwl_mld *mld, struct ieee80211_txq *txq)
+{
+ struct ieee80211_sta *sta = txq->sta;
+ struct ieee80211_link_sta *link_sta;
+ unsigned int link_id;
+ int max_size = IWL_DEFAULT_QUEUE_SIZE;
+
+ lockdep_assert_wiphy(mld->wiphy);
+
+ for_each_sta_active_link(txq->vif, sta, link_sta, link_id) {
+ if (link_sta->eht_cap.has_eht) {
+ max_size = IWL_DEFAULT_QUEUE_SIZE_EHT;
+ break;
+ }
+
+ if (link_sta->he_cap.has_he)
+ max_size = IWL_DEFAULT_QUEUE_SIZE_HE;
+ }
+
+ return max_size;
+}
+
+static int iwl_mld_allocate_txq(struct iwl_mld *mld, struct ieee80211_txq *txq)
+{
+ u8 tid = txq->tid == IEEE80211_NUM_TIDS ? IWL_MGMT_TID : txq->tid;
+ u32 fw_sta_mask = iwl_mld_fw_sta_id_mask(mld, txq->sta);
+ /* We can't know when the station is asleep or awake, so we
+ * must disable the queue hang detection.
+ */
+ unsigned int watchdog_timeout = txq->vif->type == NL80211_IFTYPE_AP ?
+ IWL_WATCHDOG_DISABLED :
+ mld->trans->mac_cfg->base->wd_timeout;
+ int queue, size;
+
+ lockdep_assert_wiphy(mld->wiphy);
+
+ if (tid == IWL_MGMT_TID)
+ size = max_t(u32, IWL_MGMT_QUEUE_SIZE,
+ mld->trans->mac_cfg->base->min_txq_size);
+ else
+ size = iwl_mld_get_queue_size(mld, txq);
+
+ queue = iwl_trans_txq_alloc(mld->trans, 0, fw_sta_mask, tid, size,
+ watchdog_timeout);
+
+ if (queue >= 0)
+ IWL_DEBUG_TX_QUEUES(mld,
+ "Enabling TXQ #%d for sta mask 0x%x tid %d\n",
+ queue, fw_sta_mask, tid);
+ return queue;
+}
+
+static int iwl_mld_add_txq(struct iwl_mld *mld, struct ieee80211_txq *txq)
+{
+ struct iwl_mld_txq *mld_txq = iwl_mld_txq_from_mac80211(txq);
+ int id;
+
+ lockdep_assert_wiphy(mld->wiphy);
+
+ /* This will alse send the SCD_QUEUE_CONFIG_CMD */
+ id = iwl_mld_allocate_txq(mld, txq);
+ if (id < 0)
+ return id;
+
+ mld_txq->fw_id = id;
+ mld_txq->status.allocated = true;
+
+ rcu_assign_pointer(mld->fw_id_to_txq[id], txq);
+
+ return 0;
+}
+
+void iwl_mld_add_txq_list(struct iwl_mld *mld)
+{
+ lockdep_assert_wiphy(mld->wiphy);
+
+ while (!list_empty(&mld->txqs_to_add)) {
+ struct ieee80211_txq *txq;
+ struct iwl_mld_txq *mld_txq =
+ list_first_entry(&mld->txqs_to_add, struct iwl_mld_txq,
+ list);
+ int failed;
+
+ txq = container_of((void *)mld_txq, struct ieee80211_txq,
+ drv_priv);
+
+ failed = iwl_mld_add_txq(mld, txq);
+
+ local_bh_disable();
+ spin_lock(&mld->add_txqs_lock);
+ list_del_init(&mld_txq->list);
+ spin_unlock(&mld->add_txqs_lock);
+ /* If the queue allocation failed, we can't transmit. Leave the
+ * frames on the txq, maybe the attempt to allocate the queue
+ * will succeed.
+ */
+ if (!failed)
+ iwl_mld_tx_from_txq(mld, txq);
+ local_bh_enable();
+ }
+}
+
+void iwl_mld_add_txqs_wk(struct wiphy *wiphy, struct wiphy_work *wk)
+{
+ struct iwl_mld *mld = container_of(wk, struct iwl_mld,
+ add_txqs_wk);
+
+ /* will reschedule to run after restart */
+ if (mld->fw_status.in_hw_restart)
+ return;
+
+ iwl_mld_add_txq_list(mld);
+}
+
+void
+iwl_mld_free_txq(struct iwl_mld *mld, u32 fw_sta_mask, u32 tid, u32 queue_id)
+{
+ struct iwl_scd_queue_cfg_cmd remove_cmd = {
+ .operation = cpu_to_le32(IWL_SCD_QUEUE_REMOVE),
+ .u.remove.tid = cpu_to_le32(tid),
+ .u.remove.sta_mask = cpu_to_le32(fw_sta_mask),
+ };
+
+ iwl_mld_send_cmd_pdu(mld,
+ WIDE_ID(DATA_PATH_GROUP, SCD_QUEUE_CONFIG_CMD),
+ &remove_cmd);
+
+ iwl_trans_txq_free(mld->trans, queue_id);
+}
+
+void iwl_mld_remove_txq(struct iwl_mld *mld, struct ieee80211_txq *txq)
+{
+ struct iwl_mld_txq *mld_txq = iwl_mld_txq_from_mac80211(txq);
+ u32 sta_msk, tid;
+
+ lockdep_assert_wiphy(mld->wiphy);
+
+ spin_lock_bh(&mld->add_txqs_lock);
+ if (!list_empty(&mld_txq->list))
+ list_del_init(&mld_txq->list);
+ spin_unlock_bh(&mld->add_txqs_lock);
+
+ if (!mld_txq->status.allocated ||
+ WARN_ON(mld_txq->fw_id >= ARRAY_SIZE(mld->fw_id_to_txq)))
+ return;
+
+ sta_msk = iwl_mld_fw_sta_id_mask(mld, txq->sta);
+
+ tid = txq->tid == IEEE80211_NUM_TIDS ? IWL_MGMT_TID :
+ txq->tid;
+
+ iwl_mld_free_txq(mld, sta_msk, tid, mld_txq->fw_id);
+
+ RCU_INIT_POINTER(mld->fw_id_to_txq[mld_txq->fw_id], NULL);
+ mld_txq->status.allocated = false;
+}
+
+#define OPT_HDR(type, skb, off) \
+ (type *)(skb_network_header(skb) + (off))
+
+static __le32
+iwl_mld_get_offload_assist(struct sk_buff *skb, bool amsdu)
+{
+ struct ieee80211_hdr *hdr = (void *)skb->data;
+ u16 mh_len = ieee80211_hdrlen(hdr->frame_control);
+ u16 offload_assist = 0;
+#if IS_ENABLED(CONFIG_INET)
+ u8 protocol = 0;
+
+ /* Do not compute checksum if already computed */
+ if (skb->ip_summed != CHECKSUM_PARTIAL)
+ goto out;
+
+ /* We do not expect to be requested to csum stuff we do not support */
+
+ /* TBD: do we also need to check
+ * !(mvm->hw->netdev_features & IWL_TX_CSUM_NETIF_FLAGS) now that all
+ * the devices we support has this flags?
+ */
+ if (WARN_ONCE(skb->protocol != htons(ETH_P_IP) &&
+ skb->protocol != htons(ETH_P_IPV6),
+ "No support for requested checksum\n")) {
+ skb_checksum_help(skb);
+ goto out;
+ }
+
+ if (skb->protocol == htons(ETH_P_IP)) {
+ protocol = ip_hdr(skb)->protocol;
+ } else {
+#if IS_ENABLED(CONFIG_IPV6)
+ struct ipv6hdr *ipv6h =
+ (struct ipv6hdr *)skb_network_header(skb);
+ unsigned int off = sizeof(*ipv6h);
+
+ protocol = ipv6h->nexthdr;
+ while (protocol != NEXTHDR_NONE && ipv6_ext_hdr(protocol)) {
+ struct ipv6_opt_hdr *hp;
+
+ /* only supported extension headers */
+ if (protocol != NEXTHDR_ROUTING &&
+ protocol != NEXTHDR_HOP &&
+ protocol != NEXTHDR_DEST) {
+ skb_checksum_help(skb);
+ goto out;
+ }
+
+ hp = OPT_HDR(struct ipv6_opt_hdr, skb, off);
+ protocol = hp->nexthdr;
+ off += ipv6_optlen(hp);
+ }
+ /* if we get here - protocol now should be TCP/UDP */
+#endif
+ }
+
+ if (protocol != IPPROTO_TCP && protocol != IPPROTO_UDP) {
+ WARN_ON_ONCE(1);
+ skb_checksum_help(skb);
+ goto out;
+ }
+
+ /* enable L4 csum */
+ offload_assist |= BIT(TX_CMD_OFFLD_L4_EN);
+
+ /* Set offset to IP header (snap).
+ * We don't support tunneling so no need to take care of inner header.
+ * Size is in words.
+ */
+ offload_assist |= (4 << TX_CMD_OFFLD_IP_HDR);
+
+ /* Do IPv4 csum for AMSDU only (no IP csum for Ipv6) */
+ if (skb->protocol == htons(ETH_P_IP) && amsdu) {
+ ip_hdr(skb)->check = 0;
+ offload_assist |= BIT(TX_CMD_OFFLD_L3_EN);
+ }
+
+ /* reset UDP/TCP header csum */
+ if (protocol == IPPROTO_TCP)
+ tcp_hdr(skb)->check = 0;
+ else
+ udp_hdr(skb)->check = 0;
+
+out:
+#endif
+ mh_len /= 2;
+ offload_assist |= mh_len << TX_CMD_OFFLD_MH_SIZE;
+
+ if (amsdu)
+ offload_assist |= BIT(TX_CMD_OFFLD_AMSDU);
+ else if (ieee80211_hdrlen(hdr->frame_control) % 4)
+ /* padding is inserted later in transport */
+ offload_assist |= BIT(TX_CMD_OFFLD_PAD);
+
+ return cpu_to_le32(offload_assist);
+}
+
+static void iwl_mld_get_basic_rates_and_band(struct iwl_mld *mld,
+ struct ieee80211_vif *vif,
+ struct ieee80211_tx_info *info,
+ unsigned long *basic_rates,
+ u8 *band)
+{
+ u32 link_id = u32_get_bits(info->control.flags,
+ IEEE80211_TX_CTRL_MLO_LINK);
+
+ *basic_rates = vif->bss_conf.basic_rates;
+ *band = info->band;
+
+ if (link_id == IEEE80211_LINK_UNSPECIFIED &&
+ ieee80211_vif_is_mld(vif)) {
+ /* shouldn't do this when >1 link is active */
+ WARN_ON(hweight16(vif->active_links) != 1);
+ link_id = __ffs(vif->active_links);
+ }
+
+ if (link_id < IEEE80211_LINK_UNSPECIFIED) {
+ struct ieee80211_bss_conf *link_conf;
+
+ rcu_read_lock();
+ link_conf = rcu_dereference(vif->link_conf[link_id]);
+ if (link_conf) {
+ *basic_rates = link_conf->basic_rates;
+ if (link_conf->chanreq.oper.chan)
+ *band = link_conf->chanreq.oper.chan->band;
+ }
+ rcu_read_unlock();
+ }
+}
+
+u8 iwl_mld_get_lowest_rate(struct iwl_mld *mld,
+ struct ieee80211_tx_info *info,
+ struct ieee80211_vif *vif)
+{
+ struct ieee80211_supported_band *sband;
+ u16 lowest_cck = IWL_RATE_COUNT, lowest_ofdm = IWL_RATE_COUNT;
+ unsigned long basic_rates;
+ u8 band, rate;
+ u32 i;
+
+ iwl_mld_get_basic_rates_and_band(mld, vif, info, &basic_rates, &band);
+
+ sband = mld->hw->wiphy->bands[band];
+ for_each_set_bit(i, &basic_rates, BITS_PER_LONG) {
+ u16 hw = sband->bitrates[i].hw_value;
+
+ if (hw >= IWL_FIRST_OFDM_RATE) {
+ if (lowest_ofdm > hw)
+ lowest_ofdm = hw;
+ } else if (lowest_cck > hw) {
+ lowest_cck = hw;
+ }
+ }
+
+ if (band == NL80211_BAND_2GHZ && !vif->p2p &&
+ vif->type != NL80211_IFTYPE_P2P_DEVICE &&
+ !(info->flags & IEEE80211_TX_CTL_NO_CCK_RATE)) {
+ if (lowest_cck != IWL_RATE_COUNT)
+ rate = lowest_cck;
+ else if (lowest_ofdm != IWL_RATE_COUNT)
+ rate = lowest_ofdm;
+ else
+ rate = IWL_FIRST_CCK_RATE;
+ } else if (lowest_ofdm != IWL_RATE_COUNT) {
+ rate = lowest_ofdm;
+ } else {
+ rate = IWL_FIRST_OFDM_RATE;
+ }
+
+ return rate;
+}
+
+static u32 iwl_mld_mac80211_rate_idx_to_fw(struct iwl_mld *mld,
+ struct ieee80211_tx_info *info,
+ int rate_idx)
+{
+ u32 rate_flags = 0;
+ u8 rate_plcp;
+
+ /* if the rate isn't a well known legacy rate, take the lowest one */
+ if (rate_idx < 0 || rate_idx >= IWL_RATE_COUNT_LEGACY)
+ rate_idx = iwl_mld_get_lowest_rate(mld, info,
+ info->control.vif);
+
+ WARN_ON_ONCE(rate_idx < 0);
+
+ /* Set CCK or OFDM flag */
+ if (rate_idx <= IWL_LAST_CCK_RATE)
+ rate_flags |= RATE_MCS_MOD_TYPE_CCK;
+ else
+ rate_flags |= RATE_MCS_MOD_TYPE_LEGACY_OFDM;
+
+ /* Legacy rates are indexed:
+ * 0 - 3 for CCK and 0 - 7 for OFDM
+ */
+ rate_plcp = (rate_idx >= IWL_FIRST_OFDM_RATE ?
+ rate_idx - IWL_FIRST_OFDM_RATE : rate_idx);
+
+ return (u32)rate_plcp | rate_flags;
+}
+
+static u32 iwl_mld_get_tx_ant(struct iwl_mld *mld,
+ struct ieee80211_tx_info *info,
+ struct ieee80211_sta *sta, __le16 fc)
+{
+ if (sta && ieee80211_is_data(fc)) {
+ struct iwl_mld_sta *mld_sta = iwl_mld_sta_from_mac80211(sta);
+
+ return BIT(mld_sta->data_tx_ant) << RATE_MCS_ANT_POS;
+ }
+
+ return BIT(mld->mgmt_tx_ant) << RATE_MCS_ANT_POS;
+}
+
+static u32 iwl_mld_get_inject_tx_rate(struct iwl_mld *mld,
+ struct ieee80211_tx_info *info,
+ struct ieee80211_sta *sta,
+ __le16 fc)
+{
+ struct ieee80211_tx_rate *rate = &info->control.rates[0];
+ u32 result;
+
+ if (rate->flags & IEEE80211_TX_RC_VHT_MCS) {
+ u8 mcs = ieee80211_rate_get_vht_mcs(rate);
+ u8 nss = ieee80211_rate_get_vht_nss(rate);
+
+ result = RATE_MCS_MOD_TYPE_VHT;
+ result |= u32_encode_bits(mcs, RATE_MCS_CODE_MSK);
+ result |= u32_encode_bits(nss, RATE_MCS_NSS_MSK);
+
+ if (rate->flags & IEEE80211_TX_RC_SHORT_GI)
+ result |= RATE_MCS_SGI_MSK;
+
+ if (rate->flags & IEEE80211_TX_RC_40_MHZ_WIDTH)
+ result |= RATE_MCS_CHAN_WIDTH_40;
+ else if (rate->flags & IEEE80211_TX_RC_80_MHZ_WIDTH)
+ result |= RATE_MCS_CHAN_WIDTH_80;
+ else if (rate->flags & IEEE80211_TX_RC_160_MHZ_WIDTH)
+ result |= RATE_MCS_CHAN_WIDTH_160;
+ } else if (rate->flags & IEEE80211_TX_RC_MCS) {
+ /* only MCS 0-15 are supported */
+ u8 mcs = rate->idx & 7;
+ u8 nss = rate->idx > 7;
+
+ result = RATE_MCS_MOD_TYPE_HT;
+ result |= u32_encode_bits(mcs, RATE_MCS_CODE_MSK);
+ result |= u32_encode_bits(nss, RATE_MCS_NSS_MSK);
+
+ if (rate->flags & IEEE80211_TX_RC_SHORT_GI)
+ result |= RATE_MCS_SGI_MSK;
+ if (rate->flags & IEEE80211_TX_RC_40_MHZ_WIDTH)
+ result |= RATE_MCS_CHAN_WIDTH_40;
+ if (info->flags & IEEE80211_TX_CTL_LDPC)
+ result |= RATE_MCS_LDPC_MSK;
+ if (u32_get_bits(info->flags, IEEE80211_TX_CTL_STBC))
+ result |= RATE_MCS_STBC_MSK;
+ } else {
+ result = iwl_mld_mac80211_rate_idx_to_fw(mld, info, rate->idx);
+ }
+
+ if (info->control.antennas)
+ result |= u32_encode_bits(info->control.antennas,
+ RATE_MCS_ANT_AB_MSK);
+ else
+ result |= iwl_mld_get_tx_ant(mld, info, sta, fc);
+
+ return result;
+}
+
+static __le32 iwl_mld_get_tx_rate_n_flags(struct iwl_mld *mld,
+ struct ieee80211_tx_info *info,
+ struct ieee80211_sta *sta, __le16 fc)
+{
+ u32 rate;
+
+ if (unlikely(info->control.flags & IEEE80211_TX_CTRL_RATE_INJECT))
+ rate = iwl_mld_get_inject_tx_rate(mld, info, sta, fc);
+ else
+ rate = iwl_mld_mac80211_rate_idx_to_fw(mld, info, -1) |
+ iwl_mld_get_tx_ant(mld, info, sta, fc);
+
+ return iwl_v3_rate_to_v2_v3(rate, mld->fw_rates_ver_3);
+}
+
+static void
+iwl_mld_fill_tx_cmd_hdr(struct iwl_tx_cmd *tx_cmd,
+ struct sk_buff *skb, bool amsdu)
+{
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+ struct ieee80211_hdr *hdr = (void *)skb->data;
+ struct ieee80211_vif *vif;
+
+ /* Copy MAC header from skb into command buffer */
+ memcpy(tx_cmd->hdr, hdr, ieee80211_hdrlen(hdr->frame_control));
+
+ if (!amsdu || !skb_is_gso(skb))
+ return;
+
+ /* As described in IEEE sta 802.11-2020, table 9-30 (Address
+ * field contents), A-MSDU address 3 should contain the BSSID
+ * address.
+ *
+ * In TSO, the skb header address 3 contains the original address 3 to
+ * correctly create all the A-MSDU subframes headers from it.
+ * Override now the address 3 in the command header with the BSSID.
+ *
+ * Note: we fill in the MLD address, but the firmware will do the
+ * necessary translation to link address after encryption.
+ */
+ vif = info->control.vif;
+ switch (vif->type) {
+ case NL80211_IFTYPE_STATION:
+ ether_addr_copy(tx_cmd->hdr->addr3, vif->cfg.ap_addr);
+ break;
+ case NL80211_IFTYPE_AP:
+ ether_addr_copy(tx_cmd->hdr->addr3, vif->addr);
+ break;
+ default:
+ break;
+ }
+}
+
+static void
+iwl_mld_fill_tx_cmd(struct iwl_mld *mld, struct sk_buff *skb,
+ struct iwl_device_tx_cmd *dev_tx_cmd,
+ struct ieee80211_sta *sta)
+{
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+ struct ieee80211_hdr *hdr = (void *)skb->data;
+ struct iwl_mld_sta *mld_sta = sta ? iwl_mld_sta_from_mac80211(sta) :
+ NULL;
+ struct iwl_tx_cmd *tx_cmd;
+ bool amsdu = ieee80211_is_data_qos(hdr->frame_control) &&
+ (*ieee80211_get_qos_ctl(hdr) &
+ IEEE80211_QOS_CTL_A_MSDU_PRESENT);
+ __le32 rate_n_flags = 0;
+ u16 flags = 0;
+
+ dev_tx_cmd->hdr.cmd = TX_CMD;
+
+ if (!info->control.hw_key)
+ flags |= IWL_TX_FLAGS_ENCRYPT_DIS;
+
+ /* For data and mgmt packets rate info comes from the fw.
+ * Only set rate/antenna for injected frames with fixed rate, or
+ * when no sta is given.
+ */
+ if (unlikely(!sta ||
+ info->control.flags & IEEE80211_TX_CTRL_RATE_INJECT)) {
+ flags |= IWL_TX_FLAGS_CMD_RATE;
+ rate_n_flags = iwl_mld_get_tx_rate_n_flags(mld, info, sta,
+ hdr->frame_control);
+ } else if (!ieee80211_is_data(hdr->frame_control) ||
+ (mld_sta &&
+ mld_sta->sta_state < IEEE80211_STA_AUTHORIZED)) {
+ /* These are important frames */
+ flags |= IWL_TX_FLAGS_HIGH_PRI;
+ }
+
+ tx_cmd = (void *)dev_tx_cmd->payload;
+
+ iwl_mld_fill_tx_cmd_hdr(tx_cmd, skb, amsdu);
+
+ tx_cmd->offload_assist = iwl_mld_get_offload_assist(skb, amsdu);
+
+ /* Total # bytes to be transmitted */
+ tx_cmd->len = cpu_to_le16((u16)skb->len);
+
+ tx_cmd->flags = cpu_to_le16(flags);
+
+ tx_cmd->rate_n_flags = rate_n_flags;
+}
+
+/* Caller of this need to check that info->control.vif is not NULL */
+static struct iwl_mld_link *
+iwl_mld_get_link_from_tx_info(struct ieee80211_tx_info *info)
+{
+ struct iwl_mld_vif *mld_vif =
+ iwl_mld_vif_from_mac80211(info->control.vif);
+ u32 link_id = u32_get_bits(info->control.flags,
+ IEEE80211_TX_CTRL_MLO_LINK);
+
+ if (link_id == IEEE80211_LINK_UNSPECIFIED) {
+ if (info->control.vif->active_links)
+ link_id = ffs(info->control.vif->active_links) - 1;
+ else
+ link_id = 0;
+ }
+
+ return rcu_dereference(mld_vif->link[link_id]);
+}
+
+static int
+iwl_mld_get_tx_queue_id(struct iwl_mld *mld, struct ieee80211_txq *txq,
+ struct sk_buff *skb)
+{
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+ struct ieee80211_hdr *hdr = (void *)skb->data;
+ __le16 fc = hdr->frame_control;
+ struct iwl_mld_vif *mld_vif;
+ struct iwl_mld_link *link;
+
+ if (txq && txq->sta)
+ return iwl_mld_txq_from_mac80211(txq)->fw_id;
+
+ if (!info->control.vif)
+ return IWL_MLD_INVALID_QUEUE;
+
+ switch (info->control.vif->type) {
+ case NL80211_IFTYPE_AP:
+ case NL80211_IFTYPE_ADHOC:
+ link = iwl_mld_get_link_from_tx_info(info);
+
+ if (WARN_ON(!link))
+ break;
+
+ /* ucast disassociate/deauth frames without a station might
+ * happen, especially with reason 7 ("Class 3 frame received
+ * from nonassociated STA").
+ */
+ if (ieee80211_is_mgmt(fc) &&
+ (!ieee80211_is_bufferable_mmpdu(skb) ||
+ ieee80211_is_deauth(fc) || ieee80211_is_disassoc(fc)))
+ return link->bcast_sta.queue_id;
+
+ if (is_multicast_ether_addr(hdr->addr1) &&
+ !ieee80211_has_order(fc))
+ return link->mcast_sta.queue_id;
+
+ WARN_ONCE(info->control.vif->type != NL80211_IFTYPE_ADHOC,
+ "Couldn't find a TXQ. fc=0x%02x", le16_to_cpu(fc));
+ return link->bcast_sta.queue_id;
+ case NL80211_IFTYPE_P2P_DEVICE:
+ mld_vif = iwl_mld_vif_from_mac80211(info->control.vif);
+
+ if (mld_vif->roc_activity != ROC_ACTIVITY_P2P_DISC &&
+ mld_vif->roc_activity != ROC_ACTIVITY_P2P_NEG) {
+ IWL_DEBUG_DROP(mld,
+ "Drop tx outside ROC with activity %d\n",
+ mld_vif->roc_activity);
+ return IWL_MLD_INVALID_DROP_TX;
+ }
+
+ WARN_ON(!ieee80211_is_mgmt(fc));
+
+ return mld_vif->aux_sta.queue_id;
+ case NL80211_IFTYPE_MONITOR:
+ mld_vif = iwl_mld_vif_from_mac80211(info->control.vif);
+ return mld_vif->deflink.mon_sta.queue_id;
+ case NL80211_IFTYPE_STATION:
+ mld_vif = iwl_mld_vif_from_mac80211(info->control.vif);
+
+ if (!(info->flags & IEEE80211_TX_CTL_TX_OFFCHAN)) {
+ IWL_DEBUG_DROP(mld, "Drop tx not off-channel\n");
+ return IWL_MLD_INVALID_DROP_TX;
+ }
+
+ if (mld_vif->roc_activity != ROC_ACTIVITY_HOTSPOT) {
+ IWL_DEBUG_DROP(mld, "Drop tx outside ROC\n");
+ return IWL_MLD_INVALID_DROP_TX;
+ }
+
+ WARN_ON(!ieee80211_is_mgmt(fc));
+ return mld_vif->aux_sta.queue_id;
+ default:
+ WARN_ONCE(1, "Unsupported vif type\n");
+ break;
+ }
+
+ return IWL_MLD_INVALID_QUEUE;
+}
+
+static void iwl_mld_probe_resp_set_noa(struct iwl_mld *mld,
+ struct sk_buff *skb)
+{
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+ struct iwl_mld_link *mld_link =
+ &iwl_mld_vif_from_mac80211(info->control.vif)->deflink;
+ struct iwl_probe_resp_data *resp_data;
+ u8 *pos;
+
+ if (!info->control.vif->p2p)
+ return;
+
+ rcu_read_lock();
+
+ resp_data = rcu_dereference(mld_link->probe_resp_data);
+ if (!resp_data)
+ goto out;
+
+ if (!resp_data->notif.noa_active)
+ goto out;
+
+ if (skb_tailroom(skb) < resp_data->noa_len) {
+ if (pskb_expand_head(skb, 0, resp_data->noa_len, GFP_ATOMIC)) {
+ IWL_ERR(mld,
+ "Failed to reallocate probe resp\n");
+ goto out;
+ }
+ }
+
+ pos = skb_put(skb, resp_data->noa_len);
+
+ *pos++ = WLAN_EID_VENDOR_SPECIFIC;
+ /* Set length of IE body (not including ID and length itself) */
+ *pos++ = resp_data->noa_len - 2;
+ *pos++ = (WLAN_OUI_WFA >> 16) & 0xff;
+ *pos++ = (WLAN_OUI_WFA >> 8) & 0xff;
+ *pos++ = WLAN_OUI_WFA & 0xff;
+ *pos++ = WLAN_OUI_TYPE_WFA_P2P;
+
+ memcpy(pos, &resp_data->notif.noa_attr,
+ resp_data->noa_len - sizeof(struct ieee80211_vendor_ie));
+
+out:
+ rcu_read_unlock();
+}
+
+/* This function must be called with BHs disabled */
+static int iwl_mld_tx_mpdu(struct iwl_mld *mld, struct sk_buff *skb,
+ struct ieee80211_txq *txq)
+{
+ struct ieee80211_hdr *hdr = (void *)skb->data;
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+ struct ieee80211_sta *sta = txq ? txq->sta : NULL;
+ struct iwl_device_tx_cmd *dev_tx_cmd;
+ int queue = iwl_mld_get_tx_queue_id(mld, txq, skb);
+ u8 tid = IWL_MAX_TID_COUNT;
+
+ if (WARN_ONCE(queue == IWL_MLD_INVALID_QUEUE, "Invalid TX Queue id") ||
+ queue == IWL_MLD_INVALID_DROP_TX)
+ return -1;
+
+ if (unlikely(ieee80211_is_any_nullfunc(hdr->frame_control)))
+ return -1;
+
+ dev_tx_cmd = iwl_trans_alloc_tx_cmd(mld->trans);
+ if (unlikely(!dev_tx_cmd))
+ return -1;
+
+ if (unlikely(ieee80211_is_probe_resp(hdr->frame_control))) {
+ if (IWL_MLD_NON_TRANSMITTING_AP)
+ return -1;
+
+ iwl_mld_probe_resp_set_noa(mld, skb);
+ }
+
+ iwl_mld_fill_tx_cmd(mld, skb, dev_tx_cmd, sta);
+
+ if (ieee80211_is_data(hdr->frame_control)) {
+ if (ieee80211_is_data_qos(hdr->frame_control))
+ tid = ieee80211_get_tid(hdr);
+ else
+ tid = IWL_TID_NON_QOS;
+ }
+
+ IWL_DEBUG_TX(mld, "TX TID:%d from Q:%d len %d\n",
+ tid, queue, skb->len);
+
+ /* From now on, we cannot access info->control */
+ memset(&info->status, 0, sizeof(info->status));
+ memset(info->driver_data, 0, sizeof(info->driver_data));
+
+ info->driver_data[1] = dev_tx_cmd;
+
+ if (iwl_trans_tx(mld->trans, skb, dev_tx_cmd, queue))
+ goto err;
+
+ /* Update low-latency counter when a packet is queued instead
+ * of after TX, it makes sense for early low-latency detection
+ */
+ if (sta)
+ iwl_mld_low_latency_update_counters(mld, hdr, sta, 0);
+
+ return 0;
+
+err:
+ iwl_trans_free_tx_cmd(mld->trans, dev_tx_cmd);
+ IWL_DEBUG_TX(mld, "TX from Q:%d dropped\n", queue);
+ return -1;
+}
+
+#ifdef CONFIG_INET
+
+/* This function handles the segmentation of a large TSO packet into multiple
+ * MPDUs, ensuring that the resulting segments conform to AMSDU limits and
+ * constraints.
+ */
+static int iwl_mld_tx_tso_segment(struct iwl_mld *mld, struct sk_buff *skb,
+ struct ieee80211_sta *sta,
+ struct sk_buff_head *mpdus_skbs)
+{
+ struct ieee80211_hdr *hdr = (void *)skb->data;
+ netdev_features_t netdev_flags = NETIF_F_CSUM_MASK | NETIF_F_SG;
+ unsigned int mss = skb_shinfo(skb)->gso_size;
+ unsigned int num_subframes, tcp_payload_len, subf_len;
+ u16 snap_ip_tcp, pad, max_tid_amsdu_len;
+ u8 tid;
+
+ snap_ip_tcp = 8 + skb_network_header_len(skb) + tcp_hdrlen(skb);
+
+ if (!ieee80211_is_data_qos(hdr->frame_control) ||
+ !sta->cur->max_rc_amsdu_len)
+ return iwl_tx_tso_segment(skb, 1, netdev_flags, mpdus_skbs);
+
+ /* Do not build AMSDU for IPv6 with extension headers.
+ * Ask stack to segment and checksum the generated MPDUs for us.
+ */
+ if (skb->protocol == htons(ETH_P_IPV6) &&
+ ((struct ipv6hdr *)skb_network_header(skb))->nexthdr !=
+ IPPROTO_TCP) {
+ netdev_flags &= ~NETIF_F_CSUM_MASK;
+ return iwl_tx_tso_segment(skb, 1, netdev_flags, mpdus_skbs);
+ }
+
+ tid = ieee80211_get_tid(hdr);
+ if (WARN_ON_ONCE(tid >= IWL_MAX_TID_COUNT))
+ return -EINVAL;
+
+ max_tid_amsdu_len = sta->cur->max_tid_amsdu_len[tid];
+ if (!max_tid_amsdu_len)
+ return iwl_tx_tso_segment(skb, 1, netdev_flags, mpdus_skbs);
+
+ /* Sub frame header + SNAP + IP header + TCP header + MSS */
+ subf_len = sizeof(struct ethhdr) + snap_ip_tcp + mss;
+ pad = (4 - subf_len) & 0x3;
+
+ /* If we have N subframes in the A-MSDU, then the A-MSDU's size is
+ * N * subf_len + (N - 1) * pad.
+ */
+ num_subframes = (max_tid_amsdu_len + pad) / (subf_len + pad);
+
+ if (sta->max_amsdu_subframes &&
+ num_subframes > sta->max_amsdu_subframes)
+ num_subframes = sta->max_amsdu_subframes;
+
+ tcp_payload_len = skb_tail_pointer(skb) - skb_transport_header(skb) -
+ tcp_hdrlen(skb) + skb->data_len;
+
+ /* Make sure we have enough TBs for the A-MSDU:
+ * 2 for each subframe
+ * 1 more for each fragment
+ * 1 more for the potential data in the header
+ */
+ if ((num_subframes * 2 + skb_shinfo(skb)->nr_frags + 1) >
+ mld->trans->info.max_skb_frags)
+ num_subframes = 1;
+
+ if (num_subframes > 1)
+ *ieee80211_get_qos_ctl(hdr) |= IEEE80211_QOS_CTL_A_MSDU_PRESENT;
+
+ /* This skb fits in one single A-MSDU */
+ if (tcp_payload_len <= num_subframes * mss) {
+ __skb_queue_tail(mpdus_skbs, skb);
+ return 0;
+ }
+
+ /* Trick the segmentation function to make it create SKBs that can fit
+ * into one A-MSDU.
+ */
+ return iwl_tx_tso_segment(skb, num_subframes, netdev_flags, mpdus_skbs);
+}
+
+/* Manages TSO (TCP Segmentation Offload) packet transmission by segmenting
+ * large packets when necessary and transmitting each segment as MPDU.
+ */
+static int iwl_mld_tx_tso(struct iwl_mld *mld, struct sk_buff *skb,
+ struct ieee80211_txq *txq)
+{
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+ struct sk_buff *orig_skb = skb;
+ struct sk_buff_head mpdus_skbs;
+ unsigned int payload_len;
+ int ret;
+
+ if (WARN_ON(!txq || !txq->sta))
+ return -1;
+
+ payload_len = skb_tail_pointer(skb) - skb_transport_header(skb) -
+ tcp_hdrlen(skb) + skb->data_len;
+
+ if (payload_len <= skb_shinfo(skb)->gso_size)
+ return iwl_mld_tx_mpdu(mld, skb, txq);
+
+ if (!info->control.vif)
+ return -1;
+
+ __skb_queue_head_init(&mpdus_skbs);
+
+ ret = iwl_mld_tx_tso_segment(mld, skb, txq->sta, &mpdus_skbs);
+ if (ret)
+ return ret;
+
+ WARN_ON(skb_queue_empty(&mpdus_skbs));
+
+ while (!skb_queue_empty(&mpdus_skbs)) {
+ skb = __skb_dequeue(&mpdus_skbs);
+
+ ret = iwl_mld_tx_mpdu(mld, skb, txq);
+ if (!ret)
+ continue;
+
+ /* Free skbs created as part of TSO logic that have not yet
+ * been dequeued
+ */
+ __skb_queue_purge(&mpdus_skbs);
+
+ /* skb here is not necessarily same as skb that entered
+ * this method, so free it explicitly.
+ */
+ if (skb == orig_skb)
+ ieee80211_free_txskb(mld->hw, skb);
+ else
+ kfree_skb(skb);
+
+ /* there was error, but we consumed skb one way or
+ * another, so return 0
+ */
+ return 0;
+ }
+
+ return 0;
+}
+#else
+static int iwl_mld_tx_tso(struct iwl_mld *mld, struct sk_buff *skb,
+ struct ieee80211_txq *txq)
+{
+ /* Impossible to get TSO without CONFIG_INET */
+ WARN_ON(1);
+
+ return -1;
+}
+#endif /* CONFIG_INET */
+
+void iwl_mld_tx_skb(struct iwl_mld *mld, struct sk_buff *skb,
+ struct ieee80211_txq *txq)
+{
+ if (skb_is_gso(skb)) {
+ if (!iwl_mld_tx_tso(mld, skb, txq))
+ return;
+ goto err;
+ }
+
+ if (likely(!iwl_mld_tx_mpdu(mld, skb, txq)))
+ return;
+
+err:
+ ieee80211_free_txskb(mld->hw, skb);
+}
+
+void iwl_mld_tx_from_txq(struct iwl_mld *mld, struct ieee80211_txq *txq)
+{
+ struct iwl_mld_txq *mld_txq = iwl_mld_txq_from_mac80211(txq);
+ struct sk_buff *skb = NULL;
+ u8 zero_addr[ETH_ALEN] = {};
+
+ /*
+ * No need for threads to be pending here, they can leave the first
+ * taker all the work.
+ *
+ * mld_txq->tx_request logic:
+ *
+ * If 0, no one is currently TXing, set to 1 to indicate current thread
+ * will now start TX and other threads should quit.
+ *
+ * If 1, another thread is currently TXing, set to 2 to indicate to
+ * that thread that there was another request. Since that request may
+ * have raced with the check whether the queue is empty, the TXing
+ * thread should check the queue's status one more time before leaving.
+ * This check is done in order to not leave any TX hanging in the queue
+ * until the next TX invocation (which may not even happen).
+ *
+ * If 2, another thread is currently TXing, and it will already double
+ * check the queue, so do nothing.
+ */
+ if (atomic_fetch_add_unless(&mld_txq->tx_request, 1, 2))
+ return;
+
+ rcu_read_lock();
+ do {
+ while (likely(!mld_txq->status.stop_full) &&
+ (skb = ieee80211_tx_dequeue(mld->hw, txq)))
+ iwl_mld_tx_skb(mld, skb, txq);
+ } while (atomic_dec_return(&mld_txq->tx_request));
+
+ IWL_DEBUG_TX(mld, "TXQ of sta %pM tid %d is now empty\n",
+ txq->sta ? txq->sta->addr : zero_addr, txq->tid);
+
+ rcu_read_unlock();
+}
+
+static void iwl_mld_hwrate_to_tx_rate(struct iwl_mld *mld,
+ __le32 rate_n_flags_fw,
+ struct ieee80211_tx_info *info)
+{
+ enum nl80211_band band = info->band;
+ struct ieee80211_tx_rate *tx_rate = &info->status.rates[0];
+ u32 rate_n_flags = iwl_v3_rate_from_v2_v3(rate_n_flags_fw,
+ mld->fw_rates_ver_3);
+ u32 sgi = rate_n_flags & RATE_MCS_SGI_MSK;
+ u32 chan_width = rate_n_flags & RATE_MCS_CHAN_WIDTH_MSK;
+ u32 format = rate_n_flags & RATE_MCS_MOD_TYPE_MSK;
+
+ if (sgi)
+ tx_rate->flags |= IEEE80211_TX_RC_SHORT_GI;
+
+ switch (chan_width) {
+ case RATE_MCS_CHAN_WIDTH_20:
+ break;
+ case RATE_MCS_CHAN_WIDTH_40:
+ tx_rate->flags |= IEEE80211_TX_RC_40_MHZ_WIDTH;
+ break;
+ case RATE_MCS_CHAN_WIDTH_80:
+ tx_rate->flags |= IEEE80211_TX_RC_80_MHZ_WIDTH;
+ break;
+ case RATE_MCS_CHAN_WIDTH_160:
+ tx_rate->flags |= IEEE80211_TX_RC_160_MHZ_WIDTH;
+ break;
+ default:
+ break;
+ }
+
+ switch (format) {
+ case RATE_MCS_MOD_TYPE_HT:
+ tx_rate->flags |= IEEE80211_TX_RC_MCS;
+ tx_rate->idx = RATE_HT_MCS_INDEX(rate_n_flags);
+ break;
+ case RATE_MCS_MOD_TYPE_VHT:
+ ieee80211_rate_set_vht(tx_rate,
+ rate_n_flags & RATE_MCS_CODE_MSK,
+ u32_get_bits(rate_n_flags,
+ RATE_MCS_NSS_MSK) + 1);
+ tx_rate->flags |= IEEE80211_TX_RC_VHT_MCS;
+ break;
+ case RATE_MCS_MOD_TYPE_HE:
+ case RATE_MCS_MOD_TYPE_EHT:
+ /* mac80211 cannot do this without ieee80211_tx_status_ext()
+ * but it only matters for radiotap
+ */
+ tx_rate->idx = 0;
+ break;
+ default:
+ tx_rate->idx =
+ iwl_mld_legacy_hw_idx_to_mac80211_idx(rate_n_flags,
+ band);
+ break;
+ }
+}
+
+void iwl_mld_handle_tx_resp_notif(struct iwl_mld *mld,
+ struct iwl_rx_packet *pkt)
+{
+ struct iwl_tx_resp *tx_resp = (void *)pkt->data;
+ int txq_id = le16_to_cpu(tx_resp->tx_queue);
+ struct agg_tx_status *agg_status = &tx_resp->status;
+ u32 status = le16_to_cpu(agg_status->status);
+ u32 pkt_len = iwl_rx_packet_payload_len(pkt);
+ size_t notif_size = sizeof(*tx_resp) + sizeof(u32);
+ int sta_id = IWL_TX_RES_GET_RA(tx_resp->ra_tid);
+ int tid = IWL_TX_RES_GET_TID(tx_resp->ra_tid);
+ struct ieee80211_link_sta *link_sta;
+ struct iwl_mld_sta *mld_sta;
+ u16 ssn;
+ struct sk_buff_head skbs;
+ u8 skb_freed = 0;
+ bool mgmt = false;
+ bool tx_failure = (status & TX_STATUS_MSK) != TX_STATUS_SUCCESS;
+
+ if (IWL_FW_CHECK(mld, tx_resp->frame_count != 1,
+ "Invalid tx_resp notif frame_count (%d)\n",
+ tx_resp->frame_count))
+ return;
+
+ /* validate the size of the variable part of the notif */
+ if (IWL_FW_CHECK(mld, notif_size != pkt_len,
+ "Invalid tx_resp notif size (expected=%zu got=%u)\n",
+ notif_size, pkt_len))
+ return;
+
+ ssn = le32_to_cpup((__le32 *)agg_status +
+ tx_resp->frame_count) & 0xFFFF;
+
+ __skb_queue_head_init(&skbs);
+
+ /* we can free until ssn % q.n_bd not inclusive */
+ iwl_trans_reclaim(mld->trans, txq_id, ssn, &skbs, false);
+
+ while (!skb_queue_empty(&skbs)) {
+ struct sk_buff *skb = __skb_dequeue(&skbs);
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+ struct ieee80211_hdr *hdr = (void *)skb->data;
+
+ skb_freed++;
+
+ iwl_trans_free_tx_cmd(mld->trans, info->driver_data[1]);
+
+ memset(&info->status, 0, sizeof(info->status));
+
+ info->flags &= ~(IEEE80211_TX_STAT_ACK | IEEE80211_TX_STAT_TX_FILTERED);
+
+ /* inform mac80211 about what happened with the frame */
+ switch (status & TX_STATUS_MSK) {
+ case TX_STATUS_SUCCESS:
+ case TX_STATUS_DIRECT_DONE:
+ info->flags |= IEEE80211_TX_STAT_ACK;
+ break;
+ default:
+ break;
+ }
+
+ /* If we are freeing multiple frames, mark all the frames
+ * but the first one as acked, since they were acknowledged
+ * before
+ */
+ if (skb_freed > 1)
+ info->flags |= IEEE80211_TX_STAT_ACK;
+
+ if (tx_failure) {
+ enum iwl_fw_ini_time_point tp =
+ IWL_FW_INI_TIME_POINT_TX_FAILED;
+
+ if (ieee80211_is_action(hdr->frame_control))
+ tp = IWL_FW_INI_TIME_POINT_TX_WFD_ACTION_FRAME_FAILED;
+ else if (ieee80211_is_mgmt(hdr->frame_control))
+ mgmt = true;
+
+ iwl_dbg_tlv_time_point(&mld->fwrt, tp, NULL);
+ }
+
+ iwl_mld_hwrate_to_tx_rate(mld, tx_resp->initial_rate, info);
+
+ if (likely(!iwl_mld_time_sync_frame(mld, skb, hdr->addr1)))
+ ieee80211_tx_status_skb(mld->hw, skb);
+ }
+
+ IWL_DEBUG_TX_REPLY(mld,
+ "TXQ %d status 0x%08x ssn=%d initial_rate 0x%x retries %d\n",
+ txq_id, status, ssn, le32_to_cpu(tx_resp->initial_rate),
+ tx_resp->failure_frame);
+
+ if (tx_failure && mgmt)
+ iwl_mld_toggle_tx_ant(mld, &mld->mgmt_tx_ant);
+
+ if (IWL_FW_CHECK(mld, sta_id >= mld->fw->ucode_capa.num_stations,
+ "Got invalid sta_id (%d)\n", sta_id))
+ return;
+
+ rcu_read_lock();
+
+ link_sta = rcu_dereference(mld->fw_id_to_link_sta[sta_id]);
+ if (!link_sta) {
+ /* This can happen if the TX cmd was sent before pre_rcu_remove
+ * but the TX response was received after
+ */
+ IWL_DEBUG_TX_REPLY(mld,
+ "Got valid sta_id (%d) but sta is NULL\n",
+ sta_id);
+ goto out;
+ }
+
+ if (IS_ERR(link_sta))
+ goto out;
+
+ mld_sta = iwl_mld_sta_from_mac80211(link_sta->sta);
+
+ if (tx_failure && mld_sta->sta_state < IEEE80211_STA_AUTHORIZED)
+ iwl_mld_toggle_tx_ant(mld, &mld_sta->data_tx_ant);
+
+ if (tid < IWL_MAX_TID_COUNT)
+ iwl_mld_count_mpdu_tx(link_sta, 1);
+
+out:
+ rcu_read_unlock();
+}
+
+static void iwl_mld_tx_reclaim_txq(struct iwl_mld *mld, int txq, int index,
+ bool in_flush)
+{
+ struct sk_buff_head reclaimed_skbs;
+
+ __skb_queue_head_init(&reclaimed_skbs);
+
+ iwl_trans_reclaim(mld->trans, txq, index, &reclaimed_skbs, in_flush);
+
+ while (!skb_queue_empty(&reclaimed_skbs)) {
+ struct sk_buff *skb = __skb_dequeue(&reclaimed_skbs);
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+
+ iwl_trans_free_tx_cmd(mld->trans, info->driver_data[1]);
+
+ memset(&info->status, 0, sizeof(info->status));
+
+ /* Packet was transmitted successfully, failures come as single
+ * frames because before failing a frame the firmware transmits
+ * it without aggregation at least once.
+ */
+ if (!in_flush)
+ info->flags |= IEEE80211_TX_STAT_ACK;
+ else
+ info->flags &= ~IEEE80211_TX_STAT_ACK;
+
+ ieee80211_tx_status_skb(mld->hw, skb);
+ }
+}
+
+int iwl_mld_flush_link_sta_txqs(struct iwl_mld *mld, u32 fw_sta_id)
+{
+ struct iwl_tx_path_flush_cmd_rsp *rsp;
+ struct iwl_tx_path_flush_cmd flush_cmd = {
+ .sta_id = cpu_to_le32(fw_sta_id),
+ .tid_mask = cpu_to_le16(0xffff),
+ };
+ struct iwl_host_cmd cmd = {
+ .id = TXPATH_FLUSH,
+ .len = { sizeof(flush_cmd), },
+ .data = { &flush_cmd, },
+ .flags = CMD_WANT_SKB,
+ };
+ int ret, num_flushed_queues;
+ u32 resp_len;
+
+ IWL_DEBUG_TX_QUEUES(mld, "flush for sta id %d tid mask 0x%x\n",
+ fw_sta_id, 0xffff);
+
+ ret = iwl_mld_send_cmd(mld, &cmd);
+ if (ret) {
+ IWL_ERR(mld, "Failed to send flush command (%d)\n", ret);
+ return ret;
+ }
+
+ resp_len = iwl_rx_packet_payload_len(cmd.resp_pkt);
+ if (IWL_FW_CHECK(mld, resp_len != sizeof(*rsp),
+ "Invalid TXPATH_FLUSH response len: %d\n",
+ resp_len)) {
+ ret = -EIO;
+ goto free_rsp;
+ }
+
+ rsp = (void *)cmd.resp_pkt->data;
+
+ if (IWL_FW_CHECK(mld, le16_to_cpu(rsp->sta_id) != fw_sta_id,
+ "sta_id %d != rsp_sta_id %d\n", fw_sta_id,
+ le16_to_cpu(rsp->sta_id))) {
+ ret = -EIO;
+ goto free_rsp;
+ }
+
+ num_flushed_queues = le16_to_cpu(rsp->num_flushed_queues);
+ if (IWL_FW_CHECK(mld, num_flushed_queues > IWL_TX_FLUSH_QUEUE_RSP,
+ "num_flushed_queues %d\n", num_flushed_queues)) {
+ ret = -EIO;
+ goto free_rsp;
+ }
+
+ for (int i = 0; i < num_flushed_queues; i++) {
+ struct iwl_flush_queue_info *queue_info = &rsp->queues[i];
+ int read_after = le16_to_cpu(queue_info->read_after_flush);
+ int txq_id = le16_to_cpu(queue_info->queue_num);
+
+ if (IWL_FW_CHECK(mld,
+ txq_id >= ARRAY_SIZE(mld->fw_id_to_txq),
+ "Invalid txq id %d\n", txq_id))
+ continue;
+
+ IWL_DEBUG_TX_QUEUES(mld,
+ "tid %d txq_id %d read-before %d read-after %d\n",
+ le16_to_cpu(queue_info->tid), txq_id,
+ le16_to_cpu(queue_info->read_before_flush),
+ read_after);
+
+ iwl_mld_tx_reclaim_txq(mld, txq_id, read_after, true);
+ }
+
+free_rsp:
+ iwl_free_resp(&cmd);
+ return ret;
+}
+
+int iwl_mld_ensure_queue(struct iwl_mld *mld, struct ieee80211_txq *txq)
+{
+ struct iwl_mld_txq *mld_txq = iwl_mld_txq_from_mac80211(txq);
+ int ret;
+
+ lockdep_assert_wiphy(mld->wiphy);
+
+ if (likely(mld_txq->status.allocated))
+ return 0;
+
+ ret = iwl_mld_add_txq(mld, txq);
+
+ spin_lock_bh(&mld->add_txqs_lock);
+ if (!list_empty(&mld_txq->list))
+ list_del_init(&mld_txq->list);
+ spin_unlock_bh(&mld->add_txqs_lock);
+
+ return ret;
+}
+
+int iwl_mld_update_sta_txqs(struct iwl_mld *mld,
+ struct ieee80211_sta *sta,
+ u32 old_sta_mask, u32 new_sta_mask)
+{
+ struct iwl_scd_queue_cfg_cmd cmd = {
+ .operation = cpu_to_le32(IWL_SCD_QUEUE_MODIFY),
+ .u.modify.old_sta_mask = cpu_to_le32(old_sta_mask),
+ .u.modify.new_sta_mask = cpu_to_le32(new_sta_mask),
+ };
+
+ lockdep_assert_wiphy(mld->wiphy);
+
+ for (int tid = 0; tid <= IWL_MAX_TID_COUNT; tid++) {
+ struct ieee80211_txq *txq =
+ sta->txq[tid != IWL_MAX_TID_COUNT ?
+ tid : IEEE80211_NUM_TIDS];
+ struct iwl_mld_txq *mld_txq =
+ iwl_mld_txq_from_mac80211(txq);
+ int ret;
+
+ if (!mld_txq->status.allocated)
+ continue;
+
+ if (tid == IWL_MAX_TID_COUNT)
+ cmd.u.modify.tid = cpu_to_le32(IWL_MGMT_TID);
+ else
+ cmd.u.modify.tid = cpu_to_le32(tid);
+
+ ret = iwl_mld_send_cmd_pdu(mld,
+ WIDE_ID(DATA_PATH_GROUP,
+ SCD_QUEUE_CONFIG_CMD),
+ &cmd);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+void iwl_mld_handle_compressed_ba_notif(struct iwl_mld *mld,
+ struct iwl_rx_packet *pkt)
+{
+ struct iwl_compressed_ba_notif *ba_res = (void *)pkt->data;
+ u32 pkt_len = iwl_rx_packet_payload_len(pkt);
+ u16 tfd_cnt = le16_to_cpu(ba_res->tfd_cnt);
+ u8 sta_id = ba_res->sta_id;
+ struct ieee80211_link_sta *link_sta;
+
+ if (!tfd_cnt)
+ return;
+
+ if (IWL_FW_CHECK(mld, struct_size(ba_res, tfd, tfd_cnt) > pkt_len,
+ "Short BA notif (tfd_cnt=%d, size:0x%x)\n",
+ tfd_cnt, pkt_len))
+ return;
+
+ IWL_DEBUG_TX_REPLY(mld,
+ "BA notif received from sta_id=%d, flags=0x%x, sent:%d, acked:%d\n",
+ sta_id, le32_to_cpu(ba_res->flags),
+ le16_to_cpu(ba_res->txed),
+ le16_to_cpu(ba_res->done));
+
+ for (int i = 0; i < tfd_cnt; i++) {
+ struct iwl_compressed_ba_tfd *ba_tfd = &ba_res->tfd[i];
+ int txq_id = le16_to_cpu(ba_tfd->q_num);
+ int index = le16_to_cpu(ba_tfd->tfd_index);
+
+ if (IWL_FW_CHECK(mld,
+ txq_id >= ARRAY_SIZE(mld->fw_id_to_txq),
+ "Invalid txq id %d\n", txq_id))
+ continue;
+
+ iwl_mld_tx_reclaim_txq(mld, txq_id, index, false);
+ }
+
+ if (IWL_FW_CHECK(mld, sta_id >= mld->fw->ucode_capa.num_stations,
+ "Got invalid sta_id (%d)\n", sta_id))
+ return;
+
+ rcu_read_lock();
+
+ link_sta = rcu_dereference(mld->fw_id_to_link_sta[sta_id]);
+ if (IWL_FW_CHECK(mld, IS_ERR_OR_NULL(link_sta),
+ "Got valid sta_id (%d) but link_sta is NULL\n",
+ sta_id))
+ goto out;
+
+ iwl_mld_count_mpdu_tx(link_sta, le16_to_cpu(ba_res->txed));
+out:
+ rcu_read_unlock();
+}
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/tx.h b/drivers/net/wireless/intel/iwlwifi/mld/tx.h
new file mode 100644
index 000000000000..520f15f9d33c
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/mld/tx.h
@@ -0,0 +1,77 @@
+/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
+/*
+ * Copyright (C) 2024 Intel Corporation
+ */
+#ifndef __iwl_mld_tx_h__
+#define __iwl_mld_tx_h__
+
+#include "mld.h"
+
+#define IWL_MLD_INVALID_QUEUE 0xFFFF
+#define IWL_MLD_INVALID_DROP_TX 0xFFFE
+
+/**
+ * struct iwl_mld_txq - TX Queue data
+ *
+ * @fw_id: the fw id of this txq. Only valid when &status.allocated is true.
+ * @status: bitmap of the txq status
+ * @status.allocated: Indicates that the queue was allocated.
+ * @status.stop_full: Indicates that the queue is full and should stop TXing.
+ * @list: list pointer, for &mld::txqs_to_add
+ * @tx_request: makes sure that if there are multiple threads that want to tx
+ * from this txq, only one of them will do all the TXing.
+ * This is needed to avoid spinning the trans txq lock, which is expensive
+ */
+struct iwl_mld_txq {
+ /* Add here fields that need clean up on restart */
+ struct_group(zeroed_on_hw_restart,
+ u16 fw_id;
+ struct {
+ u8 allocated:1;
+ u8 stop_full:1;
+ } status;
+ );
+ struct list_head list;
+ atomic_t tx_request;
+ /* And here fields that survive a fw restart */
+};
+
+static inline void iwl_mld_init_txq(struct iwl_mld_txq *mld_txq)
+{
+ INIT_LIST_HEAD(&mld_txq->list);
+ atomic_set(&mld_txq->tx_request, 0);
+}
+
+static inline struct iwl_mld_txq *
+iwl_mld_txq_from_mac80211(struct ieee80211_txq *txq)
+{
+ return (void *)txq->drv_priv;
+}
+
+void iwl_mld_add_txqs_wk(struct wiphy *wiphy, struct wiphy_work *wk);
+void iwl_mld_remove_txq(struct iwl_mld *mld, struct ieee80211_txq *txq);
+void iwl_mld_add_txq_list(struct iwl_mld *mld);
+void
+iwl_mld_free_txq(struct iwl_mld *mld, u32 fw_sta_mask, u32 tid, u32 queue_id);
+void iwl_mld_tx_from_txq(struct iwl_mld *mld, struct ieee80211_txq *txq);
+void iwl_mld_handle_tx_resp_notif(struct iwl_mld *mld,
+ struct iwl_rx_packet *pkt);
+int iwl_mld_flush_link_sta_txqs(struct iwl_mld *mld, u32 fw_sta_id);
+int iwl_mld_ensure_queue(struct iwl_mld *mld, struct ieee80211_txq *txq);
+
+int iwl_mld_update_sta_txqs(struct iwl_mld *mld,
+ struct ieee80211_sta *sta,
+ u32 old_sta_mask, u32 new_sta_mask);
+
+void iwl_mld_handle_compressed_ba_notif(struct iwl_mld *mld,
+ struct iwl_rx_packet *pkt);
+void iwl_mld_toggle_tx_ant(struct iwl_mld *mld, u8 *ant);
+
+u8 iwl_mld_get_lowest_rate(struct iwl_mld *mld,
+ struct ieee80211_tx_info *info,
+ struct ieee80211_vif *vif);
+
+void iwl_mld_tx_skb(struct iwl_mld *mld, struct sk_buff *skb,
+ struct ieee80211_txq *txq);
+
+#endif /* __iwl_mld_tx_h__ */
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/coex.c b/drivers/net/wireless/intel/iwlwifi/mvm/coex.c
index 21641d41a958..13cdc077d8d3 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/coex.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/coex.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
/*
- * Copyright (C) 2013-2014, 2018-2020, 2022-2024 Intel Corporation
+ * Copyright (C) 2013-2014, 2018-2020, 2022-2025 Intel Corporation
* Copyright (C) 2013-2015 Intel Mobile Communications GmbH
*/
#include <linux/ieee80211.h>
@@ -181,7 +181,7 @@ static int iwl_mvm_bt_coex_reduced_txp(struct iwl_mvm *mvm, u8 sta_id,
struct iwl_mvm_sta *mvmsta;
u32 value;
- if (mvm->trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_AX210)
+ if (mvm->trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_AX210)
return 0;
mvmsta = iwl_mvm_sta_from_staid_protected(mvm, sta_id);
@@ -569,7 +569,7 @@ static void iwl_mvm_bt_coex_notif_handle(struct iwl_mvm *mvm)
mvm->hw, IEEE80211_IFACE_ITER_NORMAL,
iwl_mvm_bt_notif_iterator, &data);
- if (mvm->trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_AX210) {
+ if (mvm->trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_AX210) {
rcu_read_unlock();
return;
}
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/d3.c b/drivers/net/wireless/intel/iwlwifi/mvm/d3.c
index 129b6bdf9ef9..507c03198c92 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/d3.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/d3.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
/*
- * Copyright (C) 2012-2014, 2018-2024 Intel Corporation
+ * Copyright (C) 2012-2014, 2018-2025 Intel Corporation
* Copyright (C) 2013-2015 Intel Mobile Communications GmbH
* Copyright (C) 2016-2017 Intel Deutschland GmbH
*/
@@ -300,7 +300,7 @@ static void iwl_mvm_wowlan_get_rsc_tsc_data(struct ieee80211_hw *hw,
for (i = 0; i < IWL_MAX_TID_COUNT; i++) {
pn = iwl_mvm_find_max_pn(key, ptk_pn, &seq, i,
- mvm->trans->num_rx_queues);
+ mvm->trans->info.num_rxqs);
aes_sc[i].pn = cpu_to_le64((u64)pn[5] |
((u64)pn[4] << 8) |
((u64)pn[3] << 16) |
@@ -421,7 +421,7 @@ static void iwl_mvm_wowlan_get_rsc_v5_data(struct ieee80211_hw *hw,
for (i = 0; i < IWL_MAX_TID_COUNT; i++) {
pn = iwl_mvm_find_max_pn(key, ptk_pn, &seq, i,
- mvm->trans->num_rx_queues);
+ mvm->trans->info.num_rxqs);
rsc[i] = cpu_to_le64((u64)pn[5] |
((u64)pn[4] << 8) |
((u64)pn[3] << 16) |
@@ -1266,7 +1266,7 @@ static int __iwl_mvm_suspend(struct ieee80211_hw *hw,
};
struct iwl_host_cmd d3_cfg_cmd = {
.id = D3_CONFIG_CMD,
- .flags = CMD_WANT_SKB | CMD_SEND_IN_D3,
+ .flags = CMD_WANT_SKB,
.data[0] = &d3_cfg_cmd_data,
.len[0] = sizeof(d3_cfg_cmd_data),
};
@@ -1370,11 +1370,9 @@ static int __iwl_mvm_suspend(struct ieee80211_hw *hw,
* recording before entering D3. In later devices the FW stops the
* recording automatically.
*/
- if (mvm->trans->trans_cfg->device_family < IWL_DEVICE_FAMILY_9000)
+ if (mvm->trans->mac_cfg->device_family < IWL_DEVICE_FAMILY_9000)
iwl_fw_dbg_stop_restart_recording(&mvm->fwrt, NULL, true);
- mvm->trans->system_pm_mode = IWL_PLAT_PM_MODE_D3;
-
/* must be last -- this switches firmware state */
ret = iwl_mvm_send_cmd(mvm, &d3_cfg_cmd);
if (ret)
@@ -1686,7 +1684,7 @@ static void iwl_mvm_set_aes_ptk_rx_seq(struct iwl_mvm *mvm,
for (tid = 0; tid < IWL_MAX_TID_COUNT; tid++) {
int i;
- for (i = 1; i < mvm->trans->num_rx_queues; i++)
+ for (i = 1; i < mvm->trans->info.num_rxqs; i++)
memcpy(ptk_pn->q[i].pn[tid],
status->ptk.aes.seq[tid].ccmp.pn,
IEEE80211_CCMP_PN_LEN);
@@ -2825,7 +2823,7 @@ static bool iwl_mvm_query_wakeup_reasons(struct iwl_mvm *mvm,
status->qos_seq_ctr[i] + 0x10;
}
- if (mvm->trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_22000) {
+ if (mvm->trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_22000) {
i = mvm->offload_tid;
iwl_trans_set_q_ptrs(mvm->trans,
mvm_ap_sta->tid_data[i].txq_id,
@@ -3092,8 +3090,14 @@ static void iwl_mvm_d3_disconnect_iter(void *data, u8 *mac,
ieee80211_resume_disconnect(vif);
}
-static bool iwl_mvm_check_rt_status(struct iwl_mvm *mvm,
- struct ieee80211_vif *vif)
+enum rt_status {
+ FW_ALIVE,
+ FW_NEEDS_RESET,
+ FW_ERROR,
+};
+
+static enum rt_status iwl_mvm_check_rt_status(struct iwl_mvm *mvm,
+ struct ieee80211_vif *vif)
{
u32 err_id;
@@ -3101,29 +3105,35 @@ static bool iwl_mvm_check_rt_status(struct iwl_mvm *mvm,
if (iwl_fwrt_read_err_table(mvm->trans,
mvm->trans->dbg.lmac_error_event_table[0],
&err_id)) {
- if (err_id == RF_KILL_INDICATOR_FOR_WOWLAN && vif) {
- struct cfg80211_wowlan_wakeup wakeup = {
- .rfkill_release = true,
- };
- ieee80211_report_wowlan_wakeup(vif, &wakeup,
- GFP_KERNEL);
+ if (err_id == RF_KILL_INDICATOR_FOR_WOWLAN) {
+ IWL_WARN(mvm, "Rfkill was toggled during suspend\n");
+ if (vif) {
+ struct cfg80211_wowlan_wakeup wakeup = {
+ .rfkill_release = true,
+ };
+
+ ieee80211_report_wowlan_wakeup(vif, &wakeup,
+ GFP_KERNEL);
+ }
+
+ return FW_NEEDS_RESET;
}
- return true;
+ return FW_ERROR;
}
/* check if we have lmac2 set and check for error */
if (iwl_fwrt_read_err_table(mvm->trans,
mvm->trans->dbg.lmac_error_event_table[1],
NULL))
- return true;
+ return FW_ERROR;
/* check for umac error */
if (iwl_fwrt_read_err_table(mvm->trans,
mvm->trans->dbg.umac_error_event_table,
NULL))
- return true;
+ return FW_ERROR;
- return false;
+ return FW_ALIVE;
}
/*
@@ -3376,7 +3386,7 @@ static bool iwl_mvm_wait_d3_notif(struct iwl_notif_wait_data *notif_wait,
break;
}
case WIDE_ID(PROT_OFFLOAD_GROUP, D3_END_NOTIFICATION): {
- struct iwl_mvm_d3_end_notif *notif = (void *)pkt->data;
+ struct iwl_d3_end_notif *notif = (void *)pkt->data;
d3_data->d3_end_flags = __le32_to_cpu(notif->flags);
d3_data->notif_received |= IWL_D3_NOTIF_D3_END_NOTIF;
@@ -3395,9 +3405,9 @@ static int iwl_mvm_resume_firmware(struct iwl_mvm *mvm, bool test)
int ret;
enum iwl_d3_status d3_status;
struct iwl_host_cmd cmd = {
- .id = D0I3_END_CMD,
- .flags = CMD_WANT_SKB | CMD_SEND_IN_D3,
- };
+ .id = D0I3_END_CMD,
+ .flags = CMD_WANT_SKB,
+ };
bool reset = fw_has_capa(&mvm->fw->ucode_capa,
IWL_UCODE_TLV_CAPA_CNSLDTD_D3_D0_IMG);
@@ -3415,7 +3425,7 @@ static int iwl_mvm_resume_firmware(struct iwl_mvm *mvm, bool test)
* AX210 and above don't need the command since they have
* the doorbell interrupt.
*/
- if (mvm->trans->trans_cfg->device_family <= IWL_DEVICE_FAMILY_22000 &&
+ if (mvm->trans->mac_cfg->device_family <= IWL_DEVICE_FAMILY_22000 &&
fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_D0I3_END_FIRST)) {
ret = iwl_mvm_send_cmd(mvm, &cmd);
if (ret < 0)
@@ -3492,6 +3502,7 @@ static int __iwl_mvm_resume(struct iwl_mvm *mvm, bool test)
bool d0i3_first = fw_has_capa(&mvm->fw->ucode_capa,
IWL_UCODE_TLV_CAPA_D0I3_END_FIRST);
bool resume_notif_based = iwl_mvm_d3_resume_notif_based(mvm);
+ enum rt_status rt_status;
bool keep = false;
mutex_lock(&mvm->mutex);
@@ -3515,14 +3526,19 @@ static int __iwl_mvm_resume(struct iwl_mvm *mvm, bool test)
iwl_fw_dbg_read_d3_debug_data(&mvm->fwrt);
- if (iwl_mvm_check_rt_status(mvm, vif)) {
- IWL_ERR(mvm, "FW Error occurred during suspend. Restarting.\n");
+ rt_status = iwl_mvm_check_rt_status(mvm, vif);
+ if (rt_status != FW_ALIVE) {
set_bit(STATUS_FW_ERROR, &mvm->trans->status);
- iwl_mvm_dump_nic_error_log(mvm);
- iwl_dbg_tlv_time_point(&mvm->fwrt,
- IWL_FW_INI_TIME_POINT_FW_ASSERT, NULL);
- iwl_fw_dbg_collect_desc(&mvm->fwrt, &iwl_dump_desc_assert,
- false, 0);
+ if (rt_status == FW_ERROR) {
+ IWL_ERR(mvm, "FW Error occurred during suspend. Restarting.\n");
+ iwl_mvm_dump_nic_error_log(mvm);
+ iwl_dbg_tlv_time_point(&mvm->fwrt,
+ IWL_FW_INI_TIME_POINT_FW_ASSERT,
+ NULL);
+ iwl_fw_dbg_collect_desc(&mvm->fwrt,
+ &iwl_dump_desc_assert,
+ false, 0);
+ }
ret = 1;
goto err;
}
@@ -3546,9 +3562,6 @@ static int __iwl_mvm_resume(struct iwl_mvm *mvm, bool test)
iwl_mvm_unblock_esr(mvm, vif, IWL_MVM_ESR_BLOCKED_WOWLAN);
- /* after the successful handshake, we're out of D3 */
- mvm->trans->system_pm_mode = IWL_PLAT_PM_MODE_DISABLED;
-
/* when reset is required we can't send these following commands */
if (d3_data.d3_end_flags & IWL_D0I3_RESET_REQUIRE)
goto query_wakeup_reasons;
@@ -3621,9 +3634,6 @@ out:
*/
set_bit(IWL_MVM_STATUS_HW_RESTART_REQUESTED, &mvm->status);
- /* regardless of what happened, we're now out of D3 */
- mvm->trans->system_pm_mode = IWL_PLAT_PM_MODE_DISABLED;
-
return 1;
}
@@ -3661,8 +3671,7 @@ void iwl_mvm_fast_suspend(struct iwl_mvm *mvm)
set_bit(IWL_MVM_STATUS_IN_D3, &mvm->status);
WARN_ON(iwl_mvm_power_update_device(mvm));
- mvm->trans->system_pm_mode = IWL_PLAT_PM_MODE_D3;
- ret = iwl_mvm_send_cmd_pdu(mvm, D3_CONFIG_CMD, CMD_SEND_IN_D3,
+ ret = iwl_mvm_send_cmd_pdu(mvm, D3_CONFIG_CMD, 0,
sizeof(d3_cfg_cmd_data), &d3_cfg_cmd_data);
if (ret)
IWL_ERR(mvm,
@@ -3679,6 +3688,7 @@ int iwl_mvm_fast_resume(struct iwl_mvm *mvm)
.notif_expected =
IWL_D3_NOTIF_D3_END_NOTIF,
};
+ enum rt_status rt_status;
int ret;
lockdep_assert_held(&mvm->mutex);
@@ -3688,14 +3698,20 @@ int iwl_mvm_fast_resume(struct iwl_mvm *mvm)
mvm->last_reset_or_resume_time_jiffies = jiffies;
iwl_fw_dbg_read_d3_debug_data(&mvm->fwrt);
- if (iwl_mvm_check_rt_status(mvm, NULL)) {
- IWL_ERR(mvm, "FW Error occurred during suspend. Restarting.\n");
+ rt_status = iwl_mvm_check_rt_status(mvm, NULL);
+ if (rt_status != FW_ALIVE) {
set_bit(STATUS_FW_ERROR, &mvm->trans->status);
- iwl_mvm_dump_nic_error_log(mvm);
- iwl_dbg_tlv_time_point(&mvm->fwrt,
- IWL_FW_INI_TIME_POINT_FW_ASSERT, NULL);
- iwl_fw_dbg_collect_desc(&mvm->fwrt, &iwl_dump_desc_assert,
- false, 0);
+ if (rt_status == FW_ERROR) {
+ IWL_ERR(mvm,
+ "iwl_mvm_check_rt_status failed, device is gone during suspend\n");
+ iwl_mvm_dump_nic_error_log(mvm);
+ iwl_dbg_tlv_time_point(&mvm->fwrt,
+ IWL_FW_INI_TIME_POINT_FW_ASSERT,
+ NULL);
+ iwl_fw_dbg_collect_desc(&mvm->fwrt,
+ &iwl_dump_desc_assert,
+ false, 0);
+ }
mvm->trans->state = IWL_TRANS_NO_FW;
ret = -ENODEV;
@@ -3710,7 +3726,6 @@ int iwl_mvm_fast_resume(struct iwl_mvm *mvm)
out:
clear_bit(IWL_MVM_STATUS_IN_D3, &mvm->status);
- mvm->trans->system_pm_mode = IWL_PLAT_PM_MODE_DISABLED;
mvm->fast_resume = false;
return ret;
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c b/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c
index 83e3c1160362..86a87ea89916 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
/*
- * Copyright (C) 2012-2014, 2018-2023 Intel Corporation
+ * Copyright (C) 2012-2014, 2018-2023, 2025 Intel Corporation
* Copyright (C) 2013-2015 Intel Mobile Communications GmbH
* Copyright (C) 2016-2017 Intel Deutschland GmbH
*/
@@ -542,7 +542,7 @@ static ssize_t iwl_dbgfs_tas_get_status_read(struct file *file,
size_t count, loff_t *ppos)
{
struct iwl_mvm *mvm = file->private_data;
- struct iwl_mvm_tas_status_resp *rsp = NULL;
+ struct iwl_tas_status_resp *rsp = NULL;
static const size_t bufsz = 1024;
char *buff, *pos, *endpos;
const char * const tas_dis_reason[TAS_DISABLED_REASON_MAX] = {
@@ -552,6 +552,8 @@ static ssize_t iwl_dbgfs_tas_get_status_read(struct file *file,
"Due To SAR Limit Less Than 6 dBm",
[TAS_DISABLED_REASON_INVALID] =
"N/A",
+ [TAS_DISABLED_DUE_TO_TABLE_SOURCE_INVALID] =
+ "Due to table source invalid",
};
const char * const tas_current_status[TAS_DYNA_STATUS_MAX] = {
[TAS_DYNA_INACTIVE] = "INACTIVE",
@@ -598,23 +600,19 @@ static ssize_t iwl_dbgfs_tas_get_status_read(struct file *file,
pos += scnprintf(pos, endpos - pos, "TAS Conclusion:\n");
for (i = 0; i < rsp->in_dual_radio + 1; i++) {
- if (rsp->tas_status_mac[i].band != TAS_LMAC_BAND_INVALID &&
- rsp->tas_status_mac[i].dynamic_status & BIT(TAS_DYNA_ACTIVE)) {
+ if (rsp->tas_status_mac[i].dynamic_status &
+ BIT(TAS_DYNA_ACTIVE)) {
pos += scnprintf(pos, endpos - pos, "\tON for ");
switch (rsp->tas_status_mac[i].band) {
- case TAS_LMAC_BAND_HB:
+ case PHY_BAND_5:
pos += scnprintf(pos, endpos - pos, "HB\n");
break;
- case TAS_LMAC_BAND_LB:
+ case PHY_BAND_24:
pos += scnprintf(pos, endpos - pos, "LB\n");
break;
- case TAS_LMAC_BAND_UHB:
+ case PHY_BAND_6:
pos += scnprintf(pos, endpos - pos, "UHB\n");
break;
- case TAS_LMAC_BAND_INVALID:
- pos += scnprintf(pos, endpos - pos,
- "INVALID BAND\n");
- break;
default:
pos += scnprintf(pos, endpos - pos,
"Unsupported band (%d)\n",
@@ -668,20 +666,16 @@ static ssize_t iwl_dbgfs_tas_get_status_read(struct file *file,
pos += scnprintf(pos, endpos - pos, "TAS status for ");
switch (rsp->tas_status_mac[i].band) {
- case TAS_LMAC_BAND_HB:
+ case PHY_BAND_5:
pos += scnprintf(pos, endpos - pos, "High band\n");
break;
- case TAS_LMAC_BAND_LB:
+ case PHY_BAND_24:
pos += scnprintf(pos, endpos - pos, "Low band\n");
break;
- case TAS_LMAC_BAND_UHB:
+ case PHY_BAND_6:
pos += scnprintf(pos, endpos - pos,
"Ultra high band\n");
break;
- case TAS_LMAC_BAND_INVALID:
- pos += scnprintf(pos, endpos - pos,
- "INVALID band\n");
- break;
default:
pos += scnprintf(pos, endpos - pos,
"Unsupported band (%d)\n",
@@ -704,11 +698,9 @@ static ssize_t iwl_dbgfs_tas_get_status_read(struct file *file,
pos += scnprintf(pos, endpos - pos, "Dynamic status:\n");
dyn_status = (rsp->tas_status_mac[i].dynamic_status);
- for_each_set_bit(tmp, &dyn_status, sizeof(dyn_status)) {
- if (tmp >= 0 && tmp < TAS_DYNA_STATUS_MAX)
- pos += scnprintf(pos, endpos - pos,
- "\t%s (%d)\n",
- tas_current_status[tmp], tmp);
+ for_each_set_bit(tmp, &dyn_status, TAS_DYNA_STATUS_MAX) {
+ pos += scnprintf(pos, endpos - pos, "\t%s (%d)\n",
+ tas_current_status[tmp], tmp);
}
pos += scnprintf(pos, endpos - pos,
@@ -1285,7 +1277,7 @@ static ssize_t iwl_dbgfs_inject_packet_write(struct iwl_mvm *mvm,
return -EIO;
/* supporting only MQ RX */
- if (!mvm->trans->trans_cfg->mq_rx_supported)
+ if (!mvm->trans->mac_cfg->mq_rx_supported)
return -EOPNOTSUPP;
rxb._page = alloc_pages(GFP_ATOMIC, 0);
@@ -1476,9 +1468,16 @@ static ssize_t iwl_dbgfs_fw_dbg_clear_write(struct iwl_mvm *mvm,
char *buf, size_t count,
loff_t *ppos)
{
- if (mvm->trans->trans_cfg->device_family < IWL_DEVICE_FAMILY_9000)
+ if (mvm->trans->mac_cfg->device_family < IWL_DEVICE_FAMILY_9000)
return -EOPNOTSUPP;
+ /*
+ * If the firmware is not running, silently succeed since there is
+ * no data to clear.
+ */
+ if (!iwl_mvm_firmware_running(mvm))
+ return count;
+
mutex_lock(&mvm->mutex);
iwl_fw_dbg_clear_monitor_buf(&mvm->fwrt);
mutex_unlock(&mvm->mutex);
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/ftm-initiator.c b/drivers/net/wireless/intel/iwlwifi/mvm/ftm-initiator.c
index b26141c30c61..a493ef6bedc3 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/ftm-initiator.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/ftm-initiator.c
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
/*
* Copyright (C) 2015-2017 Intel Deutschland GmbH
- * Copyright (C) 2018-2024 Intel Corporation
+ * Copyright (C) 2018-2025 Intel Corporation
*/
#include <linux/etherdevice.h>
#include <linux/math64.h>
@@ -46,107 +46,6 @@ struct iwl_mvm_ftm_iter_data {
u8 *tk;
};
-int iwl_mvm_ftm_add_pasn_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
- u8 *addr, u32 cipher, u8 *tk, u32 tk_len,
- u8 *hltk, u32 hltk_len)
-{
- struct iwl_mvm_ftm_pasn_entry *pasn = kzalloc(sizeof(*pasn),
- GFP_KERNEL);
- u32 expected_tk_len;
-
- lockdep_assert_held(&mvm->mutex);
-
- if (!pasn)
- return -ENOBUFS;
-
- iwl_mvm_ftm_remove_pasn_sta(mvm, addr);
-
- pasn->cipher = iwl_mvm_cipher_to_location_cipher(cipher);
-
- switch (pasn->cipher) {
- case IWL_LOCATION_CIPHER_CCMP_128:
- case IWL_LOCATION_CIPHER_GCMP_128:
- expected_tk_len = WLAN_KEY_LEN_CCMP;
- break;
- case IWL_LOCATION_CIPHER_GCMP_256:
- expected_tk_len = WLAN_KEY_LEN_GCMP_256;
- break;
- default:
- goto out;
- }
-
- /*
- * If associated to this AP and already have security context,
- * the TK is already configured for this station, so it
- * shouldn't be set again here.
- */
- if (vif->cfg.assoc) {
- struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
- struct ieee80211_bss_conf *link_conf;
- unsigned int link_id;
- struct ieee80211_sta *sta;
- u8 sta_id;
-
- rcu_read_lock();
- for_each_vif_active_link(vif, link_conf, link_id) {
- if (memcmp(addr, link_conf->bssid, ETH_ALEN))
- continue;
-
- sta_id = mvmvif->link[link_id]->ap_sta_id;
- sta = rcu_dereference(mvm->fw_id_to_mac_id[sta_id]);
- if (!IS_ERR_OR_NULL(sta) && sta->mfp)
- expected_tk_len = 0;
- break;
- }
- rcu_read_unlock();
- }
-
- if (tk_len != expected_tk_len ||
- (hltk_len && hltk_len != sizeof(pasn->hltk))) {
- IWL_ERR(mvm, "Invalid key length: tk_len=%u hltk_len=%u\n",
- tk_len, hltk_len);
- goto out;
- }
-
- if (!expected_tk_len && !hltk_len) {
- IWL_ERR(mvm, "TK and HLTK not set\n");
- goto out;
- }
-
- memcpy(pasn->addr, addr, sizeof(pasn->addr));
-
- if (hltk_len) {
- memcpy(pasn->hltk, hltk, sizeof(pasn->hltk));
- pasn->flags |= IWL_MVM_PASN_FLAG_HAS_HLTK;
- }
-
- if (tk && tk_len)
- memcpy(pasn->tk, tk, sizeof(pasn->tk));
-
- list_add_tail(&pasn->list, &mvm->ftm_initiator.pasn_list);
- return 0;
-out:
- kfree(pasn);
- return -EINVAL;
-}
-
-void iwl_mvm_ftm_remove_pasn_sta(struct iwl_mvm *mvm, u8 *addr)
-{
- struct iwl_mvm_ftm_pasn_entry *entry, *prev;
-
- lockdep_assert_held(&mvm->mutex);
-
- list_for_each_entry_safe(entry, prev, &mvm->ftm_initiator.pasn_list,
- list) {
- if (memcmp(entry->addr, addr, sizeof(entry->addr)))
- continue;
-
- list_del(&entry->list);
- kfree(entry);
- return;
- }
-}
-
static void iwl_mvm_ftm_reset(struct iwl_mvm *mvm)
{
struct iwl_mvm_loc_entry *e, *t;
@@ -773,7 +672,11 @@ iwl_mvm_ftm_set_secured_ranging(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
target.bssid = bssid;
target.cipher = cipher;
+ target.tk = NULL;
ieee80211_iter_keys(mvm->hw, vif, iter, &target);
+
+ if (!WARN_ON(!target.tk))
+ memcpy(tk, target.tk, TK_11AZ_LEN);
} else {
memcpy(tk, entry->tk, sizeof(entry->tk));
}
@@ -949,7 +852,7 @@ static int iwl_mvm_ftm_start_v13(struct iwl_mvm *mvm,
static int
iwl_mvm_ftm_put_target_v10(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
struct cfg80211_pmsr_request_peer *peer,
- struct iwl_tof_range_req_ap_entry_v10 *target)
+ struct iwl_tof_range_req_ap_entry *target)
{
u32 i2r_max_sts, flags;
int ret;
@@ -1021,7 +924,7 @@ static int iwl_mvm_ftm_start_v14(struct iwl_mvm *mvm,
struct ieee80211_vif *vif,
struct cfg80211_pmsr_request *req)
{
- struct iwl_tof_range_req_cmd_v14 cmd;
+ struct iwl_tof_range_req_cmd cmd;
struct iwl_host_cmd hcmd = {
.id = WIDE_ID(LOCATION_GROUP, TOF_RANGE_REQ_CMD),
.dataflags[0] = IWL_HCMD_DFL_DUP,
@@ -1035,7 +938,7 @@ static int iwl_mvm_ftm_start_v14(struct iwl_mvm *mvm,
for (i = 0; i < cmd.num_of_ap; i++) {
struct cfg80211_pmsr_request_peer *peer = &req->peers[i];
- struct iwl_tof_range_req_ap_entry_v10 *target = &cmd.ap[i];
+ struct iwl_tof_range_req_ap_entry *target = &cmd.ap[i];
err = iwl_mvm_ftm_put_target_v10(mvm, vif, peer, target);
if (err)
@@ -1301,7 +1204,7 @@ static void iwl_mvm_debug_range_resp(struct iwl_mvm *mvm, u8 index,
static void
iwl_mvm_ftm_pasn_update_pn(struct iwl_mvm *mvm,
- struct iwl_tof_range_rsp_ap_entry_ntfy_v6 *fw_ap)
+ struct iwl_tof_range_rsp_ap_entry_ntfy *fw_ap)
{
struct iwl_mvm_ftm_pasn_entry *entry;
@@ -1339,7 +1242,7 @@ static bool iwl_mvm_ftm_resp_size_validation(u8 ver, unsigned int pkt_len)
switch (ver) {
case 9:
case 8:
- return pkt_len == sizeof(struct iwl_tof_range_rsp_ntfy_v8);
+ return pkt_len == sizeof(struct iwl_tof_range_rsp_ntfy);
case 7:
return pkt_len == sizeof(struct iwl_tof_range_rsp_ntfy_v7);
case 6:
@@ -1359,7 +1262,7 @@ void iwl_mvm_ftm_range_resp(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb)
struct iwl_tof_range_rsp_ntfy_v5 *fw_resp_v5 = (void *)pkt->data;
struct iwl_tof_range_rsp_ntfy_v6 *fw_resp_v6 = (void *)pkt->data;
struct iwl_tof_range_rsp_ntfy_v7 *fw_resp_v7 = (void *)pkt->data;
- struct iwl_tof_range_rsp_ntfy_v8 *fw_resp_v8 = (void *)pkt->data;
+ struct iwl_tof_range_rsp_ntfy *fw_resp_v8 = (void *)pkt->data;
int i;
bool new_api = fw_has_api(&mvm->fw->ucode_capa,
IWL_UCODE_TLV_API_FTM_NEW_RANGE_REQ);
@@ -1395,9 +1298,9 @@ void iwl_mvm_ftm_range_resp(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb)
IWL_DEBUG_INFO(mvm, "request id: %lld, num of entries: %u\n",
mvm->ftm_initiator.req->cookie, num_of_aps);
- for (i = 0; i < num_of_aps && i < IWL_MVM_TOF_MAX_APS; i++) {
+ for (i = 0; i < num_of_aps && i < IWL_TOF_MAX_APS; i++) {
struct cfg80211_pmsr_result result = {};
- struct iwl_tof_range_rsp_ap_entry_ntfy_v6 *fw_ap;
+ struct iwl_tof_range_rsp_ap_entry_ntfy *fw_ap;
int peer_idx;
if (new_api) {
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/ftm-responder.c b/drivers/net/wireless/intel/iwlwifi/mvm/ftm-responder.c
index e6e468e81ab3..83f6e508a094 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/ftm-responder.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/ftm-responder.c
@@ -324,92 +324,6 @@ static void iwl_mvm_resp_del_pasn_sta(struct iwl_mvm *mvm,
kfree(sta);
}
-int iwl_mvm_ftm_respoder_add_pasn_sta(struct iwl_mvm *mvm,
- struct ieee80211_vif *vif,
- u8 *addr, u32 cipher, u8 *tk, u32 tk_len,
- u8 *hltk, u32 hltk_len)
-{
- int ret;
- struct iwl_mvm_pasn_sta *sta = NULL;
- struct iwl_mvm_pasn_hltk_data hltk_data = {
- .addr = addr,
- .hltk = hltk,
- };
- struct iwl_mvm_pasn_hltk_data *hltk_data_ptr = NULL;
-
- u8 cmd_ver = iwl_fw_lookup_cmd_ver(mvm->fw,
- WIDE_ID(LOCATION_GROUP, TOF_RESPONDER_DYN_CONFIG_CMD),
- 2);
-
- lockdep_assert_held(&mvm->mutex);
-
- if (cmd_ver < 3) {
- IWL_ERR(mvm, "Adding PASN station not supported by FW\n");
- return -EOPNOTSUPP;
- }
-
- if ((!hltk || !hltk_len) && (!tk || !tk_len)) {
- IWL_ERR(mvm, "TK and HLTK not set\n");
- return -EINVAL;
- }
-
- if (hltk && hltk_len) {
- if (!fw_has_capa(&mvm->fw->ucode_capa,
- IWL_UCODE_TLV_CAPA_SECURE_LTF_SUPPORT)) {
- IWL_ERR(mvm, "No support for secure LTF measurement\n");
- return -EINVAL;
- }
-
- hltk_data.cipher = iwl_mvm_cipher_to_location_cipher(cipher);
- if (hltk_data.cipher == IWL_LOCATION_CIPHER_INVALID) {
- IWL_ERR(mvm, "invalid cipher: %u\n", cipher);
- return -EINVAL;
- }
-
- hltk_data_ptr = &hltk_data;
- }
-
- if (tk && tk_len) {
- sta = kzalloc(sizeof(*sta) + tk_len, GFP_KERNEL);
- if (!sta)
- return -ENOBUFS;
-
- ret = iwl_mvm_add_pasn_sta(mvm, vif, &sta->int_sta, addr,
- cipher, tk, tk_len, &sta->keyconf);
- if (ret) {
- kfree(sta);
- return ret;
- }
-
- memcpy(sta->addr, addr, ETH_ALEN);
- list_add_tail(&sta->list, &mvm->resp_pasn_list);
- }
-
- ret = iwl_mvm_ftm_responder_dyn_cfg_v3(mvm, vif, NULL, hltk_data_ptr);
- if (ret && sta)
- iwl_mvm_resp_del_pasn_sta(mvm, vif, sta);
-
- return ret;
-}
-
-int iwl_mvm_ftm_resp_remove_pasn_sta(struct iwl_mvm *mvm,
- struct ieee80211_vif *vif, u8 *addr)
-{
- struct iwl_mvm_pasn_sta *sta, *prev;
-
- lockdep_assert_held(&mvm->mutex);
-
- list_for_each_entry_safe(sta, prev, &mvm->resp_pasn_list, list) {
- if (!memcmp(sta->addr, addr, ETH_ALEN)) {
- iwl_mvm_resp_del_pasn_sta(mvm, vif, sta);
- return 0;
- }
- }
-
- IWL_ERR(mvm, "FTM: PASN station %pM not found\n", addr);
- return -EINVAL;
-}
-
int iwl_mvm_ftm_start_responder(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
struct ieee80211_bss_conf *bss_conf)
{
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c
index df49dd2e2026..819e3228462a 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
/*
- * Copyright (C) 2012-2014, 2018-2024 Intel Corporation
+ * Copyright (C) 2012-2014, 2018-2025 Intel Corporation
* Copyright (C) 2013-2015 Intel Mobile Communications GmbH
* Copyright (C) 2016-2017 Intel Deutschland GmbH
*/
@@ -29,8 +29,8 @@
#define MVM_UCODE_CALIB_TIMEOUT (2 * HZ)
struct iwl_mvm_alive_data {
+ __le32 sku_id[3];
bool valid;
- u32 scd_base_addr;
};
static int iwl_send_tx_ant_cfg(struct iwl_mvm *mvm, u8 valid_tx_ant)
@@ -57,13 +57,13 @@ static int iwl_send_rss_cfg_cmd(struct iwl_mvm *mvm)
BIT(IWL_RSS_HASH_TYPE_IPV6_PAYLOAD),
};
- if (mvm->trans->num_rx_queues == 1)
+ if (mvm->trans->info.num_rxqs == 1)
return 0;
/* Do not direct RSS traffic to Q 0 which is our fallback queue */
for (i = 0; i < ARRAY_SIZE(cmd.indirection_table); i++)
cmd.indirection_table[i] =
- 1 + (i % (mvm->trans->num_rx_queues - 1));
+ 1 + (i % (mvm->trans->info.num_rxqs - 1));
netdev_rss_key_fill(cmd.secret_key, sizeof(cmd.secret_key));
return iwl_mvm_send_cmd_pdu(mvm, RSS_CONFIG_CMD, 0, sizeof(cmd), &cmd);
@@ -114,7 +114,7 @@ static bool iwl_alive_fn(struct iwl_notif_wait_data *notif_wait,
u32 i;
- if (version == 6) {
+ if (version >= 6) {
struct iwl_alive_ntf_v6 *palive;
if (pkt_len < sizeof(*palive))
@@ -157,6 +157,17 @@ static bool iwl_alive_fn(struct iwl_notif_wait_data *notif_wait,
}
}
}
+
+ if (version >= 8) {
+ const struct iwl_alive_ntf *palive_v8 =
+ (void *)pkt->data;
+
+ if (pkt_len < sizeof(*palive_v8))
+ return false;
+
+ IWL_DEBUG_FW(mvm, "platform id: 0x%llx\n",
+ palive_v8->platform_id);
+ }
}
if (version >= 5) {
@@ -171,14 +182,15 @@ static bool iwl_alive_fn(struct iwl_notif_wait_data *notif_wait,
lmac2 = &palive->lmac_data[1];
status = le16_to_cpu(palive->status);
- mvm->trans->sku_id[0] = le32_to_cpu(palive->sku_id.data[0]);
- mvm->trans->sku_id[1] = le32_to_cpu(palive->sku_id.data[1]);
- mvm->trans->sku_id[2] = le32_to_cpu(palive->sku_id.data[2]);
+ BUILD_BUG_ON(sizeof(palive->sku_id.data) !=
+ sizeof(alive_data->sku_id));
+ memcpy(alive_data->sku_id, palive->sku_id.data,
+ sizeof(palive->sku_id.data));
IWL_DEBUG_FW(mvm, "Got sku_id: 0x0%x 0x0%x 0x0%x\n",
- mvm->trans->sku_id[0],
- mvm->trans->sku_id[1],
- mvm->trans->sku_id[2]);
+ le32_to_cpu(alive_data->sku_id[0]),
+ le32_to_cpu(alive_data->sku_id[1]),
+ le32_to_cpu(alive_data->sku_id[2]));
} else if (iwl_rx_packet_payload_len(pkt) == sizeof(struct iwl_alive_ntf_v4)) {
struct iwl_alive_ntf_v4 *palive;
@@ -221,7 +233,7 @@ static bool iwl_alive_fn(struct iwl_notif_wait_data *notif_wait,
if (umac_error_table) {
if (umac_error_table >=
- mvm->trans->cfg->min_umac_error_event_table) {
+ mvm->trans->mac_cfg->base->min_umac_error_event_table) {
iwl_fw_umac_set_alive_err_table(mvm->trans,
umac_error_table);
} else {
@@ -233,7 +245,6 @@ static bool iwl_alive_fn(struct iwl_notif_wait_data *notif_wait,
}
}
- alive_data->scd_base_addr = le32_to_cpu(lmac1->dbg_ptrs.scd_base_ptr);
alive_data->valid = status == IWL_ALIVE_STATUS_OK;
IWL_DEBUG_FW(mvm,
@@ -282,7 +293,7 @@ static void iwl_mvm_print_pd_notification(struct iwl_mvm *mvm)
IWL_ERR(mvm, #reg_name ": 0x%x\n", iwl_read_umac_prph(trans, reg_name))
struct iwl_trans *trans = mvm->trans;
- enum iwl_device_family device_family = trans->trans_cfg->device_family;
+ enum iwl_device_family device_family = trans->mac_cfg->device_family;
if (device_family < IWL_DEVICE_FAMILY_8000)
return;
@@ -304,7 +315,6 @@ static int iwl_mvm_load_ucode_wait_alive(struct iwl_mvm *mvm,
{
struct iwl_notification_wait alive_wait;
struct iwl_mvm_alive_data alive_data = {};
- const struct fw_img *fw;
int ret;
enum iwl_ucode_type old_type = mvm->fwrt.cur_fw_img;
static const u16 alive_cmd[] = { UCODE_ALIVE_NTFY };
@@ -317,11 +327,7 @@ static int iwl_mvm_load_ucode_wait_alive(struct iwl_mvm *mvm,
iwl_fw_dbg_conf_usniffer(mvm->fw, FW_DBG_START_FROM_ALIVE) &&
!(fw_has_capa(&mvm->fw->ucode_capa,
IWL_UCODE_TLV_CAPA_USNIFFER_UNIFIED)))
- fw = iwl_get_ucode_image(mvm->fw, IWL_UCODE_REGULAR_USNIFFER);
- else
- fw = iwl_get_ucode_image(mvm->fw, ucode_type);
- if (WARN_ON(!fw))
- return -EINVAL;
+ ucode_type = IWL_UCODE_REGULAR_USNIFFER;
iwl_fw_set_current_image(&mvm->fwrt, ucode_type);
clear_bit(IWL_MVM_STATUS_FIRMWARE_RUNNING, &mvm->status);
@@ -334,7 +340,8 @@ static int iwl_mvm_load_ucode_wait_alive(struct iwl_mvm *mvm,
* For the unified firmware case, the ucode_type is not
* INIT, but we still need to run it.
*/
- ret = iwl_trans_start_fw(mvm->trans, fw, run_in_rfkill);
+ ret = iwl_trans_start_fw(mvm->trans, mvm->fw, ucode_type,
+ run_in_rfkill);
if (ret) {
iwl_fw_set_current_image(&mvm->fwrt, old_type);
iwl_remove_notification(&mvm->notif_wait, &alive_wait);
@@ -348,7 +355,7 @@ static int iwl_mvm_load_ucode_wait_alive(struct iwl_mvm *mvm,
ret = iwl_wait_notification(&mvm->notif_wait, &alive_wait,
MVM_UCODE_ALIVE_TIMEOUT);
- if (mvm->trans->trans_cfg->device_family ==
+ if (mvm->trans->mac_cfg->device_family ==
IWL_DEVICE_FAMILY_AX210) {
/* print these registers regardless of alive fail/success */
IWL_INFO(mvm, "WFPM_UMAC_PD_NOTIFICATION: 0x%x\n",
@@ -365,14 +372,14 @@ static int iwl_mvm_load_ucode_wait_alive(struct iwl_mvm *mvm,
struct iwl_trans *trans = mvm->trans;
/* SecBoot info */
- if (trans->trans_cfg->device_family >=
+ if (trans->mac_cfg->device_family >=
IWL_DEVICE_FAMILY_22000) {
IWL_ERR(mvm,
"SecBoot CPU1 Status: 0x%x, CPU2 Status: 0x%x\n",
iwl_read_umac_prph(trans, UMAG_SB_CPU_1_STATUS),
iwl_read_umac_prph(trans,
UMAG_SB_CPU_2_STATUS));
- } else if (trans->trans_cfg->device_family >=
+ } else if (trans->mac_cfg->device_family >=
IWL_DEVICE_FAMILY_8000) {
IWL_ERR(mvm,
"SecBoot CPU1 Status: 0x%x, CPU2 Status: 0x%x\n",
@@ -383,7 +390,7 @@ static int iwl_mvm_load_ucode_wait_alive(struct iwl_mvm *mvm,
iwl_mvm_print_pd_notification(mvm);
/* LMAC/UMAC PC info */
- if (trans->trans_cfg->device_family >=
+ if (trans->mac_cfg->device_family >=
IWL_DEVICE_FAMILY_22000) {
pc_data = trans->dbg.pc_data;
for (count = 0; count < trans->dbg.num_pc;
@@ -391,7 +398,7 @@ static int iwl_mvm_load_ucode_wait_alive(struct iwl_mvm *mvm,
IWL_ERR(mvm, "%s: 0x%x\n",
pc_data->pc_name,
pc_data->pc_address);
- } else if (trans->trans_cfg->device_family >=
+ } else if (trans->mac_cfg->device_family >=
IWL_DEVICE_FAMILY_9000) {
IWL_ERR(mvm, "UMAC PC: 0x%x\n",
iwl_read_umac_prph(trans,
@@ -422,16 +429,16 @@ static int iwl_mvm_load_ucode_wait_alive(struct iwl_mvm *mvm,
/* if reached this point, Alive notification was received */
iwl_mei_alive_notif(true);
+ iwl_trans_fw_alive(mvm->trans);
+
ret = iwl_pnvm_load(mvm->trans, &mvm->notif_wait,
- &mvm->fw->ucode_capa);
+ &mvm->fw->ucode_capa, alive_data.sku_id);
if (ret) {
IWL_ERR(mvm, "Timeout waiting for PNVM load!\n");
iwl_fw_set_current_image(&mvm->fwrt, old_type);
return ret;
}
- iwl_trans_fw_alive(mvm->trans, alive_data.scd_base_addr);
-
/*
* Note: all the queues are enabled as part of the interface
* initialization, but in firmware restart scenarios they
@@ -473,7 +480,7 @@ static void iwl_mvm_phy_filter_init(struct iwl_mvm *mvm,
struct iwl_phy_specific_cfg *phy_filters)
{
#ifdef CONFIG_ACPI
- *phy_filters = mvm->phy_filters;
+ *phy_filters = mvm->fwrt.phy_filters;
#endif /* CONFIG_ACPI */
}
@@ -490,7 +497,7 @@ static void iwl_mvm_uats_init(struct iwl_mvm *mvm)
.dataflags[0] = IWL_HCMD_DFL_NOCOPY,
};
- if (mvm->trans->trans_cfg->device_family < IWL_DEVICE_FAMILY_AX210) {
+ if (mvm->trans->mac_cfg->device_family < IWL_DEVICE_FAMILY_AX210) {
IWL_DEBUG_RADIO(mvm, "UATS feature is not supported\n");
return;
}
@@ -504,11 +511,10 @@ static void iwl_mvm_uats_init(struct iwl_mvm *mvm)
return;
}
- ret = iwl_uefi_get_uats_table(mvm->trans, &mvm->fwrt);
- if (ret < 0) {
- IWL_DEBUG_FW(mvm, "failed to read UATS table (%d)\n", ret);
+ iwl_uefi_get_uats_table(mvm->trans, &mvm->fwrt);
+
+ if (!mvm->fwrt.uats_valid)
return;
- }
ret = iwl_mvm_send_cmd(mvm, &cmd);
if (ret < 0)
@@ -578,7 +584,7 @@ static int iwl_send_phy_cfg_cmd(struct iwl_mvm *mvm)
/* set flags extra PHY configuration flags from the device's cfg */
phy_cfg_cmd.phy_cfg |=
- cpu_to_le32(mvm->trans->trans_cfg->extra_phy_cfg_flags);
+ cpu_to_le32(mvm->trans->mac_cfg->extra_phy_cfg_flags);
phy_cfg_cmd.calib_control.event_trigger =
mvm->fw->default_calib[ucode_type].event_trigger;
@@ -617,7 +623,7 @@ static int iwl_run_unified_mvm_ucode(struct iwl_mvm *mvm)
mvm->rfkill_safe_init_done = false;
- if (mvm->trans->trans_cfg->device_family == IWL_DEVICE_FAMILY_AX210) {
+ if (mvm->trans->mac_cfg->device_family == IWL_DEVICE_FAMILY_AX210) {
sb_cfg = iwl_read_umac_prph(mvm->trans, SB_MODIFY_CFG_FLAG);
/* if needed, we'll reset this on our way out later */
mvm->fw_product_reset = sb_cfg == SB_CFG_RESIDES_IN_ROM;
@@ -651,11 +657,6 @@ static int iwl_run_unified_mvm_ucode(struct iwl_mvm *mvm)
iwl_dbg_tlv_time_point(&mvm->fwrt, IWL_FW_INI_TIME_POINT_AFTER_ALIVE,
NULL);
- if (mvm->trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_BZ)
- mvm->trans->step_urm = !!(iwl_read_umac_prph(mvm->trans,
- CNVI_PMU_STEP_FLOW) &
- CNVI_PMU_STEP_FLOW_FORCE_URM);
-
/* Send init config command to mark that we are sending NVM access
* commands
*/
@@ -756,7 +757,7 @@ int iwl_run_init_mvm_ucode(struct iwl_mvm *mvm)
goto remove_notif;
}
- if (mvm->trans->trans_cfg->device_family < IWL_DEVICE_FAMILY_8000) {
+ if (mvm->trans->mac_cfg->device_family < IWL_DEVICE_FAMILY_8000) {
ret = iwl_mvm_send_bt_init_conf(mvm);
if (ret)
goto remove_notif;
@@ -1094,22 +1095,6 @@ static int iwl_mvm_ppag_init(struct iwl_mvm *mvm)
return iwl_mvm_ppag_send_cmd(mvm);
}
-static bool
-iwl_mvm_add_to_tas_block_list(u16 *list, u8 *size, u16 mcc)
-{
- /* Verify that there is room for another country */
- if (*size >= IWL_WTAS_BLACK_LIST_MAX)
- return false;
-
- for (u8 i = 0; i < *size; i++) {
- if (list[i] == mcc)
- return true;
- }
-
- list[*size++] = mcc;
- return true;
-}
-
static void iwl_mvm_tas_init(struct iwl_mvm *mvm)
{
u32 cmd_id = WIDE_ID(REGULATORY_AND_NVM_GROUP, TAS_CONFIG);
@@ -1150,10 +1135,10 @@ static void iwl_mvm_tas_init(struct iwl_mvm *mvm)
IWL_DEBUG_RADIO(mvm,
"System vendor '%s' is not in the approved list, disabling TAS in US and Canada.\n",
dmi_get_system_info(DMI_SYS_VENDOR) ?: "<unknown>");
- if ((!iwl_mvm_add_to_tas_block_list(data.block_list_array,
+ if ((!iwl_add_mcc_to_tas_block_list(data.block_list_array,
&data.block_list_size,
IWL_MCC_US)) ||
- (!iwl_mvm_add_to_tas_block_list(data.block_list_array,
+ (!iwl_add_mcc_to_tas_block_list(data.block_list_array,
&data.block_list_size,
IWL_MCC_CANADA))) {
IWL_DEBUG_RADIO(mvm,
@@ -1213,38 +1198,6 @@ static void iwl_mvm_tas_init(struct iwl_mvm *mvm)
IWL_DEBUG_RADIO(mvm, "failed to send TAS_CONFIG (%d)\n", ret);
}
-static bool iwl_mvm_eval_dsm_rfi(struct iwl_mvm *mvm)
-{
- u32 value = 0;
- /* default behaviour is disabled */
- bool bios_enable_rfi = false;
- int ret = iwl_bios_get_dsm(&mvm->fwrt, DSM_FUNC_RFI_CONFIG, &value);
-
-
- if (ret < 0) {
- IWL_DEBUG_RADIO(mvm, "Failed to get DSM RFI, ret=%d\n", ret);
- return bios_enable_rfi;
- }
-
- value &= DSM_VALUE_RFI_DISABLE;
- /* RFI BIOS CONFIG value can be 0 or 3 only.
- * i.e 0 means DDR and DLVR enabled. 3 means DDR and DLVR disabled.
- * 1 and 2 are invalid BIOS configurations, So, it's not possible to
- * disable ddr/dlvr separately.
- */
- if (!value) {
- IWL_DEBUG_RADIO(mvm, "DSM RFI is evaluated to enable\n");
- bios_enable_rfi = true;
- } else if (value == DSM_VALUE_RFI_DISABLE) {
- IWL_DEBUG_RADIO(mvm, "DSM RFI is evaluated to disable\n");
- } else {
- IWL_DEBUG_RADIO(mvm,
- "DSM RFI got invalid value, value=%d\n", value);
- }
-
- return bios_enable_rfi;
-}
-
static void iwl_mvm_lari_cfg(struct iwl_mvm *mvm)
{
struct iwl_lari_config_change_cmd cmd;
@@ -1318,7 +1271,7 @@ void iwl_mvm_get_bios_tables(struct iwl_mvm *mvm)
}
}
- iwl_acpi_get_phy_filters(&mvm->fwrt, &mvm->phy_filters);
+ iwl_acpi_get_phy_filters(&mvm->fwrt);
if (iwl_bios_get_eckv(&mvm->fwrt, &mvm->ext_clock_valid))
IWL_DEBUG_RADIO(mvm, "ECKV table doesn't exist in BIOS\n");
@@ -1631,7 +1584,7 @@ int iwl_mvm_up(struct iwl_mvm *mvm)
iwl_mvm_uats_init(mvm);
if (iwl_rfi_supported(mvm)) {
- if (iwl_mvm_eval_dsm_rfi(mvm))
+ if (iwl_rfi_is_enabled_in_bios(&mvm->fwrt))
iwl_rfi_send_config_cmd(mvm, NULL);
}
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/led.c b/drivers/net/wireless/intel/iwlwifi/mvm/led.c
index 1ea7c44250d4..c3cc1ea3ccc9 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/led.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/led.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
/*
- * Copyright (C) 2012-2014, 2018-2019 Intel Corporation
+ * Copyright (C) 2012-2014, 2018-2019, 2025 Intel Corporation
* Copyright (C) 2017 Intel Deutschland GmbH
*/
#include <linux/leds.h>
@@ -102,7 +102,7 @@ void iwl_mvm_leds_sync(struct iwl_mvm *mvm)
* if we control through the register, we're doing it
* even when the firmware isn't up, so no need to sync
*/
- if (mvm->trans->trans_cfg->device_family < IWL_DEVICE_FAMILY_8000)
+ if (mvm->trans->mac_cfg->device_family < IWL_DEVICE_FAMILY_8000)
return;
iwl_mvm_led_set(mvm, mvm->led.brightness > 0);
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c
index 6b06732441c3..9098a36530cc 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
/*
- * Copyright (C) 2012-2014, 2018-2024 Intel Corporation
+ * Copyright (C) 2012-2014, 2018-2025 Intel Corporation
* Copyright (C) 2013-2014 Intel Mobile Communications GmbH
* Copyright (C) 2015-2017 Intel Deutschland GmbH
*/
@@ -938,12 +938,19 @@ u8 iwl_mvm_mac_ctxt_get_lowest_rate(struct iwl_mvm *mvm,
u16 iwl_mvm_mac_ctxt_get_beacon_flags(const struct iwl_fw *fw, u8 rate_idx)
{
- u16 flags = iwl_mvm_mac80211_idx_to_hwrate(fw, rate_idx);
bool is_new_rate = iwl_fw_lookup_cmd_ver(fw, BEACON_TEMPLATE_CMD, 0) > 10;
+ u16 flags, cck_flag;
- if (rate_idx <= IWL_FIRST_CCK_RATE)
- flags |= is_new_rate ? IWL_MAC_BEACON_CCK
- : IWL_MAC_BEACON_CCK_V1;
+ if (is_new_rate) {
+ flags = iwl_mvm_mac80211_idx_to_hwrate(fw, rate_idx);
+ cck_flag = IWL_MAC_BEACON_CCK;
+ } else {
+ cck_flag = IWL_MAC_BEACON_CCK_V1;
+ flags = iwl_fw_rate_idx_to_plcp(rate_idx);
+ }
+
+ if (rate_idx <= IWL_LAST_CCK_RATE)
+ flags |= cck_flag;
return flags;
}
@@ -969,7 +976,7 @@ u8 iwl_mvm_mac_ctxt_get_beacon_rate(struct iwl_mvm *mvm,
static void iwl_mvm_mac_ctxt_set_tx(struct iwl_mvm *mvm,
struct ieee80211_vif *vif,
struct sk_buff *beacon,
- struct iwl_tx_cmd *tx)
+ struct iwl_tx_cmd_v6 *tx)
{
struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
struct ieee80211_tx_info *info;
@@ -1960,26 +1967,3 @@ void iwl_mvm_channel_switch_error_notif(struct iwl_mvm *mvm,
ieee80211_channel_switch_disconnect(vif);
rcu_read_unlock();
}
-
-void iwl_mvm_rx_missed_vap_notif(struct iwl_mvm *mvm,
- struct iwl_rx_cmd_buffer *rxb)
-{
- struct iwl_rx_packet *pkt = rxb_addr(rxb);
- struct iwl_missed_vap_notif *mb = (void *)pkt->data;
- struct ieee80211_vif *vif;
- u32 id = le32_to_cpu(mb->mac_id);
-
- IWL_DEBUG_INFO(mvm,
- "missed_vap notify mac_id=%u, num_beacon_intervals_elapsed=%u, profile_periodicity=%u\n",
- le32_to_cpu(mb->mac_id),
- mb->num_beacon_intervals_elapsed,
- mb->profile_periodicity);
-
- rcu_read_lock();
-
- vif = iwl_mvm_rcu_dereference_vif_id(mvm, id, true);
- if (vif)
- iwl_mvm_connection_loss(mvm, vif, "missed vap beacon");
-
- rcu_read_unlock();
-}
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c
index af6644b7e95f..0f056a6641bd 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
/*
- * Copyright (C) 2012-2014, 2018-2024 Intel Corporation
+ * Copyright (C) 2012-2014, 2018-2025 Intel Corporation
* Copyright (C) 2013-2015 Intel Mobile Communications GmbH
* Copyright (C) 2016-2017 Intel Deutschland GmbH
*/
@@ -70,7 +70,7 @@ static const struct ieee80211_iface_combination iwl_mvm_iface_combinations[] = {
};
static const struct cfg80211_pmsr_capabilities iwl_mvm_pmsr_capa = {
- .max_peers = IWL_MVM_TOF_MAX_APS,
+ .max_peers = IWL_TOF_MAX_APS,
.report_ap_tsf = 1,
.randomize_mac_addr = 1,
@@ -146,7 +146,7 @@ struct ieee80211_regdomain *iwl_mvm_get_regdomain(struct wiphy *wiphy,
MCC_UPDATE_CMD, 0);
IWL_DEBUG_LAR(mvm, "MCC update response version: %d\n", resp_ver);
- regd = iwl_parse_nvm_mcc_info(mvm->trans->dev, mvm->cfg,
+ regd = iwl_parse_nvm_mcc_info(mvm->trans,
__le32_to_cpu(resp->n_channels),
resp->channels,
__le16_to_cpu(resp->mcc),
@@ -272,9 +272,10 @@ static const u8 tm_if_types_ext_capa_sta[] = {
__bf_shf(IEEE80211_EML_CAP_EMLSR_PADDING_DELAY) | \
IEEE80211_EML_CAP_EMLSR_TRANSITION_DELAY_64US << \
__bf_shf(IEEE80211_EML_CAP_EMLSR_TRANSITION_DELAY))
-#define IWL_MVM_MLD_CAPA_OPS FIELD_PREP_CONST( \
+#define IWL_MVM_MLD_CAPA_OPS (FIELD_PREP_CONST( \
IEEE80211_MLD_CAP_OP_TID_TO_LINK_MAP_NEG_SUPP, \
- IEEE80211_MLD_CAP_OP_TID_TO_LINK_MAP_NEG_SUPP_SAME)
+ IEEE80211_MLD_CAP_OP_TID_TO_LINK_MAP_NEG_SUPP_SAME) | \
+ IEEE80211_MLD_CAP_OP_LINK_RECONF_SUPPORT)
static const struct wiphy_iftype_ext_capab add_iftypes_ext_capa[] = {
{
@@ -310,8 +311,8 @@ int iwl_mvm_op_set_antenna(struct ieee80211_hw *hw, u32 tx_ant, u32 rx_ant)
struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
/* This has been tested on those devices only */
- if (mvm->trans->trans_cfg->device_family != IWL_DEVICE_FAMILY_9000 &&
- mvm->trans->trans_cfg->device_family != IWL_DEVICE_FAMILY_22000)
+ if (mvm->trans->mac_cfg->device_family != IWL_DEVICE_FAMILY_9000 &&
+ mvm->trans->mac_cfg->device_family != IWL_DEVICE_FAMILY_22000)
return -EOPNOTSUPP;
if (!mvm->nvm_data)
@@ -390,7 +391,7 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)
* for older devices. We also don't see this issue on any newer
* devices.
*/
- if (mvm->trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_9000)
+ if (mvm->trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_9000)
ieee80211_hw_set(hw, TX_AMSDU);
ieee80211_hw_set(hw, TX_FRAG_LIST);
@@ -401,7 +402,7 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)
/* We want to use the mac80211's reorder buffer for 9000 */
if (iwl_mvm_has_new_rx_api(mvm) &&
- mvm->trans->trans_cfg->device_family > IWL_DEVICE_FAMILY_9000)
+ mvm->trans->mac_cfg->device_family > IWL_DEVICE_FAMILY_9000)
ieee80211_hw_set(hw, SUPPORTS_REORDERING_BUFFER);
if (fw_has_capa(&mvm->fw->ucode_capa,
@@ -416,10 +417,10 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)
return -EINVAL;
}
- if (mvm->trans->num_rx_queues > 1)
+ if (mvm->trans->info.num_rxqs > 1)
ieee80211_hw_set(hw, USES_RSS);
- if (mvm->trans->max_skb_frags)
+ if (mvm->trans->info.max_skb_frags)
hw->netdev_features = NETIF_F_HIGHDMA | NETIF_F_SG;
hw->queues = IEEE80211_NUM_ACS;
@@ -440,7 +441,7 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)
hw->uapsd_queues = IWL_MVM_UAPSD_QUEUES;
hw->uapsd_max_sp_len = IWL_UAPSD_MAX_SP;
- hw->max_tx_fragments = mvm->trans->max_skb_frags;
+ hw->max_tx_fragments = mvm->trans->info.max_skb_frags;
BUILD_BUG_ON(ARRAY_SIZE(mvm->ciphers) < ARRAY_SIZE(mvm_ciphers) + 6);
memcpy(mvm->ciphers, mvm_ciphers, sizeof(mvm_ciphers));
@@ -543,7 +544,7 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)
hw->wiphy->regulatory_flags |= REGULATORY_CUSTOM_REG |
REGULATORY_DISABLE_BEACON_HINTS;
- if (mvm->trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_AX210)
+ if (mvm->trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_AX210)
wiphy_ext_feature_set(hw->wiphy,
NL80211_EXT_FEATURE_DFS_CONCURRENT);
@@ -609,7 +610,7 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)
hw->wiphy->bands[NL80211_BAND_6GHZ] =
&mvm->nvm_data->bands[NL80211_BAND_6GHZ];
- hw->wiphy->hw_version = mvm->trans->hw_id;
+ hw->wiphy->hw_version = mvm->trans->info.hw_id;
if (iwlmvm_mod_params.power_scheme != IWL_POWER_SCHEME_CAM)
hw->wiphy->flags |= WIPHY_FLAG_PS_ON_BY_DEFAULT;
@@ -730,8 +731,6 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)
mvm->rts_threshold = IEEE80211_MAX_RTS_THRESHOLD;
- ieee80211_hw_set(hw, DISALLOW_PUNCTURING_5GHZ);
-
#ifdef CONFIG_PM_SLEEP
if ((unified || mvm->fw->img[IWL_UCODE_WOWLAN].num_sec) &&
device_can_wakeup(mvm->trans->dev)) {
@@ -770,7 +769,7 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)
hw->wiphy->features |= NL80211_FEATURE_TDLS_CHANNEL_SWITCH;
}
- hw->netdev_features |= mvm->cfg->features;
+ hw->netdev_features |= mvm->trans->mac_cfg->base->features;
if (!iwl_mvm_is_csum_supported(mvm))
hw->netdev_features &= ~IWL_CSUM_NETIF_FLAGS_MASK;
@@ -1378,7 +1377,7 @@ void __iwl_mvm_mac_stop(struct iwl_mvm *mvm, bool suspend)
iwl_mvm_rm_aux_sta(mvm);
if (suspend &&
- mvm->trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_22000) {
+ mvm->trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_22000) {
iwl_mvm_fast_suspend(mvm);
/* From this point on, we won't touch the device */
iwl_mvm_mei_device_state(mvm, false);
@@ -1987,15 +1986,8 @@ static void iwl_mvm_mac_remove_interface(struct ieee80211_hw *hw,
* interface is be handled as part of the stop_ap flow.
*/
if (vif->type == NL80211_IFTYPE_AP ||
- vif->type == NL80211_IFTYPE_ADHOC) {
-#ifdef CONFIG_NL80211_TESTMODE
- if (vif == mvm->noa_vif) {
- mvm->noa_vif = NULL;
- mvm->noa_duration = 0;
- }
-#endif
+ vif->type == NL80211_IFTYPE_ADHOC)
goto out;
- }
iwl_mvm_power_update_mac(mvm);
@@ -3058,7 +3050,7 @@ static int iwl_mvm_start_ap_ibss(struct ieee80211_hw *hw,
* context. For the newer, the beacon is a resource that belongs to a
* MAC, so need to send beacon template after adding the mac.
*/
- if (mvm->trans->trans_cfg->device_family > IWL_DEVICE_FAMILY_22000) {
+ if (mvm->trans->mac_cfg->device_family > IWL_DEVICE_FAMILY_22000) {
/* Add the mac context */
ret = iwl_mvm_mac_ctxt_add(mvm, vif);
if (ret)
@@ -4099,6 +4091,21 @@ iwl_mvm_sta_state_authorized_to_assoc(struct iwl_mvm *mvm,
return 0;
}
+void iwl_mvm_smps_workaround(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
+ bool update)
+{
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+
+ if (!iwl_mvm_has_rlc_offload(mvm) ||
+ iwl_fw_lookup_cmd_ver(mvm->fw, MAC_PM_POWER_TABLE, 0) >= 2)
+ return;
+
+ mvmvif->ps_disabled = !vif->cfg.ps;
+
+ if (update)
+ iwl_mvm_power_update_mac(mvm);
+}
+
/* Common part for MLD and non-MLD modes */
int iwl_mvm_mac_sta_state_common(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
@@ -4191,6 +4198,7 @@ int iwl_mvm_mac_sta_state_common(struct ieee80211_hw *hw,
new_state == IEEE80211_STA_AUTHORIZED) {
ret = iwl_mvm_sta_state_assoc_to_authorized(mvm, vif, sta,
callbacks);
+ iwl_mvm_smps_workaround(mvm, vif, true);
} else if (old_state == IEEE80211_STA_AUTHORIZED &&
new_state == IEEE80211_STA_ASSOC) {
ret = iwl_mvm_sta_state_authorized_to_assoc(mvm, vif, sta,
@@ -4379,7 +4387,7 @@ static int __iwl_mvm_mac_set_key(struct ieee80211_hw *hw,
switch (key->cipher) {
case WLAN_CIPHER_SUITE_TKIP:
- if (!mvm->trans->trans_cfg->gen2) {
+ if (!mvm->trans->mac_cfg->gen2) {
key->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIC;
key->flags |= IEEE80211_KEY_FLAG_PUT_IV_SPACE;
} else if (vif->type == NL80211_IFTYPE_STATION) {
@@ -4496,7 +4504,7 @@ static int __iwl_mvm_mac_set_key(struct ieee80211_hw *hw,
WARN_ON(rcu_access_pointer(mvmsta->ptk_pn[keyidx]));
ptk_pn = kzalloc(struct_size(ptk_pn, q,
- mvm->trans->num_rx_queues),
+ mvm->trans->info.num_rxqs),
GFP_KERNEL);
if (!ptk_pn) {
ret = -ENOMEM;
@@ -4505,7 +4513,7 @@ static int __iwl_mvm_mac_set_key(struct ieee80211_hw *hw,
for (tid = 0; tid < IWL_MAX_TID_COUNT; tid++) {
ieee80211_get_key_rx_seq(key, tid, &seq);
- for (q = 0; q < mvm->trans->num_rx_queues; q++)
+ for (q = 0; q < mvm->trans->info.num_rxqs; q++)
memcpy(ptk_pn->q[q].pn[tid],
seq.ccmp.pn,
IEEE80211_CCMP_PN_LEN);
@@ -5509,70 +5517,6 @@ static int iwl_mvm_set_tim(struct ieee80211_hw *hw, struct ieee80211_sta *sta,
&mvm_sta->vif->bss_conf);
}
-#ifdef CONFIG_NL80211_TESTMODE
-static const struct nla_policy iwl_mvm_tm_policy[IWL_MVM_TM_ATTR_MAX + 1] = {
- [IWL_MVM_TM_ATTR_CMD] = { .type = NLA_U32 },
- [IWL_MVM_TM_ATTR_NOA_DURATION] = { .type = NLA_U32 },
- [IWL_MVM_TM_ATTR_BEACON_FILTER_STATE] = { .type = NLA_U32 },
-};
-
-static int __iwl_mvm_mac_testmode_cmd(struct iwl_mvm *mvm,
- struct ieee80211_vif *vif,
- void *data, int len)
-{
- struct nlattr *tb[IWL_MVM_TM_ATTR_MAX + 1];
- int err;
- u32 noa_duration;
-
- err = nla_parse_deprecated(tb, IWL_MVM_TM_ATTR_MAX, data, len,
- iwl_mvm_tm_policy, NULL);
- if (err)
- return err;
-
- if (!tb[IWL_MVM_TM_ATTR_CMD])
- return -EINVAL;
-
- switch (nla_get_u32(tb[IWL_MVM_TM_ATTR_CMD])) {
- case IWL_MVM_TM_CMD_SET_NOA:
- if (!vif || vif->type != NL80211_IFTYPE_AP || !vif->p2p ||
- !vif->bss_conf.enable_beacon ||
- !tb[IWL_MVM_TM_ATTR_NOA_DURATION])
- return -EINVAL;
-
- noa_duration = nla_get_u32(tb[IWL_MVM_TM_ATTR_NOA_DURATION]);
- if (noa_duration >= vif->bss_conf.beacon_int)
- return -EINVAL;
-
- mvm->noa_duration = noa_duration;
- mvm->noa_vif = vif;
-
- return iwl_mvm_update_quotas(mvm, true, NULL);
- case IWL_MVM_TM_CMD_SET_BEACON_FILTER:
- /* must be associated client vif - ignore authorized */
- if (!vif || vif->type != NL80211_IFTYPE_STATION ||
- !vif->cfg.assoc || !vif->bss_conf.dtim_period ||
- !tb[IWL_MVM_TM_ATTR_BEACON_FILTER_STATE])
- return -EINVAL;
-
- if (nla_get_u32(tb[IWL_MVM_TM_ATTR_BEACON_FILTER_STATE]))
- return iwl_mvm_enable_beacon_filter(mvm, vif);
- return iwl_mvm_disable_beacon_filter(mvm, vif);
- }
-
- return -EOPNOTSUPP;
-}
-
-int iwl_mvm_mac_testmode_cmd(struct ieee80211_hw *hw,
- struct ieee80211_vif *vif,
- void *data, int len)
-{
- struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
-
- guard(mvm)(mvm);
- return __iwl_mvm_mac_testmode_cmd(mvm, vif, data, len);
-}
-#endif
-
void iwl_mvm_channel_switch(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
struct ieee80211_channel_switch *chsw)
{
@@ -6124,12 +6068,12 @@ static void iwl_mvm_set_sta_rate(u32 rate_n_flags, struct rate_info *rinfo)
break;
}
- if (format == RATE_MCS_CCK_MSK ||
- format == RATE_MCS_LEGACY_OFDM_MSK) {
+ if (format == RATE_MCS_MOD_TYPE_CCK ||
+ format == RATE_MCS_MOD_TYPE_LEGACY_OFDM) {
int rate = u32_get_bits(rate_n_flags, RATE_LEGACY_RATE_MSK);
/* add the offset needed to get to the legacy ofdm indices */
- if (format == RATE_MCS_LEGACY_OFDM_MSK)
+ if (format == RATE_MCS_MOD_TYPE_LEGACY_OFDM)
rate += IWL_FIRST_OFDM_RATE;
switch (rate) {
@@ -6174,7 +6118,7 @@ static void iwl_mvm_set_sta_rate(u32 rate_n_flags, struct rate_info *rinfo)
rinfo->nss = u32_get_bits(rate_n_flags,
RATE_MCS_NSS_MSK) + 1;
- rinfo->mcs = format == RATE_MCS_HT_MSK ?
+ rinfo->mcs = format == RATE_MCS_MOD_TYPE_HT ?
RATE_HT_MCS_INDEX(rate_n_flags) :
u32_get_bits(rate_n_flags, RATE_MCS_CODE_MSK);
@@ -6182,11 +6126,11 @@ static void iwl_mvm_set_sta_rate(u32 rate_n_flags, struct rate_info *rinfo)
rinfo->flags |= RATE_INFO_FLAGS_SHORT_GI;
switch (format) {
- case RATE_MCS_EHT_MSK:
+ case RATE_MCS_MOD_TYPE_EHT:
/* TODO: GI/LTF/RU. How does the firmware encode them? */
rinfo->flags |= RATE_INFO_FLAGS_EHT_MCS;
break;
- case RATE_MCS_HE_MSK:
+ case RATE_MCS_MOD_TYPE_HE:
gi_ltf = u32_get_bits(rate_n_flags, RATE_MCS_HE_GI_LTF_MSK);
rinfo->flags |= RATE_INFO_FLAGS_HE_MCS;
@@ -6227,10 +6171,10 @@ static void iwl_mvm_set_sta_rate(u32 rate_n_flags, struct rate_info *rinfo)
if (rate_n_flags & RATE_HE_DUAL_CARRIER_MODE_MSK)
rinfo->he_dcm = 1;
break;
- case RATE_MCS_HT_MSK:
+ case RATE_MCS_MOD_TYPE_HT:
rinfo->flags |= RATE_INFO_FLAGS_MCS;
break;
- case RATE_MCS_VHT_MSK:
+ case RATE_MCS_MOD_TYPE_VHT:
rinfo->flags |= RATE_INFO_FLAGS_VHT_MCS;
break;
}
@@ -6410,17 +6354,10 @@ void iwl_mvm_sync_rx_queues_internal(struct iwl_mvm *mvm,
bool sync,
const void *data, u32 size)
{
- struct {
- struct iwl_rxq_sync_cmd cmd;
- struct iwl_mvm_internal_rxq_notif notif;
- } __packed cmd = {
- .cmd.rxq_mask = cpu_to_le32(BIT(mvm->trans->num_rx_queues) - 1),
- .cmd.count =
- cpu_to_le32(sizeof(struct iwl_mvm_internal_rxq_notif) +
- size),
- .notif.type = type,
- .notif.sync = sync,
- };
+ DEFINE_RAW_FLEX(struct iwl_rxq_sync_cmd, cmd, payload,
+ sizeof(struct iwl_mvm_internal_rxq_notif));
+ struct iwl_mvm_internal_rxq_notif *notif =
+ (struct iwl_mvm_internal_rxq_notif *)cmd->payload;
struct iwl_host_cmd hcmd = {
.id = WIDE_ID(DATA_PATH_GROUP, TRIGGER_RX_QUEUES_NOTIF_CMD),
.data[0] = &cmd,
@@ -6431,16 +6368,22 @@ void iwl_mvm_sync_rx_queues_internal(struct iwl_mvm *mvm,
};
int ret;
+ cmd->rxq_mask = cpu_to_le32(BIT(mvm->trans->info.num_rxqs) - 1);
+ cmd->count = cpu_to_le32(sizeof(struct iwl_mvm_internal_rxq_notif) +
+ size);
+ notif->type = type;
+ notif->sync = sync;
+
/* size must be a multiple of DWORD */
- if (WARN_ON(cmd.cmd.count & cpu_to_le32(3)))
+ if (WARN_ON(cmd->count & cpu_to_le32(3)))
return;
if (!iwl_mvm_has_new_rx_api(mvm))
return;
if (sync) {
- cmd.notif.cookie = mvm->queue_sync_cookie;
- mvm->queue_sync_state = (1 << mvm->trans->num_rx_queues) - 1;
+ notif->cookie = mvm->queue_sync_cookie;
+ mvm->queue_sync_state = (1 << mvm->trans->info.num_rxqs) - 1;
}
ret = iwl_mvm_send_cmd(mvm, &hcmd);
@@ -6633,8 +6576,6 @@ const struct ieee80211_ops iwl_mvm_hw_ops = {
.sync_rx_queues = iwl_mvm_sync_rx_queues,
- CFG80211_TESTMODE_CMD(iwl_mvm_mac_testmode_cmd)
-
#ifdef CONFIG_PM_SLEEP
/* look at d3.c */
.suspend = iwl_mvm_suspend,
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mld-mac.c b/drivers/net/wireless/intel/iwlwifi/mvm/mld-mac.c
index bb7851042177..81ca9ff67be9 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/mld-mac.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/mld-mac.c
@@ -1,17 +1,25 @@
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
/*
- * Copyright (C) 2022 - 2024 Intel Corporation
+ * Copyright (C) 2022 - 2025 Intel Corporation
*/
#include "mvm.h"
static void iwl_mvm_mld_set_he_support(struct iwl_mvm *mvm,
struct ieee80211_vif *vif,
- struct iwl_mac_config_cmd *cmd)
+ struct iwl_mac_config_cmd *cmd,
+ int cmd_ver)
{
- if (vif->type == NL80211_IFTYPE_AP)
- cmd->he_ap_support = cpu_to_le16(1);
- else
- cmd->he_support = cpu_to_le16(1);
+ if (vif->type == NL80211_IFTYPE_AP) {
+ if (cmd_ver == 2)
+ cmd->wifi_gen_v2.he_ap_support = cpu_to_le16(1);
+ else
+ cmd->wifi_gen.he_ap_support = 1;
+ } else {
+ if (cmd_ver == 2)
+ cmd->wifi_gen_v2.he_support = cpu_to_le16(1);
+ else
+ cmd->wifi_gen.he_support = 1;
+ }
}
static void iwl_mvm_mld_mac_ctxt_cmd_common(struct iwl_mvm *mvm,
@@ -22,6 +30,12 @@ static void iwl_mvm_mld_mac_ctxt_cmd_common(struct iwl_mvm *mvm,
struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
struct ieee80211_bss_conf *link_conf;
unsigned int link_id;
+ int cmd_ver = iwl_fw_lookup_cmd_ver(mvm->fw,
+ WIDE_ID(MAC_CONF_GROUP,
+ MAC_CONFIG_CMD), 0);
+
+ if (WARN_ON(cmd_ver < 1 && cmd_ver > 3))
+ return;
cmd->id_and_color = cpu_to_le32(mvmvif->id);
cmd->action = cpu_to_le32(action);
@@ -30,8 +44,8 @@ static void iwl_mvm_mld_mac_ctxt_cmd_common(struct iwl_mvm *mvm,
memcpy(cmd->local_mld_addr, vif->addr, ETH_ALEN);
- cmd->he_support = 0;
- cmd->eht_support = 0;
+ cmd->wifi_gen_v2.he_support = 0;
+ cmd->wifi_gen_v2.eht_support = 0;
/* should be set by specific context type handler */
cmd->filter_flags = 0;
@@ -51,8 +65,11 @@ static void iwl_mvm_mld_mac_ctxt_cmd_common(struct iwl_mvm *mvm,
* and enable both when we have MLO.
*/
if (ieee80211_vif_is_mld(vif)) {
- iwl_mvm_mld_set_he_support(mvm, vif, cmd);
- cmd->eht_support = cpu_to_le32(1);
+ iwl_mvm_mld_set_he_support(mvm, vif, cmd, cmd_ver);
+ if (cmd_ver == 2)
+ cmd->wifi_gen_v2.eht_support = cpu_to_le32(1);
+ else
+ cmd->wifi_gen.eht_support = 1;
return;
}
@@ -63,16 +80,19 @@ static void iwl_mvm_mld_mac_ctxt_cmd_common(struct iwl_mvm *mvm,
continue;
if (link_conf->he_support)
- iwl_mvm_mld_set_he_support(mvm, vif, cmd);
+ iwl_mvm_mld_set_he_support(mvm, vif, cmd, cmd_ver);
- /* it's not reasonable to have EHT without HE and FW API doesn't
+ /* It's not reasonable to have EHT without HE and FW API doesn't
* support it. Ignore EHT in this case.
*/
if (!link_conf->he_support && link_conf->eht_support)
continue;
if (link_conf->eht_support) {
- cmd->eht_support = cpu_to_le32(1);
+ if (cmd_ver == 2)
+ cmd->wifi_gen_v2.eht_support = cpu_to_le32(1);
+ else
+ cmd->wifi_gen.eht_support = 1;
break;
}
}
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mld-mac80211.c b/drivers/net/wireless/intel/iwlwifi/mvm/mld-mac80211.c
index 341a2a7a49ec..bf24f8cb673e 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/mld-mac80211.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/mld-mac80211.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
/*
- * Copyright (C) 2022-2024 Intel Corporation
+ * Copyright (C) 2022-2025 Intel Corporation
*/
#include "mvm.h"
@@ -150,19 +150,6 @@ static void iwl_mvm_mld_mac_remove_interface(struct ieee80211_hw *hw,
iwl_mvm_vif_dbgfs_rm_link(mvm, vif);
- /* For AP/GO interface, the tear down of the resources allocated to the
- * interface is be handled as part of the stop_ap flow.
- */
- if (vif->type == NL80211_IFTYPE_AP ||
- vif->type == NL80211_IFTYPE_ADHOC) {
-#ifdef CONFIG_NL80211_TESTMODE
- if (vif == mvm->noa_vif) {
- mvm->noa_vif = NULL;
- mvm->noa_duration = 0;
- }
-#endif
- }
-
iwl_mvm_power_update_mac(mvm);
/* Before the interface removal, mac80211 would cancel the ROC, and the
@@ -887,6 +874,7 @@ static void iwl_mvm_mld_vif_cfg_changed_station(struct iwl_mvm *mvm,
}
if (changes & BSS_CHANGED_PS) {
+ iwl_mvm_smps_workaround(mvm, vif, false);
ret = iwl_mvm_power_update_mac(mvm);
if (ret)
IWL_ERR(mvm, "failed to update power mode\n");
@@ -1402,8 +1390,6 @@ const struct ieee80211_ops iwl_mvm_mld_hw_ops = {
.sync_rx_queues = iwl_mvm_sync_rx_queues,
- CFG80211_TESTMODE_CMD(iwl_mvm_mac_testmode_cmd)
-
#ifdef CONFIG_PM_SLEEP
/* look at d3.c */
.suspend = iwl_mvm_suspend,
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mld-sta.c b/drivers/net/wireless/intel/iwlwifi/mvm/mld-sta.c
index 2f159024eeb8..e1010521c3ea 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/mld-sta.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/mld-sta.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
/*
- * Copyright (C) 2022-2024 Intel Corporation
+ * Copyright (C) 2022-2025 Intel Corporation
*/
#include "mvm.h"
#include "time-sync.h"
@@ -48,9 +48,13 @@ u32 iwl_mvm_sta_fw_id_mask(struct iwl_mvm *mvm, struct ieee80211_sta *sta,
static int iwl_mvm_mld_send_sta_cmd(struct iwl_mvm *mvm,
struct iwl_sta_cfg_cmd *cmd)
{
+ u32 cmd_id = WIDE_ID(MAC_CONF_GROUP, STA_CONFIG_CMD);
+ int cmd_len = iwl_fw_lookup_cmd_ver(mvm->fw, cmd_id, 0) > 1 ?
+ sizeof(*cmd) :
+ sizeof(struct iwl_sta_cfg_cmd_v1);
int ret = iwl_mvm_send_cmd_pdu(mvm,
WIDE_ID(MAC_CONF_GROUP, STA_CONFIG_CMD),
- 0, sizeof(*cmd), cmd);
+ 0, cmd_len, cmd);
if (ret)
IWL_ERR(mvm, "STA_CONFIG_CMD send failed, ret=0x%x\n", ret);
return ret;
@@ -121,7 +125,7 @@ static int iwl_mvm_add_aux_sta_to_fw(struct iwl_mvm *mvm,
{
int ret;
- struct iwl_mvm_aux_sta_cmd cmd = {
+ struct iwl_aux_sta_cmd cmd = {
.sta_id = cpu_to_le32(sta->sta_id),
.lmac_id = cpu_to_le32(lmac_id),
};
@@ -144,7 +148,7 @@ int iwl_mvm_mld_add_int_sta_with_queue(struct iwl_mvm *mvm,
{
int ret, txq;
unsigned int wdg_timeout = _wdg_timeout ? *_wdg_timeout :
- mvm->trans->trans_cfg->base_params->wd_timeout;
+ mvm->trans->mac_cfg->base->wd_timeout;
if (WARN_ON_ONCE(sta->sta_id == IWL_INVALID_STA))
return -ENOSPC;
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h
index ee769da72e68..a4f412e750d0 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
/*
- * Copyright (C) 2012-2014, 2018-2024 Intel Corporation
+ * Copyright (C) 2012-2014, 2018-2025 Intel Corporation
* Copyright (C) 2013-2015 Intel Mobile Communications GmbH
* Copyright (C) 2016-2017 Intel Deutschland GmbH
*/
@@ -126,7 +126,7 @@ struct iwl_mvm_time_event_data {
/* Power management */
/**
- * enum iwl_power_scheme
+ * enum iwl_power_scheme - iwl power schemes
* @IWL_POWER_SCHEME_CAM: Continuously Active Mode
* @IWL_POWER_SCHEME_BPS: Balanced Power Save (default)
* @IWL_POWER_SCHEME_LP: Low Power
@@ -664,6 +664,8 @@ enum iwl_mvm_sched_scan_pass_all_states {
* @min_backoff: The minimal tx backoff due to power restrictions
* @params: Parameters to configure the thermal throttling algorithm.
* @throttle: Is thermal throttling is active?
+ * @power_budget_mw: maximum cTDP power budget as defined for this system and
+ * device
*/
struct iwl_mvm_tt_mgmt {
struct delayed_work ct_kill_exit;
@@ -672,6 +674,8 @@ struct iwl_mvm_tt_mgmt {
u32 min_backoff;
struct iwl_tt_params params;
bool throttle;
+
+ u32 power_budget_mw;
};
#ifdef CONFIG_THERMAL
@@ -999,7 +1003,7 @@ struct iwl_mvm {
struct iwl_trans *trans;
const struct iwl_fw *fw;
- const struct iwl_cfg *cfg;
+ const struct iwl_rf_cfg *cfg;
struct iwl_phy_db *phy_db;
struct ieee80211_hw *hw;
@@ -1033,6 +1037,8 @@ struct iwl_mvm {
u8 cca_40mhz_workaround;
+ u8 fw_rates_ver;
+
u32 ampdu_ref;
bool ampdu_toggle;
@@ -1246,11 +1252,6 @@ struct iwl_mvm {
struct iwl_time_quota_cmd last_quota_cmd;
-#ifdef CONFIG_NL80211_TESTMODE
- u32 noa_duration;
- struct ieee80211_vif *noa_vif;
-#endif
-
/* Tx queues */
u16 aux_queue;
u16 snif_queue;
@@ -1310,7 +1311,7 @@ struct iwl_mvm {
struct cfg80211_pmsr_request *req;
struct wireless_dev *req_wdev;
struct list_head loc_list;
- int responses[IWL_MVM_TOF_MAX_APS];
+ int responses[IWL_TOF_MAX_APS];
struct {
struct list_head resp;
} smooth;
@@ -1348,10 +1349,6 @@ struct iwl_mvm {
__le16 cur_aid;
u8 cur_bssid[ETH_ALEN];
-#ifdef CONFIG_ACPI
- struct iwl_phy_specific_cfg phy_filters;
-#endif
-
/* report rx timestamp in ptp clock time */
bool rx_ts_ptp;
@@ -1562,7 +1559,7 @@ static inline bool iwl_mvm_is_lar_supported(struct iwl_mvm *mvm)
* Enable LAR only if it is supported by the FW (TLV) &&
* enabled in the NVM
*/
- if (mvm->cfg->nvm_type == IWL_NVM_EXT)
+ if (mvm->trans->cfg->nvm_type == IWL_NVM_EXT)
return nvm_lar && tlv_lar;
else
return tlv_lar;
@@ -1626,13 +1623,13 @@ static inline bool iwl_mvm_has_new_station_api(const struct iwl_fw *fw)
static inline bool iwl_mvm_has_new_tx_api(struct iwl_mvm *mvm)
{
/* TODO - replace with TLV once defined */
- return mvm->trans->trans_cfg->gen2;
+ return mvm->trans->mac_cfg->gen2;
}
static inline bool iwl_mvm_has_unified_ucode(struct iwl_mvm *mvm)
{
/* TODO - better define this */
- return mvm->trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_22000;
+ return mvm->trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_22000;
}
static inline bool iwl_mvm_is_cdb_supported(struct iwl_mvm *mvm)
@@ -1657,7 +1654,7 @@ static inline bool iwl_mvm_cdb_scan_api(struct iwl_mvm *mvm)
* but then there's a little bit of code in scan that won't make
* any sense...
*/
- return mvm->trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_22000;
+ return mvm->trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_22000;
}
static inline bool iwl_mvm_is_scan_ext_chan_supported(struct iwl_mvm *mvm)
@@ -1732,13 +1729,13 @@ static inline bool iwl_mvm_is_ctdp_supported(struct iwl_mvm *mvm)
static inline bool iwl_mvm_is_esr_supported(struct iwl_trans *trans)
{
- if (CSR_HW_RFID_IS_CDB(trans->hw_rf_id))
+ if (CSR_HW_RFID_IS_CDB(trans->info.hw_rf_id))
return false;
- switch (CSR_HW_RFID_TYPE(trans->hw_rf_id)) {
+ switch (CSR_HW_RFID_TYPE(trans->info.hw_rf_id)) {
case IWL_CFG_RF_TYPE_FM:
/* Step A doesn't support eSR */
- return CSR_HW_RFID_STEP(trans->hw_rf_id);
+ return CSR_HW_RFID_STEP(trans->info.hw_rf_id);
case IWL_CFG_RF_TYPE_WH:
case IWL_CFG_RF_TYPE_PE:
return true;
@@ -1757,8 +1754,8 @@ static inline int iwl_mvm_max_active_links(struct iwl_mvm *mvm,
/* Check if HW supports eSR or STR */
if (iwl_mvm_is_esr_supported(trans) ||
- (CSR_HW_RFID_TYPE(trans->hw_rf_id) == IWL_CFG_RF_TYPE_FM &&
- CSR_HW_RFID_IS_CDB(trans->hw_rf_id)))
+ (CSR_HW_RFID_TYPE(trans->info.hw_rf_id) == IWL_CFG_RF_TYPE_FM &&
+ CSR_HW_RFID_IS_CDB(trans->info.hw_rf_id)))
return IWL_FW_MAX_ACTIVE_LINKS_NUM;
return 1;
@@ -1771,7 +1768,7 @@ extern const u8 iwl_mvm_ac_to_bz_tx_fifo[];
static inline u8 iwl_mvm_mac_ac_to_tx_fifo(struct iwl_mvm *mvm,
enum ieee80211_ac_numbers ac)
{
- if (mvm->trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_BZ)
+ if (mvm->trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_BZ)
return iwl_mvm_ac_to_bz_tx_fifo[ac];
if (iwl_mvm_has_new_tx_api(mvm))
return iwl_mvm_ac_to_gen2_tx_fifo[ac];
@@ -1810,9 +1807,6 @@ int iwl_mvm_legacy_rate_to_mac80211_idx(u32 rate_n_flags,
void iwl_mvm_hwrate_to_tx_rate(u32 rate_n_flags,
enum nl80211_band band,
struct ieee80211_tx_rate *r);
-void iwl_mvm_hwrate_to_tx_rate_v1(u32 rate_n_flags,
- enum nl80211_band band,
- struct ieee80211_tx_rate *r);
u8 iwl_mvm_mac80211_idx_to_hwrate(const struct iwl_fw *fw, int rate_idx);
u8 iwl_mvm_mac80211_ac_to_ucode_ac(enum ieee80211_ac_numbers ac);
bool iwl_mvm_is_nic_ack_enabled(struct iwl_mvm *mvm, struct ieee80211_vif *vif);
@@ -1843,9 +1837,9 @@ int iwl_mvm_tx_skb_sta(struct iwl_mvm *mvm, struct sk_buff *skb,
struct ieee80211_sta *sta);
int iwl_mvm_tx_skb_non_sta(struct iwl_mvm *mvm, struct sk_buff *skb);
void iwl_mvm_set_tx_cmd(struct iwl_mvm *mvm, struct sk_buff *skb,
- struct iwl_tx_cmd *tx_cmd,
+ struct iwl_tx_cmd_v6 *tx_cmd,
struct ieee80211_tx_info *info, u8 sta_id);
-void iwl_mvm_set_tx_cmd_rate(struct iwl_mvm *mvm, struct iwl_tx_cmd *tx_cmd,
+void iwl_mvm_set_tx_cmd_rate(struct iwl_mvm *mvm, struct iwl_tx_cmd_v6 *tx_cmd,
struct ieee80211_tx_info *info,
struct ieee80211_sta *sta, __le16 fc);
void iwl_mvm_mac_itxq_xmit(struct ieee80211_hw *hw, struct ieee80211_txq *txq);
@@ -1876,7 +1870,7 @@ int iwl_mvm_set_sta_pkt_ext(struct iwl_mvm *mvm,
void iwl_mvm_async_handlers_purge(struct iwl_mvm *mvm);
static inline void iwl_mvm_set_tx_cmd_ccmp(struct ieee80211_tx_info *info,
- struct iwl_tx_cmd *tx_cmd)
+ struct iwl_tx_cmd_v6 *tx_cmd)
{
struct ieee80211_key_conf *keyconf = info->control.hw_key;
@@ -2095,8 +2089,6 @@ void iwl_mvm_mac_ctxt_recalc_tsf_id(struct iwl_mvm *mvm,
struct ieee80211_vif *vif);
void iwl_mvm_probe_resp_data_notif(struct iwl_mvm *mvm,
struct iwl_rx_cmd_buffer *rxb);
-void iwl_mvm_rx_missed_vap_notif(struct iwl_mvm *mvm,
- struct iwl_rx_cmd_buffer *rxb);
void iwl_mvm_channel_switch_start_notif(struct iwl_mvm *mvm,
struct iwl_rx_cmd_buffer *rxb);
void iwl_mvm_channel_switch_error_notif(struct iwl_mvm *mvm,
@@ -2140,6 +2132,10 @@ bool iwl_mvm_mld_valid_link_pair(struct ieee80211_vif *vif,
const struct iwl_mvm_link_sel_data *b);
s8 iwl_mvm_average_dbm_values(const struct iwl_umac_scan_channel_survey_notif *notif);
+
+
+extern const struct iwl_hcmd_arr iwl_mvm_groups[];
+extern const unsigned int iwl_mvm_groups_size;
#endif
/* AP and IBSS */
@@ -2461,7 +2457,7 @@ void iwl_mvm_vif_set_low_latency(struct iwl_mvm_vif *mvmvif, bool set,
*/
static inline u32 iwl_mvm_flushable_queues(struct iwl_mvm *mvm)
{
- return ((BIT(mvm->trans->trans_cfg->base_params->num_of_queues) - 1) &
+ return ((BIT(mvm->trans->mac_cfg->base->num_of_queues) - 1) &
~BIT(IWL_MVM_DQA_CMD_QUEUE));
}
@@ -2520,12 +2516,6 @@ void iwl_mvm_ftm_restart_responder(struct iwl_mvm *mvm,
struct ieee80211_bss_conf *bss_conf);
void iwl_mvm_ftm_responder_stats(struct iwl_mvm *mvm,
struct iwl_rx_cmd_buffer *rxb);
-int iwl_mvm_ftm_resp_remove_pasn_sta(struct iwl_mvm *mvm,
- struct ieee80211_vif *vif, u8 *addr);
-int iwl_mvm_ftm_respoder_add_pasn_sta(struct iwl_mvm *mvm,
- struct ieee80211_vif *vif,
- u8 *addr, u32 cipher, u8 *tk, u32 tk_len,
- u8 *hltk, u32 hltk_len);
void iwl_mvm_ftm_responder_clear(struct iwl_mvm *mvm,
struct ieee80211_vif *vif);
@@ -2540,10 +2530,6 @@ int iwl_mvm_ftm_start(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
void iwl_mvm_ftm_abort(struct iwl_mvm *mvm, struct cfg80211_pmsr_request *req);
void iwl_mvm_ftm_initiator_smooth_config(struct iwl_mvm *mvm);
void iwl_mvm_ftm_initiator_smooth_stop(struct iwl_mvm *mvm);
-int iwl_mvm_ftm_add_pasn_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
- u8 *addr, u32 cipher, u8 *tk, u32 tk_len,
- u8 *hltk, u32 hltk_len);
-void iwl_mvm_ftm_remove_pasn_sta(struct iwl_mvm *mvm, u8 *addr);
/* TDLS */
@@ -3043,4 +3029,7 @@ iwl_mvm_send_ap_tx_power_constraint_cmd(struct iwl_mvm *mvm,
struct ieee80211_vif *vif,
struct ieee80211_bss_conf *bss_conf,
bool is_ap);
+
+void iwl_mvm_smps_workaround(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
+ bool update);
#endif /* __IWL_MVM_H__ */
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/nvm.c b/drivers/net/wireless/intel/iwlwifi/mvm/nvm.c
index 80ec59c58ae4..953218f1e025 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/nvm.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/nvm.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
/*
- * Copyright (C) 2012-2014, 2018-2019, 2021-2024 Intel Corporation
+ * Copyright (C) 2012-2014, 2018-2019, 2021-2025 Intel Corporation
* Copyright (C) 2013-2015 Intel Mobile Communications GmbH
* Copyright (C) 2016-2017 Intel Deutschland GmbH
*/
@@ -120,7 +120,7 @@ static int iwl_nvm_read_chunk(struct iwl_mvm *mvm, u16 section,
} else {
IWL_DEBUG_EEPROM(mvm->trans->dev,
"NVM access command failed with status %d (device: %s)\n",
- ret, mvm->trans->name);
+ ret, mvm->trans->info.name);
ret = -ENODATA;
}
goto exit;
@@ -191,7 +191,7 @@ static int iwl_nvm_read_section(struct iwl_mvm *mvm, u16 section,
while (ret == length) {
/* Check no memory assumptions fail and cause an overflow */
if ((size_read + offset + length) >
- mvm->trans->trans_cfg->base_params->eeprom_size) {
+ mvm->trans->mac_cfg->base->eeprom_size) {
IWL_ERR(mvm, "EEPROM size is too small for NVM\n");
return -ENOBUFS;
}
@@ -206,7 +206,7 @@ static int iwl_nvm_read_section(struct iwl_mvm *mvm, u16 section,
offset += ret;
}
- iwl_nvm_fixups(mvm->trans->hw_id, section, data, offset);
+ iwl_nvm_fixups(mvm->trans->info.hw_id, section, data, offset);
IWL_DEBUG_EEPROM(mvm->trans->dev,
"NVM section %d read completed\n", section);
@@ -226,7 +226,7 @@ iwl_parse_nvm_sections(struct iwl_mvm *mvm)
/* Checking for required sections */
if (mvm->trans->cfg->nvm_type == IWL_NVM) {
if (!mvm->nvm_sections[NVM_SECTION_TYPE_SW].data ||
- !mvm->nvm_sections[mvm->cfg->nvm_hw_section_num].data) {
+ !mvm->nvm_sections[mvm->trans->mac_cfg->base->nvm_hw_section_num].data) {
IWL_ERR(mvm, "Can't parse empty OTP/NVM sections\n");
return NULL;
}
@@ -244,7 +244,7 @@ iwl_parse_nvm_sections(struct iwl_mvm *mvm)
return NULL;
}
/* MAC_OVERRIDE or at least HW section must exist */
- if (!mvm->nvm_sections[mvm->cfg->nvm_hw_section_num].data &&
+ if (!mvm->nvm_sections[mvm->trans->mac_cfg->base->nvm_hw_section_num].data &&
!mvm->nvm_sections[NVM_SECTION_TYPE_MAC_OVERRIDE].data) {
IWL_ERR(mvm,
"Can't parse mac_address, empty sections\n");
@@ -260,7 +260,7 @@ iwl_parse_nvm_sections(struct iwl_mvm *mvm)
}
}
- hw = (const __be16 *)sections[mvm->cfg->nvm_hw_section_num].data;
+ hw = (const __be16 *)sections[mvm->trans->mac_cfg->base->nvm_hw_section_num].data;
sw = (const __le16 *)sections[NVM_SECTION_TYPE_SW].data;
calib = (const __le16 *)sections[NVM_SECTION_TYPE_CALIBRATION].data;
mac_override =
@@ -308,16 +308,15 @@ int iwl_nvm_init(struct iwl_mvm *mvm)
int ret, section;
u32 size_read = 0;
u8 *nvm_buffer, *temp;
- const char *nvm_file_C = mvm->cfg->default_nvm_file_C_step;
- if (WARN_ON_ONCE(mvm->cfg->nvm_hw_section_num >= NVM_MAX_NUM_SECTIONS))
+ if (WARN_ON_ONCE(mvm->trans->mac_cfg->base->nvm_hw_section_num >= NVM_MAX_NUM_SECTIONS))
return -EINVAL;
/* load NVM values from nic */
/* Read From FW NVM */
IWL_DEBUG_EEPROM(mvm->trans->dev, "Read from NVM\n");
- nvm_buffer = kmalloc(mvm->trans->trans_cfg->base_params->eeprom_size,
+ nvm_buffer = kmalloc(mvm->trans->mac_cfg->base->eeprom_size,
GFP_KERNEL);
if (!nvm_buffer)
return -ENOMEM;
@@ -338,7 +337,7 @@ int iwl_nvm_init(struct iwl_mvm *mvm)
break;
}
- iwl_nvm_fixups(mvm->trans->hw_id, section, temp, ret);
+ iwl_nvm_fixups(mvm->trans->info.hw_id, section, temp, ret);
mvm->nvm_sections[section].data = temp;
mvm->nvm_sections[section].length = ret;
@@ -367,7 +366,7 @@ int iwl_nvm_init(struct iwl_mvm *mvm)
mvm->nvm_reg_blob.size = ret;
break;
default:
- if (section == mvm->cfg->nvm_hw_section_num) {
+ if (section == mvm->trans->mac_cfg->base->nvm_hw_section_num) {
mvm->nvm_hw_blob.data = temp;
mvm->nvm_hw_blob.size = ret;
break;
@@ -384,21 +383,8 @@ int iwl_nvm_init(struct iwl_mvm *mvm)
/* read External NVM file from the mod param */
ret = iwl_read_external_nvm(mvm->trans, mvm->nvm_file_name,
mvm->nvm_sections);
- if (ret) {
- mvm->nvm_file_name = nvm_file_C;
-
- if ((ret == -EFAULT || ret == -ENOENT) &&
- mvm->nvm_file_name) {
- /* in case nvm file was failed try again */
- ret = iwl_read_external_nvm(mvm->trans,
- mvm->nvm_file_name,
- mvm->nvm_sections);
- if (ret)
- return ret;
- } else {
- return ret;
- }
- }
+ if (ret)
+ return ret;
}
/* parse the relevant nvm sections */
@@ -554,7 +540,7 @@ int iwl_mvm_init_mcc(struct iwl_mvm *mvm)
struct ieee80211_regdomain *regd;
char mcc[3];
- if (mvm->cfg->nvm_type == IWL_NVM_EXT) {
+ if (mvm->trans->cfg->nvm_type == IWL_NVM_EXT) {
tlv_lar = fw_has_capa(&mvm->fw->ucode_capa,
IWL_UCODE_TLV_CAPA_LAR_SUPPORT);
nvm_lar = mvm->nvm_data->lar_enabled;
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c
index 984f407f7027..a2dc5c3b0596 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
/*
- * Copyright (C) 2012-2014, 2018-2024 Intel Corporation
+ * Copyright (C) 2012-2014, 2018-2025 Intel Corporation
* Copyright (C) 2013-2015 Intel Mobile Communications GmbH
* Copyright (C) 2016-2017 Intel Deutschland GmbH
*/
@@ -92,11 +92,11 @@ static void iwl_mvm_nic_config(struct iwl_op_mode *op_mode)
IWL_DEBUG_INFO(mvm, "Radio type=0x%x-0x%x-0x%x\n", radio_cfg_type,
radio_cfg_step, radio_cfg_dash);
- if (mvm->trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_AX210)
+ if (mvm->trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_AX210)
return;
/* SKU control */
- reg_val = CSR_HW_REV_STEP_DASH(mvm->trans->hw_rev);
+ reg_val = CSR_HW_REV_STEP_DASH(mvm->trans->info.hw_rev);
/* radio configuration */
reg_val |= radio_cfg_type << CSR_HW_IF_CONFIG_REG_POS_PHY_TYPE;
@@ -114,7 +114,7 @@ static void iwl_mvm_nic_config(struct iwl_op_mode *op_mode)
* unrelated errors. Need to further investigate this, but for now
* we'll separate cases.
*/
- if (mvm->trans->trans_cfg->device_family < IWL_DEVICE_FAMILY_8000)
+ if (mvm->trans->mac_cfg->device_family < IWL_DEVICE_FAMILY_8000)
reg_val |= CSR_HW_IF_CONFIG_REG_BIT_RADIO_SI;
if (iwl_fw_dbg_is_d3_debug_enabled(&mvm->fwrt))
@@ -135,7 +135,7 @@ static void iwl_mvm_nic_config(struct iwl_op_mode *op_mode)
* (PCIe power is lost before PERST# is asserted), causing ME FW
* to lose ownership and not being able to obtain it back.
*/
- if (!mvm->trans->cfg->apmg_not_supported)
+ if (!mvm->trans->mac_cfg->base->apmg_not_supported)
iwl_set_bits_mask_prph(mvm->trans, APMG_PS_CTRL_REG,
APMG_PS_CTRL_EARLY_PWR_OFF_RESET_DIS,
~APMG_PS_CTRL_EARLY_PWR_OFF_RESET_DIS);
@@ -145,7 +145,7 @@ static void iwl_mvm_rx_esr_mode_notif(struct iwl_mvm *mvm,
struct iwl_rx_cmd_buffer *rxb)
{
struct iwl_rx_packet *pkt = rxb_addr(rxb);
- struct iwl_mvm_esr_mode_notif *notif = (void *)pkt->data;
+ struct iwl_esr_mode_notif *notif = (void *)pkt->data;
struct ieee80211_vif *vif = iwl_mvm_get_bss_vif(mvm);
/* FW recommendations is only for entering EMLSR */
@@ -495,7 +495,7 @@ static const struct iwl_rx_handlers iwl_mvm_rx_handlers[] = {
RX_HANDLER_GRP(DATA_PATH_GROUP, ESR_MODE_NOTIF,
iwl_mvm_rx_esr_mode_notif,
RX_HANDLER_ASYNC_LOCKED_WIPHY,
- struct iwl_mvm_esr_mode_notif),
+ struct iwl_esr_mode_notif),
RX_HANDLER_GRP(DATA_PATH_GROUP, MONITOR_NOTIF,
iwl_mvm_rx_monitor_notif, RX_HANDLER_ASYNC_LOCKED,
@@ -777,7 +777,8 @@ static const struct iwl_hcmd_names iwl_mvm_bt_coex_names[] = {
HCMD_NAME(PROFILE_NOTIF),
};
-static const struct iwl_hcmd_arr iwl_mvm_groups[] = {
+VISIBLE_IF_IWLWIFI_KUNIT
+const struct iwl_hcmd_arr iwl_mvm_groups[] = {
[LEGACY_GROUP] = HCMD_ARR(iwl_mvm_legacy_names),
[LONG_GROUP] = HCMD_ARR(iwl_mvm_legacy_names),
[SYSTEM_GROUP] = HCMD_ARR(iwl_mvm_system_names),
@@ -793,6 +794,11 @@ static const struct iwl_hcmd_arr iwl_mvm_groups[] = {
[DEBUG_GROUP] = HCMD_ARR(iwl_mvm_debug_names),
[STATISTICS_GROUP] = HCMD_ARR(iwl_mvm_statistics_names),
};
+EXPORT_SYMBOL_IF_IWLWIFI_KUNIT(iwl_mvm_groups);
+#if IS_ENABLED(CONFIG_IWLWIFI_KUNIT_TESTS)
+const unsigned int iwl_mvm_groups_size = ARRAY_SIZE(iwl_mvm_groups);
+EXPORT_SYMBOL_IF_IWLWIFI_KUNIT(iwl_mvm_groups_size);
+#endif
/* this forward declaration can avoid to export the function */
static void iwl_mvm_async_handlers_wk(struct work_struct *wk);
@@ -1272,13 +1278,12 @@ static void iwl_mvm_trig_link_selection(struct wiphy *wiphy,
}
static struct iwl_op_mode *
-iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg,
+iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_rf_cfg *cfg,
const struct iwl_fw *fw, struct dentry *dbgfs_dir)
{
struct ieee80211_hw *hw;
struct iwl_op_mode *op_mode;
struct iwl_mvm *mvm;
- struct iwl_trans_config trans_cfg = {};
static const u8 no_reclaim_cmds[] = {
TX_CMD,
};
@@ -1286,6 +1291,7 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg,
size_t scan_size;
u32 min_backoff;
struct iwl_mvm_csme_conn_info *csme_conn_info __maybe_unused;
+ int ratecheck;
int err;
/*
@@ -1306,18 +1312,13 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg,
if (!hw)
return ERR_PTR(-ENOMEM);
- if (trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_BZ)
+ if (trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_BZ)
max_agg = 512;
else
max_agg = IEEE80211_MAX_AMPDU_BUF_HE;
hw->max_rx_aggregation_subframes = max_agg;
- if (cfg->max_tx_agg_size)
- hw->max_tx_aggregation_subframes = cfg->max_tx_agg_size;
- else
- hw->max_tx_aggregation_subframes = max_agg;
-
op_mode = hw->priv;
mvm = IWL_OP_MODE_GET_MVM(op_mode);
@@ -1337,19 +1338,58 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg,
mvm->init_status = 0;
+ /* start with v1 rates */
+ mvm->fw_rates_ver = 1;
+
+ /* check for rates version 2 */
+ ratecheck =
+ (iwl_fw_lookup_cmd_ver(mvm->fw, TX_CMD, 0) >= 8) +
+ (iwl_fw_lookup_notif_ver(mvm->fw, DATA_PATH_GROUP,
+ TLC_MNG_UPDATE_NOTIF, 0) >= 3) +
+ (iwl_fw_lookup_notif_ver(mvm->fw, LEGACY_GROUP,
+ REPLY_RX_MPDU_CMD, 0) >= 4) +
+ (iwl_fw_lookup_notif_ver(mvm->fw, LONG_GROUP, TX_CMD, 0) >= 6);
+ if (ratecheck != 0 && ratecheck != 4) {
+ IWL_ERR(mvm, "Firmware has inconsistent rates\n");
+ err = -EINVAL;
+ goto out_free;
+ }
+ if (ratecheck == 4)
+ mvm->fw_rates_ver = 2;
+
+ /* check for rates version 3 */
+ ratecheck =
+ (iwl_fw_lookup_cmd_ver(mvm->fw, TX_CMD, 0) >= 11) +
+ (iwl_fw_lookup_notif_ver(mvm->fw, DATA_PATH_GROUP,
+ TLC_MNG_UPDATE_NOTIF, 0) >= 4) +
+ (iwl_fw_lookup_notif_ver(mvm->fw, LEGACY_GROUP,
+ REPLY_RX_MPDU_CMD, 0) >= 6) +
+ (iwl_fw_lookup_notif_ver(mvm->fw, DATA_PATH_GROUP,
+ RX_NO_DATA_NOTIF, 0) >= 4) +
+ (iwl_fw_lookup_notif_ver(mvm->fw, LONG_GROUP, TX_CMD, 0) >= 9);
+ if (ratecheck != 0 && ratecheck != 5) {
+ IWL_ERR(mvm, "Firmware has inconsistent rates\n");
+ err = -EINVAL;
+ goto out_free;
+ }
+ if (ratecheck == 5)
+ mvm->fw_rates_ver = 3;
+
+ trans->conf.rx_mpdu_cmd = REPLY_RX_MPDU_CMD;
+
if (iwl_mvm_has_new_rx_api(mvm)) {
op_mode->ops = &iwl_mvm_ops_mq;
- trans->rx_mpdu_cmd_hdr_size =
- (trans->trans_cfg->device_family >=
+ trans->conf.rx_mpdu_cmd_hdr_size =
+ (trans->mac_cfg->device_family >=
IWL_DEVICE_FAMILY_AX210) ?
sizeof(struct iwl_rx_mpdu_desc) :
IWL_RX_DESC_SIZE_V1;
} else {
op_mode->ops = &iwl_mvm_ops;
- trans->rx_mpdu_cmd_hdr_size =
+ trans->conf.rx_mpdu_cmd_hdr_size =
sizeof(struct iwl_rx_mpdu_res_start);
- if (WARN_ON(trans->num_rx_queues > 1)) {
+ if (WARN_ON(trans->info.num_rxqs > 1)) {
err = -EINVAL;
goto out_free;
}
@@ -1437,53 +1477,50 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg,
* Populate the state variables that the transport layer needs
* to know about.
*/
- trans_cfg.op_mode = op_mode;
- trans_cfg.no_reclaim_cmds = no_reclaim_cmds;
- trans_cfg.n_no_reclaim_cmds = ARRAY_SIZE(no_reclaim_cmds);
+ BUILD_BUG_ON(sizeof(no_reclaim_cmds) >
+ sizeof(trans->conf.no_reclaim_cmds));
+ memcpy(trans->conf.no_reclaim_cmds, no_reclaim_cmds,
+ sizeof(no_reclaim_cmds));
+ trans->conf.n_no_reclaim_cmds = ARRAY_SIZE(no_reclaim_cmds);
- trans_cfg.rx_buf_size = iwl_amsdu_size_to_rxb_size();
+ trans->conf.rx_buf_size = iwl_amsdu_size_to_rxb_size();
- trans->wide_cmd_header = true;
- trans_cfg.bc_table_dword =
- mvm->trans->trans_cfg->device_family < IWL_DEVICE_FAMILY_AX210;
+ trans->conf.wide_cmd_header = true;
- trans_cfg.command_groups = iwl_mvm_groups;
- trans_cfg.command_groups_size = ARRAY_SIZE(iwl_mvm_groups);
+ trans->conf.command_groups = iwl_mvm_groups;
+ trans->conf.command_groups_size = ARRAY_SIZE(iwl_mvm_groups);
- trans_cfg.cmd_queue = IWL_MVM_DQA_CMD_QUEUE;
- trans_cfg.cmd_fifo = IWL_MVM_TX_FIFO_CMD;
- trans_cfg.scd_set_active = true;
+ trans->conf.cmd_queue = IWL_MVM_DQA_CMD_QUEUE;
+ trans->conf.cmd_fifo = IWL_MVM_TX_FIFO_CMD;
+ trans->conf.scd_set_active = true;
- trans_cfg.cb_data_offs = offsetof(struct ieee80211_tx_info,
- driver_data[2]);
+ trans->conf.cb_data_offs = offsetof(struct ieee80211_tx_info,
+ driver_data[2]);
snprintf(mvm->hw->wiphy->fw_version,
sizeof(mvm->hw->wiphy->fw_version),
"%.31s", fw->fw_version);
- trans_cfg.fw_reset_handshake = fw_has_capa(&mvm->fw->ucode_capa,
- IWL_UCODE_TLV_CAPA_FW_RESET_HANDSHAKE);
+ trans->conf.fw_reset_handshake =
+ fw_has_capa(&mvm->fw->ucode_capa,
+ IWL_UCODE_TLV_CAPA_FW_RESET_HANDSHAKE);
- trans_cfg.queue_alloc_cmd_ver =
+ trans->conf.queue_alloc_cmd_ver =
iwl_fw_lookup_cmd_ver(mvm->fw,
WIDE_ID(DATA_PATH_GROUP,
SCD_QUEUE_CONFIG_CMD),
0);
mvm->sta_remove_requires_queue_remove =
- trans_cfg.queue_alloc_cmd_ver > 0;
+ trans->conf.queue_alloc_cmd_ver > 0;
mvm->mld_api_is_used = iwl_mvm_has_mld_api(mvm->fw);
/* Configure transport layer */
- iwl_trans_configure(mvm->trans, &trans_cfg);
+ iwl_trans_op_mode_enter(mvm->trans, op_mode);
- trans->rx_mpdu_cmd = REPLY_RX_MPDU_CMD;
trans->dbg.dest_tlv = mvm->fw->dbg.dest_tlv;
trans->dbg.n_dest_reg = mvm->fw->dbg.n_dest_reg;
- trans->iml = mvm->fw->iml;
- trans->iml_len = mvm->fw->iml_len;
-
/* set up notification wait support */
iwl_notification_wait_init(&mvm->notif_wait);
@@ -2100,7 +2137,7 @@ static bool iwl_mvm_sw_reset(struct iwl_op_mode *op_mode,
mvm->fwrt.trans->dbg.restart_required = false;
ieee80211_restart_hw(mvm->hw);
return true;
- } else if (mvm->trans->trans_cfg->device_family <= IWL_DEVICE_FAMILY_8000) {
+ } else if (mvm->trans->mac_cfg->device_family <= IWL_DEVICE_FAMILY_8000) {
ieee80211_restart_hw(mvm->hw);
return true;
}
@@ -2125,7 +2162,6 @@ static void iwl_op_mode_mvm_device_powered_off(struct iwl_op_mode *op_mode)
mutex_lock(&mvm->mutex);
clear_bit(IWL_MVM_STATUS_IN_D3, &mvm->status);
- mvm->trans->system_pm_mode = IWL_PLAT_PM_MODE_DISABLED;
iwl_mvm_stop_device(mvm);
mvm->fast_resume = false;
mutex_unlock(&mvm->mutex);
@@ -2165,7 +2201,7 @@ static void iwl_mvm_rx_mq_rss(struct iwl_op_mode *op_mode,
struct iwl_rx_packet *pkt = rxb_addr(rxb);
u16 cmd = WIDE_ID(pkt->hdr.group_id, pkt->hdr.cmd);
- if (unlikely(queue >= mvm->trans->num_rx_queues))
+ if (unlikely(queue >= mvm->trans->info.num_rxqs))
return;
if (unlikely(cmd == WIDE_ID(LEGACY_GROUP, FRAME_RELEASE)))
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/power.c b/drivers/net/wireless/intel/iwlwifi/mvm/power.c
index a386b315e52f..0057fddf88f0 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/power.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/power.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
/*
- * Copyright (C) 2012-2014, 2018-2019, 2021-2024 Intel Corporation
+ * Copyright (C) 2012-2014, 2018-2019, 2021-2025 Intel Corporation
* Copyright (C) 2013-2014 Intel Mobile Communications GmbH
* Copyright (C) 2015-2017 Intel Deutschland GmbH
*/
@@ -376,6 +376,9 @@ static void iwl_mvm_power_build_cmd(struct iwl_mvm *mvm,
if (!vif->cfg.ps || !mvmvif->pm_enabled)
return;
+ if (iwl_fw_lookup_cmd_ver(mvm->fw, MAC_PM_POWER_TABLE, 0) >= 2)
+ cmd->flags |= cpu_to_le16(POWER_FLAGS_ENABLE_SMPS_MSK);
+
if (iwl_mvm_vif_low_latency(mvmvif) && vif->p2p &&
(!fw_has_capa(&mvm->fw->ucode_capa,
IWL_UCODE_TLV_CAPA_SHORT_PM_TIMEOUTS) ||
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/quota.c b/drivers/net/wireless/intel/iwlwifi/mvm/quota.c
index aad2614af9ad..798a7e4bea83 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/quota.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/quota.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
/*
- * Copyright (C) 2012-2014, 2018, 2021-2022 Intel Corporation
+ * Copyright (C) 2012-2014, 2018, 2021-2022, 2025 Intel Corporation
* Copyright (C) 2013-2014 Intel Mobile Communications GmbH
* Copyright (C) 2016-2017 Intel Deutschland GmbH
*/
@@ -86,45 +86,6 @@ static void iwl_mvm_quota_iterator(void *_data, u8 *mac,
}
}
-static void iwl_mvm_adjust_quota_for_noa(struct iwl_mvm *mvm,
- struct iwl_time_quota_cmd *cmd)
-{
-#ifdef CONFIG_NL80211_TESTMODE
- struct iwl_mvm_vif *mvmvif;
- int i, phy_id = -1, beacon_int = 0;
-
- if (!mvm->noa_duration || !mvm->noa_vif)
- return;
-
- mvmvif = iwl_mvm_vif_from_mac80211(mvm->noa_vif);
- if (!mvmvif->ap_ibss_active)
- return;
-
- phy_id = mvmvif->deflink.phy_ctxt->id;
- beacon_int = mvm->noa_vif->bss_conf.beacon_int;
-
- for (i = 0; i < MAX_BINDINGS; i++) {
- struct iwl_time_quota_data *data =
- iwl_mvm_quota_cmd_get_quota(mvm, cmd,
- i);
- u32 id_n_c = le32_to_cpu(data->id_and_color);
- u32 id = (id_n_c & FW_CTXT_ID_MSK) >> FW_CTXT_ID_POS;
- u32 quota = le32_to_cpu(data->quota);
-
- if (id != phy_id)
- continue;
-
- quota *= (beacon_int - mvm->noa_duration);
- quota /= beacon_int;
-
- IWL_DEBUG_QUOTA(mvm, "quota: adjust for NoA from %d to %d\n",
- le32_to_cpu(data->quota), quota);
-
- data->quota = cpu_to_le32(quota);
- }
-#endif
-}
-
int iwl_mvm_update_quotas(struct iwl_mvm *mvm,
bool force_update,
struct ieee80211_vif *disabled_vif)
@@ -260,8 +221,6 @@ int iwl_mvm_update_quotas(struct iwl_mvm *mvm,
}
}
- iwl_mvm_adjust_quota_for_noa(mvm, &cmd);
-
/* check that we have non-zero quota for all valid bindings */
for (i = 0; i < MAX_BINDINGS; i++) {
qdata = iwl_mvm_quota_cmd_get_quota(mvm, &cmd, i);
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rs-fw.c b/drivers/net/wireless/intel/iwlwifi/mvm/rs-fw.c
index de5ac000272e..89ac4c6b3e54 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/rs-fw.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/rs-fw.c
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
/*
* Copyright (C) 2017 Intel Deutschland GmbH
- * Copyright (C) 2018-2024 Intel Corporation
+ * Copyright (C) 2018-2025 Intel Corporation
*/
#include "rs.h"
#include "fw-api.h"
@@ -72,7 +72,7 @@ static u16 rs_fw_get_config_flags(struct iwl_mvm *mvm,
u16 flags = 0;
/* get STBC flags */
- if (mvm->cfg->ht_params->stbc &&
+ if (mvm->cfg->ht_params.stbc &&
(num_of_ant(iwl_mvm_get_valid_tx_ant(mvm)) > 1)) {
if (he_cap->has_he && he_cap->he_cap_elem.phy_cap_info[2] &
IEEE80211_HE_PHY_CAP2_STBC_RX_UNDER_80MHZ)
@@ -83,7 +83,7 @@ static u16 rs_fw_get_config_flags(struct iwl_mvm *mvm,
flags |= IWL_TLC_MNG_CFG_FLAGS_STBC_MSK;
}
- if (mvm->cfg->ht_params->ldpc &&
+ if (mvm->cfg->ht_params.ldpc &&
((ht_cap->cap & IEEE80211_HT_CAP_LDPC_CODING) ||
(vht_ena && (vht_cap->cap & IEEE80211_VHT_CAP_RXLDPC))))
flags |= IWL_TLC_MNG_CFG_FLAGS_LDPC_MSK;
@@ -454,22 +454,11 @@ void iwl_mvm_tlc_update_notif(struct iwl_mvm *mvm,
if (flags & IWL_TLC_NOTIF_FLAG_RATE) {
char pretty_rate[100];
- if (iwl_fw_lookup_notif_ver(mvm->fw, DATA_PATH_GROUP,
- TLC_MNG_UPDATE_NOTIF, 0) < 3) {
- rs_pretty_print_rate_v1(pretty_rate,
- sizeof(pretty_rate),
- le32_to_cpu(notif->rate));
- IWL_DEBUG_RATE(mvm,
- "Got rate in old format. Rate: %s. Converting.\n",
- pretty_rate);
- lq_sta->last_rate_n_flags =
- iwl_new_rate_from_v1(le32_to_cpu(notif->rate));
- } else {
- lq_sta->last_rate_n_flags = le32_to_cpu(notif->rate);
- }
+ lq_sta->last_rate_n_flags =
+ iwl_mvm_v3_rate_from_fw(notif->rate, mvm->fw_rates_ver);
rs_pretty_print_rate(pretty_rate, sizeof(pretty_rate),
lq_sta->last_rate_n_flags);
- IWL_DEBUG_RATE(mvm, "new rate: %s\n", pretty_rate);
+ IWL_DEBUG_RATE(mvm, "rate: %s\n", pretty_rate);
}
if (flags & IWL_TLC_NOTIF_FLAG_AMSDU && !mvm_link_sta->orig_amsdu_len) {
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rs.c b/drivers/net/wireless/intel/iwlwifi/mvm/rs.c
index a8c4e354e2ce..5802ed80a9ca 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/rs.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/rs.c
@@ -2,6 +2,7 @@
/******************************************************************************
*
* Copyright(c) 2005 - 2014, 2018 - 2023 Intel Corporation. All rights reserved.
+ * Copyright(c) 2025 Intel Corporation
* Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
* Copyright(c) 2016 - 2017 Intel Deutschland GmbH
*****************************************************************************/
@@ -895,7 +896,7 @@ static int rs_rate_from_ucode_rate(const u32 ucode_rate,
WARN_ON_ONCE(1);
}
} else if (ucode_rate & RATE_MCS_VHT_MSK_V1) {
- nss = FIELD_GET(RATE_MCS_NSS_MSK, ucode_rate) + 1;
+ nss = FIELD_GET(RATE_VHT_MCS_NSS_MSK, ucode_rate) + 1;
if (nss == 1) {
rate->type = LQ_VHT_SISO;
@@ -909,7 +910,7 @@ static int rs_rate_from_ucode_rate(const u32 ucode_rate,
WARN_ON_ONCE(1);
}
} else if (ucode_rate & RATE_MCS_HE_MSK_V1) {
- nss = FIELD_GET(RATE_MCS_NSS_MSK, ucode_rate) + 1;
+ nss = FIELD_GET(RATE_VHT_MCS_NSS_MSK, ucode_rate) + 1;
if (nss == 1) {
rate->type = LQ_HE_SISO;
@@ -1783,7 +1784,7 @@ static enum rs_action rs_get_rate_action(struct iwl_mvm *mvm,
if ((high_tpt != IWL_INVALID_VALUE) &&
(high_tpt > current_tpt)) {
IWL_DEBUG_RATE(mvm,
- "Higher rate is better. Increate rate\n");
+ "Higher rate is better. Increase rate\n");
return RS_ACTION_UPSCALE;
}
@@ -2696,8 +2697,10 @@ static void rs_drv_get_rate(void *mvm_r, struct ieee80211_sta *sta,
lq_sta = mvm_sta;
spin_lock_bh(&lq_sta->pers.lock);
- iwl_mvm_hwrate_to_tx_rate_v1(lq_sta->last_rate_n_flags,
- info->band, &info->control.rates[0]);
+ iwl_mvm_hwrate_to_tx_rate(iwl_mvm_v3_rate_from_fw(
+ cpu_to_le32(lq_sta->last_rate_n_flags),
+ 1),
+ info->band, &info->control.rates[0]);
info->control.rates[0].count = 1;
/* Report the optimal rate based on rssi and STA caps if we haven't
@@ -2707,8 +2710,12 @@ static void rs_drv_get_rate(void *mvm_r, struct ieee80211_sta *sta,
optimal_rate = rs_get_optimal_rate(mvm, lq_sta);
last_ucode_rate = ucode_rate_from_rs_rate(mvm,
optimal_rate);
- iwl_mvm_hwrate_to_tx_rate_v1(last_ucode_rate, info->band,
- &txrc->reported_rate);
+ last_ucode_rate =
+ iwl_mvm_v3_rate_from_fw(cpu_to_le32(last_ucode_rate),
+ 1);
+ iwl_mvm_hwrate_to_tx_rate(last_ucode_rate, info->band,
+ &txrc->reported_rate);
+ txrc->reported_rate.count = 1;
}
spin_unlock_bh(&lq_sta->pers.lock);
}
@@ -2813,11 +2820,11 @@ static void rs_ht_init(struct iwl_mvm *mvm,
lq_sta->active_mimo2_rate &= ~((u16)0x2);
lq_sta->active_mimo2_rate <<= IWL_FIRST_OFDM_RATE;
- if (mvm->cfg->ht_params->ldpc &&
+ if (mvm->cfg->ht_params.ldpc &&
(ht_cap->cap & IEEE80211_HT_CAP_LDPC_CODING))
lq_sta->ldpc = true;
- if (mvm->cfg->ht_params->stbc &&
+ if (mvm->cfg->ht_params.stbc &&
(num_of_ant(iwl_mvm_get_valid_tx_ant(mvm)) > 1) &&
(ht_cap->cap & IEEE80211_HT_CAP_RX_STBC))
lq_sta->stbc_capable = true;
@@ -2832,11 +2839,11 @@ static void rs_vht_init(struct iwl_mvm *mvm,
{
rs_vht_set_enabled_rates(sta, vht_cap, lq_sta);
- if (mvm->cfg->ht_params->ldpc &&
+ if (mvm->cfg->ht_params.ldpc &&
(vht_cap->cap & IEEE80211_VHT_CAP_RXLDPC))
lq_sta->ldpc = true;
- if (mvm->cfg->ht_params->stbc &&
+ if (mvm->cfg->ht_params.stbc &&
(num_of_ant(iwl_mvm_get_valid_tx_ant(mvm)) > 1) &&
(vht_cap->cap & IEEE80211_VHT_CAP_RXSTBC_MASK))
lq_sta->stbc_capable = true;
@@ -2887,10 +2894,10 @@ void iwl_mvm_update_frame_stats(struct iwl_mvm *mvm, u32 rate, bool agg)
if (rate & RATE_MCS_HT_MSK_V1) {
mvm->drv_rx_stats.ht_frames++;
- nss = ((rate & RATE_HT_MCS_NSS_MSK_V1) >> RATE_HT_MCS_NSS_POS_V1) + 1;
+ nss = FIELD_GET(RATE_HT_MCS_MIMO2_MSK, rate) + 1;
} else if (rate & RATE_MCS_VHT_MSK_V1) {
mvm->drv_rx_stats.vht_frames++;
- nss = FIELD_GET(RATE_MCS_NSS_MSK, rate) + 1;
+ nss = FIELD_GET(RATE_VHT_MCS_NSS_MSK, rate) + 1;
} else {
mvm->drv_rx_stats.legacy_frames++;
}
@@ -3304,7 +3311,7 @@ static void rs_build_rates_table_from_fixed(struct iwl_mvm *mvm,
if (num_of_ant(ant) == 1)
lq_cmd->single_stream_ant_msk = ant;
- if (!mvm->trans->trans_cfg->gen2)
+ if (!mvm->trans->mac_cfg->gen2)
lq_cmd->agg_frame_cnt_limit = LINK_QUAL_AGG_FRAME_LIMIT_DEF;
else
lq_cmd->agg_frame_cnt_limit =
@@ -3673,16 +3680,15 @@ int rs_pretty_print_rate_v1(char *buf, int bufsz, const u32 rate)
if (rate & RATE_MCS_VHT_MSK_V1) {
type = "VHT";
mcs = rate & RATE_VHT_MCS_RATE_CODE_MSK;
- nss = FIELD_GET(RATE_MCS_NSS_MSK, rate) + 1;
+ nss = FIELD_GET(RATE_VHT_MCS_NSS_MSK, rate) + 1;
} else if (rate & RATE_MCS_HT_MSK_V1) {
type = "HT";
mcs = rate & RATE_HT_MCS_INDEX_MSK_V1;
- nss = ((rate & RATE_HT_MCS_NSS_MSK_V1)
- >> RATE_HT_MCS_NSS_POS_V1) + 1;
+ nss = FIELD_GET(RATE_HT_MCS_MIMO2_MSK, rate) + 1;
} else if (rate & RATE_MCS_HE_MSK_V1) {
type = "HE";
mcs = rate & RATE_VHT_MCS_RATE_CODE_MSK;
- nss = FIELD_GET(RATE_MCS_NSS_MSK, rate) + 1;
+ nss = FIELD_GET(RATE_VHT_MCS_NSS_MSK, rate) + 1;
} else {
type = "Unknown"; /* shouldn't happen */
}
@@ -4172,3 +4178,167 @@ int iwl_mvm_tx_protection(struct iwl_mvm *mvm, struct iwl_mvm_sta *mvmsta,
else
return rs_drv_tx_protection(mvm, mvmsta, enable);
}
+
+static u32 iwl_legacy_rate_to_fw_idx(u32 rate_n_flags)
+{
+ int rate = rate_n_flags & RATE_LEGACY_RATE_MSK_V1;
+ int idx;
+ bool ofdm = !(rate_n_flags & RATE_MCS_CCK_MSK_V1);
+ int offset = ofdm ? IWL_FIRST_OFDM_RATE : 0;
+ int last = ofdm ? IWL_RATE_COUNT_LEGACY : IWL_FIRST_OFDM_RATE;
+
+ for (idx = offset; idx < last; idx++)
+ if (iwl_fw_rate_idx_to_plcp(idx) == rate)
+ return idx - offset;
+ return IWL_RATE_INVALID;
+}
+
+u32 iwl_mvm_v3_rate_from_fw(__le32 rate, u8 rate_ver)
+{
+ u32 rate_v3 = 0, rate_v1;
+ u32 dup = 0;
+
+ if (rate_ver > 1)
+ return iwl_v3_rate_from_v2_v3(rate, rate_ver >= 3);
+
+ rate_v1 = le32_to_cpu(rate);
+ if (rate_v1 == 0)
+ return rate_v1;
+ /* convert rate */
+ if (rate_v1 & RATE_MCS_HT_MSK_V1) {
+ u32 nss;
+
+ rate_v3 |= RATE_MCS_MOD_TYPE_HT;
+ rate_v3 |=
+ rate_v1 & RATE_HT_MCS_RATE_CODE_MSK_V1;
+ nss = u32_get_bits(rate_v1, RATE_HT_MCS_MIMO2_MSK);
+ rate_v3 |= u32_encode_bits(nss, RATE_MCS_NSS_MSK);
+ } else if (rate_v1 & RATE_MCS_VHT_MSK_V1 ||
+ rate_v1 & RATE_MCS_HE_MSK_V1) {
+ u32 nss = u32_get_bits(rate_v1, RATE_VHT_MCS_NSS_MSK);
+
+ rate_v3 |= rate_v1 & RATE_VHT_MCS_RATE_CODE_MSK;
+
+ rate_v3 |= u32_encode_bits(nss, RATE_MCS_NSS_MSK);
+
+ if (rate_v1 & RATE_MCS_HE_MSK_V1) {
+ u32 he_type_bits = rate_v1 & RATE_MCS_HE_TYPE_MSK_V1;
+ u32 he_type = he_type_bits >> RATE_MCS_HE_TYPE_POS_V1;
+ u32 he_106t = (rate_v1 & RATE_MCS_HE_106T_MSK_V1) >>
+ RATE_MCS_HE_106T_POS_V1;
+ u32 he_gi_ltf = (rate_v1 & RATE_MCS_HE_GI_LTF_MSK_V1) >>
+ RATE_MCS_HE_GI_LTF_POS;
+
+ if ((he_type_bits == RATE_MCS_HE_TYPE_SU ||
+ he_type_bits == RATE_MCS_HE_TYPE_EXT_SU) &&
+ he_gi_ltf == RATE_MCS_HE_SU_4_LTF)
+ /* the new rate have an additional bit to
+ * represent the value 4 rather then using SGI
+ * bit for this purpose - as it was done in the
+ * old rate
+ */
+ he_gi_ltf += (rate_v1 & RATE_MCS_SGI_MSK_V1) >>
+ RATE_MCS_SGI_POS_V1;
+
+ rate_v3 |= he_gi_ltf << RATE_MCS_HE_GI_LTF_POS;
+ rate_v3 |= he_type << RATE_MCS_HE_TYPE_POS;
+ rate_v3 |= he_106t << RATE_MCS_HE_106T_POS;
+ rate_v3 |= rate_v1 & RATE_HE_DUAL_CARRIER_MODE_MSK;
+ rate_v3 |= RATE_MCS_MOD_TYPE_HE;
+ } else {
+ rate_v3 |= RATE_MCS_MOD_TYPE_VHT;
+ }
+ /* if legacy format */
+ } else {
+ u32 legacy_rate = iwl_legacy_rate_to_fw_idx(rate_v1);
+
+ if (WARN_ON_ONCE(legacy_rate == IWL_RATE_INVALID))
+ legacy_rate = (rate_v1 & RATE_MCS_CCK_MSK_V1) ?
+ IWL_FIRST_CCK_RATE : IWL_FIRST_OFDM_RATE;
+
+ rate_v3 |= legacy_rate;
+ if (!(rate_v1 & RATE_MCS_CCK_MSK_V1))
+ rate_v3 |= RATE_MCS_MOD_TYPE_LEGACY_OFDM;
+ }
+
+ /* convert flags */
+ if (rate_v1 & RATE_MCS_LDPC_MSK_V1)
+ rate_v3 |= RATE_MCS_LDPC_MSK;
+ rate_v3 |= (rate_v1 & RATE_MCS_CHAN_WIDTH_MSK_V1) |
+ (rate_v1 & RATE_MCS_ANT_AB_MSK) |
+ (rate_v1 & RATE_MCS_STBC_MSK) |
+ (rate_v1 & RATE_MCS_BF_MSK);
+
+ dup = (rate_v1 & RATE_MCS_DUP_MSK_V1) >> RATE_MCS_DUP_POS_V1;
+ if (dup) {
+ rate_v3 |= RATE_MCS_DUP_MSK;
+ rate_v3 |= dup << RATE_MCS_CHAN_WIDTH_POS;
+ }
+
+ if ((!(rate_v1 & RATE_MCS_HE_MSK_V1)) &&
+ (rate_v1 & RATE_MCS_SGI_MSK_V1))
+ rate_v3 |= RATE_MCS_SGI_MSK;
+
+ return rate_v3;
+}
+
+__le32 iwl_mvm_v3_rate_to_fw(u32 rate, u8 rate_ver)
+{
+ u32 result = 0;
+ int rate_idx;
+
+ if (rate_ver > 1)
+ return iwl_v3_rate_to_v2_v3(rate, rate_ver > 2);
+
+ switch (rate & RATE_MCS_MOD_TYPE_MSK) {
+ case RATE_MCS_MOD_TYPE_CCK:
+ result = RATE_MCS_CCK_MSK_V1;
+ fallthrough;
+ case RATE_MCS_MOD_TYPE_LEGACY_OFDM:
+ rate_idx = u32_get_bits(rate, RATE_LEGACY_RATE_MSK);
+ if (!(result & RATE_MCS_CCK_MSK_V1))
+ rate_idx += IWL_FIRST_OFDM_RATE;
+ result |= u32_encode_bits(iwl_fw_rate_idx_to_plcp(rate_idx),
+ RATE_LEGACY_RATE_MSK_V1);
+ break;
+ case RATE_MCS_MOD_TYPE_HT:
+ result = RATE_MCS_HT_MSK_V1;
+ result |= u32_encode_bits(u32_get_bits(rate,
+ RATE_HT_MCS_CODE_MSK),
+ RATE_HT_MCS_RATE_CODE_MSK_V1);
+ result |= u32_encode_bits(u32_get_bits(rate,
+ RATE_MCS_NSS_MSK),
+ RATE_HT_MCS_MIMO2_MSK);
+ break;
+ case RATE_MCS_MOD_TYPE_VHT:
+ result = RATE_MCS_VHT_MSK_V1;
+ result |= u32_encode_bits(u32_get_bits(rate,
+ RATE_VHT_MCS_NSS_MSK),
+ RATE_MCS_CODE_MSK);
+ result |= u32_encode_bits(u32_get_bits(rate, RATE_MCS_NSS_MSK),
+ RATE_VHT_MCS_NSS_MSK);
+ break;
+ case RATE_MCS_MOD_TYPE_HE: /* not generated */
+ default:
+ WARN_ONCE(1, "bad modulation type %d\n",
+ u32_get_bits(rate, RATE_MCS_MOD_TYPE_MSK));
+ return 0;
+ }
+
+ if (rate & RATE_MCS_LDPC_MSK)
+ result |= RATE_MCS_LDPC_MSK_V1;
+ WARN_ON_ONCE(u32_get_bits(rate, RATE_MCS_CHAN_WIDTH_MSK) >
+ RATE_MCS_CHAN_WIDTH_160_VAL);
+ result |= (rate & RATE_MCS_CHAN_WIDTH_MSK_V1) |
+ (rate & RATE_MCS_ANT_AB_MSK) |
+ (rate & RATE_MCS_STBC_MSK) |
+ (rate & RATE_MCS_BF_MSK);
+
+ /* not handling DUP since we don't use it */
+ WARN_ON_ONCE(rate & RATE_MCS_DUP_MSK);
+
+ if (rate & RATE_MCS_SGI_MSK)
+ result |= RATE_MCS_SGI_MSK_V1;
+
+ return cpu_to_le32(result);
+}
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rs.h b/drivers/net/wireless/intel/iwlwifi/mvm/rs.h
index ea81cb236d5c..69259ebb966b 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/rs.h
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/rs.h
@@ -3,7 +3,7 @@
*
* Copyright(c) 2015 Intel Mobile Communications GmbH
* Copyright(c) 2017 Intel Deutschland GmbH
- * Copyright (C) 2003 - 2014, 2018 - 2024 Intel Corporation
+ * Copyright (C) 2003 - 2014, 2018 - 2025 Intel Corporation
*****************************************************************************/
#ifndef __rs_h__
@@ -424,6 +424,9 @@ void iwl_mvm_rate_control_unregister(void);
struct iwl_mvm_sta;
+u32 iwl_mvm_v3_rate_from_fw(__le32 rate, u8 rate_ver);
+__le32 iwl_mvm_v3_rate_to_fw(u32 rate, u8 rate_ver);
+
int iwl_mvm_tx_protection(struct iwl_mvm *mvm, struct iwl_mvm_sta *mvmsta,
bool enable);
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rx.c b/drivers/net/wireless/intel/iwlwifi/mvm/rx.c
index 2dbef7b46355..8eb0aa448c85 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/rx.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/rx.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
/*
- * Copyright (C) 2012-2014, 2018-2024 Intel Corporation
+ * Copyright (C) 2012-2014, 2018-2025 Intel Corporation
* Copyright (C) 2013-2015 Intel Mobile Communications GmbH
* Copyright (C) 2016-2017 Intel Deutschland GmbH
*/
@@ -490,8 +490,6 @@ void iwl_mvm_rx_rx_mpdu(struct iwl_mvm *mvm, struct napi_struct *napi,
if (!(rate_n_flags & RATE_MCS_CCK_MSK_V1) &&
rate_n_flags & RATE_MCS_SGI_MSK_V1)
rx_status->enc_flags |= RX_ENC_FLAG_SHORT_GI;
- if (rate_n_flags & RATE_HT_MCS_GF_MSK)
- rx_status->enc_flags |= RX_ENC_FLAG_HT_GF;
if (rate_n_flags & RATE_MCS_LDPC_MSK_V1)
rx_status->enc_flags |= RX_ENC_FLAG_LDPC;
if (rate_n_flags & RATE_MCS_HT_MSK_V1) {
@@ -1001,7 +999,7 @@ static void iwl_mvm_update_esr_mode_tpt(struct iwl_mvm *mvm)
sec_link = mvmvif->link[sec_link]->fw_link_id;
/* Sum up RX and TX MPDUs from the different queues/links */
- for (int q = 0; q < mvm->trans->num_rx_queues; q++) {
+ for (int q = 0; q < mvm->trans->info.num_rxqs; q++) {
spin_lock_bh(&mvmsta->mpdu_counters[q].lock);
/* The link IDs that doesn't exist will contain 0 */
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c b/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c
index 09fd8752046e..077aadbf95db 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
/*
- * Copyright (C) 2012-2014, 2018-2024 Intel Corporation
+ * Copyright (C) 2012-2014, 2018-2025 Intel Corporation
* Copyright (C) 2013-2015 Intel Mobile Communications GmbH
* Copyright (C) 2015-2017 Intel Deutschland GmbH
*/
@@ -171,7 +171,7 @@ static int iwl_mvm_create_skb(struct iwl_mvm *mvm, struct sk_buff *skb,
shdr->type != htons(ETH_P_PAE) &&
shdr->type != htons(ETH_P_TDLS))))
skb->ip_summed = CHECKSUM_NONE;
- else if (mvm->trans->trans_cfg->device_family < IWL_DEVICE_FAMILY_BZ)
+ else if (mvm->trans->mac_cfg->device_family < IWL_DEVICE_FAMILY_BZ)
/* mac80211 assumes full CSUM including SNAP header */
skb_postpush_rcsum(skb, shdr, sizeof(*shdr));
}
@@ -409,7 +409,7 @@ static int iwl_mvm_rx_crypto(struct iwl_mvm *mvm, struct ieee80211_sta *sta,
!(status & IWL_RX_MPDU_RES_STATUS_TTAK_OK))
return 0;
- if (mvm->trans->trans_cfg->gen2 &&
+ if (mvm->trans->mac_cfg->gen2 &&
!(status & RX_MPDU_RES_STATUS_MIC_OK))
stats->flag |= RX_FLAG_MMIC_ERROR;
@@ -426,7 +426,7 @@ static int iwl_mvm_rx_crypto(struct iwl_mvm *mvm, struct ieee80211_sta *sta,
if (pkt_flags & FH_RSCSR_RADA_EN) {
stats->flag |= RX_FLAG_ICV_STRIPPED;
- if (mvm->trans->trans_cfg->gen2)
+ if (mvm->trans->mac_cfg->gen2)
stats->flag |= RX_FLAG_MMIC_STRIPPED;
}
@@ -462,7 +462,7 @@ static void iwl_mvm_rx_csum(struct iwl_mvm *mvm,
{
struct iwl_rx_mpdu_desc *desc = (void *)pkt->data;
- if (mvm->trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_AX210) {
+ if (mvm->trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_AX210) {
if (pkt->len_n_flags & cpu_to_le32(FH_RSCSR_RPA_EN)) {
u16 hwsum = be16_to_cpu(desc->v3.raw_xsum);
@@ -744,7 +744,7 @@ static bool iwl_mvm_reorder(struct iwl_mvm *mvm,
baid = (reorder & IWL_RX_MPDU_REORDER_BAID_MASK) >>
IWL_RX_MPDU_REORDER_BAID_SHIFT;
- if (mvm->trans->trans_cfg->device_family == IWL_DEVICE_FAMILY_9000)
+ if (mvm->trans->mac_cfg->device_family == IWL_DEVICE_FAMILY_9000)
return false;
/*
@@ -995,7 +995,7 @@ iwl_mvm_decode_he_phy_ru_alloc(struct iwl_mvm_rx_phy_data *phy_data,
*/
u8 ru = le32_get_bits(phy_data->d1, IWL_RX_PHY_DATA1_HE_RU_ALLOC_MASK);
u32 rate_n_flags = phy_data->rate_n_flags;
- u32 he_type = rate_n_flags & RATE_MCS_HE_TYPE_MSK_V1;
+ u32 he_type = rate_n_flags & RATE_MCS_HE_TYPE_MSK;
u8 offs = 0;
rx_status->bw = RATE_INFO_BW_HE_RU;
@@ -1050,13 +1050,13 @@ iwl_mvm_decode_he_phy_ru_alloc(struct iwl_mvm_rx_phy_data *phy_data,
if (he_mu)
he_mu->flags2 |=
- le16_encode_bits(FIELD_GET(RATE_MCS_CHAN_WIDTH_MSK_V1,
+ le16_encode_bits(FIELD_GET(RATE_MCS_CHAN_WIDTH_MSK,
rate_n_flags),
IEEE80211_RADIOTAP_HE_MU_FLAGS2_BW_FROM_SIG_A_BW);
- else if (he_type == RATE_MCS_HE_TYPE_TRIG_V1)
+ else if (he_type == RATE_MCS_HE_TYPE_TRIG)
he->data6 |=
cpu_to_le16(IEEE80211_RADIOTAP_HE_DATA6_TB_PPDU_BW_KNOWN) |
- le16_encode_bits(FIELD_GET(RATE_MCS_CHAN_WIDTH_MSK_V1,
+ le16_encode_bits(FIELD_GET(RATE_MCS_CHAN_WIDTH_MSK,
rate_n_flags),
IEEE80211_RADIOTAP_HE_DATA6_TB_PPDU_BW);
}
@@ -1944,7 +1944,7 @@ static void iwl_mvm_rx_fill_status(struct iwl_mvm *mvm,
}
/* must be before L-SIG data */
- if (format == RATE_MCS_HE_MSK)
+ if (format == RATE_MCS_MOD_TYPE_HE)
iwl_mvm_rx_he(mvm, skb, phy_data, queue);
iwl_mvm_decode_lsig(skb, phy_data);
@@ -1966,45 +1966,45 @@ static void iwl_mvm_rx_fill_status(struct iwl_mvm *mvm,
phy_data->energy_a, phy_data->energy_b);
/* using TLV format and must be after all fixed len fields */
- if (format == RATE_MCS_EHT_MSK)
+ if (format == RATE_MCS_MOD_TYPE_EHT)
iwl_mvm_rx_eht(mvm, skb, phy_data, queue);
if (unlikely(mvm->monitor_on))
iwl_mvm_add_rtap_sniffer_config(mvm, skb);
- is_sgi = format == RATE_MCS_HE_MSK ?
+ is_sgi = format == RATE_MCS_MOD_TYPE_HE ?
iwl_he_is_sgi(rate_n_flags) :
rate_n_flags & RATE_MCS_SGI_MSK;
- if (!(format == RATE_MCS_CCK_MSK) && is_sgi)
+ if (!(format == RATE_MCS_MOD_TYPE_CCK) && is_sgi)
rx_status->enc_flags |= RX_ENC_FLAG_SHORT_GI;
if (rate_n_flags & RATE_MCS_LDPC_MSK)
rx_status->enc_flags |= RX_ENC_FLAG_LDPC;
switch (format) {
- case RATE_MCS_VHT_MSK:
+ case RATE_MCS_MOD_TYPE_VHT:
rx_status->encoding = RX_ENC_VHT;
break;
- case RATE_MCS_HE_MSK:
+ case RATE_MCS_MOD_TYPE_HE:
rx_status->encoding = RX_ENC_HE;
rx_status->he_dcm =
!!(rate_n_flags & RATE_HE_DUAL_CARRIER_MODE_MSK);
break;
- case RATE_MCS_EHT_MSK:
+ case RATE_MCS_MOD_TYPE_EHT:
rx_status->encoding = RX_ENC_EHT;
break;
}
switch (format) {
- case RATE_MCS_HT_MSK:
+ case RATE_MCS_MOD_TYPE_HT:
rx_status->encoding = RX_ENC_HT;
rx_status->rate_idx = RATE_HT_MCS_INDEX(rate_n_flags);
rx_status->enc_flags |= stbc << RX_ENC_FLAG_STBC_SHIFT;
break;
- case RATE_MCS_VHT_MSK:
- case RATE_MCS_HE_MSK:
- case RATE_MCS_EHT_MSK:
+ case RATE_MCS_MOD_TYPE_VHT:
+ case RATE_MCS_MOD_TYPE_HE:
+ case RATE_MCS_MOD_TYPE_EHT:
rx_status->nss =
u32_get_bits(rate_n_flags, RATE_MCS_NSS_MSK) + 1;
rx_status->rate_idx = rate_n_flags & RATE_MCS_CODE_MSK;
@@ -2048,7 +2048,7 @@ void iwl_mvm_rx_mpdu_mq(struct iwl_mvm *mvm, struct napi_struct *napi,
if (unlikely(test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)))
return;
- if (mvm->trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_AX210)
+ if (mvm->trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_AX210)
desc_size = sizeof(*desc);
else
desc_size = IWL_RX_DESC_SIZE_V1;
@@ -2058,8 +2058,10 @@ void iwl_mvm_rx_mpdu_mq(struct iwl_mvm *mvm, struct napi_struct *napi,
return;
}
- if (mvm->trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_AX210) {
- phy_data.rate_n_flags = le32_to_cpu(desc->v3.rate_n_flags);
+ if (mvm->trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_AX210) {
+ phy_data.rate_n_flags =
+ iwl_mvm_v3_rate_from_fw(desc->v3.rate_n_flags,
+ mvm->fw_rates_ver);
phy_data.channel = desc->v3.channel;
phy_data.gp2_on_air_rise = le32_to_cpu(desc->v3.gp2_on_air_rise);
phy_data.energy_a = desc->v3.energy_a;
@@ -2072,7 +2074,9 @@ void iwl_mvm_rx_mpdu_mq(struct iwl_mvm *mvm, struct napi_struct *napi,
phy_data.eht_d4 = desc->phy_eht_data4;
phy_data.d5 = desc->v3.phy_data5;
} else {
- phy_data.rate_n_flags = le32_to_cpu(desc->v1.rate_n_flags);
+ phy_data.rate_n_flags =
+ iwl_mvm_v3_rate_from_fw(desc->v1.rate_n_flags,
+ mvm->fw_rates_ver);
phy_data.channel = desc->v1.channel;
phy_data.gp2_on_air_rise = le32_to_cpu(desc->v1.gp2_on_air_rise);
phy_data.energy_a = desc->v1.energy_a;
@@ -2084,13 +2088,6 @@ void iwl_mvm_rx_mpdu_mq(struct iwl_mvm *mvm, struct napi_struct *napi,
phy_data.d3 = desc->v1.phy_data3;
}
- if (iwl_fw_lookup_notif_ver(mvm->fw, LEGACY_GROUP,
- REPLY_RX_MPDU_CMD, 0) < 4) {
- phy_data.rate_n_flags = iwl_new_rate_from_v1(phy_data.rate_n_flags);
- IWL_DEBUG_DROP(mvm, "Got old format rate, converting. New rate: 0x%x\n",
- phy_data.rate_n_flags);
- }
-
format = phy_data.rate_n_flags & RATE_MCS_MOD_TYPE_MSK;
len = le16_to_cpu(desc->mpdu_len);
@@ -2138,14 +2135,14 @@ void iwl_mvm_rx_mpdu_mq(struct iwl_mvm *mvm, struct napi_struct *napi,
}
/* set the preamble flag if appropriate */
- if (format == RATE_MCS_CCK_MSK &&
+ if (format == RATE_MCS_MOD_TYPE_CCK &&
phy_data.phy_info & IWL_RX_MPDU_PHY_SHORT_PREAMBLE)
rx_status->enc_flags |= RX_ENC_FLAG_SHORTPRE;
if (likely(!(phy_data.phy_info & IWL_RX_MPDU_PHY_TSF_OVERLOAD))) {
u64 tsf_on_air_rise;
- if (mvm->trans->trans_cfg->device_family >=
+ if (mvm->trans->mac_cfg->device_family >=
IWL_DEVICE_FAMILY_AX210)
tsf_on_air_rise = le64_to_cpu(desc->v3.tsf_on_air_rise);
else
@@ -2157,7 +2154,8 @@ void iwl_mvm_rx_mpdu_mq(struct iwl_mvm *mvm, struct napi_struct *napi,
}
if (iwl_mvm_is_band_in_rx_supported(mvm)) {
- u8 band = BAND_IN_RX_STATUS(desc->mac_phy_idx);
+ u8 band = u8_get_bits(desc->mac_phy_band,
+ IWL_RX_MPDU_MAC_PHY_BAND_BAND_MASK);
rx_status->band = iwl_mvm_nl80211_band_from_phy(band);
} else {
@@ -2303,7 +2301,7 @@ void iwl_mvm_rx_mpdu_mq(struct iwl_mvm *mvm, struct napi_struct *napi,
*qc &= ~IEEE80211_QOS_CTL_A_MSDU_PRESENT;
- if (mvm->trans->trans_cfg->device_family ==
+ if (mvm->trans->mac_cfg->device_family ==
IWL_DEVICE_FAMILY_9000) {
iwl_mvm_flip_address(hdr->addr3);
@@ -2349,7 +2347,7 @@ void iwl_mvm_rx_mpdu_mq(struct iwl_mvm *mvm, struct napi_struct *napi,
if (!iwl_mvm_reorder(mvm, napi, queue, sta, skb, desc) &&
likely(!iwl_mvm_time_sync_frame(mvm, skb, hdr->addr2)) &&
likely(!iwl_mvm_mei_filter_scan(mvm, skb))) {
- if (mvm->trans->trans_cfg->device_family == IWL_DEVICE_FAMILY_9000 &&
+ if (mvm->trans->mac_cfg->device_family == IWL_DEVICE_FAMILY_9000 &&
(desc->mac_flags2 & IWL_RX_MPDU_MFLG2_AMSDU) &&
!(desc->amsdu_info & IWL_RX_MPDU_AMSDU_LAST_SUBFRAME))
rx_status->flag |= RX_FLAG_AMSDU_MORE;
@@ -2383,7 +2381,6 @@ void iwl_mvm_rx_monitor_no_data(struct iwl_mvm *mvm, struct napi_struct *napi,
phy_data.d1 = desc->phy_info[1];
phy_data.phy_info = IWL_RX_MPDU_PHY_TSF_OVERLOAD;
phy_data.gp2_on_air_rise = le32_to_cpu(desc->on_air_rise_time);
- phy_data.rate_n_flags = le32_to_cpu(desc->rate);
phy_data.energy_a = u32_get_bits(rssi, RX_NO_DATA_CHAIN_A_MSK);
phy_data.energy_b = u32_get_bits(rssi, RX_NO_DATA_CHAIN_B_MSK);
phy_data.channel = u32_get_bits(rssi, RX_NO_DATA_CHANNEL_MSK);
@@ -2391,14 +2388,8 @@ void iwl_mvm_rx_monitor_no_data(struct iwl_mvm *mvm, struct napi_struct *napi,
phy_data.rx_vec[0] = desc->rx_vec[0];
phy_data.rx_vec[1] = desc->rx_vec[1];
- if (iwl_fw_lookup_notif_ver(mvm->fw, DATA_PATH_GROUP,
- RX_NO_DATA_NOTIF, 0) < 2) {
- IWL_DEBUG_DROP(mvm, "Got an old rate format. Old rate: 0x%x\n",
- phy_data.rate_n_flags);
- phy_data.rate_n_flags = iwl_new_rate_from_v1(phy_data.rate_n_flags);
- IWL_DEBUG_DROP(mvm, " Rate after conversion to the new format: 0x%x\n",
- phy_data.rate_n_flags);
- }
+ phy_data.rate_n_flags = iwl_mvm_v3_rate_from_fw(desc->rate,
+ mvm->fw_rates_ver);
format = phy_data.rate_n_flags & RATE_MCS_MOD_TYPE_MSK;
@@ -2411,7 +2402,7 @@ void iwl_mvm_rx_monitor_no_data(struct iwl_mvm *mvm, struct napi_struct *napi,
phy_data.rx_vec[2] = desc->rx_vec[2];
phy_data.rx_vec[3] = desc->rx_vec[3];
} else {
- if (format == RATE_MCS_EHT_MSK)
+ if (format == RATE_MCS_MOD_TYPE_EHT)
/* no support for EHT before version 3 API */
return;
}
@@ -2472,17 +2463,17 @@ void iwl_mvm_rx_monitor_no_data(struct iwl_mvm *mvm, struct napi_struct *napi,
* may be up to 8 spatial streams.
*/
switch (format) {
- case RATE_MCS_VHT_MSK:
+ case RATE_MCS_MOD_TYPE_VHT:
rx_status->nss =
le32_get_bits(desc->rx_vec[0],
RX_NO_DATA_RX_VEC0_VHT_NSTS_MSK) + 1;
break;
- case RATE_MCS_HE_MSK:
+ case RATE_MCS_MOD_TYPE_HE:
rx_status->nss =
le32_get_bits(desc->rx_vec[0],
RX_NO_DATA_RX_VEC0_HE_NSTS_MSK) + 1;
break;
- case RATE_MCS_EHT_MSK:
+ case RATE_MCS_MOD_TYPE_EHT:
rx_status->nss =
le32_get_bits(desc->rx_vec[2],
RX_NO_DATA_RX_VEC2_EHT_NSTS_MSK) + 1;
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/sta.c b/drivers/net/wireless/intel/iwlwifi/mvm/sta.c
index 7a4844ec3c10..8ec4a007b4b0 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/sta.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/sta.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
/*
- * Copyright (C) 2012-2015, 2018-2024 Intel Corporation
+ * Copyright (C) 2012-2015, 2018-2025 Intel Corporation
* Copyright (C) 2013-2015 Intel Mobile Communications GmbH
* Copyright (C) 2016-2017 Intel Deutschland GmbH
*/
@@ -791,10 +791,10 @@ static int iwl_mvm_find_free_queue(struct iwl_mvm *mvm, u8 sta_id,
lockdep_assert_held(&mvm->mutex);
- if (WARN(maxq >= mvm->trans->trans_cfg->base_params->num_of_queues,
+ if (WARN(maxq >= mvm->trans->mac_cfg->base->num_of_queues,
"max queue %d >= num_of_queues (%d)", maxq,
- mvm->trans->trans_cfg->base_params->num_of_queues))
- maxq = mvm->trans->trans_cfg->base_params->num_of_queues - 1;
+ mvm->trans->mac_cfg->base->num_of_queues))
+ maxq = mvm->trans->mac_cfg->base->num_of_queues - 1;
/* This should not be hit with new TX path */
if (WARN_ON(iwl_mvm_has_new_tx_api(mvm)))
@@ -852,7 +852,7 @@ int iwl_mvm_tvqm_enable_txq(struct iwl_mvm *mvm,
if (tid == IWL_MAX_TID_COUNT) {
tid = IWL_MGMT_TID;
size = max_t(u32, IWL_MGMT_QUEUE_SIZE,
- mvm->trans->cfg->min_txq_size);
+ mvm->trans->mac_cfg->base->min_txq_size);
} else {
size = iwl_mvm_get_queue_size(sta);
}
@@ -1765,7 +1765,7 @@ int iwl_mvm_sta_init(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
mvm_sta->deflink.sta_id = sta_id;
rcu_assign_pointer(mvm_sta->link[0], &mvm_sta->deflink);
- if (!mvm->trans->trans_cfg->gen2)
+ if (!mvm->trans->mac_cfg->gen2)
mvm_sta->deflink.lq_sta.rs_drv.pers.max_agg_bufsize =
LINK_QUAL_AGG_FRAME_LIMIT_DEF;
else
@@ -1798,7 +1798,7 @@ int iwl_mvm_sta_init(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
if (iwl_mvm_has_new_rx_api(mvm)) {
int q;
- dup_data = kcalloc(mvm->trans->num_rx_queues,
+ dup_data = kcalloc(mvm->trans->info.num_rxqs,
sizeof(*dup_data), GFP_KERNEL);
if (!dup_data)
return -ENOMEM;
@@ -1811,7 +1811,7 @@ int iwl_mvm_sta_init(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
* This thus allows receiving a packet with seqno 0 and the
* retry bit set as the very first packet on a new TID.
*/
- for (q = 0; q < mvm->trans->num_rx_queues; q++)
+ for (q = 0; q < mvm->trans->info.num_rxqs; q++)
memset(dup_data[q].last_seq, 0xff,
sizeof(dup_data[q].last_seq));
mvm_sta->dup_data = dup_data;
@@ -1839,11 +1839,11 @@ int iwl_mvm_sta_init(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
if (vif->type == NL80211_IFTYPE_STATION && !vif->p2p &&
!sta->tdls && ieee80211_vif_is_mld(vif)) {
mvm_sta->mpdu_counters =
- kcalloc(mvm->trans->num_rx_queues,
+ kcalloc(mvm->trans->info.num_rxqs,
sizeof(*mvm_sta->mpdu_counters),
GFP_KERNEL);
if (mvm_sta->mpdu_counters)
- for (int q = 0; q < mvm->trans->num_rx_queues; q++)
+ for (int q = 0; q < mvm->trans->info.num_rxqs; q++)
spin_lock_init(&mvm_sta->mpdu_counters[q].lock);
}
@@ -2189,7 +2189,7 @@ static void iwl_mvm_enable_aux_snif_queue(struct iwl_mvm *mvm, u16 queue,
u8 sta_id, u8 fifo)
{
unsigned int wdg_timeout =
- mvm->trans->trans_cfg->base_params->wd_timeout;
+ mvm->trans->mac_cfg->base->wd_timeout;
struct iwl_trans_txq_scd_cfg cfg = {
.fifo = fifo,
.sta_id = sta_id,
@@ -2206,7 +2206,7 @@ static void iwl_mvm_enable_aux_snif_queue(struct iwl_mvm *mvm, u16 queue,
static int iwl_mvm_enable_aux_snif_queue_tvqm(struct iwl_mvm *mvm, u8 sta_id)
{
unsigned int wdg_timeout =
- mvm->trans->trans_cfg->base_params->wd_timeout;
+ mvm->trans->mac_cfg->base->wd_timeout;
WARN_ON(!iwl_mvm_has_new_tx_api(mvm));
@@ -2717,7 +2717,7 @@ static void iwl_mvm_free_reorder(struct iwl_mvm *mvm,
iwl_mvm_sync_rxq_del_ba(mvm, data->baid);
- for (i = 0; i < mvm->trans->num_rx_queues; i++) {
+ for (i = 0; i < mvm->trans->info.num_rxqs; i++) {
int j;
struct iwl_mvm_reorder_buffer *reorder_buf =
&data->reorder_buf[i];
@@ -2750,7 +2750,7 @@ static void iwl_mvm_init_reorder_buffer(struct iwl_mvm *mvm,
{
int i;
- for (i = 0; i < mvm->trans->num_rx_queues; i++) {
+ for (i = 0; i < mvm->trans->info.num_rxqs; i++) {
struct iwl_mvm_reorder_buffer *reorder_buf =
&data->reorder_buf[i];
struct iwl_mvm_reorder_buf_entry *entries =
@@ -2925,7 +2925,7 @@ int iwl_mvm_sta_rx_agg(struct iwl_mvm *mvm, struct ieee80211_sta *sta,
* before starting the BA session in the firmware
*/
baid_data = kzalloc(sizeof(*baid_data) +
- mvm->trans->num_rx_queues *
+ mvm->trans->info.num_rxqs *
reorder_buf_size,
GFP_KERNEL);
if (!baid_data)
@@ -3177,7 +3177,7 @@ int iwl_mvm_sta_tx_agg_start(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
* to align the wrap around of ssn so we compare relevant values.
*/
normalized_ssn = tid_data->ssn;
- if (mvm->trans->trans_cfg->gen2)
+ if (mvm->trans->mac_cfg->gen2)
normalized_ssn &= 0xff;
if (normalized_ssn == tid_data->next_reclaimed) {
@@ -4305,73 +4305,12 @@ u16 iwl_mvm_tid_queued(struct iwl_mvm *mvm, struct iwl_mvm_tid_data *tid_data)
* In 22000 HW, the next_reclaimed index is only 8 bit, so we'll need
* to align the wrap around of ssn so we compare relevant values.
*/
- if (mvm->trans->trans_cfg->gen2)
+ if (mvm->trans->mac_cfg->gen2)
sn &= 0xff;
return ieee80211_sn_sub(sn, tid_data->next_reclaimed);
}
-int iwl_mvm_add_pasn_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
- struct iwl_mvm_int_sta *sta, u8 *addr, u32 cipher,
- u8 *key, u32 key_len,
- struct ieee80211_key_conf *keyconf)
-{
- int ret;
- u16 queue;
- struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
- unsigned int wdg_timeout =
- iwl_mvm_get_wd_timeout(mvm, vif);
- bool mld = iwl_mvm_has_mld_api(mvm->fw);
- u32 type = IWL_STA_LINK;
-
- if (mld)
- type = STATION_TYPE_PEER;
-
- ret = iwl_mvm_allocate_int_sta(mvm, sta, 0,
- NL80211_IFTYPE_UNSPECIFIED, type);
- if (ret)
- return ret;
-
- if (mld)
- ret = iwl_mvm_mld_add_int_sta_with_queue(mvm, sta, addr,
- mvmvif->deflink.fw_link_id,
- &queue,
- IWL_MAX_TID_COUNT,
- &wdg_timeout);
- else
- ret = iwl_mvm_add_int_sta_with_queue(mvm, mvmvif->id,
- mvmvif->color, addr, sta,
- &queue,
- IWL_MVM_TX_FIFO_BE);
- if (ret)
- goto out;
-
- keyconf->cipher = cipher;
- memcpy(keyconf->key, key, key_len);
- keyconf->keylen = key_len;
- keyconf->flags = IEEE80211_KEY_FLAG_PAIRWISE;
-
- if (mld) {
- /* The MFP flag is set according to the station mfp field. Since
- * we don't have a station, set it manually.
- */
- u32 key_flags =
- iwl_mvm_get_sec_flags(mvm, vif, NULL, keyconf) |
- IWL_SEC_KEY_FLAG_MFP;
- u32 sta_mask = BIT(sta->sta_id);
-
- ret = iwl_mvm_mld_send_key(mvm, sta_mask, key_flags, keyconf);
- } else {
- ret = iwl_mvm_send_sta_key(mvm, sta->sta_id, keyconf, false,
- 0, NULL, 0, 0, true);
- }
-
-out:
- if (ret)
- iwl_mvm_dealloc_int_sta(mvm, sta);
- return ret;
-}
-
void iwl_mvm_cancel_channel_switch(struct iwl_mvm *mvm,
struct ieee80211_vif *vif,
u32 id)
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/sta.h b/drivers/net/wireless/intel/iwlwifi/mvm/sta.h
index 6856f7440ef3..6b183f5e9bbc 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/sta.h
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/sta.h
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
/*
- * Copyright (C) 2012-2014, 2018-2024 Intel Corporation
+ * Copyright (C) 2012-2014, 2018-2025 Intel Corporation
* Copyright (C) 2013-2014 Intel Mobile Communications GmbH
* Copyright (C) 2015-2016 Intel Deutschland GmbH
*/
@@ -225,8 +225,6 @@ struct iwl_mvm_vif;
* @IWL_AGG_ON: aggregation session is up
* @IWL_EMPTYING_HW_QUEUE_ADDBA: establishing a BA session - waiting for the
* HW queue to be empty from packets for this RA /TID.
- * @IWL_EMPTYING_HW_QUEUE_DELBA: tearing down a BA session - waiting for the
- * HW queue to be empty from packets for this RA /TID.
*/
enum iwl_mvm_agg_state {
IWL_AGG_OFF = 0,
@@ -234,7 +232,6 @@ enum iwl_mvm_agg_state {
IWL_AGG_STARTING,
IWL_AGG_ON,
IWL_EMPTYING_HW_QUEUE_ADDBA,
- IWL_EMPTYING_HW_QUEUE_DELBA,
};
/**
@@ -262,7 +259,7 @@ struct iwl_mvm_tid_data {
u16 seq_number;
u16 next_reclaimed;
/* The rest is Tx AGG related */
- u32 rate_n_flags;
+ __le32 rate_n_flags;
u8 lq_color;
bool amsdu_in_ampdu_allowed;
enum iwl_mvm_agg_state state;
@@ -597,10 +594,6 @@ void iwl_mvm_modify_all_sta_disable_tx(struct iwl_mvm *mvm,
void iwl_mvm_csa_client_absent(struct iwl_mvm *mvm, struct ieee80211_vif *vif);
int iwl_mvm_sta_ensure_queue(struct iwl_mvm *mvm, struct ieee80211_txq *txq);
void iwl_mvm_add_new_dqa_stream_wk(struct work_struct *wk);
-int iwl_mvm_add_pasn_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
- struct iwl_mvm_int_sta *sta, u8 *addr, u32 cipher,
- u8 *key, u32 key_len,
- struct ieee80211_key_conf *key_conf_out);
void iwl_mvm_cancel_channel_switch(struct iwl_mvm *mvm,
struct ieee80211_vif *vif,
u32 id);
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/tests/Makefile b/drivers/net/wireless/intel/iwlwifi/mvm/tests/Makefile
index 6bd56a28cffd..895d53f223e9 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/tests/Makefile
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/tests/Makefile
@@ -1,3 +1,3 @@
-iwlmvm-tests-y += module.o links.o scan.o
+iwlmvm-tests-y += module.o links.o scan.o hcmd.o
obj-$(CONFIG_IWLWIFI_KUNIT_TESTS) += iwlmvm-tests.o
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/tests/hcmd.c b/drivers/net/wireless/intel/iwlwifi/mvm/tests/hcmd.c
new file mode 100644
index 000000000000..1fee0320c756
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/tests/hcmd.c
@@ -0,0 +1,38 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
+/*
+ * KUnit tests for channel helper functions
+ *
+ * Copyright (C) 2025 Intel Corporation
+ */
+#include <kunit/test.h>
+
+#include <iwl-trans.h>
+#include "../mvm.h"
+
+MODULE_IMPORT_NS("EXPORTED_FOR_KUNIT_TESTING");
+
+static void test_hcmd_names_sorted(struct kunit *test)
+{
+ for (int i = 0; i < iwl_mvm_groups_size; i++) {
+ const struct iwl_hcmd_arr *arr = &iwl_mvm_groups[i];
+
+ if (!arr->arr)
+ continue;
+
+ for (int j = 0; j < arr->size - 1; j++)
+ KUNIT_EXPECT_LE(test, arr->arr[j].cmd_id,
+ arr->arr[j + 1].cmd_id);
+ }
+}
+
+static struct kunit_case hcmd_names_cases[] = {
+ KUNIT_CASE(test_hcmd_names_sorted),
+ {},
+};
+
+static struct kunit_suite hcmd_names = {
+ .name = "iwlmvm-hcmd-names",
+ .test_cases = hcmd_names_cases,
+};
+
+kunit_test_suite(hcmd_names);
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/time-event.c b/drivers/net/wireless/intel/iwlwifi/mvm/time-event.c
index 9216c43a35c4..478408f802d9 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/time-event.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/time-event.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
/*
- * Copyright (C) 2012-2014, 2018-2024 Intel Corporation
+ * Copyright (C) 2012-2014, 2018-2025 Intel Corporation
* Copyright (C) 2013-2015 Intel Mobile Communications GmbH
* Copyright (C) 2017 Intel Deutschland GmbH
*/
@@ -775,11 +775,13 @@ static void iwl_mvm_roc_rm_cmd(struct iwl_mvm *mvm, u32 activity)
.action = cpu_to_le32(FW_CTXT_ACTION_REMOVE),
.activity = cpu_to_le32(activity),
};
+ u8 ver = iwl_fw_lookup_cmd_ver(mvm->fw, WIDE_ID(MAC_CONF_GROUP, ROC_CMD), 0);
+ u16 cmd_len = ver < 6 ? sizeof(struct iwl_roc_req_v5) : sizeof(roc_cmd);
int ret;
lockdep_assert_held(&mvm->mutex);
ret = iwl_mvm_send_cmd_pdu(mvm, WIDE_ID(MAC_CONF_GROUP, ROC_CMD), 0,
- sizeof(roc_cmd), &roc_cmd);
+ cmd_len, &roc_cmd);
if (ret)
IWL_ERR(mvm, "Couldn't send the ROC_CMD: %d\n", ret);
}
@@ -1030,6 +1032,8 @@ void iwl_mvm_rx_session_protect_notif(struct iwl_mvm *mvm,
/* End TE, notify mac80211 */
mvmvif->time_event_data.id = SESSION_PROTECT_CONF_MAX_ID;
mvmvif->time_event_data.link_id = -1;
+ /* set the bit so the ROC cleanup will actually clean up */
+ set_bit(IWL_MVM_STATUS_ROC_P2P_RUNNING, &mvm->status);
iwl_mvm_roc_finished(mvm);
ieee80211_remain_on_channel_expired(mvm->hw);
} else if (le32_to_cpu(notif->start)) {
@@ -1105,6 +1109,8 @@ int iwl_mvm_roc_add_cmd(struct iwl_mvm *mvm,
.activity = cpu_to_le32(activity),
.sta_id = cpu_to_le32(mvm->aux_sta.sta_id),
};
+ u8 ver = iwl_fw_lookup_cmd_ver(mvm->fw, WIDE_ID(MAC_CONF_GROUP, ROC_CMD), 0);
+ u16 cmd_len = ver < 6 ? sizeof(struct iwl_roc_req_v5) : sizeof(roc_req);
struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
lockdep_assert_held(&mvm->mutex);
@@ -1134,7 +1140,7 @@ int iwl_mvm_roc_add_cmd(struct iwl_mvm *mvm,
memcpy(roc_req.node_addr, vif->addr, ETH_ALEN);
res = iwl_mvm_send_cmd_pdu(mvm, WIDE_ID(MAC_CONF_GROUP, ROC_CMD),
- 0, sizeof(roc_req), &roc_req);
+ 0, cmd_len, &roc_req);
if (!res)
mvmvif->roc_activity = activity;
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/tt.c b/drivers/net/wireless/intel/iwlwifi/mvm/tt.c
index d92470960b38..53bab21ebae2 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/tt.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/tt.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
/*
- * Copyright (C) 2012-2014, 2019-2022, 2024 Intel Corporation
+ * Copyright (C) 2012-2014, 2019-2022, 2024-2025 Intel Corporation
* Copyright (C) 2013-2014 Intel Mobile Communications GmbH
* Copyright (C) 2015-2016 Intel Deutschland GmbH
*/
@@ -8,6 +8,9 @@
#include "mvm.h"
+#define IWL_MVM_NUM_CTDP_STEPS 20
+#define IWL_MVM_MIN_CTDP_BUDGET_MW 150
+
#define IWL_MVM_TEMP_NOTIF_WAIT_TIMEOUT HZ
void iwl_mvm_enter_ctkill(struct iwl_mvm *mvm)
@@ -105,7 +108,7 @@ static bool iwl_mvm_temp_notif_wait(struct iwl_notif_wait_data *notif_wait,
void iwl_mvm_temp_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb)
{
struct iwl_rx_packet *pkt = rxb_addr(rxb);
- struct iwl_dts_measurement_notif_v2 *notif_v2;
+ struct iwl_dts_measurement_notif *notif_v2;
int len = iwl_rx_packet_payload_len(pkt);
int temp;
u32 ths_crossed;
@@ -479,43 +482,28 @@ static const struct iwl_tt_params iwl_mvm_default_tt_params = {
.support_tx_backoff = true,
};
-/* budget in mWatt */
-static const u32 iwl_mvm_cdev_budgets[] = {
- 2400, /* cooling state 0 */
- 2000, /* cooling state 1 */
- 1800, /* cooling state 2 */
- 1600, /* cooling state 3 */
- 1400, /* cooling state 4 */
- 1200, /* cooling state 5 */
- 1000, /* cooling state 6 */
- 900, /* cooling state 7 */
- 800, /* cooling state 8 */
- 700, /* cooling state 9 */
- 650, /* cooling state 10 */
- 600, /* cooling state 11 */
- 550, /* cooling state 12 */
- 500, /* cooling state 13 */
- 450, /* cooling state 14 */
- 400, /* cooling state 15 */
- 350, /* cooling state 16 */
- 300, /* cooling state 17 */
- 250, /* cooling state 18 */
- 200, /* cooling state 19 */
- 150, /* cooling state 20 */
-};
-
int iwl_mvm_ctdp_command(struct iwl_mvm *mvm, u32 op, u32 state)
{
- struct iwl_mvm_ctdp_cmd cmd = {
+ struct iwl_ctdp_cmd cmd = {
.operation = cpu_to_le32(op),
- .budget = cpu_to_le32(iwl_mvm_cdev_budgets[state]),
.window_size = 0,
};
+ u32 budget;
int ret;
u32 status;
lockdep_assert_held(&mvm->mutex);
+ /* Do a linear scale from IWL_MVM_MIN_CTDP_BUDGET_MW to the configured
+ * maximum in the predefined number of steps.
+ */
+ budget = ((mvm->thermal_throttle.power_budget_mw -
+ IWL_MVM_MIN_CTDP_BUDGET_MW) *
+ (IWL_MVM_NUM_CTDP_STEPS - 1 - state)) /
+ (IWL_MVM_NUM_CTDP_STEPS - 1) +
+ IWL_MVM_MIN_CTDP_BUDGET_MW;
+ cmd.budget = cpu_to_le32(budget);
+
status = 0;
ret = iwl_mvm_send_cmd_pdu_status(mvm, WIDE_ID(PHY_OPS_GROUP,
CTDP_CONFIG_CMD),
@@ -552,8 +540,8 @@ int iwl_mvm_ctdp_command(struct iwl_mvm *mvm, u32 op, u32 state)
#ifdef CONFIG_THERMAL
static int compare_temps(const void *a, const void *b)
{
- return ((s16)le16_to_cpu(*(__le16 *)a) -
- (s16)le16_to_cpu(*(__le16 *)b));
+ return ((s16)le16_to_cpu(*(const __le16 *)a) -
+ (s16)le16_to_cpu(*(const __le16 *)b));
}
struct iwl_trip_walk_data {
@@ -707,7 +695,7 @@ static void iwl_mvm_thermal_zone_register(struct iwl_mvm *mvm)
static int iwl_mvm_tcool_get_max_state(struct thermal_cooling_device *cdev,
unsigned long *state)
{
- *state = ARRAY_SIZE(iwl_mvm_cdev_budgets) - 1;
+ *state = IWL_MVM_NUM_CTDP_STEPS - 1;
return 0;
}
@@ -733,7 +721,7 @@ static int iwl_mvm_tcool_set_cur_state(struct thermal_cooling_device *cdev,
mvm->fwrt.cur_fw_img != IWL_UCODE_REGULAR)
return -EIO;
- if (new_state >= ARRAY_SIZE(iwl_mvm_cdev_budgets))
+ if (new_state >= IWL_MVM_NUM_CTDP_STEPS)
return -EINVAL;
return iwl_mvm_ctdp_command(mvm, CTDP_CMD_OPERATION_START,
@@ -794,6 +782,47 @@ static void iwl_mvm_cooling_device_unregister(struct iwl_mvm *mvm)
}
#endif /* CONFIG_THERMAL */
+static u32 iwl_mvm_ctdp_get_max_budget(struct iwl_mvm *mvm)
+{
+ u64 bios_power_budget = 0;
+ u32 default_power_budget;
+
+ switch (CSR_HW_RFID_TYPE(mvm->trans->info.hw_rf_id)) {
+ case IWL_CFG_RF_TYPE_JF2:
+ case IWL_CFG_RF_TYPE_JF1:
+ default_power_budget = 2000;
+ break;
+ case IWL_CFG_RF_TYPE_HR2:
+ case IWL_CFG_RF_TYPE_HR1:
+ default_power_budget = 2400;
+ break;
+ case IWL_CFG_RF_TYPE_GF:
+ /* dual-radio devices have a higher budget */
+ if (CSR_HW_RFID_IS_CDB(mvm->trans->info.hw_rf_id))
+ default_power_budget = 5200;
+ else
+ default_power_budget = 2880;
+ break;
+ case IWL_CFG_RF_TYPE_FM:
+ default_power_budget = 3450;
+ break;
+ default:
+ default_power_budget = 5550;
+ break;
+ }
+
+ iwl_bios_get_pwr_limit(&mvm->fwrt, &bios_power_budget);
+
+ /* 32bit in UEFI, 16bit in ACPI; use BIOS value if it is in range */
+ if (bios_power_budget &&
+ bios_power_budget != 0xffff && bios_power_budget != 0xffffffff &&
+ bios_power_budget >= IWL_MVM_MIN_CTDP_BUDGET_MW &&
+ bios_power_budget <= default_power_budget)
+ return (u32)bios_power_budget;
+
+ return default_power_budget;
+}
+
void iwl_mvm_thermal_initialize(struct iwl_mvm *mvm, u32 min_backoff)
{
struct iwl_mvm_tt_mgmt *tt = &mvm->thermal_throttle;
@@ -805,6 +834,8 @@ void iwl_mvm_thermal_initialize(struct iwl_mvm *mvm, u32 min_backoff)
else
tt->params = iwl_mvm_default_tt_params;
+ tt->power_budget_mw = iwl_mvm_ctdp_get_max_budget(mvm);
+ IWL_DEBUG_TEMP(mvm, "cTDP power budget: %d mW\n", tt->power_budget_mw);
tt->throttle = false;
tt->dynamic_smps = false;
tt->min_backoff = min_backoff;
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/tx.c b/drivers/net/wireless/intel/iwlwifi/mvm/tx.c
index f67afb66ef2b..ac2cf1b8ce23 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/tx.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/tx.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
/*
- * Copyright (C) 2012-2014, 2018-2024 Intel Corporation
+ * Copyright (C) 2012-2014, 2018-2025 Intel Corporation
* Copyright (C) 2013-2015 Intel Mobile Communications GmbH
* Copyright (C) 2016-2017 Intel Deutschland GmbH
*/
@@ -148,7 +148,7 @@ out:
* Sets most of the Tx cmd's fields
*/
void iwl_mvm_set_tx_cmd(struct iwl_mvm *mvm, struct sk_buff *skb,
- struct iwl_tx_cmd *tx_cmd,
+ struct iwl_tx_cmd_v6 *tx_cmd,
struct ieee80211_tx_info *info, u8 sta_id)
{
struct ieee80211_hdr *hdr = (void *)skb->data;
@@ -283,14 +283,10 @@ static u32 iwl_mvm_convert_rate_idx(struct iwl_mvm *mvm,
(rate_idx <= IWL_LAST_CCK_RATE);
/* Set CCK or OFDM flag */
- if (iwl_fw_lookup_cmd_ver(mvm->fw, TX_CMD, 0) > 8) {
- if (!is_cck)
- rate_flags |= RATE_MCS_LEGACY_OFDM_MSK;
- else
- rate_flags |= RATE_MCS_CCK_MSK;
- } else if (is_cck) {
- rate_flags |= RATE_MCS_CCK_MSK_V1;
- }
+ if (!is_cck)
+ rate_flags |= RATE_MCS_MOD_TYPE_LEGACY_OFDM;
+ else
+ rate_flags |= RATE_MCS_MOD_TYPE_CCK;
return (u32)rate_plcp | rate_flags;
}
@@ -303,45 +299,35 @@ static u32 iwl_mvm_get_inject_tx_rate(struct iwl_mvm *mvm,
struct ieee80211_tx_rate *rate = &info->control.rates[0];
u32 result;
- /*
- * we only care about legacy/HT/VHT so far, so we can
- * build in v1 and use iwl_new_rate_from_v1()
- */
-
if (rate->flags & IEEE80211_TX_RC_VHT_MCS) {
u8 mcs = ieee80211_rate_get_vht_mcs(rate);
u8 nss = ieee80211_rate_get_vht_nss(rate);
- result = RATE_MCS_VHT_MSK_V1;
+ result = RATE_MCS_MOD_TYPE_VHT;
result |= u32_encode_bits(mcs, RATE_VHT_MCS_RATE_CODE_MSK);
result |= u32_encode_bits(nss, RATE_MCS_NSS_MSK);
if (rate->flags & IEEE80211_TX_RC_SHORT_GI)
- result |= RATE_MCS_SGI_MSK_V1;
+ result |= RATE_MCS_SGI_MSK;
if (rate->flags & IEEE80211_TX_RC_40_MHZ_WIDTH)
- result |= u32_encode_bits(1, RATE_MCS_CHAN_WIDTH_MSK_V1);
+ result |= RATE_MCS_CHAN_WIDTH_40;
else if (rate->flags & IEEE80211_TX_RC_80_MHZ_WIDTH)
- result |= u32_encode_bits(2, RATE_MCS_CHAN_WIDTH_MSK_V1);
+ result |= RATE_MCS_CHAN_WIDTH_80;
else if (rate->flags & IEEE80211_TX_RC_160_MHZ_WIDTH)
- result |= u32_encode_bits(3, RATE_MCS_CHAN_WIDTH_MSK_V1);
-
- if (iwl_fw_lookup_notif_ver(mvm->fw, LONG_GROUP, TX_CMD, 0) > 6)
- result = iwl_new_rate_from_v1(result);
+ result |= RATE_MCS_CHAN_WIDTH_160;
} else if (rate->flags & IEEE80211_TX_RC_MCS) {
- result = RATE_MCS_HT_MSK_V1;
- result |= u32_encode_bits(rate->idx,
- RATE_HT_MCS_RATE_CODE_MSK_V1 |
- RATE_HT_MCS_NSS_MSK_V1);
+ result = RATE_MCS_MOD_TYPE_HT;
+ result |= u32_encode_bits(rate->idx & 0x7,
+ RATE_HT_MCS_CODE_MSK);
+ result |= u32_encode_bits(rate->idx >> 3,
+ RATE_MCS_NSS_MSK);
if (rate->flags & IEEE80211_TX_RC_SHORT_GI)
- result |= RATE_MCS_SGI_MSK_V1;
+ result |= RATE_MCS_SGI_MSK;
if (rate->flags & IEEE80211_TX_RC_40_MHZ_WIDTH)
- result |= u32_encode_bits(1, RATE_MCS_CHAN_WIDTH_MSK_V1);
+ result |= RATE_MCS_CHAN_WIDTH_40;
if (info->flags & IEEE80211_TX_CTL_LDPC)
- result |= RATE_MCS_LDPC_MSK_V1;
+ result |= RATE_MCS_LDPC_MSK;
if (u32_get_bits(info->flags, IEEE80211_TX_CTL_STBC))
result |= RATE_MCS_STBC_MSK;
-
- if (iwl_fw_lookup_notif_ver(mvm->fw, LONG_GROUP, TX_CMD, 0) > 6)
- result = iwl_new_rate_from_v1(result);
} else {
int rate_idx = info->control.rates[0].idx;
@@ -391,21 +377,25 @@ static u32 iwl_mvm_get_tx_rate(struct iwl_mvm *mvm,
return iwl_mvm_convert_rate_idx(mvm, info, rate_idx);
}
-static u32 iwl_mvm_get_tx_rate_n_flags(struct iwl_mvm *mvm,
- struct ieee80211_tx_info *info,
- struct ieee80211_sta *sta, __le16 fc)
+static __le32 iwl_mvm_get_tx_rate_n_flags(struct iwl_mvm *mvm,
+ struct ieee80211_tx_info *info,
+ struct ieee80211_sta *sta, __le16 fc)
{
+ u32 rate;
+
if (unlikely(info->control.flags & IEEE80211_TX_CTRL_RATE_INJECT))
- return iwl_mvm_get_inject_tx_rate(mvm, info, sta, fc);
+ rate = iwl_mvm_get_inject_tx_rate(mvm, info, sta, fc);
+ else
+ rate = iwl_mvm_get_tx_rate(mvm, info, sta, fc) |
+ iwl_mvm_get_tx_ant(mvm, info, sta, fc);
- return iwl_mvm_get_tx_rate(mvm, info, sta, fc) |
- iwl_mvm_get_tx_ant(mvm, info, sta, fc);
+ return iwl_mvm_v3_rate_to_fw(rate, mvm->fw_rates_ver);
}
/*
* Sets the fields in the Tx cmd that are rate related
*/
-void iwl_mvm_set_tx_cmd_rate(struct iwl_mvm *mvm, struct iwl_tx_cmd *tx_cmd,
+void iwl_mvm_set_tx_cmd_rate(struct iwl_mvm *mvm, struct iwl_tx_cmd_v6 *tx_cmd,
struct ieee80211_tx_info *info,
struct ieee80211_sta *sta, __le16 fc)
{
@@ -443,8 +433,7 @@ void iwl_mvm_set_tx_cmd_rate(struct iwl_mvm *mvm, struct iwl_tx_cmd *tx_cmd,
}
/* Set the rate in the TX cmd */
- tx_cmd->rate_n_flags =
- cpu_to_le32(iwl_mvm_get_tx_rate_n_flags(mvm, info, sta, fc));
+ tx_cmd->rate_n_flags = iwl_mvm_get_tx_rate_n_flags(mvm, info, sta, fc);
}
static inline void iwl_mvm_set_tx_cmd_pn(struct ieee80211_tx_info *info,
@@ -469,7 +458,7 @@ static inline void iwl_mvm_set_tx_cmd_pn(struct ieee80211_tx_info *info,
*/
static void iwl_mvm_set_tx_cmd_crypto(struct iwl_mvm *mvm,
struct ieee80211_tx_info *info,
- struct iwl_tx_cmd *tx_cmd,
+ struct iwl_tx_cmd_v6 *tx_cmd,
struct sk_buff *skb_frag,
int hdrlen)
{
@@ -543,7 +532,7 @@ static bool iwl_mvm_use_host_rate(struct iwl_mvm *mvm,
* (since we don't necesarily know the link), but FW rate
* selection was fixed.
*/
- return mvm->trans->trans_cfg->device_family < IWL_DEVICE_FAMILY_BZ;
+ return mvm->trans->mac_cfg->device_family < IWL_DEVICE_FAMILY_BZ;
}
static void iwl_mvm_copy_hdr(void *cmd, const void *hdr, int hdrlen,
@@ -567,7 +556,7 @@ iwl_mvm_set_tx_params(struct iwl_mvm *mvm, struct sk_buff *skb,
{
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
struct iwl_device_tx_cmd *dev_cmd;
- struct iwl_tx_cmd *tx_cmd;
+ struct iwl_tx_cmd_v6 *tx_cmd;
dev_cmd = iwl_trans_alloc_tx_cmd(mvm->trans);
@@ -577,7 +566,7 @@ iwl_mvm_set_tx_params(struct iwl_mvm *mvm, struct sk_buff *skb,
dev_cmd->hdr.cmd = TX_CMD;
if (iwl_mvm_has_new_tx_api(mvm)) {
- u32 rate_n_flags = 0;
+ __le32 rate_n_flags = 0;
u16 flags = 0;
struct iwl_mvm_sta *mvmsta = sta ?
iwl_mvm_sta_from_mac80211(sta) : NULL;
@@ -609,9 +598,9 @@ iwl_mvm_set_tx_params(struct iwl_mvm *mvm, struct sk_buff *skb,
flags |= IWL_TX_FLAGS_HIGH_PRI;
}
- if (mvm->trans->trans_cfg->device_family >=
+ if (mvm->trans->mac_cfg->device_family >=
IWL_DEVICE_FAMILY_AX210) {
- struct iwl_tx_cmd_gen3 *cmd = (void *)dev_cmd->payload;
+ struct iwl_tx_cmd *cmd = (void *)dev_cmd->payload;
u32 offload_assist = iwl_mvm_tx_csum(mvm, skb,
info, amsdu);
@@ -624,9 +613,9 @@ iwl_mvm_set_tx_params(struct iwl_mvm *mvm, struct sk_buff *skb,
iwl_mvm_copy_hdr(cmd->hdr, hdr, hdrlen, addr3_override);
cmd->flags = cpu_to_le16(flags);
- cmd->rate_n_flags = cpu_to_le32(rate_n_flags);
+ cmd->rate_n_flags = rate_n_flags;
} else {
- struct iwl_tx_cmd_gen2 *cmd = (void *)dev_cmd->payload;
+ struct iwl_tx_cmd_v9 *cmd = (void *)dev_cmd->payload;
u16 offload_assist = iwl_mvm_tx_csum(mvm, skb,
info, amsdu);
@@ -639,12 +628,12 @@ iwl_mvm_set_tx_params(struct iwl_mvm *mvm, struct sk_buff *skb,
iwl_mvm_copy_hdr(cmd->hdr, hdr, hdrlen, addr3_override);
cmd->flags = cpu_to_le32(flags);
- cmd->rate_n_flags = cpu_to_le32(rate_n_flags);
+ cmd->rate_n_flags = rate_n_flags;
}
goto out;
}
- tx_cmd = (struct iwl_tx_cmd *)dev_cmd->payload;
+ tx_cmd = (struct iwl_tx_cmd_v6 *)dev_cmd->payload;
if (info->control.hw_key)
iwl_mvm_set_tx_cmd_crypto(mvm, info, tx_cmd, skb, hdrlen);
@@ -1023,7 +1012,7 @@ static int iwl_mvm_tx_tso(struct iwl_mvm *mvm, struct sk_buff *skb,
* 1 more for the potential data in the header
*/
if ((num_subframes * 2 + skb_shinfo(skb)->nr_frags + 1) >
- mvm->trans->max_skb_frags)
+ mvm->trans->info.max_skb_frags)
num_subframes = 1;
if (num_subframes > 1)
@@ -1185,7 +1174,7 @@ static int iwl_mvm_tx_mpdu(struct iwl_mvm *mvm, struct sk_buff *skb,
seq_number &= IEEE80211_SCTL_SEQ;
if (!iwl_mvm_has_new_tx_api(mvm)) {
- struct iwl_tx_cmd *tx_cmd = (void *)dev_cmd->payload;
+ struct iwl_tx_cmd_v6 *tx_cmd = (void *)dev_cmd->payload;
hdr->seq_ctrl &= cpu_to_le16(IEEE80211_SCTL_FRAG);
hdr->seq_ctrl |= cpu_to_le16(seq_number);
@@ -1372,8 +1361,7 @@ static void iwl_mvm_check_ratid_empty(struct iwl_mvm *mvm,
lockdep_assert_held(&mvmsta->lock);
- if ((tid_data->state == IWL_AGG_ON ||
- tid_data->state == IWL_EMPTYING_HW_QUEUE_DELBA) &&
+ if (tid_data->state == IWL_AGG_ON &&
iwl_mvm_tid_queued(mvm, tid_data) == 0) {
/*
* Now that this aggregation or DQA queue is empty tell
@@ -1388,7 +1376,7 @@ static void iwl_mvm_check_ratid_empty(struct iwl_mvm *mvm,
* to align the wrap around of ssn so we compare relevant values.
*/
normalized_ssn = tid_data->ssn;
- if (mvm->trans->trans_cfg->gen2)
+ if (mvm->trans->mac_cfg->gen2)
normalized_ssn &= 0xff;
if (normalized_ssn != tid_data->next_reclaimed)
@@ -1402,15 +1390,6 @@ static void iwl_mvm_check_ratid_empty(struct iwl_mvm *mvm,
tid_data->state = IWL_AGG_STARTING;
ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid);
break;
-
- case IWL_EMPTYING_HW_QUEUE_DELBA:
- IWL_DEBUG_TX_QUEUES(mvm,
- "Can continue DELBA flow ssn = next_recl = %d\n",
- tid_data->next_reclaimed);
- tid_data->state = IWL_AGG_OFF;
- ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid);
- break;
-
default:
break;
}
@@ -1477,7 +1456,7 @@ void iwl_mvm_hwrate_to_tx_rate(u32 rate_n_flags,
struct ieee80211_tx_rate *r)
{
u32 format = rate_n_flags & RATE_MCS_MOD_TYPE_MSK;
- u32 rate = format == RATE_MCS_HT_MSK ?
+ u32 rate = format == RATE_MCS_MOD_TYPE_HT ?
RATE_HT_MCS_INDEX(rate_n_flags) :
rate_n_flags & RATE_MCS_CODE_MSK;
@@ -1487,68 +1466,51 @@ void iwl_mvm_hwrate_to_tx_rate(u32 rate_n_flags,
if (rate_n_flags & RATE_MCS_SGI_MSK)
r->flags |= IEEE80211_TX_RC_SHORT_GI;
- if (format == RATE_MCS_HT_MSK) {
+ switch (format) {
+ case RATE_MCS_MOD_TYPE_HT:
r->flags |= IEEE80211_TX_RC_MCS;
r->idx = rate;
- } else if (format == RATE_MCS_VHT_MSK) {
+ break;
+ case RATE_MCS_MOD_TYPE_VHT:
ieee80211_rate_set_vht(r, rate,
FIELD_GET(RATE_MCS_NSS_MSK,
rate_n_flags) + 1);
r->flags |= IEEE80211_TX_RC_VHT_MCS;
- } else if (format == RATE_MCS_HE_MSK) {
+ break;
+ case RATE_MCS_MOD_TYPE_HE:
+ case RATE_MCS_MOD_TYPE_EHT:
/* mac80211 cannot do this without ieee80211_tx_status_ext()
* but it only matters for radiotap */
r->idx = 0;
- } else {
+ break;
+ default:
r->idx = iwl_mvm_legacy_hw_idx_to_mac80211_idx(rate_n_flags,
band);
}
}
-void iwl_mvm_hwrate_to_tx_rate_v1(u32 rate_n_flags,
- enum nl80211_band band,
- struct ieee80211_tx_rate *r)
-{
- if (rate_n_flags & RATE_HT_MCS_GF_MSK)
- r->flags |= IEEE80211_TX_RC_GREEN_FIELD;
-
- r->flags |=
- iwl_mvm_get_hwrate_chan_width(rate_n_flags &
- RATE_MCS_CHAN_WIDTH_MSK_V1);
-
- if (rate_n_flags & RATE_MCS_SGI_MSK_V1)
- r->flags |= IEEE80211_TX_RC_SHORT_GI;
- if (rate_n_flags & RATE_MCS_HT_MSK_V1) {
- r->flags |= IEEE80211_TX_RC_MCS;
- r->idx = rate_n_flags & RATE_HT_MCS_INDEX_MSK_V1;
- } else if (rate_n_flags & RATE_MCS_VHT_MSK_V1) {
- ieee80211_rate_set_vht(
- r, rate_n_flags & RATE_VHT_MCS_RATE_CODE_MSK,
- FIELD_GET(RATE_MCS_NSS_MSK, rate_n_flags) + 1);
- r->flags |= IEEE80211_TX_RC_VHT_MCS;
- } else {
- r->idx = iwl_mvm_legacy_rate_to_mac80211_idx(rate_n_flags,
- band);
- }
-}
-
/*
* translate ucode response to mac80211 tx status control values
*/
-static void iwl_mvm_hwrate_to_tx_status(const struct iwl_fw *fw,
- u32 rate_n_flags,
+static void iwl_mvm_hwrate_to_tx_status(struct iwl_mvm *mvm,
+ __le32 rate_n_flags,
struct ieee80211_tx_info *info)
{
struct ieee80211_tx_rate *r = &info->status.rates[0];
+ u32 rate;
- if (iwl_fw_lookup_notif_ver(fw, LONG_GROUP,
- TX_CMD, 0) <= 6)
- rate_n_flags = iwl_new_rate_from_v1(rate_n_flags);
+ /*
+ * Technically this conversion is incorrect for BA status, however:
+ * - we only use the BA notif data for older firmware that have
+ * host rate scaling and don't use newer rate formats
+ * - the firmware API changed together for BA notif and TX CMD
+ * as well
+ */
+ rate = iwl_mvm_v3_rate_from_fw(rate_n_flags, mvm->fw_rates_ver);
info->status.antenna =
- ((rate_n_flags & RATE_MCS_ANT_AB_MSK) >> RATE_MCS_ANT_POS);
- iwl_mvm_hwrate_to_tx_rate(rate_n_flags,
- info->band, r);
+ ((rate & RATE_MCS_ANT_AB_MSK) >> RATE_MCS_ANT_POS);
+ iwl_mvm_hwrate_to_tx_rate(rate, info->band, r);
}
static void iwl_mvm_tx_status_check_trigger(struct iwl_mvm *mvm,
@@ -1613,7 +1575,7 @@ static inline u32 iwl_mvm_get_scd_ssn(struct iwl_mvm *mvm,
u32 val = le32_to_cpup((__le32 *)iwl_mvm_get_agg_status(mvm, tx_resp) +
tx_resp->frame_count);
- if (mvm->trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_AX210)
+ if (mvm->trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_AX210)
return val & 0xFFFF;
return val & 0xFFF;
}
@@ -1700,9 +1662,7 @@ static void iwl_mvm_rx_tx_cmd_single(struct iwl_mvm *mvm,
info->status.rates[0].count = tx_resp->failure_frame + 1;
- iwl_mvm_hwrate_to_tx_status(mvm->fw,
- le32_to_cpu(tx_resp->initial_rate),
- info);
+ iwl_mvm_hwrate_to_tx_status(mvm, tx_resp->initial_rate, info);
/* Don't assign the converted initial_rate, because driver
* TLC uses this and doesn't support the new FW rate
@@ -1944,7 +1904,7 @@ static void iwl_mvm_rx_tx_cmd_agg(struct iwl_mvm *mvm,
if (!WARN_ON_ONCE(!mvmsta)) {
mvmsta->tid_data[tid].rate_n_flags =
- le32_to_cpu(tx_resp->initial_rate);
+ tx_resp->initial_rate;
mvmsta->tid_data[tid].tx_time =
le16_to_cpu(tx_resp->wireless_media_time);
mvmsta->tid_data[tid].lq_color =
@@ -1969,7 +1929,7 @@ void iwl_mvm_rx_tx_cmd(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb)
static void iwl_mvm_tx_reclaim(struct iwl_mvm *mvm, int sta_id, int tid,
int txq, int index,
- struct ieee80211_tx_info *tx_info, u32 rate,
+ struct ieee80211_tx_info *tx_info, __le32 rate,
bool is_flush)
{
struct sk_buff_head reclaimed_skbs;
@@ -2053,7 +2013,9 @@ static void iwl_mvm_tx_reclaim(struct iwl_mvm *mvm, int sta_id, int tid,
tx_info->status.status_driver_data[0] =
RS_DRV_DATA_PACK(tid_data->lq_color,
tx_info->status.status_driver_data[0]);
- tx_info->status.status_driver_data[1] = (void *)(uintptr_t)rate;
+ /* the value is only consumed for old FW that has v1 rates anyway */
+ tx_info->status.status_driver_data[1] =
+ (void *)(uintptr_t)le32_to_cpu(rate);
skb_queue_walk(&reclaimed_skbs, skb) {
struct ieee80211_hdr *hdr = (void *)skb->data;
@@ -2072,7 +2034,7 @@ static void iwl_mvm_tx_reclaim(struct iwl_mvm *mvm, int sta_id, int tid,
info->flags |= IEEE80211_TX_STAT_AMPDU;
memcpy(&info->status, &tx_info->status,
sizeof(tx_info->status));
- iwl_mvm_hwrate_to_tx_status(mvm->fw, rate, info);
+ iwl_mvm_hwrate_to_tx_status(mvm, rate, info);
}
}
@@ -2095,7 +2057,7 @@ static void iwl_mvm_tx_reclaim(struct iwl_mvm *mvm, int sta_id, int tid,
goto out;
tx_info->band = chanctx_conf->def.chan->band;
- iwl_mvm_hwrate_to_tx_status(mvm->fw, rate, tx_info);
+ iwl_mvm_hwrate_to_tx_status(mvm, rate, tx_info);
IWL_DEBUG_TX_REPLY(mvm, "No reclaim. Update rs directly\n");
iwl_mvm_rs_tx_status(mvm, sta, tid, tx_info, false);
@@ -2184,7 +2146,7 @@ void iwl_mvm_rx_ba_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb)
(int)(le16_to_cpu(ba_tfd->q_num)),
le16_to_cpu(ba_tfd->tfd_index),
&ba_info,
- le32_to_cpu(ba_res->tx_rate), false);
+ ba_res->tx_rate, false);
}
if (mvmsta) {
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/utils.c b/drivers/net/wireless/intel/iwlwifi/mvm/utils.c
index dd890dcd1505..62da0132f383 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/utils.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/utils.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
/*
- * Copyright (C) 2012-2014, 2018-2024 Intel Corporation
+ * Copyright (C) 2012-2014, 2018-2025 Intel Corporation
* Copyright (C) 2013-2014 Intel Mobile Communications GmbH
* Copyright (C) 2015-2017 Intel Deutschland GmbH
*/
@@ -142,7 +142,7 @@ int iwl_mvm_legacy_hw_idx_to_mac80211_idx(u32 rate_n_flags,
int rate = rate_n_flags & RATE_LEGACY_RATE_MSK;
bool is_LB = band == NL80211_BAND_2GHZ;
- if (format == RATE_MCS_LEGACY_OFDM_MSK)
+ if (format == RATE_MCS_MOD_TYPE_LEGACY_OFDM)
return is_LB ? rate + IWL_FIRST_OFDM_RATE :
rate;
@@ -169,15 +169,9 @@ int iwl_mvm_legacy_rate_to_mac80211_idx(u32 rate_n_flags,
u8 iwl_mvm_mac80211_idx_to_hwrate(const struct iwl_fw *fw, int rate_idx)
{
- if (iwl_fw_lookup_cmd_ver(fw, TX_CMD, 0) > 8)
- /* In the new rate legacy rates are indexed:
- * 0 - 3 for CCK and 0 - 7 for OFDM.
- */
- return (rate_idx >= IWL_FIRST_OFDM_RATE ?
- rate_idx - IWL_FIRST_OFDM_RATE :
- rate_idx);
-
- return iwl_fw_rate_idx_to_plcp(rate_idx);
+ return (rate_idx >= IWL_FIRST_OFDM_RATE ?
+ rate_idx - IWL_FIRST_OFDM_RATE :
+ rate_idx);
}
u8 iwl_mvm_mac80211_ac_to_ucode_ac(enum ieee80211_ac_numbers ac)
@@ -748,7 +742,7 @@ unsigned int iwl_mvm_get_wd_timeout(struct iwl_mvm *mvm,
struct ieee80211_vif *vif)
{
unsigned int default_timeout =
- mvm->trans->trans_cfg->base_params->wd_timeout;
+ mvm->trans->mac_cfg->base->wd_timeout;
/*
* We can't know when the station is asleep or awake, so we
@@ -1187,9 +1181,9 @@ u32 iwl_mvm_get_systime(struct iwl_mvm *mvm)
{
u32 reg_addr = DEVICE_SYSTEM_TIME_REG;
- if (mvm->trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_22000 &&
- mvm->trans->cfg->gp2_reg_addr)
- reg_addr = mvm->trans->cfg->gp2_reg_addr;
+ if (mvm->trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_22000 &&
+ mvm->trans->mac_cfg->base->gp2_reg_addr)
+ reg_addr = mvm->trans->mac_cfg->base->gp2_reg_addr;
return iwl_read_prph(mvm->trans, reg_addr);
}
diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/ctxt-info-gen3.c b/drivers/net/wireless/intel/iwlwifi/pcie/ctxt-info-v2.c
index 838c426db7f0..976fd1f58da4 100644
--- a/drivers/net/wireless/intel/iwlwifi/pcie/ctxt-info-gen3.c
+++ b/drivers/net/wireless/intel/iwlwifi/pcie/ctxt-info-v2.c
@@ -1,11 +1,11 @@
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
/*
- * Copyright (C) 2018-2024 Intel Corporation
+ * Copyright (C) 2018-2025 Intel Corporation
*/
#include <linux/dmi.h>
#include "iwl-trans.h"
#include "iwl-fh.h"
-#include "iwl-context-info-gen3.h"
+#include "iwl-context-info-v2.h"
#include "internal.h"
#include "iwl-prph.h"
@@ -97,11 +97,12 @@ out:
*control_flags |= IWL_PRPH_SCRATCH_EARLY_DEBUG_EN | dbg_flags;
}
-int iwl_pcie_ctxt_info_gen3_init(struct iwl_trans *trans,
- const struct fw_img *fw)
+int iwl_pcie_ctxt_info_v2_alloc(struct iwl_trans *trans,
+ const struct iwl_fw *fw,
+ const struct fw_img *img)
{
struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
- struct iwl_context_info_gen3 *ctxt_info_gen3;
+ struct iwl_context_info_v2 *ctxt_info_v2;
struct iwl_prph_scratch *prph_scratch;
struct iwl_prph_scratch_ctrl_cfg *prph_sc_ctrl;
struct iwl_prph_info *prph_info;
@@ -109,9 +110,9 @@ int iwl_pcie_ctxt_info_gen3_init(struct iwl_trans *trans,
u32 control_flags_ext = 0;
int ret;
int cmdq_size = max_t(u32, IWL_CMD_QUEUE_SIZE,
- trans->cfg->min_txq_size);
+ trans->mac_cfg->base->min_txq_size);
- switch (trans_pcie->rx_buf_size) {
+ switch (trans->conf.rx_buf_size) {
case IWL_AMSDU_DEF:
return -EINVAL;
case IWL_AMSDU_2K:
@@ -131,12 +132,15 @@ int iwl_pcie_ctxt_info_gen3_init(struct iwl_trans *trans,
break;
}
- if (trans->dsbr_urm_fw_dependent)
+ if (trans->conf.dsbr_urm_fw_dependent)
control_flags_ext |= IWL_PRPH_SCRATCH_EXT_URM_FW;
- if (trans->dsbr_urm_permanent)
+ if (trans->conf.dsbr_urm_permanent)
control_flags_ext |= IWL_PRPH_SCRATCH_EXT_URM_PERM;
+ if (trans->conf.ext_32khz_clock_valid)
+ control_flags_ext |= IWL_PRPH_SCRATCH_EXT_32KHZ_CLK_VALID;
+
/* Allocate prph scratch */
prph_scratch = dma_alloc_coherent(trans->dev, sizeof(*prph_scratch),
&trans_pcie->prph_scratch_dma_addr,
@@ -148,16 +152,16 @@ int iwl_pcie_ctxt_info_gen3_init(struct iwl_trans *trans,
prph_sc_ctrl->version.version = 0;
prph_sc_ctrl->version.mac_id =
- cpu_to_le16((u16)trans->hw_rev);
+ cpu_to_le16((u16)trans->info.hw_rev);
prph_sc_ctrl->version.size = cpu_to_le16(sizeof(*prph_scratch) / 4);
control_flags |= IWL_PRPH_SCRATCH_MTR_MODE;
control_flags |= IWL_PRPH_MTR_FORMAT_256B & IWL_PRPH_SCRATCH_MTR_FORMAT;
- if (trans->trans_cfg->imr_enabled)
+ if (trans->mac_cfg->imr_enabled)
control_flags |= IWL_PRPH_SCRATCH_IMR_DEBUG_EN;
- if (CSR_HW_REV_TYPE(trans->hw_rev) == IWL_CFG_MAC_TYPE_GL &&
+ if (CSR_HW_REV_TYPE(trans->info.hw_rev) == IWL_CFG_MAC_TYPE_GL &&
iwl_is_force_scu_active_approved()) {
control_flags |= IWL_PRPH_SCRATCH_SCU_FORCE_ACTIVE;
IWL_DEBUG_FW(trans,
@@ -165,6 +169,11 @@ int iwl_pcie_ctxt_info_gen3_init(struct iwl_trans *trans,
IWL_PRPH_SCRATCH_SCU_FORCE_ACTIVE);
}
+ if (trans->do_top_reset) {
+ WARN_ON(trans->mac_cfg->device_family < IWL_DEVICE_FAMILY_SC);
+ control_flags |= IWL_PRPH_SCRATCH_TOP_RESET;
+ }
+
/* initialize RX default queue */
prph_sc_ctrl->rbd_cfg.free_rbd_addr =
cpu_to_le64(trans_pcie->rxq->bd_dma);
@@ -175,15 +184,16 @@ int iwl_pcie_ctxt_info_gen3_init(struct iwl_trans *trans,
prph_sc_ctrl->control.control_flags_ext = cpu_to_le32(control_flags_ext);
/* initialize the Step equalizer data */
- prph_sc_ctrl->step_cfg.mbx_addr_0 = cpu_to_le32(trans->mbx_addr_0_step);
- prph_sc_ctrl->step_cfg.mbx_addr_1 = cpu_to_le32(trans->mbx_addr_1_step);
+ prph_sc_ctrl->step_cfg.mbx_addr_0 =
+ cpu_to_le32(trans->conf.mbx_addr_0_step);
+ prph_sc_ctrl->step_cfg.mbx_addr_1 =
+ cpu_to_le32(trans->conf.mbx_addr_1_step);
/* allocate ucode sections in dram and set addresses */
- ret = iwl_pcie_init_fw_sec(trans, fw, &prph_scratch->dram);
+ ret = iwl_pcie_init_fw_sec(trans, img, &prph_scratch->dram.common);
if (ret)
goto err_free_prph_scratch;
-
/* Allocate prph information
* currently we don't assign to the prph info anything, but it would get
* assigned later
@@ -203,42 +213,58 @@ int iwl_pcie_ctxt_info_gen3_init(struct iwl_trans *trans,
}
/* Allocate context info */
- ctxt_info_gen3 = dma_alloc_coherent(trans->dev,
- sizeof(*ctxt_info_gen3),
- &trans_pcie->ctxt_info_dma_addr,
- GFP_KERNEL);
- if (!ctxt_info_gen3) {
+ ctxt_info_v2 = dma_alloc_coherent(trans->dev,
+ sizeof(*ctxt_info_v2),
+ &trans_pcie->ctxt_info_dma_addr,
+ GFP_KERNEL);
+ if (!ctxt_info_v2) {
ret = -ENOMEM;
goto err_free_prph_info;
}
- ctxt_info_gen3->prph_info_base_addr =
+ ctxt_info_v2->prph_info_base_addr =
cpu_to_le64(trans_pcie->prph_info_dma_addr);
- ctxt_info_gen3->prph_scratch_base_addr =
+ ctxt_info_v2->prph_scratch_base_addr =
cpu_to_le64(trans_pcie->prph_scratch_dma_addr);
- ctxt_info_gen3->prph_scratch_size =
- cpu_to_le32(sizeof(*prph_scratch));
- ctxt_info_gen3->cr_head_idx_arr_base_addr =
+
+ /*
+ * This code assumes the FSEQ is last and we can make that
+ * optional; old devices _should_ be fine with a bigger size,
+ * but in simulation we check the size more precisely.
+ */
+ BUILD_BUG_ON(offsetofend(typeof(*prph_scratch), dram.common) +
+ sizeof(prph_scratch->dram.fseq_img) !=
+ sizeof(*prph_scratch));
+ if (control_flags_ext & IWL_PRPH_SCRATCH_EXT_EXT_FSEQ)
+ ctxt_info_v2->prph_scratch_size =
+ cpu_to_le32(sizeof(*prph_scratch));
+ else
+ ctxt_info_v2->prph_scratch_size =
+ cpu_to_le32(offsetofend(typeof(*prph_scratch),
+ dram.common));
+
+ ctxt_info_v2->cr_head_idx_arr_base_addr =
cpu_to_le64(trans_pcie->rxq->rb_stts_dma);
- ctxt_info_gen3->tr_tail_idx_arr_base_addr =
+ ctxt_info_v2->tr_tail_idx_arr_base_addr =
cpu_to_le64(trans_pcie->prph_info_dma_addr + PAGE_SIZE / 2);
- ctxt_info_gen3->cr_tail_idx_arr_base_addr =
+ ctxt_info_v2->cr_tail_idx_arr_base_addr =
cpu_to_le64(trans_pcie->prph_info_dma_addr + 3 * PAGE_SIZE / 4);
- ctxt_info_gen3->mtr_base_addr =
- cpu_to_le64(trans_pcie->txqs.txq[trans_pcie->txqs.cmd.q_id]->dma_addr);
- ctxt_info_gen3->mcr_base_addr =
+ ctxt_info_v2->mtr_base_addr =
+ cpu_to_le64(trans_pcie->txqs.txq[trans->conf.cmd_queue]->dma_addr);
+ ctxt_info_v2->mcr_base_addr =
cpu_to_le64(trans_pcie->rxq->used_bd_dma);
- ctxt_info_gen3->mtr_size =
+ ctxt_info_v2->mtr_size =
cpu_to_le16(TFD_QUEUE_CB_SIZE(cmdq_size));
- ctxt_info_gen3->mcr_size =
- cpu_to_le16(RX_QUEUE_CB_SIZE(trans->cfg->num_rbds));
+ ctxt_info_v2->mcr_size =
+ cpu_to_le16(RX_QUEUE_CB_SIZE(iwl_trans_get_num_rbds(trans)));
- trans_pcie->ctxt_info_gen3 = ctxt_info_gen3;
+ trans_pcie->ctxt_info_v2 = ctxt_info_v2;
trans_pcie->prph_info = prph_info;
trans_pcie->prph_scratch = prph_scratch;
/* Allocate IML */
- trans_pcie->iml = dma_alloc_coherent(trans->dev, trans->iml_len,
+ trans_pcie->iml_len = fw->iml_len;
+ trans_pcie->iml = dma_alloc_coherent(trans->dev, fw->iml_len,
&trans_pcie->iml_dma_addr,
GFP_KERNEL);
if (!trans_pcie->iml) {
@@ -246,27 +272,15 @@ int iwl_pcie_ctxt_info_gen3_init(struct iwl_trans *trans,
goto err_free_ctxt_info;
}
- memcpy(trans_pcie->iml, trans->iml, trans->iml_len);
-
- iwl_enable_fw_load_int_ctx_info(trans);
-
- /* kick FW self load */
- iwl_write64(trans, CSR_CTXT_INFO_ADDR,
- trans_pcie->ctxt_info_dma_addr);
- iwl_write64(trans, CSR_IML_DATA_ADDR,
- trans_pcie->iml_dma_addr);
- iwl_write32(trans, CSR_IML_SIZE_ADDR, trans->iml_len);
-
- iwl_set_bit(trans, CSR_CTXT_INFO_BOOT_CTRL,
- CSR_AUTO_FUNC_BOOT_ENA);
+ memcpy(trans_pcie->iml, fw->iml, fw->iml_len);
return 0;
err_free_ctxt_info:
- dma_free_coherent(trans->dev, sizeof(*trans_pcie->ctxt_info_gen3),
- trans_pcie->ctxt_info_gen3,
+ dma_free_coherent(trans->dev, sizeof(*trans_pcie->ctxt_info_v2),
+ trans_pcie->ctxt_info_v2,
trans_pcie->ctxt_info_dma_addr);
- trans_pcie->ctxt_info_gen3 = NULL;
+ trans_pcie->ctxt_info_v2 = NULL;
err_free_prph_info:
dma_free_coherent(trans->dev, PAGE_SIZE, prph_info,
trans_pcie->prph_info_dma_addr);
@@ -280,14 +294,31 @@ err_free_prph_scratch:
}
-void iwl_pcie_ctxt_info_gen3_free(struct iwl_trans *trans, bool alive)
+void iwl_pcie_ctxt_info_v2_kick(struct iwl_trans *trans)
+{
+ struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+
+ iwl_enable_fw_load_int_ctx_info(trans, trans->do_top_reset);
+
+ /* kick FW self load */
+ iwl_write64(trans, CSR_CTXT_INFO_ADDR, trans_pcie->ctxt_info_dma_addr);
+ iwl_write64(trans, CSR_IML_DATA_ADDR, trans_pcie->iml_dma_addr);
+ iwl_write32(trans, CSR_IML_SIZE_ADDR, trans_pcie->iml_len);
+
+ iwl_set_bit(trans, CSR_CTXT_INFO_BOOT_CTRL,
+ CSR_AUTO_FUNC_BOOT_ENA);
+}
+
+void iwl_pcie_ctxt_info_v2_free(struct iwl_trans *trans, bool alive)
{
struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
if (trans_pcie->iml) {
- dma_free_coherent(trans->dev, trans->iml_len, trans_pcie->iml,
+ dma_free_coherent(trans->dev, trans_pcie->iml_len,
+ trans_pcie->iml,
trans_pcie->iml_dma_addr);
trans_pcie->iml_dma_addr = 0;
+ trans_pcie->iml_len = 0;
trans_pcie->iml = NULL;
}
@@ -296,15 +327,15 @@ void iwl_pcie_ctxt_info_gen3_free(struct iwl_trans *trans, bool alive)
if (alive)
return;
- if (!trans_pcie->ctxt_info_gen3)
+ if (!trans_pcie->ctxt_info_v2)
return;
- /* ctxt_info_gen3 and prph_scratch are still needed for PNVM load */
- dma_free_coherent(trans->dev, sizeof(*trans_pcie->ctxt_info_gen3),
- trans_pcie->ctxt_info_gen3,
+ /* ctxt_info_v2 and prph_scratch are still needed for PNVM load */
+ dma_free_coherent(trans->dev, sizeof(*trans_pcie->ctxt_info_v2),
+ trans_pcie->ctxt_info_v2,
trans_pcie->ctxt_info_dma_addr);
trans_pcie->ctxt_info_dma_addr = 0;
- trans_pcie->ctxt_info_gen3 = NULL;
+ trans_pcie->ctxt_info_v2 = NULL;
dma_free_coherent(trans->dev, sizeof(*trans_pcie->prph_scratch),
trans_pcie->prph_scratch,
@@ -319,9 +350,9 @@ void iwl_pcie_ctxt_info_gen3_free(struct iwl_trans *trans, bool alive)
trans_pcie->prph_info = NULL;
}
-static int iwl_pcie_load_payloads_continuously(struct iwl_trans *trans,
- const struct iwl_pnvm_image *pnvm_data,
- struct iwl_dram_data *dram)
+static int iwl_pcie_load_payloads_contig(struct iwl_trans *trans,
+ const struct iwl_pnvm_image *pnvm_data,
+ struct iwl_dram_data *dram)
{
u32 len, len0, len1;
@@ -408,9 +439,9 @@ static int iwl_pcie_load_payloads_segments
}
-int iwl_trans_pcie_ctx_info_gen3_load_pnvm(struct iwl_trans *trans,
- const struct iwl_pnvm_image *pnvm_payloads,
- const struct iwl_ucode_capabilities *capa)
+int iwl_trans_pcie_ctx_info_v2_load_pnvm(struct iwl_trans *trans,
+ const struct iwl_pnvm_image *pnvm_payloads,
+ const struct iwl_ucode_capabilities *capa)
{
struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
struct iwl_prph_scratch_ctrl_cfg *prph_sc_ctrl =
@@ -425,7 +456,7 @@ int iwl_trans_pcie_ctx_info_gen3_load_pnvm(struct iwl_trans *trans,
if (WARN_ON(prph_sc_ctrl->pnvm_cfg.pnvm_size))
return -EBUSY;
- if (trans->trans_cfg->device_family < IWL_DEVICE_FAMILY_AX210)
+ if (trans->mac_cfg->device_family < IWL_DEVICE_FAMILY_AX210)
return 0;
if (!pnvm_payloads->n_chunks) {
@@ -442,10 +473,8 @@ int iwl_trans_pcie_ctx_info_gen3_load_pnvm(struct iwl_trans *trans,
trans->pnvm_loaded = true;
} else {
/* save only in one DRAM section */
- ret = iwl_pcie_load_payloads_continuously
- (trans,
- pnvm_payloads,
- &dram_regions->drams[0]);
+ ret = iwl_pcie_load_payloads_contig(trans, pnvm_payloads,
+ &dram_regions->drams[0]);
if (!ret) {
dram_regions->n_regions = 1;
trans->pnvm_loaded = true;
@@ -480,7 +509,7 @@ static void iwl_pcie_set_pnvm_segments(struct iwl_trans *trans)
cpu_to_le32(iwl_dram_regions_size(dram_regions));
}
-static void iwl_pcie_set_continuous_pnvm(struct iwl_trans *trans)
+static void iwl_pcie_set_contig_pnvm(struct iwl_trans *trans)
{
struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
struct iwl_prph_scratch_ctrl_cfg *prph_sc_ctrl =
@@ -492,21 +521,21 @@ static void iwl_pcie_set_continuous_pnvm(struct iwl_trans *trans)
cpu_to_le32(trans_pcie->pnvm_data.drams[0].size);
}
-void iwl_trans_pcie_ctx_info_gen3_set_pnvm(struct iwl_trans *trans,
- const struct iwl_ucode_capabilities *capa)
+void iwl_trans_pcie_ctx_info_v2_set_pnvm(struct iwl_trans *trans,
+ const struct iwl_ucode_capabilities *capa)
{
- if (trans->trans_cfg->device_family < IWL_DEVICE_FAMILY_AX210)
+ if (trans->mac_cfg->device_family < IWL_DEVICE_FAMILY_AX210)
return;
if (fw_has_capa(capa, IWL_UCODE_TLV_CAPA_FRAGMENTED_PNVM_IMG))
iwl_pcie_set_pnvm_segments(trans);
else
- iwl_pcie_set_continuous_pnvm(trans);
+ iwl_pcie_set_contig_pnvm(trans);
}
-int iwl_trans_pcie_ctx_info_gen3_load_reduce_power(struct iwl_trans *trans,
- const struct iwl_pnvm_image *payloads,
- const struct iwl_ucode_capabilities *capa)
+int iwl_trans_pcie_ctx_info_v2_load_reduce_power(struct iwl_trans *trans,
+ const struct iwl_pnvm_image *payloads,
+ const struct iwl_ucode_capabilities *capa)
{
struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
struct iwl_prph_scratch_ctrl_cfg *prph_sc_ctrl =
@@ -518,7 +547,7 @@ int iwl_trans_pcie_ctx_info_gen3_load_reduce_power(struct iwl_trans *trans,
if (trans->reduce_power_loaded)
return 0;
- if (trans->trans_cfg->device_family < IWL_DEVICE_FAMILY_AX210)
+ if (trans->mac_cfg->device_family < IWL_DEVICE_FAMILY_AX210)
return 0;
if (WARN_ON(prph_sc_ctrl->reduce_power_cfg.size))
@@ -538,10 +567,8 @@ int iwl_trans_pcie_ctx_info_gen3_load_reduce_power(struct iwl_trans *trans,
trans->reduce_power_loaded = true;
} else {
/* save only in one DRAM section */
- ret = iwl_pcie_load_payloads_continuously
- (trans,
- payloads,
- &dram_regions->drams[0]);
+ ret = iwl_pcie_load_payloads_contig(trans, payloads,
+ &dram_regions->drams[0]);
if (!ret) {
dram_regions->n_regions = 1;
trans->reduce_power_loaded = true;
@@ -564,7 +591,7 @@ static void iwl_pcie_set_reduce_power_segments(struct iwl_trans *trans)
cpu_to_le32(iwl_dram_regions_size(dram_regions));
}
-static void iwl_pcie_set_continuous_reduce_power(struct iwl_trans *trans)
+static void iwl_pcie_set_contig_reduce_power(struct iwl_trans *trans)
{
struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
struct iwl_prph_scratch_ctrl_cfg *prph_sc_ctrl =
@@ -577,15 +604,15 @@ static void iwl_pcie_set_continuous_reduce_power(struct iwl_trans *trans)
}
void
-iwl_trans_pcie_ctx_info_gen3_set_reduce_power(struct iwl_trans *trans,
- const struct iwl_ucode_capabilities *capa)
+iwl_trans_pcie_ctx_info_v2_set_reduce_power(struct iwl_trans *trans,
+ const struct iwl_ucode_capabilities *capa)
{
- if (trans->trans_cfg->device_family < IWL_DEVICE_FAMILY_AX210)
+ if (trans->mac_cfg->device_family < IWL_DEVICE_FAMILY_AX210)
return;
if (fw_has_capa(capa, IWL_UCODE_TLV_CAPA_FRAGMENTED_PNVM_IMG))
iwl_pcie_set_reduce_power_segments(trans);
else
- iwl_pcie_set_continuous_reduce_power(trans);
+ iwl_pcie_set_contig_reduce_power(trans);
}
diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/ctxt-info.c b/drivers/net/wireless/intel/iwlwifi/pcie/ctxt-info.c
index 344e4d5a1c6e..cb36baac14da 100644
--- a/drivers/net/wireless/intel/iwlwifi/pcie/ctxt-info.c
+++ b/drivers/net/wireless/intel/iwlwifi/pcie/ctxt-info.c
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
/*
* Copyright (C) 2017 Intel Deutschland GmbH
- * Copyright (C) 2018-2024 Intel Corporation
+ * Copyright (C) 2018-2025 Intel Corporation
*/
#include "iwl-trans.h"
#include "iwl-fh.h"
@@ -83,7 +83,7 @@ void iwl_pcie_ctxt_info_free_paging(struct iwl_trans *trans)
int iwl_pcie_init_fw_sec(struct iwl_trans *trans,
const struct fw_img *fw,
- struct iwl_context_info_dram *ctxt_dram)
+ struct iwl_context_info_dram_nonfseq *ctxt_dram)
{
struct iwl_self_init_dram *dram = &trans->init_dram;
int i, ret, lmac_cnt, umac_cnt, paging_cnt;
@@ -161,7 +161,7 @@ int iwl_pcie_init_fw_sec(struct iwl_trans *trans,
}
int iwl_pcie_ctxt_info_init(struct iwl_trans *trans,
- const struct fw_img *fw)
+ const struct fw_img *img)
{
struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
struct iwl_context_info *ctxt_info;
@@ -180,11 +180,11 @@ int iwl_pcie_ctxt_info_init(struct iwl_trans *trans,
ctxt_info->version.version = 0;
ctxt_info->version.mac_id =
- cpu_to_le16((u16)trans->hw_rev);
+ cpu_to_le16((u16)trans->info.hw_rev);
/* size is in DWs */
ctxt_info->version.size = cpu_to_le16(sizeof(*ctxt_info) / 4);
- switch (trans_pcie->rx_buf_size) {
+ switch (trans->conf.rx_buf_size) {
case IWL_AMSDU_2K:
rb_size = IWL_CTXT_INFO_RB_SIZE_2K;
break;
@@ -202,10 +202,10 @@ int iwl_pcie_ctxt_info_init(struct iwl_trans *trans,
rb_size = IWL_CTXT_INFO_RB_SIZE_4K;
}
- WARN_ON(RX_QUEUE_CB_SIZE(trans->cfg->num_rbds) > 12);
+ WARN_ON(RX_QUEUE_CB_SIZE(iwl_trans_get_num_rbds(trans)) > 12);
control_flags = IWL_CTXT_INFO_TFD_FORMAT_LONG;
control_flags |=
- u32_encode_bits(RX_QUEUE_CB_SIZE(trans->cfg->num_rbds),
+ u32_encode_bits(RX_QUEUE_CB_SIZE(iwl_trans_get_num_rbds(trans)),
IWL_CTXT_INFO_RB_CB_SIZE);
control_flags |= u32_encode_bits(rb_size, IWL_CTXT_INFO_RB_SIZE);
ctxt_info->control.control_flags = cpu_to_le32(control_flags);
@@ -218,12 +218,12 @@ int iwl_pcie_ctxt_info_init(struct iwl_trans *trans,
/* initialize TX command queue */
ctxt_info->hcmd_cfg.cmd_queue_addr =
- cpu_to_le64(trans_pcie->txqs.txq[trans_pcie->txqs.cmd.q_id]->dma_addr);
+ cpu_to_le64(trans_pcie->txqs.txq[trans->conf.cmd_queue]->dma_addr);
ctxt_info->hcmd_cfg.cmd_queue_size =
TFD_QUEUE_CB_SIZE(IWL_CMD_QUEUE_SIZE);
/* allocate ucode sections in dram and set addresses */
- ret = iwl_pcie_init_fw_sec(trans, fw, &ctxt_info->dram);
+ ret = iwl_pcie_init_fw_sec(trans, img, &ctxt_info->dram);
if (ret) {
dma_free_coherent(trans->dev, sizeof(*trans_pcie->ctxt_info),
ctxt_info, trans_pcie->ctxt_info_dma_addr);
@@ -232,7 +232,7 @@ int iwl_pcie_ctxt_info_init(struct iwl_trans *trans,
trans_pcie->ctxt_info = ctxt_info;
- iwl_enable_fw_load_int_ctx_info(trans);
+ iwl_enable_fw_load_int_ctx_info(trans, false);
/* Configure debug, if exists */
if (iwl_pcie_dbg_on(trans))
diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/drv.c b/drivers/net/wireless/intel/iwlwifi/pcie/drv.c
index e0b657b2f74b..656f8b06c27b 100644
--- a/drivers/net/wireless/intel/iwlwifi/pcie/drv.c
+++ b/drivers/net/wireless/intel/iwlwifi/pcie/drv.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
/*
- * Copyright (C) 2005-2014, 2018-2024 Intel Corporation
+ * Copyright (C) 2005-2014, 2018-2025 Intel Corporation
* Copyright (C) 2013-2015 Intel Mobile Communications GmbH
* Copyright (C) 2016-2017 Intel Deutschland GmbH
*/
@@ -17,15 +17,13 @@
#include "iwl-prph.h"
#include "internal.h"
-#define TRANS_CFG_MARKER BIT(0)
#define _IS_A(cfg, _struct) __builtin_types_compatible_p(typeof(cfg), \
struct _struct)
extern int _invalid_type;
-#define _TRANS_CFG_MARKER(cfg) \
- (__builtin_choose_expr(_IS_A(cfg, iwl_cfg_trans_params), \
- TRANS_CFG_MARKER, \
- __builtin_choose_expr(_IS_A(cfg, iwl_cfg), 0, _invalid_type)))
-#define _ASSIGN_CFG(cfg) (_TRANS_CFG_MARKER(cfg) + (kernel_ulong_t)&(cfg))
+#define _TRANS_CFG_CHECK(cfg) \
+ (__builtin_choose_expr(_IS_A(cfg, iwl_mac_cfg), \
+ 0, _invalid_type))
+#define _ASSIGN_CFG(cfg) (_TRANS_CFG_CHECK(cfg) + (kernel_ulong_t)&(cfg))
#define IWL_PCI_DEVICE(dev, subdev, cfg) \
.vendor = PCI_VENDOR_ID_INTEL, .device = (dev), \
@@ -35,1170 +33,1038 @@ extern int _invalid_type;
/* Hardware specific file defines the PCI IDs table for that hardware module */
VISIBLE_IF_IWLWIFI_KUNIT const struct pci_device_id iwl_hw_card_ids[] = {
#if IS_ENABLED(CONFIG_IWLDVM)
- {IWL_PCI_DEVICE(0x4232, 0x1201, iwl5100_agn_cfg)}, /* Mini Card */
- {IWL_PCI_DEVICE(0x4232, 0x1301, iwl5100_agn_cfg)}, /* Half Mini Card */
- {IWL_PCI_DEVICE(0x4232, 0x1204, iwl5100_agn_cfg)}, /* Mini Card */
- {IWL_PCI_DEVICE(0x4232, 0x1304, iwl5100_agn_cfg)}, /* Half Mini Card */
- {IWL_PCI_DEVICE(0x4232, 0x1205, iwl5100_bgn_cfg)}, /* Mini Card */
- {IWL_PCI_DEVICE(0x4232, 0x1305, iwl5100_bgn_cfg)}, /* Half Mini Card */
- {IWL_PCI_DEVICE(0x4232, 0x1206, iwl5100_abg_cfg)}, /* Mini Card */
- {IWL_PCI_DEVICE(0x4232, 0x1306, iwl5100_abg_cfg)}, /* Half Mini Card */
- {IWL_PCI_DEVICE(0x4232, 0x1221, iwl5100_agn_cfg)}, /* Mini Card */
- {IWL_PCI_DEVICE(0x4232, 0x1321, iwl5100_agn_cfg)}, /* Half Mini Card */
- {IWL_PCI_DEVICE(0x4232, 0x1224, iwl5100_agn_cfg)}, /* Mini Card */
- {IWL_PCI_DEVICE(0x4232, 0x1324, iwl5100_agn_cfg)}, /* Half Mini Card */
- {IWL_PCI_DEVICE(0x4232, 0x1225, iwl5100_bgn_cfg)}, /* Mini Card */
- {IWL_PCI_DEVICE(0x4232, 0x1325, iwl5100_bgn_cfg)}, /* Half Mini Card */
- {IWL_PCI_DEVICE(0x4232, 0x1226, iwl5100_abg_cfg)}, /* Mini Card */
- {IWL_PCI_DEVICE(0x4232, 0x1326, iwl5100_abg_cfg)}, /* Half Mini Card */
- {IWL_PCI_DEVICE(0x4237, 0x1211, iwl5100_agn_cfg)}, /* Mini Card */
- {IWL_PCI_DEVICE(0x4237, 0x1311, iwl5100_agn_cfg)}, /* Half Mini Card */
- {IWL_PCI_DEVICE(0x4237, 0x1214, iwl5100_agn_cfg)}, /* Mini Card */
- {IWL_PCI_DEVICE(0x4237, 0x1314, iwl5100_agn_cfg)}, /* Half Mini Card */
- {IWL_PCI_DEVICE(0x4237, 0x1215, iwl5100_bgn_cfg)}, /* Mini Card */
- {IWL_PCI_DEVICE(0x4237, 0x1315, iwl5100_bgn_cfg)}, /* Half Mini Card */
- {IWL_PCI_DEVICE(0x4237, 0x1216, iwl5100_abg_cfg)}, /* Mini Card */
- {IWL_PCI_DEVICE(0x4237, 0x1316, iwl5100_abg_cfg)}, /* Half Mini Card */
+ {IWL_PCI_DEVICE(0x4232, 0x1201, iwl5000_mac_cfg)}, /* Mini Card */
+ {IWL_PCI_DEVICE(0x4232, 0x1301, iwl5000_mac_cfg)}, /* Half Mini Card */
+ {IWL_PCI_DEVICE(0x4232, 0x1204, iwl5000_mac_cfg)}, /* Mini Card */
+ {IWL_PCI_DEVICE(0x4232, 0x1304, iwl5000_mac_cfg)}, /* Half Mini Card */
+ {IWL_PCI_DEVICE(0x4232, 0x1205, iwl5000_mac_cfg)}, /* Mini Card */
+ {IWL_PCI_DEVICE(0x4232, 0x1305, iwl5000_mac_cfg)}, /* Half Mini Card */
+ {IWL_PCI_DEVICE(0x4232, 0x1206, iwl5000_mac_cfg)}, /* Mini Card */
+ {IWL_PCI_DEVICE(0x4232, 0x1306, iwl5000_mac_cfg)}, /* Half Mini Card */
+ {IWL_PCI_DEVICE(0x4232, 0x1221, iwl5000_mac_cfg)}, /* Mini Card */
+ {IWL_PCI_DEVICE(0x4232, 0x1321, iwl5000_mac_cfg)}, /* Half Mini Card */
+ {IWL_PCI_DEVICE(0x4232, 0x1224, iwl5000_mac_cfg)}, /* Mini Card */
+ {IWL_PCI_DEVICE(0x4232, 0x1324, iwl5000_mac_cfg)}, /* Half Mini Card */
+ {IWL_PCI_DEVICE(0x4232, 0x1225, iwl5000_mac_cfg)}, /* Mini Card */
+ {IWL_PCI_DEVICE(0x4232, 0x1325, iwl5000_mac_cfg)}, /* Half Mini Card */
+ {IWL_PCI_DEVICE(0x4232, 0x1226, iwl5000_mac_cfg)}, /* Mini Card */
+ {IWL_PCI_DEVICE(0x4232, 0x1326, iwl5000_mac_cfg)}, /* Half Mini Card */
+ {IWL_PCI_DEVICE(0x4237, 0x1211, iwl5000_mac_cfg)}, /* Mini Card */
+ {IWL_PCI_DEVICE(0x4237, 0x1311, iwl5000_mac_cfg)}, /* Half Mini Card */
+ {IWL_PCI_DEVICE(0x4237, 0x1214, iwl5000_mac_cfg)}, /* Mini Card */
+ {IWL_PCI_DEVICE(0x4237, 0x1314, iwl5000_mac_cfg)}, /* Half Mini Card */
+ {IWL_PCI_DEVICE(0x4237, 0x1215, iwl5000_mac_cfg)}, /* Mini Card */
+ {IWL_PCI_DEVICE(0x4237, 0x1315, iwl5000_mac_cfg)}, /* Half Mini Card */
+ {IWL_PCI_DEVICE(0x4237, 0x1216, iwl5000_mac_cfg)}, /* Mini Card */
+ {IWL_PCI_DEVICE(0x4237, 0x1316, iwl5000_mac_cfg)}, /* Half Mini Card */
/* 5300 Series WiFi */
- {IWL_PCI_DEVICE(0x4235, 0x1021, iwl5300_agn_cfg)}, /* Mini Card */
- {IWL_PCI_DEVICE(0x4235, 0x1121, iwl5300_agn_cfg)}, /* Half Mini Card */
- {IWL_PCI_DEVICE(0x4235, 0x1024, iwl5300_agn_cfg)}, /* Mini Card */
- {IWL_PCI_DEVICE(0x4235, 0x1124, iwl5300_agn_cfg)}, /* Half Mini Card */
- {IWL_PCI_DEVICE(0x4235, 0x1001, iwl5300_agn_cfg)}, /* Mini Card */
- {IWL_PCI_DEVICE(0x4235, 0x1101, iwl5300_agn_cfg)}, /* Half Mini Card */
- {IWL_PCI_DEVICE(0x4235, 0x1004, iwl5300_agn_cfg)}, /* Mini Card */
- {IWL_PCI_DEVICE(0x4235, 0x1104, iwl5300_agn_cfg)}, /* Half Mini Card */
- {IWL_PCI_DEVICE(0x4236, 0x1011, iwl5300_agn_cfg)}, /* Mini Card */
- {IWL_PCI_DEVICE(0x4236, 0x1111, iwl5300_agn_cfg)}, /* Half Mini Card */
- {IWL_PCI_DEVICE(0x4236, 0x1014, iwl5300_agn_cfg)}, /* Mini Card */
- {IWL_PCI_DEVICE(0x4236, 0x1114, iwl5300_agn_cfg)}, /* Half Mini Card */
+ {IWL_PCI_DEVICE(0x4235, 0x1021, iwl5000_mac_cfg)}, /* Mini Card */
+ {IWL_PCI_DEVICE(0x4235, 0x1121, iwl5000_mac_cfg)}, /* Half Mini Card */
+ {IWL_PCI_DEVICE(0x4235, 0x1024, iwl5000_mac_cfg)}, /* Mini Card */
+ {IWL_PCI_DEVICE(0x4235, 0x1124, iwl5000_mac_cfg)}, /* Half Mini Card */
+ {IWL_PCI_DEVICE(0x4235, 0x1001, iwl5000_mac_cfg)}, /* Mini Card */
+ {IWL_PCI_DEVICE(0x4235, 0x1101, iwl5000_mac_cfg)}, /* Half Mini Card */
+ {IWL_PCI_DEVICE(0x4235, 0x1004, iwl5000_mac_cfg)}, /* Mini Card */
+ {IWL_PCI_DEVICE(0x4235, 0x1104, iwl5000_mac_cfg)}, /* Half Mini Card */
+ {IWL_PCI_DEVICE(0x4236, 0x1011, iwl5000_mac_cfg)}, /* Mini Card */
+ {IWL_PCI_DEVICE(0x4236, 0x1111, iwl5000_mac_cfg)}, /* Half Mini Card */
+ {IWL_PCI_DEVICE(0x4236, 0x1014, iwl5000_mac_cfg)}, /* Mini Card */
+ {IWL_PCI_DEVICE(0x4236, 0x1114, iwl5000_mac_cfg)}, /* Half Mini Card */
/* 5350 Series WiFi/WiMax */
- {IWL_PCI_DEVICE(0x423A, 0x1001, iwl5350_agn_cfg)}, /* Mini Card */
- {IWL_PCI_DEVICE(0x423A, 0x1021, iwl5350_agn_cfg)}, /* Mini Card */
- {IWL_PCI_DEVICE(0x423B, 0x1011, iwl5350_agn_cfg)}, /* Mini Card */
+ {IWL_PCI_DEVICE(0x423A, 0x1001, iwl5000_mac_cfg)}, /* Mini Card */
+ {IWL_PCI_DEVICE(0x423A, 0x1021, iwl5000_mac_cfg)}, /* Mini Card */
+ {IWL_PCI_DEVICE(0x423B, 0x1011, iwl5000_mac_cfg)}, /* Mini Card */
/* 5150 Series Wifi/WiMax */
- {IWL_PCI_DEVICE(0x423C, 0x1201, iwl5150_agn_cfg)}, /* Mini Card */
- {IWL_PCI_DEVICE(0x423C, 0x1301, iwl5150_agn_cfg)}, /* Half Mini Card */
- {IWL_PCI_DEVICE(0x423C, 0x1206, iwl5150_abg_cfg)}, /* Mini Card */
- {IWL_PCI_DEVICE(0x423C, 0x1306, iwl5150_abg_cfg)}, /* Half Mini Card */
- {IWL_PCI_DEVICE(0x423C, 0x1221, iwl5150_agn_cfg)}, /* Mini Card */
- {IWL_PCI_DEVICE(0x423C, 0x1321, iwl5150_agn_cfg)}, /* Half Mini Card */
- {IWL_PCI_DEVICE(0x423C, 0x1326, iwl5150_abg_cfg)}, /* Half Mini Card */
-
- {IWL_PCI_DEVICE(0x423D, 0x1211, iwl5150_agn_cfg)}, /* Mini Card */
- {IWL_PCI_DEVICE(0x423D, 0x1311, iwl5150_agn_cfg)}, /* Half Mini Card */
- {IWL_PCI_DEVICE(0x423D, 0x1216, iwl5150_abg_cfg)}, /* Mini Card */
- {IWL_PCI_DEVICE(0x423D, 0x1316, iwl5150_abg_cfg)}, /* Half Mini Card */
+ {IWL_PCI_DEVICE(0x423C, 0x1201, iwl5150_mac_cfg)}, /* Mini Card */
+ {IWL_PCI_DEVICE(0x423C, 0x1301, iwl5150_mac_cfg)}, /* Half Mini Card */
+ {IWL_PCI_DEVICE(0x423C, 0x1206, iwl5150_mac_cfg)}, /* Mini Card */
+ {IWL_PCI_DEVICE(0x423C, 0x1306, iwl5150_mac_cfg)}, /* Half Mini Card */
+ {IWL_PCI_DEVICE(0x423C, 0x1221, iwl5150_mac_cfg)}, /* Mini Card */
+ {IWL_PCI_DEVICE(0x423C, 0x1321, iwl5150_mac_cfg)}, /* Half Mini Card */
+ {IWL_PCI_DEVICE(0x423C, 0x1326, iwl5150_mac_cfg)}, /* Half Mini Card */
+
+ {IWL_PCI_DEVICE(0x423D, 0x1211, iwl5150_mac_cfg)}, /* Mini Card */
+ {IWL_PCI_DEVICE(0x423D, 0x1311, iwl5150_mac_cfg)}, /* Half Mini Card */
+ {IWL_PCI_DEVICE(0x423D, 0x1216, iwl5150_mac_cfg)}, /* Mini Card */
+ {IWL_PCI_DEVICE(0x423D, 0x1316, iwl5150_mac_cfg)}, /* Half Mini Card */
/* 6x00 Series */
- {IWL_PCI_DEVICE(0x422B, 0x1101, iwl6000_3agn_cfg)},
- {IWL_PCI_DEVICE(0x422B, 0x1108, iwl6000_3agn_cfg)},
- {IWL_PCI_DEVICE(0x422B, 0x1121, iwl6000_3agn_cfg)},
- {IWL_PCI_DEVICE(0x422B, 0x1128, iwl6000_3agn_cfg)},
- {IWL_PCI_DEVICE(0x422C, 0x1301, iwl6000i_2agn_cfg)},
- {IWL_PCI_DEVICE(0x422C, 0x1306, iwl6000i_2abg_cfg)},
- {IWL_PCI_DEVICE(0x422C, 0x1307, iwl6000i_2bg_cfg)},
- {IWL_PCI_DEVICE(0x422C, 0x1321, iwl6000i_2agn_cfg)},
- {IWL_PCI_DEVICE(0x422C, 0x1326, iwl6000i_2abg_cfg)},
- {IWL_PCI_DEVICE(0x4238, 0x1111, iwl6000_3agn_cfg)},
- {IWL_PCI_DEVICE(0x4238, 0x1118, iwl6000_3agn_cfg)},
- {IWL_PCI_DEVICE(0x4239, 0x1311, iwl6000i_2agn_cfg)},
- {IWL_PCI_DEVICE(0x4239, 0x1316, iwl6000i_2abg_cfg)},
+ {IWL_PCI_DEVICE(0x422B, 0x1101, iwl6000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x422B, 0x1108, iwl6000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x422B, 0x1121, iwl6000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x422B, 0x1128, iwl6000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x422C, 0x1301, iwl6000i_mac_cfg)},
+ {IWL_PCI_DEVICE(0x422C, 0x1306, iwl6000i_mac_cfg)},
+ {IWL_PCI_DEVICE(0x422C, 0x1307, iwl6000i_mac_cfg)},
+ {IWL_PCI_DEVICE(0x422C, 0x1321, iwl6000i_mac_cfg)},
+ {IWL_PCI_DEVICE(0x422C, 0x1326, iwl6000i_mac_cfg)},
+ {IWL_PCI_DEVICE(0x4238, 0x1111, iwl6000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x4238, 0x1118, iwl6000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x4239, 0x1311, iwl6000i_mac_cfg)},
+ {IWL_PCI_DEVICE(0x4239, 0x1316, iwl6000i_mac_cfg)},
/* 6x05 Series */
- {IWL_PCI_DEVICE(0x0082, 0x1301, iwl6005_2agn_cfg)},
- {IWL_PCI_DEVICE(0x0082, 0x1306, iwl6005_2abg_cfg)},
- {IWL_PCI_DEVICE(0x0082, 0x1307, iwl6005_2bg_cfg)},
- {IWL_PCI_DEVICE(0x0082, 0x1308, iwl6005_2agn_cfg)},
- {IWL_PCI_DEVICE(0x0082, 0x1321, iwl6005_2agn_cfg)},
- {IWL_PCI_DEVICE(0x0082, 0x1326, iwl6005_2abg_cfg)},
- {IWL_PCI_DEVICE(0x0082, 0x1328, iwl6005_2agn_cfg)},
- {IWL_PCI_DEVICE(0x0085, 0x1311, iwl6005_2agn_cfg)},
- {IWL_PCI_DEVICE(0x0085, 0x1318, iwl6005_2agn_cfg)},
- {IWL_PCI_DEVICE(0x0085, 0x1316, iwl6005_2abg_cfg)},
- {IWL_PCI_DEVICE(0x0082, 0xC020, iwl6005_2agn_sff_cfg)},
- {IWL_PCI_DEVICE(0x0085, 0xC220, iwl6005_2agn_sff_cfg)},
- {IWL_PCI_DEVICE(0x0085, 0xC228, iwl6005_2agn_sff_cfg)},
- {IWL_PCI_DEVICE(0x0082, 0x4820, iwl6005_2agn_d_cfg)},
- {IWL_PCI_DEVICE(0x0082, 0x1304, iwl6005_2agn_mow1_cfg)},/* low 5GHz active */
- {IWL_PCI_DEVICE(0x0082, 0x1305, iwl6005_2agn_mow2_cfg)},/* high 5GHz active */
+ {IWL_PCI_DEVICE(0x0082, 0x1301, iwl6005_mac_cfg)},
+ {IWL_PCI_DEVICE(0x0082, 0x1306, iwl6005_mac_cfg)},
+ {IWL_PCI_DEVICE(0x0082, 0x1307, iwl6005_mac_cfg)},
+ {IWL_PCI_DEVICE(0x0082, 0x1308, iwl6005_mac_cfg)},
+ {IWL_PCI_DEVICE(0x0082, 0x1321, iwl6005_mac_cfg)},
+ {IWL_PCI_DEVICE(0x0082, 0x1326, iwl6005_mac_cfg)},
+ {IWL_PCI_DEVICE(0x0082, 0x1328, iwl6005_mac_cfg)},
+ {IWL_PCI_DEVICE(0x0085, 0x1311, iwl6005_mac_cfg)},
+ {IWL_PCI_DEVICE(0x0085, 0x1318, iwl6005_mac_cfg)},
+ {IWL_PCI_DEVICE(0x0085, 0x1316, iwl6005_mac_cfg)},
+ {IWL_PCI_DEVICE(0x0082, 0xC020, iwl6005_mac_cfg)},
+ {IWL_PCI_DEVICE(0x0085, 0xC220, iwl6005_mac_cfg)},
+ {IWL_PCI_DEVICE(0x0085, 0xC228, iwl6005_mac_cfg)},
+ {IWL_PCI_DEVICE(0x0082, 0x4820, iwl6005_mac_cfg)},
+ {IWL_PCI_DEVICE(0x0082, 0x1304, iwl6005_mac_cfg)},/* low 5GHz active */
+ {IWL_PCI_DEVICE(0x0082, 0x1305, iwl6005_mac_cfg)},/* high 5GHz active */
/* 6x30 Series */
- {IWL_PCI_DEVICE(0x008A, 0x5305, iwl1030_bgn_cfg)},
- {IWL_PCI_DEVICE(0x008A, 0x5307, iwl1030_bg_cfg)},
- {IWL_PCI_DEVICE(0x008A, 0x5325, iwl1030_bgn_cfg)},
- {IWL_PCI_DEVICE(0x008A, 0x5327, iwl1030_bg_cfg)},
- {IWL_PCI_DEVICE(0x008B, 0x5315, iwl1030_bgn_cfg)},
- {IWL_PCI_DEVICE(0x008B, 0x5317, iwl1030_bg_cfg)},
- {IWL_PCI_DEVICE(0x0090, 0x5211, iwl6030_2agn_cfg)},
- {IWL_PCI_DEVICE(0x0090, 0x5215, iwl6030_2bgn_cfg)},
- {IWL_PCI_DEVICE(0x0090, 0x5216, iwl6030_2abg_cfg)},
- {IWL_PCI_DEVICE(0x0091, 0x5201, iwl6030_2agn_cfg)},
- {IWL_PCI_DEVICE(0x0091, 0x5205, iwl6030_2bgn_cfg)},
- {IWL_PCI_DEVICE(0x0091, 0x5206, iwl6030_2abg_cfg)},
- {IWL_PCI_DEVICE(0x0091, 0x5207, iwl6030_2bg_cfg)},
- {IWL_PCI_DEVICE(0x0091, 0x5221, iwl6030_2agn_cfg)},
- {IWL_PCI_DEVICE(0x0091, 0x5225, iwl6030_2bgn_cfg)},
- {IWL_PCI_DEVICE(0x0091, 0x5226, iwl6030_2abg_cfg)},
+ {IWL_PCI_DEVICE(0x008A, 0x5305, iwl1000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x008A, 0x5307, iwl1000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x008A, 0x5325, iwl1000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x008A, 0x5327, iwl1000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x008B, 0x5315, iwl1000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x008B, 0x5317, iwl1000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x0090, 0x5211, iwl6030_mac_cfg)},
+ {IWL_PCI_DEVICE(0x0090, 0x5215, iwl6030_mac_cfg)},
+ {IWL_PCI_DEVICE(0x0090, 0x5216, iwl6030_mac_cfg)},
+ {IWL_PCI_DEVICE(0x0091, 0x5201, iwl6030_mac_cfg)},
+ {IWL_PCI_DEVICE(0x0091, 0x5205, iwl6030_mac_cfg)},
+ {IWL_PCI_DEVICE(0x0091, 0x5206, iwl6030_mac_cfg)},
+ {IWL_PCI_DEVICE(0x0091, 0x5207, iwl6030_mac_cfg)},
+ {IWL_PCI_DEVICE(0x0091, 0x5221, iwl6030_mac_cfg)},
+ {IWL_PCI_DEVICE(0x0091, 0x5225, iwl6030_mac_cfg)},
+ {IWL_PCI_DEVICE(0x0091, 0x5226, iwl6030_mac_cfg)},
/* 6x50 WiFi/WiMax Series */
- {IWL_PCI_DEVICE(0x0087, 0x1301, iwl6050_2agn_cfg)},
- {IWL_PCI_DEVICE(0x0087, 0x1306, iwl6050_2abg_cfg)},
- {IWL_PCI_DEVICE(0x0087, 0x1321, iwl6050_2agn_cfg)},
- {IWL_PCI_DEVICE(0x0087, 0x1326, iwl6050_2abg_cfg)},
- {IWL_PCI_DEVICE(0x0089, 0x1311, iwl6050_2agn_cfg)},
- {IWL_PCI_DEVICE(0x0089, 0x1316, iwl6050_2abg_cfg)},
+ {IWL_PCI_DEVICE(0x0087, 0x1301, iwl6050_mac_cfg)},
+ {IWL_PCI_DEVICE(0x0087, 0x1306, iwl6050_mac_cfg)},
+ {IWL_PCI_DEVICE(0x0087, 0x1321, iwl6050_mac_cfg)},
+ {IWL_PCI_DEVICE(0x0087, 0x1326, iwl6050_mac_cfg)},
+ {IWL_PCI_DEVICE(0x0089, 0x1311, iwl6050_mac_cfg)},
+ {IWL_PCI_DEVICE(0x0089, 0x1316, iwl6050_mac_cfg)},
/* 6150 WiFi/WiMax Series */
- {IWL_PCI_DEVICE(0x0885, 0x1305, iwl6150_bgn_cfg)},
- {IWL_PCI_DEVICE(0x0885, 0x1307, iwl6150_bg_cfg)},
- {IWL_PCI_DEVICE(0x0885, 0x1325, iwl6150_bgn_cfg)},
- {IWL_PCI_DEVICE(0x0885, 0x1327, iwl6150_bg_cfg)},
- {IWL_PCI_DEVICE(0x0886, 0x1315, iwl6150_bgn_cfg)},
- {IWL_PCI_DEVICE(0x0886, 0x1317, iwl6150_bg_cfg)},
+ {IWL_PCI_DEVICE(0x0885, 0x1305, iwl6150_mac_cfg)},
+ {IWL_PCI_DEVICE(0x0885, 0x1307, iwl6150_mac_cfg)},
+ {IWL_PCI_DEVICE(0x0885, 0x1325, iwl6150_mac_cfg)},
+ {IWL_PCI_DEVICE(0x0885, 0x1327, iwl6150_mac_cfg)},
+ {IWL_PCI_DEVICE(0x0886, 0x1315, iwl6150_mac_cfg)},
+ {IWL_PCI_DEVICE(0x0886, 0x1317, iwl6150_mac_cfg)},
/* 1000 Series WiFi */
- {IWL_PCI_DEVICE(0x0083, 0x1205, iwl1000_bgn_cfg)},
- {IWL_PCI_DEVICE(0x0083, 0x1305, iwl1000_bgn_cfg)},
- {IWL_PCI_DEVICE(0x0083, 0x1225, iwl1000_bgn_cfg)},
- {IWL_PCI_DEVICE(0x0083, 0x1325, iwl1000_bgn_cfg)},
- {IWL_PCI_DEVICE(0x0084, 0x1215, iwl1000_bgn_cfg)},
- {IWL_PCI_DEVICE(0x0084, 0x1315, iwl1000_bgn_cfg)},
- {IWL_PCI_DEVICE(0x0083, 0x1206, iwl1000_bg_cfg)},
- {IWL_PCI_DEVICE(0x0083, 0x1306, iwl1000_bg_cfg)},
- {IWL_PCI_DEVICE(0x0083, 0x1226, iwl1000_bg_cfg)},
- {IWL_PCI_DEVICE(0x0083, 0x1326, iwl1000_bg_cfg)},
- {IWL_PCI_DEVICE(0x0084, 0x1216, iwl1000_bg_cfg)},
- {IWL_PCI_DEVICE(0x0084, 0x1316, iwl1000_bg_cfg)},
+ {IWL_PCI_DEVICE(0x0083, 0x1205, iwl1000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x0083, 0x1305, iwl1000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x0083, 0x1225, iwl1000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x0083, 0x1325, iwl1000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x0084, 0x1215, iwl1000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x0084, 0x1315, iwl1000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x0083, 0x1206, iwl1000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x0083, 0x1306, iwl1000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x0083, 0x1226, iwl1000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x0083, 0x1326, iwl1000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x0084, 0x1216, iwl1000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x0084, 0x1316, iwl1000_mac_cfg)},
/* 100 Series WiFi */
- {IWL_PCI_DEVICE(0x08AE, 0x1005, iwl100_bgn_cfg)},
- {IWL_PCI_DEVICE(0x08AE, 0x1007, iwl100_bg_cfg)},
- {IWL_PCI_DEVICE(0x08AF, 0x1015, iwl100_bgn_cfg)},
- {IWL_PCI_DEVICE(0x08AF, 0x1017, iwl100_bg_cfg)},
- {IWL_PCI_DEVICE(0x08AE, 0x1025, iwl100_bgn_cfg)},
- {IWL_PCI_DEVICE(0x08AE, 0x1027, iwl100_bg_cfg)},
+ {IWL_PCI_DEVICE(0x08AE, 0x1005, iwl1000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x08AE, 0x1007, iwl1000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x08AF, 0x1015, iwl1000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x08AF, 0x1017, iwl1000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x08AE, 0x1025, iwl1000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x08AE, 0x1027, iwl1000_mac_cfg)},
/* 130 Series WiFi */
- {IWL_PCI_DEVICE(0x0896, 0x5005, iwl130_bgn_cfg)},
- {IWL_PCI_DEVICE(0x0896, 0x5007, iwl130_bg_cfg)},
- {IWL_PCI_DEVICE(0x0897, 0x5015, iwl130_bgn_cfg)},
- {IWL_PCI_DEVICE(0x0897, 0x5017, iwl130_bg_cfg)},
- {IWL_PCI_DEVICE(0x0896, 0x5025, iwl130_bgn_cfg)},
- {IWL_PCI_DEVICE(0x0896, 0x5027, iwl130_bg_cfg)},
+ {IWL_PCI_DEVICE(0x0896, 0x5005, iwl1000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x0896, 0x5007, iwl1000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x0897, 0x5015, iwl1000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x0897, 0x5017, iwl1000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x0896, 0x5025, iwl1000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x0896, 0x5027, iwl1000_mac_cfg)},
/* 2x00 Series */
- {IWL_PCI_DEVICE(0x0890, 0x4022, iwl2000_2bgn_cfg)},
- {IWL_PCI_DEVICE(0x0891, 0x4222, iwl2000_2bgn_cfg)},
- {IWL_PCI_DEVICE(0x0890, 0x4422, iwl2000_2bgn_cfg)},
- {IWL_PCI_DEVICE(0x0890, 0x4822, iwl2000_2bgn_d_cfg)},
+ {IWL_PCI_DEVICE(0x0890, 0x4022, iwl2000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x0891, 0x4222, iwl2000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x0890, 0x4422, iwl2000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x0890, 0x4822, iwl2000_mac_cfg)},
/* 2x30 Series */
- {IWL_PCI_DEVICE(0x0887, 0x4062, iwl2030_2bgn_cfg)},
- {IWL_PCI_DEVICE(0x0888, 0x4262, iwl2030_2bgn_cfg)},
- {IWL_PCI_DEVICE(0x0887, 0x4462, iwl2030_2bgn_cfg)},
+ {IWL_PCI_DEVICE(0x0887, 0x4062, iwl2030_mac_cfg)},
+ {IWL_PCI_DEVICE(0x0888, 0x4262, iwl2030_mac_cfg)},
+ {IWL_PCI_DEVICE(0x0887, 0x4462, iwl2030_mac_cfg)},
/* 6x35 Series */
- {IWL_PCI_DEVICE(0x088E, 0x4060, iwl6035_2agn_cfg)},
- {IWL_PCI_DEVICE(0x088E, 0x406A, iwl6035_2agn_sff_cfg)},
- {IWL_PCI_DEVICE(0x088F, 0x4260, iwl6035_2agn_cfg)},
- {IWL_PCI_DEVICE(0x088F, 0x426A, iwl6035_2agn_sff_cfg)},
- {IWL_PCI_DEVICE(0x088E, 0x4460, iwl6035_2agn_cfg)},
- {IWL_PCI_DEVICE(0x088E, 0x446A, iwl6035_2agn_sff_cfg)},
- {IWL_PCI_DEVICE(0x088E, 0x4860, iwl6035_2agn_cfg)},
- {IWL_PCI_DEVICE(0x088F, 0x5260, iwl6035_2agn_cfg)},
+ {IWL_PCI_DEVICE(0x088E, 0x4060, iwl6030_mac_cfg)},
+ {IWL_PCI_DEVICE(0x088E, 0x406A, iwl6030_mac_cfg)},
+ {IWL_PCI_DEVICE(0x088F, 0x4260, iwl6030_mac_cfg)},
+ {IWL_PCI_DEVICE(0x088F, 0x426A, iwl6030_mac_cfg)},
+ {IWL_PCI_DEVICE(0x088E, 0x4460, iwl6030_mac_cfg)},
+ {IWL_PCI_DEVICE(0x088E, 0x446A, iwl6030_mac_cfg)},
+ {IWL_PCI_DEVICE(0x088E, 0x4860, iwl6030_mac_cfg)},
+ {IWL_PCI_DEVICE(0x088F, 0x5260, iwl6030_mac_cfg)},
/* 105 Series */
- {IWL_PCI_DEVICE(0x0894, 0x0022, iwl105_bgn_cfg)},
- {IWL_PCI_DEVICE(0x0895, 0x0222, iwl105_bgn_cfg)},
- {IWL_PCI_DEVICE(0x0894, 0x0422, iwl105_bgn_cfg)},
- {IWL_PCI_DEVICE(0x0894, 0x0822, iwl105_bgn_d_cfg)},
+ {IWL_PCI_DEVICE(0x0894, 0x0022, iwl105_mac_cfg)},
+ {IWL_PCI_DEVICE(0x0895, 0x0222, iwl105_mac_cfg)},
+ {IWL_PCI_DEVICE(0x0894, 0x0422, iwl105_mac_cfg)},
+ {IWL_PCI_DEVICE(0x0894, 0x0822, iwl105_mac_cfg)},
/* 135 Series */
- {IWL_PCI_DEVICE(0x0892, 0x0062, iwl135_bgn_cfg)},
- {IWL_PCI_DEVICE(0x0893, 0x0262, iwl135_bgn_cfg)},
- {IWL_PCI_DEVICE(0x0892, 0x0462, iwl135_bgn_cfg)},
+ {IWL_PCI_DEVICE(0x0892, 0x0062, iwl135_mac_cfg)},
+ {IWL_PCI_DEVICE(0x0893, 0x0262, iwl135_mac_cfg)},
+ {IWL_PCI_DEVICE(0x0892, 0x0462, iwl135_mac_cfg)},
#endif /* CONFIG_IWLDVM */
#if IS_ENABLED(CONFIG_IWLMVM)
/* 7260 Series */
- {IWL_PCI_DEVICE(0x08B1, 0x4070, iwl7260_2ac_cfg)},
- {IWL_PCI_DEVICE(0x08B1, 0x4072, iwl7260_2ac_cfg)},
- {IWL_PCI_DEVICE(0x08B1, 0x4170, iwl7260_2ac_cfg)},
- {IWL_PCI_DEVICE(0x08B1, 0x4C60, iwl7260_2ac_cfg)},
- {IWL_PCI_DEVICE(0x08B1, 0x4C70, iwl7260_2ac_cfg)},
- {IWL_PCI_DEVICE(0x08B1, 0x4060, iwl7260_2n_cfg)},
- {IWL_PCI_DEVICE(0x08B1, 0x406A, iwl7260_2n_cfg)},
- {IWL_PCI_DEVICE(0x08B1, 0x4160, iwl7260_2n_cfg)},
- {IWL_PCI_DEVICE(0x08B1, 0x4062, iwl7260_n_cfg)},
- {IWL_PCI_DEVICE(0x08B1, 0x4162, iwl7260_n_cfg)},
- {IWL_PCI_DEVICE(0x08B2, 0x4270, iwl7260_2ac_cfg)},
- {IWL_PCI_DEVICE(0x08B2, 0x4272, iwl7260_2ac_cfg)},
- {IWL_PCI_DEVICE(0x08B2, 0x4260, iwl7260_2n_cfg)},
- {IWL_PCI_DEVICE(0x08B2, 0x426A, iwl7260_2n_cfg)},
- {IWL_PCI_DEVICE(0x08B2, 0x4262, iwl7260_n_cfg)},
- {IWL_PCI_DEVICE(0x08B1, 0x4470, iwl7260_2ac_cfg)},
- {IWL_PCI_DEVICE(0x08B1, 0x4472, iwl7260_2ac_cfg)},
- {IWL_PCI_DEVICE(0x08B1, 0x4460, iwl7260_2n_cfg)},
- {IWL_PCI_DEVICE(0x08B1, 0x446A, iwl7260_2n_cfg)},
- {IWL_PCI_DEVICE(0x08B1, 0x4462, iwl7260_n_cfg)},
- {IWL_PCI_DEVICE(0x08B1, 0x4870, iwl7260_2ac_cfg)},
- {IWL_PCI_DEVICE(0x08B1, 0x486E, iwl7260_2ac_cfg)},
- {IWL_PCI_DEVICE(0x08B1, 0x4A70, iwl7260_2ac_cfg_high_temp)},
- {IWL_PCI_DEVICE(0x08B1, 0x4A6E, iwl7260_2ac_cfg_high_temp)},
- {IWL_PCI_DEVICE(0x08B1, 0x4A6C, iwl7260_2ac_cfg_high_temp)},
- {IWL_PCI_DEVICE(0x08B1, 0x4570, iwl7260_2ac_cfg)},
- {IWL_PCI_DEVICE(0x08B1, 0x4560, iwl7260_2n_cfg)},
- {IWL_PCI_DEVICE(0x08B2, 0x4370, iwl7260_2ac_cfg)},
- {IWL_PCI_DEVICE(0x08B2, 0x4360, iwl7260_2n_cfg)},
- {IWL_PCI_DEVICE(0x08B1, 0x5070, iwl7260_2ac_cfg)},
- {IWL_PCI_DEVICE(0x08B1, 0x5072, iwl7260_2ac_cfg)},
- {IWL_PCI_DEVICE(0x08B1, 0x5170, iwl7260_2ac_cfg)},
- {IWL_PCI_DEVICE(0x08B1, 0x5770, iwl7260_2ac_cfg)},
- {IWL_PCI_DEVICE(0x08B1, 0x4020, iwl7260_2n_cfg)},
- {IWL_PCI_DEVICE(0x08B1, 0x402A, iwl7260_2n_cfg)},
- {IWL_PCI_DEVICE(0x08B2, 0x4220, iwl7260_2n_cfg)},
- {IWL_PCI_DEVICE(0x08B1, 0x4420, iwl7260_2n_cfg)},
- {IWL_PCI_DEVICE(0x08B1, 0xC070, iwl7260_2ac_cfg)},
- {IWL_PCI_DEVICE(0x08B1, 0xC072, iwl7260_2ac_cfg)},
- {IWL_PCI_DEVICE(0x08B1, 0xC170, iwl7260_2ac_cfg)},
- {IWL_PCI_DEVICE(0x08B1, 0xC060, iwl7260_2n_cfg)},
- {IWL_PCI_DEVICE(0x08B1, 0xC06A, iwl7260_2n_cfg)},
- {IWL_PCI_DEVICE(0x08B1, 0xC160, iwl7260_2n_cfg)},
- {IWL_PCI_DEVICE(0x08B1, 0xC062, iwl7260_n_cfg)},
- {IWL_PCI_DEVICE(0x08B1, 0xC162, iwl7260_n_cfg)},
- {IWL_PCI_DEVICE(0x08B1, 0xC770, iwl7260_2ac_cfg)},
- {IWL_PCI_DEVICE(0x08B1, 0xC760, iwl7260_2n_cfg)},
- {IWL_PCI_DEVICE(0x08B2, 0xC270, iwl7260_2ac_cfg)},
- {IWL_PCI_DEVICE(0x08B1, 0xCC70, iwl7260_2ac_cfg)},
- {IWL_PCI_DEVICE(0x08B1, 0xCC60, iwl7260_2ac_cfg)},
- {IWL_PCI_DEVICE(0x08B2, 0xC272, iwl7260_2ac_cfg)},
- {IWL_PCI_DEVICE(0x08B2, 0xC260, iwl7260_2n_cfg)},
- {IWL_PCI_DEVICE(0x08B2, 0xC26A, iwl7260_n_cfg)},
- {IWL_PCI_DEVICE(0x08B2, 0xC262, iwl7260_n_cfg)},
- {IWL_PCI_DEVICE(0x08B1, 0xC470, iwl7260_2ac_cfg)},
- {IWL_PCI_DEVICE(0x08B1, 0xC472, iwl7260_2ac_cfg)},
- {IWL_PCI_DEVICE(0x08B1, 0xC460, iwl7260_2n_cfg)},
- {IWL_PCI_DEVICE(0x08B1, 0xC462, iwl7260_n_cfg)},
- {IWL_PCI_DEVICE(0x08B1, 0xC570, iwl7260_2ac_cfg)},
- {IWL_PCI_DEVICE(0x08B1, 0xC560, iwl7260_2n_cfg)},
- {IWL_PCI_DEVICE(0x08B2, 0xC370, iwl7260_2ac_cfg)},
- {IWL_PCI_DEVICE(0x08B1, 0xC360, iwl7260_2n_cfg)},
- {IWL_PCI_DEVICE(0x08B1, 0xC020, iwl7260_2n_cfg)},
- {IWL_PCI_DEVICE(0x08B1, 0xC02A, iwl7260_2n_cfg)},
- {IWL_PCI_DEVICE(0x08B2, 0xC220, iwl7260_2n_cfg)},
- {IWL_PCI_DEVICE(0x08B1, 0xC420, iwl7260_2n_cfg)},
+ {IWL_PCI_DEVICE(0x08B1, 0x4070, iwl7000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x08B1, 0x4072, iwl7000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x08B1, 0x4170, iwl7000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x08B1, 0x4C60, iwl7000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x08B1, 0x4C70, iwl7000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x08B1, 0x4060, iwl7000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x08B1, 0x406A, iwl7000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x08B1, 0x4160, iwl7000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x08B1, 0x4062, iwl7000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x08B1, 0x4162, iwl7000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x08B2, 0x4270, iwl7000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x08B2, 0x4272, iwl7000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x08B2, 0x4260, iwl7000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x08B2, 0x426A, iwl7000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x08B2, 0x4262, iwl7000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x08B1, 0x4470, iwl7000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x08B1, 0x4472, iwl7000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x08B1, 0x4460, iwl7000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x08B1, 0x446A, iwl7000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x08B1, 0x4462, iwl7000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x08B1, 0x4870, iwl7000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x08B1, 0x486E, iwl7000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x08B1, 0x4A70, iwl7000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x08B1, 0x4A6E, iwl7000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x08B1, 0x4A6C, iwl7000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x08B1, 0x4570, iwl7000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x08B1, 0x4560, iwl7000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x08B2, 0x4370, iwl7000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x08B2, 0x4360, iwl7000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x08B1, 0x5070, iwl7000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x08B1, 0x5072, iwl7000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x08B1, 0x5170, iwl7000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x08B1, 0x5770, iwl7000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x08B1, 0x4020, iwl7000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x08B1, 0x402A, iwl7000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x08B2, 0x4220, iwl7000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x08B1, 0x4420, iwl7000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x08B1, 0xC070, iwl7000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x08B1, 0xC072, iwl7000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x08B1, 0xC170, iwl7000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x08B1, 0xC060, iwl7000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x08B1, 0xC06A, iwl7000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x08B1, 0xC160, iwl7000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x08B1, 0xC062, iwl7000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x08B1, 0xC162, iwl7000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x08B1, 0xC770, iwl7000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x08B1, 0xC760, iwl7000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x08B2, 0xC270, iwl7000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x08B1, 0xCC70, iwl7000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x08B1, 0xCC60, iwl7000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x08B2, 0xC272, iwl7000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x08B2, 0xC260, iwl7000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x08B2, 0xC26A, iwl7000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x08B2, 0xC262, iwl7000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x08B1, 0xC470, iwl7000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x08B1, 0xC472, iwl7000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x08B1, 0xC460, iwl7000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x08B1, 0xC462, iwl7000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x08B1, 0xC570, iwl7000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x08B1, 0xC560, iwl7000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x08B2, 0xC370, iwl7000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x08B1, 0xC360, iwl7000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x08B1, 0xC020, iwl7000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x08B1, 0xC02A, iwl7000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x08B2, 0xC220, iwl7000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x08B1, 0xC420, iwl7000_mac_cfg)},
/* 3160 Series */
- {IWL_PCI_DEVICE(0x08B3, 0x0070, iwl3160_2ac_cfg)},
- {IWL_PCI_DEVICE(0x08B3, 0x0072, iwl3160_2ac_cfg)},
- {IWL_PCI_DEVICE(0x08B3, 0x0170, iwl3160_2ac_cfg)},
- {IWL_PCI_DEVICE(0x08B3, 0x0172, iwl3160_2ac_cfg)},
- {IWL_PCI_DEVICE(0x08B3, 0x0060, iwl3160_2n_cfg)},
- {IWL_PCI_DEVICE(0x08B3, 0x0062, iwl3160_n_cfg)},
- {IWL_PCI_DEVICE(0x08B4, 0x0270, iwl3160_2ac_cfg)},
- {IWL_PCI_DEVICE(0x08B4, 0x0272, iwl3160_2ac_cfg)},
- {IWL_PCI_DEVICE(0x08B3, 0x0470, iwl3160_2ac_cfg)},
- {IWL_PCI_DEVICE(0x08B3, 0x0472, iwl3160_2ac_cfg)},
- {IWL_PCI_DEVICE(0x08B4, 0x0370, iwl3160_2ac_cfg)},
- {IWL_PCI_DEVICE(0x08B3, 0x8070, iwl3160_2ac_cfg)},
- {IWL_PCI_DEVICE(0x08B3, 0x8072, iwl3160_2ac_cfg)},
- {IWL_PCI_DEVICE(0x08B3, 0x8170, iwl3160_2ac_cfg)},
- {IWL_PCI_DEVICE(0x08B3, 0x8172, iwl3160_2ac_cfg)},
- {IWL_PCI_DEVICE(0x08B3, 0x8060, iwl3160_2n_cfg)},
- {IWL_PCI_DEVICE(0x08B3, 0x8062, iwl3160_n_cfg)},
- {IWL_PCI_DEVICE(0x08B4, 0x8270, iwl3160_2ac_cfg)},
- {IWL_PCI_DEVICE(0x08B4, 0x8370, iwl3160_2ac_cfg)},
- {IWL_PCI_DEVICE(0x08B4, 0x8272, iwl3160_2ac_cfg)},
- {IWL_PCI_DEVICE(0x08B3, 0x8470, iwl3160_2ac_cfg)},
- {IWL_PCI_DEVICE(0x08B3, 0x8570, iwl3160_2ac_cfg)},
- {IWL_PCI_DEVICE(0x08B3, 0x1070, iwl3160_2ac_cfg)},
- {IWL_PCI_DEVICE(0x08B3, 0x1170, iwl3160_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x08B3, 0x0070, iwl7000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x08B3, 0x0072, iwl7000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x08B3, 0x0170, iwl7000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x08B3, 0x0172, iwl7000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x08B3, 0x0060, iwl7000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x08B3, 0x0062, iwl7000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x08B4, 0x0270, iwl7000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x08B4, 0x0272, iwl7000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x08B3, 0x0470, iwl7000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x08B3, 0x0472, iwl7000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x08B4, 0x0370, iwl7000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x08B3, 0x8070, iwl7000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x08B3, 0x8072, iwl7000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x08B3, 0x8170, iwl7000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x08B3, 0x8172, iwl7000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x08B3, 0x8060, iwl7000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x08B3, 0x8062, iwl7000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x08B4, 0x8270, iwl7000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x08B4, 0x8370, iwl7000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x08B4, 0x8272, iwl7000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x08B3, 0x8470, iwl7000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x08B3, 0x8570, iwl7000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x08B3, 0x1070, iwl7000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x08B3, 0x1170, iwl7000_mac_cfg)},
/* 3165 Series */
- {IWL_PCI_DEVICE(0x3165, 0x4010, iwl3165_2ac_cfg)},
- {IWL_PCI_DEVICE(0x3165, 0x4012, iwl3165_2ac_cfg)},
- {IWL_PCI_DEVICE(0x3166, 0x4212, iwl3165_2ac_cfg)},
- {IWL_PCI_DEVICE(0x3165, 0x4410, iwl3165_2ac_cfg)},
- {IWL_PCI_DEVICE(0x3165, 0x4510, iwl3165_2ac_cfg)},
- {IWL_PCI_DEVICE(0x3165, 0x4110, iwl3165_2ac_cfg)},
- {IWL_PCI_DEVICE(0x3166, 0x4310, iwl3165_2ac_cfg)},
- {IWL_PCI_DEVICE(0x3166, 0x4210, iwl3165_2ac_cfg)},
- {IWL_PCI_DEVICE(0x3165, 0x8010, iwl3165_2ac_cfg)},
- {IWL_PCI_DEVICE(0x3165, 0x8110, iwl3165_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x3165, 0x4010, iwl7000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x3165, 0x4012, iwl7000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x3166, 0x4212, iwl7000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x3165, 0x4410, iwl7000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x3165, 0x4510, iwl7000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x3165, 0x4110, iwl7000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x3166, 0x4310, iwl7000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x3166, 0x4210, iwl7000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x3165, 0x8010, iwl7000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x3165, 0x8110, iwl7000_mac_cfg)},
/* 3168 Series */
- {IWL_PCI_DEVICE(0x24FB, 0x2010, iwl3168_2ac_cfg)},
- {IWL_PCI_DEVICE(0x24FB, 0x2110, iwl3168_2ac_cfg)},
- {IWL_PCI_DEVICE(0x24FB, 0x2050, iwl3168_2ac_cfg)},
- {IWL_PCI_DEVICE(0x24FB, 0x2150, iwl3168_2ac_cfg)},
- {IWL_PCI_DEVICE(0x24FB, 0x0000, iwl3168_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x24FB, 0x2010, iwl7000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x24FB, 0x2110, iwl7000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x24FB, 0x2050, iwl7000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x24FB, 0x2150, iwl7000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x24FB, 0x0000, iwl7000_mac_cfg)},
/* 7265 Series */
- {IWL_PCI_DEVICE(0x095A, 0x5010, iwl7265_2ac_cfg)},
- {IWL_PCI_DEVICE(0x095A, 0x5110, iwl7265_2ac_cfg)},
- {IWL_PCI_DEVICE(0x095A, 0x5100, iwl7265_2ac_cfg)},
- {IWL_PCI_DEVICE(0x095B, 0x5310, iwl7265_2ac_cfg)},
- {IWL_PCI_DEVICE(0x095B, 0x5302, iwl7265_n_cfg)},
- {IWL_PCI_DEVICE(0x095B, 0x5210, iwl7265_2ac_cfg)},
- {IWL_PCI_DEVICE(0x095A, 0x5C10, iwl7265_2ac_cfg)},
- {IWL_PCI_DEVICE(0x095A, 0x5012, iwl7265_2ac_cfg)},
- {IWL_PCI_DEVICE(0x095A, 0x5412, iwl7265_2ac_cfg)},
- {IWL_PCI_DEVICE(0x095A, 0x5410, iwl7265_2ac_cfg)},
- {IWL_PCI_DEVICE(0x095A, 0x5510, iwl7265_2ac_cfg)},
- {IWL_PCI_DEVICE(0x095A, 0x5400, iwl7265_2ac_cfg)},
- {IWL_PCI_DEVICE(0x095A, 0x1010, iwl7265_2ac_cfg)},
- {IWL_PCI_DEVICE(0x095A, 0x5000, iwl7265_2n_cfg)},
- {IWL_PCI_DEVICE(0x095A, 0x500A, iwl7265_2n_cfg)},
- {IWL_PCI_DEVICE(0x095B, 0x5200, iwl7265_2n_cfg)},
- {IWL_PCI_DEVICE(0x095A, 0x5002, iwl7265_n_cfg)},
- {IWL_PCI_DEVICE(0x095A, 0x5102, iwl7265_n_cfg)},
- {IWL_PCI_DEVICE(0x095B, 0x5202, iwl7265_n_cfg)},
- {IWL_PCI_DEVICE(0x095A, 0x9010, iwl7265_2ac_cfg)},
- {IWL_PCI_DEVICE(0x095A, 0x9012, iwl7265_2ac_cfg)},
- {IWL_PCI_DEVICE(0x095A, 0x900A, iwl7265_2ac_cfg)},
- {IWL_PCI_DEVICE(0x095A, 0x9110, iwl7265_2ac_cfg)},
- {IWL_PCI_DEVICE(0x095A, 0x9112, iwl7265_2ac_cfg)},
- {IWL_PCI_DEVICE(0x095B, 0x9210, iwl7265_2ac_cfg)},
- {IWL_PCI_DEVICE(0x095B, 0x9200, iwl7265_2ac_cfg)},
- {IWL_PCI_DEVICE(0x095A, 0x9510, iwl7265_2ac_cfg)},
- {IWL_PCI_DEVICE(0x095B, 0x9310, iwl7265_2ac_cfg)},
- {IWL_PCI_DEVICE(0x095A, 0x9410, iwl7265_2ac_cfg)},
- {IWL_PCI_DEVICE(0x095A, 0x5020, iwl7265_2n_cfg)},
- {IWL_PCI_DEVICE(0x095A, 0x502A, iwl7265_2n_cfg)},
- {IWL_PCI_DEVICE(0x095A, 0x5420, iwl7265_2n_cfg)},
- {IWL_PCI_DEVICE(0x095A, 0x5090, iwl7265_2ac_cfg)},
- {IWL_PCI_DEVICE(0x095A, 0x5190, iwl7265_2ac_cfg)},
- {IWL_PCI_DEVICE(0x095A, 0x5590, iwl7265_2ac_cfg)},
- {IWL_PCI_DEVICE(0x095B, 0x5290, iwl7265_2ac_cfg)},
- {IWL_PCI_DEVICE(0x095A, 0x5490, iwl7265_2ac_cfg)},
- {IWL_PCI_DEVICE(0x095A, 0x5F10, iwl7265_2ac_cfg)},
- {IWL_PCI_DEVICE(0x095B, 0x5212, iwl7265_2ac_cfg)},
- {IWL_PCI_DEVICE(0x095B, 0x520A, iwl7265_2ac_cfg)},
- {IWL_PCI_DEVICE(0x095A, 0x9000, iwl7265_2ac_cfg)},
- {IWL_PCI_DEVICE(0x095A, 0x9400, iwl7265_2ac_cfg)},
- {IWL_PCI_DEVICE(0x095A, 0x9E10, iwl7265_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x095A, 0x5010, iwl7000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x095A, 0x5110, iwl7000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x095A, 0x5100, iwl7000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x095B, 0x5310, iwl7000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x095B, 0x5302, iwl7000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x095B, 0x5210, iwl7000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x095A, 0x5C10, iwl7000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x095A, 0x5012, iwl7000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x095A, 0x5412, iwl7000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x095A, 0x5410, iwl7000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x095A, 0x5510, iwl7000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x095A, 0x5400, iwl7000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x095A, 0x1010, iwl7000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x095A, 0x5000, iwl7000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x095A, 0x500A, iwl7000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x095B, 0x5200, iwl7000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x095A, 0x5002, iwl7000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x095A, 0x5102, iwl7000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x095B, 0x5202, iwl7000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x095A, 0x9010, iwl7000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x095A, 0x9012, iwl7000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x095A, 0x900A, iwl7000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x095A, 0x9110, iwl7000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x095A, 0x9112, iwl7000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x095B, 0x9210, iwl7000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x095B, 0x9200, iwl7000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x095A, 0x9510, iwl7000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x095B, 0x9310, iwl7000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x095A, 0x9410, iwl7000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x095A, 0x5020, iwl7000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x095A, 0x502A, iwl7000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x095A, 0x5420, iwl7000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x095A, 0x5090, iwl7000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x095A, 0x5190, iwl7000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x095A, 0x5590, iwl7000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x095B, 0x5290, iwl7000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x095A, 0x5490, iwl7000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x095A, 0x5F10, iwl7000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x095B, 0x5212, iwl7000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x095B, 0x520A, iwl7000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x095A, 0x9000, iwl7000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x095A, 0x9400, iwl7000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x095A, 0x9E10, iwl7000_mac_cfg)},
/* 8000 Series */
- {IWL_PCI_DEVICE(0x24F3, 0x0010, iwl8260_2ac_cfg)},
- {IWL_PCI_DEVICE(0x24F3, 0x1010, iwl8260_2ac_cfg)},
- {IWL_PCI_DEVICE(0x24F3, 0x10B0, iwl8260_2ac_cfg)},
- {IWL_PCI_DEVICE(0x24F3, 0x0130, iwl8260_2ac_cfg)},
- {IWL_PCI_DEVICE(0x24F3, 0x1130, iwl8260_2ac_cfg)},
- {IWL_PCI_DEVICE(0x24F3, 0x0132, iwl8260_2ac_cfg)},
- {IWL_PCI_DEVICE(0x24F3, 0x1132, iwl8260_2ac_cfg)},
- {IWL_PCI_DEVICE(0x24F3, 0x0110, iwl8260_2ac_cfg)},
- {IWL_PCI_DEVICE(0x24F3, 0x01F0, iwl8260_2ac_cfg)},
- {IWL_PCI_DEVICE(0x24F3, 0x0012, iwl8260_2ac_cfg)},
- {IWL_PCI_DEVICE(0x24F3, 0x1012, iwl8260_2ac_cfg)},
- {IWL_PCI_DEVICE(0x24F3, 0x1110, iwl8260_2ac_cfg)},
- {IWL_PCI_DEVICE(0x24F3, 0x0050, iwl8260_2ac_cfg)},
- {IWL_PCI_DEVICE(0x24F3, 0x0250, iwl8260_2ac_cfg)},
- {IWL_PCI_DEVICE(0x24F3, 0x1050, iwl8260_2ac_cfg)},
- {IWL_PCI_DEVICE(0x24F3, 0x0150, iwl8260_2ac_cfg)},
- {IWL_PCI_DEVICE(0x24F3, 0x1150, iwl8260_2ac_cfg)},
- {IWL_PCI_DEVICE(0x24F4, 0x0030, iwl8260_2ac_cfg)},
- {IWL_PCI_DEVICE(0x24F4, 0x1030, iwl8260_2ac_cfg)},
- {IWL_PCI_DEVICE(0x24F3, 0xC010, iwl8260_2ac_cfg)},
- {IWL_PCI_DEVICE(0x24F3, 0xC110, iwl8260_2ac_cfg)},
- {IWL_PCI_DEVICE(0x24F3, 0xD010, iwl8260_2ac_cfg)},
- {IWL_PCI_DEVICE(0x24F3, 0xC050, iwl8260_2ac_cfg)},
- {IWL_PCI_DEVICE(0x24F3, 0xD050, iwl8260_2ac_cfg)},
- {IWL_PCI_DEVICE(0x24F3, 0xD0B0, iwl8260_2ac_cfg)},
- {IWL_PCI_DEVICE(0x24F3, 0xB0B0, iwl8260_2ac_cfg)},
- {IWL_PCI_DEVICE(0x24F3, 0x8010, iwl8260_2ac_cfg)},
- {IWL_PCI_DEVICE(0x24F3, 0x8110, iwl8260_2ac_cfg)},
- {IWL_PCI_DEVICE(0x24F3, 0x9010, iwl8260_2ac_cfg)},
- {IWL_PCI_DEVICE(0x24F3, 0x9110, iwl8260_2ac_cfg)},
- {IWL_PCI_DEVICE(0x24F4, 0x8030, iwl8260_2ac_cfg)},
- {IWL_PCI_DEVICE(0x24F4, 0x9030, iwl8260_2ac_cfg)},
- {IWL_PCI_DEVICE(0x24F4, 0xC030, iwl8260_2ac_cfg)},
- {IWL_PCI_DEVICE(0x24F4, 0xD030, iwl8260_2ac_cfg)},
- {IWL_PCI_DEVICE(0x24F3, 0x8130, iwl8260_2ac_cfg)},
- {IWL_PCI_DEVICE(0x24F3, 0x9130, iwl8260_2ac_cfg)},
- {IWL_PCI_DEVICE(0x24F3, 0x8132, iwl8260_2ac_cfg)},
- {IWL_PCI_DEVICE(0x24F3, 0x9132, iwl8260_2ac_cfg)},
- {IWL_PCI_DEVICE(0x24F3, 0x8050, iwl8260_2ac_cfg)},
- {IWL_PCI_DEVICE(0x24F3, 0x8150, iwl8260_2ac_cfg)},
- {IWL_PCI_DEVICE(0x24F3, 0x9050, iwl8260_2ac_cfg)},
- {IWL_PCI_DEVICE(0x24F3, 0x9150, iwl8260_2ac_cfg)},
- {IWL_PCI_DEVICE(0x24F3, 0x0004, iwl8260_2n_cfg)},
- {IWL_PCI_DEVICE(0x24F3, 0x0044, iwl8260_2n_cfg)},
- {IWL_PCI_DEVICE(0x24F5, 0x0010, iwl4165_2ac_cfg)},
- {IWL_PCI_DEVICE(0x24F6, 0x0030, iwl4165_2ac_cfg)},
- {IWL_PCI_DEVICE(0x24F3, 0x0810, iwl8260_2ac_cfg)},
- {IWL_PCI_DEVICE(0x24F3, 0x0910, iwl8260_2ac_cfg)},
- {IWL_PCI_DEVICE(0x24F3, 0x0850, iwl8260_2ac_cfg)},
- {IWL_PCI_DEVICE(0x24F3, 0x0950, iwl8260_2ac_cfg)},
- {IWL_PCI_DEVICE(0x24F3, 0x0930, iwl8260_2ac_cfg)},
- {IWL_PCI_DEVICE(0x24F3, 0x0000, iwl8265_2ac_cfg)},
- {IWL_PCI_DEVICE(0x24F3, 0x4010, iwl8260_2ac_cfg)},
- {IWL_PCI_DEVICE(0x24FD, 0x0010, iwl8265_2ac_cfg)},
- {IWL_PCI_DEVICE(0x24FD, 0x0110, iwl8265_2ac_cfg)},
- {IWL_PCI_DEVICE(0x24FD, 0x1110, iwl8265_2ac_cfg)},
- {IWL_PCI_DEVICE(0x24FD, 0x1130, iwl8265_2ac_cfg)},
- {IWL_PCI_DEVICE(0x24FD, 0x0130, iwl8265_2ac_cfg)},
- {IWL_PCI_DEVICE(0x24FD, 0x1010, iwl8265_2ac_cfg)},
- {IWL_PCI_DEVICE(0x24FD, 0x10D0, iwl8265_2ac_cfg)},
- {IWL_PCI_DEVICE(0x24FD, 0x0050, iwl8265_2ac_cfg)},
- {IWL_PCI_DEVICE(0x24FD, 0x0150, iwl8265_2ac_cfg)},
- {IWL_PCI_DEVICE(0x24FD, 0x9010, iwl8265_2ac_cfg)},
- {IWL_PCI_DEVICE(0x24FD, 0x8110, iwl8265_2ac_cfg)},
- {IWL_PCI_DEVICE(0x24FD, 0x8050, iwl8265_2ac_cfg)},
- {IWL_PCI_DEVICE(0x24FD, 0x8010, iwl8265_2ac_cfg)},
- {IWL_PCI_DEVICE(0x24FD, 0x0810, iwl8265_2ac_cfg)},
- {IWL_PCI_DEVICE(0x24FD, 0x9110, iwl8265_2ac_cfg)},
- {IWL_PCI_DEVICE(0x24FD, 0x8130, iwl8265_2ac_cfg)},
- {IWL_PCI_DEVICE(0x24FD, 0x0910, iwl8265_2ac_cfg)},
- {IWL_PCI_DEVICE(0x24FD, 0x0930, iwl8265_2ac_cfg)},
- {IWL_PCI_DEVICE(0x24FD, 0x0950, iwl8265_2ac_cfg)},
- {IWL_PCI_DEVICE(0x24FD, 0x0850, iwl8265_2ac_cfg)},
- {IWL_PCI_DEVICE(0x24FD, 0x1014, iwl8265_2ac_cfg)},
- {IWL_PCI_DEVICE(0x24FD, 0x3E02, iwl8275_2ac_cfg)},
- {IWL_PCI_DEVICE(0x24FD, 0x3E01, iwl8275_2ac_cfg)},
- {IWL_PCI_DEVICE(0x24FD, 0x1012, iwl8275_2ac_cfg)},
- {IWL_PCI_DEVICE(0x24FD, 0x0012, iwl8275_2ac_cfg)},
- {IWL_PCI_DEVICE(0x24FD, 0x0014, iwl8265_2ac_cfg)},
- {IWL_PCI_DEVICE(0x24FD, 0x9074, iwl8265_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x24F3, 0x0010, iwl8000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x24F3, 0x1010, iwl8000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x24F3, 0x10B0, iwl8000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x24F3, 0x0130, iwl8000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x24F3, 0x1130, iwl8000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x24F3, 0x0132, iwl8000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x24F3, 0x1132, iwl8000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x24F3, 0x0110, iwl8000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x24F3, 0x01F0, iwl8000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x24F3, 0x0012, iwl8000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x24F3, 0x1012, iwl8000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x24F3, 0x1110, iwl8000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x24F3, 0x0050, iwl8000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x24F3, 0x0250, iwl8000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x24F3, 0x1050, iwl8000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x24F3, 0x0150, iwl8000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x24F3, 0x1150, iwl8000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x24F4, 0x0030, iwl8000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x24F4, 0x1030, iwl8000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x24F3, 0xC010, iwl8000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x24F3, 0xC110, iwl8000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x24F3, 0xD010, iwl8000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x24F3, 0xC050, iwl8000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x24F3, 0xD050, iwl8000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x24F3, 0xD0B0, iwl8000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x24F3, 0xB0B0, iwl8000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x24F3, 0x8010, iwl8000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x24F3, 0x8110, iwl8000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x24F3, 0x9010, iwl8000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x24F3, 0x9110, iwl8000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x24F4, 0x8030, iwl8000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x24F4, 0x9030, iwl8000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x24F4, 0xC030, iwl8000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x24F4, 0xD030, iwl8000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x24F3, 0x8130, iwl8000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x24F3, 0x9130, iwl8000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x24F3, 0x8132, iwl8000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x24F3, 0x9132, iwl8000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x24F3, 0x8050, iwl8000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x24F3, 0x8150, iwl8000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x24F3, 0x9050, iwl8000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x24F3, 0x9150, iwl8000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x24F3, 0x0004, iwl8000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x24F3, 0x0044, iwl8000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x24F5, 0x0010, iwl8000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x24F6, 0x0030, iwl8000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x24F3, 0x0810, iwl8000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x24F3, 0x0910, iwl8000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x24F3, 0x0850, iwl8000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x24F3, 0x0950, iwl8000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x24F3, 0x0930, iwl8000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x24F3, 0x0000, iwl8000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x24F3, 0x4010, iwl8000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x24F3, 0xC030, iwl8000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x24F3, 0xD030, iwl8000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x24FD, 0x0010, iwl8000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x24FD, 0x0110, iwl8000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x24FD, 0x1110, iwl8000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x24FD, 0x1130, iwl8000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x24FD, 0x0130, iwl8000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x24FD, 0x1010, iwl8000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x24FD, 0x10D0, iwl8000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x24FD, 0x0050, iwl8000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x24FD, 0x0150, iwl8000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x24FD, 0x9010, iwl8000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x24FD, 0x8110, iwl8000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x24FD, 0x8050, iwl8000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x24FD, 0x8010, iwl8000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x24FD, 0x0810, iwl8000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x24FD, 0x9110, iwl8000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x24FD, 0x8130, iwl8000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x24FD, 0x0910, iwl8000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x24FD, 0x0930, iwl8000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x24FD, 0x0950, iwl8000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x24FD, 0x0850, iwl8000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x24FD, 0x1014, iwl8000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x24FD, 0x3E02, iwl8000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x24FD, 0x3E01, iwl8000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x24FD, 0x1012, iwl8000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x24FD, 0x0012, iwl8000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x24FD, 0x0014, iwl8000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x24FD, 0x9074, iwl8000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x24FD, 0x1431, iwl8000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x24FD, 0x1432, iwl8000_mac_cfg)},
/* 9000 Series */
- {IWL_PCI_DEVICE(0x2526, PCI_ANY_ID, iwl9000_trans_cfg)},
- {IWL_PCI_DEVICE(0x271B, PCI_ANY_ID, iwl9000_trans_cfg)},
- {IWL_PCI_DEVICE(0x271C, PCI_ANY_ID, iwl9000_trans_cfg)},
- {IWL_PCI_DEVICE(0x30DC, PCI_ANY_ID, iwl9560_long_latency_trans_cfg)},
- {IWL_PCI_DEVICE(0x31DC, PCI_ANY_ID, iwl9560_shared_clk_trans_cfg)},
- {IWL_PCI_DEVICE(0x9DF0, PCI_ANY_ID, iwl9560_trans_cfg)},
- {IWL_PCI_DEVICE(0xA370, PCI_ANY_ID, iwl9560_trans_cfg)},
+ {IWL_PCI_DEVICE(0x2526, PCI_ANY_ID, iwl9000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x271B, PCI_ANY_ID, iwl9000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x271C, PCI_ANY_ID, iwl9000_mac_cfg)},
+ {IWL_PCI_DEVICE(0x30DC, PCI_ANY_ID, iwl9560_long_latency_mac_cfg)},
+ {IWL_PCI_DEVICE(0x31DC, PCI_ANY_ID, iwl9560_shared_clk_mac_cfg)},
+ {IWL_PCI_DEVICE(0x9DF0, PCI_ANY_ID, iwl9560_mac_cfg)},
+ {IWL_PCI_DEVICE(0xA370, PCI_ANY_ID, iwl9560_mac_cfg)},
/* Qu devices */
- {IWL_PCI_DEVICE(0x02F0, PCI_ANY_ID, iwl_qu_trans_cfg)},
- {IWL_PCI_DEVICE(0x06F0, PCI_ANY_ID, iwl_qu_trans_cfg)},
+ {IWL_PCI_DEVICE(0x02F0, PCI_ANY_ID, iwl_qu_mac_cfg)},
+ {IWL_PCI_DEVICE(0x06F0, PCI_ANY_ID, iwl_qu_mac_cfg)},
- {IWL_PCI_DEVICE(0x34F0, PCI_ANY_ID, iwl_qu_medium_latency_trans_cfg)},
- {IWL_PCI_DEVICE(0x3DF0, PCI_ANY_ID, iwl_qu_medium_latency_trans_cfg)},
- {IWL_PCI_DEVICE(0x4DF0, PCI_ANY_ID, iwl_qu_medium_latency_trans_cfg)},
+ {IWL_PCI_DEVICE(0x34F0, PCI_ANY_ID, iwl_qu_medium_latency_mac_cfg)},
+ {IWL_PCI_DEVICE(0x3DF0, PCI_ANY_ID, iwl_qu_medium_latency_mac_cfg)},
+ {IWL_PCI_DEVICE(0x4DF0, PCI_ANY_ID, iwl_qu_medium_latency_mac_cfg)},
- {IWL_PCI_DEVICE(0x43F0, PCI_ANY_ID, iwl_qu_long_latency_trans_cfg)},
- {IWL_PCI_DEVICE(0xA0F0, PCI_ANY_ID, iwl_qu_long_latency_trans_cfg)},
+ {IWL_PCI_DEVICE(0x43F0, PCI_ANY_ID, iwl_qu_long_latency_mac_cfg)},
+ {IWL_PCI_DEVICE(0xA0F0, PCI_ANY_ID, iwl_qu_long_latency_mac_cfg)},
- {IWL_PCI_DEVICE(0x2723, PCI_ANY_ID, iwl_ax200_trans_cfg)},
+ {IWL_PCI_DEVICE(0x2723, PCI_ANY_ID, iwl_ax200_mac_cfg)},
-/* So devices */
- {IWL_PCI_DEVICE(0x2725, PCI_ANY_ID, iwl_so_trans_cfg)},
- {IWL_PCI_DEVICE(0x7A70, PCI_ANY_ID, iwl_so_long_latency_imr_trans_cfg)},
- {IWL_PCI_DEVICE(0x7AF0, PCI_ANY_ID, iwl_so_trans_cfg)},
- {IWL_PCI_DEVICE(0x51F0, PCI_ANY_ID, iwl_so_long_latency_trans_cfg)},
- {IWL_PCI_DEVICE(0x51F1, PCI_ANY_ID, iwl_so_long_latency_imr_trans_cfg)},
- {IWL_PCI_DEVICE(0x54F0, PCI_ANY_ID, iwl_so_long_latency_trans_cfg)},
- {IWL_PCI_DEVICE(0x7F70, PCI_ANY_ID, iwl_so_trans_cfg)},
+/* Ty/So devices */
+ {IWL_PCI_DEVICE(0x2725, PCI_ANY_ID, iwl_ty_mac_cfg)},
+ {IWL_PCI_DEVICE(0x7A70, PCI_ANY_ID, iwl_so_long_latency_imr_mac_cfg)},
+ {IWL_PCI_DEVICE(0x7AF0, PCI_ANY_ID, iwl_so_mac_cfg)},
+ {IWL_PCI_DEVICE(0x51F0, PCI_ANY_ID, iwl_so_long_latency_mac_cfg)},
+ {IWL_PCI_DEVICE(0x51F1, PCI_ANY_ID, iwl_so_long_latency_imr_mac_cfg)},
+ {IWL_PCI_DEVICE(0x54F0, PCI_ANY_ID, iwl_so_long_latency_mac_cfg)},
+ {IWL_PCI_DEVICE(0x7F70, PCI_ANY_ID, iwl_so_mac_cfg)},
/* Ma devices */
- {IWL_PCI_DEVICE(0x2729, PCI_ANY_ID, iwl_ma_trans_cfg)},
- {IWL_PCI_DEVICE(0x7E40, PCI_ANY_ID, iwl_ma_trans_cfg)},
-
+ {IWL_PCI_DEVICE(0x2729, PCI_ANY_ID, iwl_ma_mac_cfg)},
+ {IWL_PCI_DEVICE(0x7E40, PCI_ANY_ID, iwl_ma_mac_cfg)},
+#endif /* CONFIG_IWLMVM */
+#if IS_ENABLED(CONFIG_IWLMLD)
/* Bz devices */
- {IWL_PCI_DEVICE(0x272b, PCI_ANY_ID, iwl_gl_trans_cfg)},
- {IWL_PCI_DEVICE(0xA840, 0x0000, iwl_bz_trans_cfg)},
- {IWL_PCI_DEVICE(0xA840, 0x0090, iwl_bz_trans_cfg)},
- {IWL_PCI_DEVICE(0xA840, 0x0094, iwl_bz_trans_cfg)},
- {IWL_PCI_DEVICE(0xA840, 0x0098, iwl_bz_trans_cfg)},
- {IWL_PCI_DEVICE(0xA840, 0x009C, iwl_bz_trans_cfg)},
- {IWL_PCI_DEVICE(0xA840, 0x00C0, iwl_bz_trans_cfg)},
- {IWL_PCI_DEVICE(0xA840, 0x00C4, iwl_bz_trans_cfg)},
- {IWL_PCI_DEVICE(0xA840, 0x00E0, iwl_bz_trans_cfg)},
- {IWL_PCI_DEVICE(0xA840, 0x00E4, iwl_bz_trans_cfg)},
- {IWL_PCI_DEVICE(0xA840, 0x00E8, iwl_bz_trans_cfg)},
- {IWL_PCI_DEVICE(0xA840, 0x00EC, iwl_bz_trans_cfg)},
- {IWL_PCI_DEVICE(0xA840, 0x0100, iwl_bz_trans_cfg)},
- {IWL_PCI_DEVICE(0xA840, 0x0110, iwl_bz_trans_cfg)},
- {IWL_PCI_DEVICE(0xA840, 0x0114, iwl_bz_trans_cfg)},
- {IWL_PCI_DEVICE(0xA840, 0x0118, iwl_bz_trans_cfg)},
- {IWL_PCI_DEVICE(0xA840, 0x011C, iwl_bz_trans_cfg)},
- {IWL_PCI_DEVICE(0xA840, 0x0310, iwl_bz_trans_cfg)},
- {IWL_PCI_DEVICE(0xA840, 0x0314, iwl_bz_trans_cfg)},
- {IWL_PCI_DEVICE(0xA840, 0x0510, iwl_bz_trans_cfg)},
- {IWL_PCI_DEVICE(0xA840, 0x0A10, iwl_bz_trans_cfg)},
- {IWL_PCI_DEVICE(0xA840, 0x1671, iwl_bz_trans_cfg)},
- {IWL_PCI_DEVICE(0xA840, 0x1672, iwl_bz_trans_cfg)},
- {IWL_PCI_DEVICE(0xA840, 0x1771, iwl_bz_trans_cfg)},
- {IWL_PCI_DEVICE(0xA840, 0x1772, iwl_bz_trans_cfg)},
- {IWL_PCI_DEVICE(0xA840, 0x1791, iwl_bz_trans_cfg)},
- {IWL_PCI_DEVICE(0xA840, 0x1792, iwl_bz_trans_cfg)},
- {IWL_PCI_DEVICE(0xA840, 0x4090, iwl_bz_trans_cfg)},
- {IWL_PCI_DEVICE(0xA840, 0x40C4, iwl_bz_trans_cfg)},
- {IWL_PCI_DEVICE(0xA840, 0x40E0, iwl_bz_trans_cfg)},
- {IWL_PCI_DEVICE(0xA840, 0x4110, iwl_bz_trans_cfg)},
- {IWL_PCI_DEVICE(0xA840, 0x4314, iwl_bz_trans_cfg)},
- {IWL_PCI_DEVICE(0x7740, PCI_ANY_ID, iwl_bz_trans_cfg)},
- {IWL_PCI_DEVICE(0x4D40, PCI_ANY_ID, iwl_bz_trans_cfg)},
+ {IWL_PCI_DEVICE(0x272b, PCI_ANY_ID, iwl_gl_mac_cfg)},
+ {IWL_PCI_DEVICE(0xA840, 0x0000, iwl_bz_mac_cfg)},
+ {IWL_PCI_DEVICE(0xA840, 0x0090, iwl_bz_mac_cfg)},
+ {IWL_PCI_DEVICE(0xA840, 0x0094, iwl_bz_mac_cfg)},
+ {IWL_PCI_DEVICE(0xA840, 0x0098, iwl_bz_mac_cfg)},
+ {IWL_PCI_DEVICE(0xA840, 0x009C, iwl_bz_mac_cfg)},
+ {IWL_PCI_DEVICE(0xA840, 0x00C0, iwl_bz_mac_cfg)},
+ {IWL_PCI_DEVICE(0xA840, 0x00C4, iwl_bz_mac_cfg)},
+ {IWL_PCI_DEVICE(0xA840, 0x00E0, iwl_bz_mac_cfg)},
+ {IWL_PCI_DEVICE(0xA840, 0x00E4, iwl_bz_mac_cfg)},
+ {IWL_PCI_DEVICE(0xA840, 0x00E8, iwl_bz_mac_cfg)},
+ {IWL_PCI_DEVICE(0xA840, 0x00EC, iwl_bz_mac_cfg)},
+ {IWL_PCI_DEVICE(0xA840, 0x0100, iwl_bz_mac_cfg)},
+ {IWL_PCI_DEVICE(0xA840, 0x0110, iwl_bz_mac_cfg)},
+ {IWL_PCI_DEVICE(0xA840, 0x0114, iwl_bz_mac_cfg)},
+ {IWL_PCI_DEVICE(0xA840, 0x0118, iwl_bz_mac_cfg)},
+ {IWL_PCI_DEVICE(0xA840, 0x011C, iwl_bz_mac_cfg)},
+ {IWL_PCI_DEVICE(0xA840, 0x0310, iwl_bz_mac_cfg)},
+ {IWL_PCI_DEVICE(0xA840, 0x0314, iwl_bz_mac_cfg)},
+ {IWL_PCI_DEVICE(0xA840, 0x0510, iwl_bz_mac_cfg)},
+ {IWL_PCI_DEVICE(0xA840, 0x0A10, iwl_bz_mac_cfg)},
+ {IWL_PCI_DEVICE(0xA840, 0x1671, iwl_bz_mac_cfg)},
+ {IWL_PCI_DEVICE(0xA840, 0x1672, iwl_bz_mac_cfg)},
+ {IWL_PCI_DEVICE(0xA840, 0x1771, iwl_bz_mac_cfg)},
+ {IWL_PCI_DEVICE(0xA840, 0x1772, iwl_bz_mac_cfg)},
+ {IWL_PCI_DEVICE(0xA840, 0x1791, iwl_bz_mac_cfg)},
+ {IWL_PCI_DEVICE(0xA840, 0x1792, iwl_bz_mac_cfg)},
+ {IWL_PCI_DEVICE(0xA840, 0x4090, iwl_bz_mac_cfg)},
+ {IWL_PCI_DEVICE(0xA840, 0x40C4, iwl_bz_mac_cfg)},
+ {IWL_PCI_DEVICE(0xA840, 0x40E0, iwl_bz_mac_cfg)},
+ {IWL_PCI_DEVICE(0xA840, 0x4110, iwl_bz_mac_cfg)},
+ {IWL_PCI_DEVICE(0xA840, 0x4314, iwl_bz_mac_cfg)},
+ {IWL_PCI_DEVICE(0xA840, 0x1775, iwl_bz_mac_cfg)},
+ {IWL_PCI_DEVICE(0xA840, 0x1776, iwl_bz_mac_cfg)},
+ {IWL_PCI_DEVICE(0x7740, PCI_ANY_ID, iwl_bz_mac_cfg)},
+ {IWL_PCI_DEVICE(0x4D40, PCI_ANY_ID, iwl_bz_mac_cfg)},
/* Sc devices */
- {IWL_PCI_DEVICE(0xE440, PCI_ANY_ID, iwl_sc_trans_cfg)},
- {IWL_PCI_DEVICE(0xE340, PCI_ANY_ID, iwl_sc_trans_cfg)},
- {IWL_PCI_DEVICE(0xD340, PCI_ANY_ID, iwl_sc_trans_cfg)},
- {IWL_PCI_DEVICE(0x6E70, PCI_ANY_ID, iwl_sc_trans_cfg)},
-
-/* Dr devices */
- {IWL_PCI_DEVICE(0x272F, PCI_ANY_ID, iwl_dr_trans_cfg)},
-#endif /* CONFIG_IWLMVM */
+ {IWL_PCI_DEVICE(0xE440, PCI_ANY_ID, iwl_sc_mac_cfg)},
+ {IWL_PCI_DEVICE(0xE340, PCI_ANY_ID, iwl_sc_mac_cfg)},
+ {IWL_PCI_DEVICE(0xD340, PCI_ANY_ID, iwl_sc_mac_cfg)},
+ {IWL_PCI_DEVICE(0x6E70, PCI_ANY_ID, iwl_sc_mac_cfg)},
+#endif /* CONFIG_IWLMLD */
{0}
};
MODULE_DEVICE_TABLE(pci, iwl_hw_card_ids);
EXPORT_SYMBOL_IF_IWLWIFI_KUNIT(iwl_hw_card_ids);
-#define _IWL_DEV_INFO(_device, _subdevice, _mac_type, _mac_step, _rf_type, \
- _rf_id, _rf_step, _no_160, _cores, _cdb, _cfg, _name) \
- { .device = (_device), .subdevice = (_subdevice), .cfg = &(_cfg), \
- .name = _name, .mac_type = _mac_type, .rf_type = _rf_type, .rf_step = _rf_step, \
- .no_160 = _no_160, .cores = _cores, .rf_id = _rf_id, \
- .mac_step = _mac_step, .cdb = _cdb, .jacket = IWL_CFG_ANY }
-
-#define IWL_DEV_INFO(_device, _subdevice, _cfg, _name) \
- _IWL_DEV_INFO(_device, _subdevice, IWL_CFG_ANY, IWL_CFG_ANY, \
- IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_ANY, \
- IWL_CFG_ANY, _cfg, _name)
+#define _IWL_DEV_INFO(_cfg, _name, ...) { \
+ .cfg = &_cfg, \
+ .name = _name, \
+ .device = IWL_CFG_ANY, \
+ .subdevice = IWL_CFG_ANY, \
+ .subdevice_m_h = 15, \
+ __VA_ARGS__ \
+}
+#define IWL_DEV_INFO(_cfg, _name, ...) \
+ _IWL_DEV_INFO(_cfg, _name, __VA_ARGS__)
+
+#define DEVICE(n) .device = (n)
+#define SUBDEV(n) .subdevice = (n)
+#define _LOWEST_BIT(n) (__builtin_ffs(n) - 1)
+#define _BIT_ABOVE_MASK(n) ((n) + (1 << _LOWEST_BIT(n)))
+#define _HIGHEST_BIT(n) (__builtin_ffs(_BIT_ABOVE_MASK(n)) - 2)
+#define _IS_POW2(n) (((n) & ((n) - 1)) == 0)
+#define _IS_CONTIG(n) _IS_POW2(_BIT_ABOVE_MASK(n))
+#define _CHECK_MASK(m) BUILD_BUG_ON_ZERO(!_IS_CONTIG(m))
+#define SUBDEV_MASKED(v, m) .subdevice = (v) + _CHECK_MASK(m), \
+ .subdevice_m_l = _LOWEST_BIT(m), \
+ .subdevice_m_h = _HIGHEST_BIT(m)
+#define RF_TYPE(n) .match_rf_type = 1, \
+ .rf_type = IWL_CFG_RF_TYPE_##n
+#define RF_STEP(n) .match_rf_step = 1, \
+ .rf_step = SILICON_##n##_STEP
+#define RF_ID(n) .match_rf_id = 1, \
+ .rf_id = IWL_CFG_RF_ID_##n
+#define NO_CDB .match_cdb = 1, .cdb = 0
+#define CDB .match_cdb = 1, .cdb = 1
+#define BW_NOT_LIMITED .match_bw_limit = 1, .bw_limit = 0
+#define BW_LIMITED .match_bw_limit = 1, .bw_limit = 1
VISIBLE_IF_IWLWIFI_KUNIT const struct iwl_dev_info iwl_dev_info_table[] = {
+#if IS_ENABLED(CONFIG_IWLDVM)
+ IWL_DEV_INFO(iwl5100_n_cfg, iwl5100_agn_name,
+ DEVICE(0x4232), SUBDEV_MASKED(0x1, 0xF)),
+ IWL_DEV_INFO(iwl5100_n_cfg, iwl5100_agn_name,
+ DEVICE(0x4232), SUBDEV_MASKED(0x4, 0xF)),
+ IWL_DEV_INFO(iwl5100_n_cfg, iwl5100_bgn_name,
+ DEVICE(0x4232), SUBDEV_MASKED(0x5, 0xF)),
+ IWL_DEV_INFO(iwl5100_abg_cfg, iwl5100_abg_name,
+ DEVICE(0x4232), SUBDEV_MASKED(0x6, 0xF)),
+ IWL_DEV_INFO(iwl5100_n_cfg, iwl5100_agn_name,
+ DEVICE(0x4237), SUBDEV_MASKED(0x1, 0xF)),
+ IWL_DEV_INFO(iwl5100_n_cfg, iwl5100_agn_name,
+ DEVICE(0x4237), SUBDEV_MASKED(0x4, 0xF)),
+ IWL_DEV_INFO(iwl5100_n_cfg, iwl5100_bgn_name,
+ DEVICE(0x4237), SUBDEV_MASKED(0x5, 0xF)),
+ IWL_DEV_INFO(iwl5100_abg_cfg, iwl5100_abg_name,
+ DEVICE(0x4237), SUBDEV_MASKED(0x6, 0xF)),
+
+/* 5300 Series WiFi */
+ IWL_DEV_INFO(iwl5300_agn_cfg, iwl5300_agn_name,
+ DEVICE(0x4235), SUBDEV_MASKED(0x1, 0xF)),
+ IWL_DEV_INFO(iwl5300_agn_cfg, iwl5300_agn_name,
+ DEVICE(0x4235), SUBDEV_MASKED(0x4, 0xF)),
+ IWL_DEV_INFO(iwl5300_agn_cfg, iwl5300_agn_name,
+ DEVICE(0x4236), SUBDEV_MASKED(0x1, 0xF)),
+ IWL_DEV_INFO(iwl5300_agn_cfg, iwl5300_agn_name,
+ DEVICE(0x4236), SUBDEV_MASKED(0x4, 0xF)),
+
+/* 5350 Series WiFi/WiMax */
+ IWL_DEV_INFO(iwl5350_agn_cfg, iwl5350_agn_name,
+ DEVICE(0x423A)),
+ IWL_DEV_INFO(iwl5350_agn_cfg, iwl5350_agn_name,
+ DEVICE(0x423B)),
+
+/* 5150 Series Wifi/WiMax */
+ IWL_DEV_INFO(iwl5150_agn_cfg, iwl5150_agn_name,
+ DEVICE(0x423C), SUBDEV_MASKED(0x1, 0xF)),
+ IWL_DEV_INFO(iwl5150_abg_cfg, iwl5150_abg_name,
+ DEVICE(0x423C), SUBDEV_MASKED(0x6, 0xF)),
+
+ IWL_DEV_INFO(iwl5150_agn_cfg, iwl5150_agn_name,
+ DEVICE(0x423D), SUBDEV_MASKED(0x1, 0xF)),
+ IWL_DEV_INFO(iwl5150_abg_cfg, iwl5150_abg_name,
+ DEVICE(0x423D), SUBDEV_MASKED(0x6, 0xF)),
+
+/* 6x00 Series */
+ IWL_DEV_INFO(iwl6000_3agn_cfg, iwl6000_3agn_name,
+ DEVICE(0x422B), SUBDEV_MASKED(0x1, 0xF)),
+ IWL_DEV_INFO(iwl6000_3agn_cfg, iwl6000_3agn_name,
+ DEVICE(0x422B), SUBDEV_MASKED(0x8, 0xF)),
+ IWL_DEV_INFO(iwl6000i_2agn_cfg, iwl6000i_2agn_name,
+ DEVICE(0x422C), SUBDEV_MASKED(0x1, 0xF)),
+ IWL_DEV_INFO(iwl6000i_non_n_cfg, iwl6000i_2abg_name,
+ DEVICE(0x422C), SUBDEV_MASKED(0x6, 0xF)),
+ IWL_DEV_INFO(iwl6000i_non_n_cfg, iwl6000i_2bg_name,
+ DEVICE(0x422C), SUBDEV_MASKED(0x7, 0xF)),
+ IWL_DEV_INFO(iwl6000_3agn_cfg, iwl6000_3agn_name,
+ DEVICE(0x4238), SUBDEV(0x1111)),
+ IWL_DEV_INFO(iwl6000_3agn_cfg, iwl6000_3agn_name,
+ DEVICE(0x4238), SUBDEV(0x1118)),
+ IWL_DEV_INFO(iwl6000i_2agn_cfg, iwl6000i_2agn_name,
+ DEVICE(0x4239), SUBDEV(0x1311)),
+ IWL_DEV_INFO(iwl6000i_non_n_cfg, iwl6000i_2abg_name,
+ DEVICE(0x4239), SUBDEV(0x1316)),
+
+/* 6x05 Series */
+ IWL_DEV_INFO(iwl6005_n_cfg, iwl6005_2agn_name,
+ DEVICE(0x0082), SUBDEV_MASKED(0x1, 0xF)),
+ IWL_DEV_INFO(iwl6005_non_n_cfg, iwl6005_2abg_name,
+ DEVICE(0x0082), SUBDEV_MASKED(0x6, 0xF)),
+ IWL_DEV_INFO(iwl6005_non_n_cfg, iwl6005_2bg_name,
+ DEVICE(0x0082), SUBDEV_MASKED(0x7, 0xF)),
+ IWL_DEV_INFO(iwl6005_n_cfg, iwl6005_2agn_name,
+ DEVICE(0x0082), SUBDEV_MASKED(0x8, 0xF)),
+
+ IWL_DEV_INFO(iwl6005_n_cfg, iwl6005_2agn_name,
+ DEVICE(0x0085), SUBDEV_MASKED(0x1, 0xF)),
+ IWL_DEV_INFO(iwl6005_n_cfg, iwl6005_2agn_name,
+ DEVICE(0x0085), SUBDEV_MASKED(0x8, 0xF)),
+ IWL_DEV_INFO(iwl6005_non_n_cfg, iwl6005_2abg_name,
+ DEVICE(0x0085), SUBDEV_MASKED(0x6, 0xF)),
+
+ IWL_DEV_INFO(iwl6005_n_cfg, iwl6005_2agn_sff_name,
+ DEVICE(0x0082), SUBDEV_MASKED(0xC000, 0xF000)),
+ IWL_DEV_INFO(iwl6005_n_cfg, iwl6005_2agn_d_name,
+ DEVICE(0x0082), SUBDEV(0x4820)),
+ IWL_DEV_INFO(iwl6005_n_cfg, iwl6005_2agn_mow1_name,
+ DEVICE(0x0082), SUBDEV(0x1304)),/* low 5GHz active */
+ IWL_DEV_INFO(iwl6005_n_cfg, iwl6005_2agn_mow2_name,
+ DEVICE(0x0082), SUBDEV(0x1305)),/* high 5GHz active */
+
+/* 6x30 Series */
+ IWL_DEV_INFO(iwl6030_n_cfg, iwl1030_bgn_name,
+ DEVICE(0x008A), SUBDEV_MASKED(0x5, 0xF)),
+ IWL_DEV_INFO(iwl6030_non_n_cfg, iwl1030_bg_name,
+ DEVICE(0x008A), SUBDEV_MASKED(0x7, 0xF)),
+ IWL_DEV_INFO(iwl6030_n_cfg, iwl1030_bgn_name,
+ DEVICE(0x008B), SUBDEV(0x5315)),
+ IWL_DEV_INFO(iwl6030_non_n_cfg, iwl1030_bg_name,
+ DEVICE(0x008B), SUBDEV(0x5317)),
+ IWL_DEV_INFO(iwl6030_n_cfg, iwl6030_2agn_name,
+ DEVICE(0x0090), SUBDEV(0x5211)),
+ IWL_DEV_INFO(iwl6030_n_cfg, iwl6030_2bgn_name,
+ DEVICE(0x0090), SUBDEV(0x5215)),
+ IWL_DEV_INFO(iwl6030_non_n_cfg, iwl6030_2abg_name,
+ DEVICE(0x0090), SUBDEV(0x5216)),
+ IWL_DEV_INFO(iwl6030_n_cfg, iwl6030_2agn_name,
+ DEVICE(0x0091), SUBDEV_MASKED(0x1, 0xF)),
+ IWL_DEV_INFO(iwl6030_n_cfg, iwl6030_2bgn_name,
+ DEVICE(0x0091), SUBDEV_MASKED(0x5, 0xF)),
+ IWL_DEV_INFO(iwl6030_non_n_cfg, iwl6030_2abg_name,
+ DEVICE(0x0091), SUBDEV_MASKED(0x6, 0xF)),
+ IWL_DEV_INFO(iwl6030_non_n_cfg, iwl6030_2bg_name,
+ DEVICE(0x0091), SUBDEV(0x5207)),
+
+/* 6x50 WiFi/WiMax Series */
+ IWL_DEV_INFO(iwl6050_2agn_cfg, iwl6050_2agn_name,
+ DEVICE(0x0087), SUBDEV_MASKED(0x1, 0xF)),
+ IWL_DEV_INFO(iwl6050_2abg_cfg, iwl6050_2abg_name,
+ DEVICE(0x0087), SUBDEV_MASKED(0x6, 0xF)),
+ IWL_DEV_INFO(iwl6050_2agn_cfg, iwl6050_2agn_name,
+ DEVICE(0x0089), SUBDEV(0x1311)),
+ IWL_DEV_INFO(iwl6050_2abg_cfg, iwl6050_2abg_name,
+ DEVICE(0x0089), SUBDEV(0x1316)),
+
+/* 6150 WiFi/WiMax Series */
+ IWL_DEV_INFO(iwl6150_bgn_cfg, iwl6150_bgn_name,
+ DEVICE(0x0885), SUBDEV_MASKED(0x5, 0xF)),
+ IWL_DEV_INFO(iwl6150_bg_cfg, iwl6150_bg_name,
+ DEVICE(0x0885), SUBDEV_MASKED(0x7, 0xF)),
+ IWL_DEV_INFO(iwl6150_bgn_cfg, iwl6150_bgn_name,
+ DEVICE(0x0886), SUBDEV(0x1315)),
+ IWL_DEV_INFO(iwl6150_bg_cfg, iwl6150_bg_name,
+ DEVICE(0x0886), SUBDEV(0x1317)),
+
+/* 1000 Series WiFi */
+ IWL_DEV_INFO(iwl1000_bgn_cfg, iwl1000_bgn_name,
+ DEVICE(0x0083), SUBDEV_MASKED(0x5, 0xF)),
+ IWL_DEV_INFO(iwl1000_bg_cfg, iwl1000_bg_name,
+ DEVICE(0x0083), SUBDEV_MASKED(0x6, 0xF)),
+ IWL_DEV_INFO(iwl1000_bg_cfg, iwl1000_bg_name,
+ DEVICE(0x0084), SUBDEV(0x1216)),
+ IWL_DEV_INFO(iwl1000_bg_cfg, iwl1000_bg_name,
+ DEVICE(0x0084), SUBDEV(0x1316)),
+
+/* 100 Series WiFi */
+ IWL_DEV_INFO(iwl100_bgn_cfg, iwl100_bgn_name,
+ DEVICE(0x08AE), SUBDEV_MASKED(0x5, 0xF)),
+ IWL_DEV_INFO(iwl100_bg_cfg, iwl100_bg_name,
+ DEVICE(0x08AE), SUBDEV_MASKED(0x7, 0xF)),
+ IWL_DEV_INFO(iwl100_bgn_cfg, iwl100_bgn_name,
+ DEVICE(0x08AF), SUBDEV(0x1015)),
+ IWL_DEV_INFO(iwl100_bg_cfg, iwl100_bg_name,
+ DEVICE(0x08AF), SUBDEV(0x1017)),
+
+/* 130 Series WiFi */
+ IWL_DEV_INFO(iwl130_bgn_cfg, iwl130_bgn_name,
+ DEVICE(0x0896), SUBDEV_MASKED(0x5, 0xF)),
+ IWL_DEV_INFO(iwl130_bg_cfg, iwl130_bg_name,
+ DEVICE(0x0896), SUBDEV_MASKED(0x7, 0xF)),
+ IWL_DEV_INFO(iwl130_bgn_cfg, iwl130_bgn_name,
+ DEVICE(0x0897), SUBDEV(0x5015)),
+ IWL_DEV_INFO(iwl130_bg_cfg, iwl130_bg_name,
+ DEVICE(0x0897), SUBDEV(0x5017)),
+
+/* 2x00 Series */
+ IWL_DEV_INFO(iwl2000_2bgn_cfg, iwl2000_2bgn_name,
+ DEVICE(0x0890), SUBDEV(0x4022)),
+ IWL_DEV_INFO(iwl2000_2bgn_cfg, iwl2000_2bgn_name,
+ DEVICE(0x0891), SUBDEV(0x4222)),
+ IWL_DEV_INFO(iwl2000_2bgn_cfg, iwl2000_2bgn_name,
+ DEVICE(0x0890), SUBDEV(0x4422)),
+ IWL_DEV_INFO(iwl2000_2bgn_cfg, iwl2000_2bgn_d_name,
+ DEVICE(0x0890), SUBDEV(0x4822)),
+
+/* 2x30 Series */
+ IWL_DEV_INFO(iwl2030_2bgn_cfg, iwl2030_2bgn_name,
+ DEVICE(0x0887)),
+ IWL_DEV_INFO(iwl2030_2bgn_cfg, iwl2030_2bgn_name,
+ DEVICE(0x0888), SUBDEV(0x4262)),
+
+/* 6x35 Series */
+ IWL_DEV_INFO(iwl6035_2agn_cfg, iwl6035_2agn_name,
+ DEVICE(0x088E), SUBDEV_MASKED(0x0, 0xF)),
+ IWL_DEV_INFO(iwl6035_2agn_cfg, iwl6035_2agn_sff_name,
+ DEVICE(0x088E), SUBDEV_MASKED(0xA, 0xF)),
+ IWL_DEV_INFO(iwl6035_2agn_cfg, iwl6035_2agn_name,
+ DEVICE(0x088F), SUBDEV_MASKED(0x0, 0xF)),
+ IWL_DEV_INFO(iwl6035_2agn_cfg, iwl6035_2agn_sff_name,
+ DEVICE(0x088F), SUBDEV_MASKED(0xA, 0xF)),
+
+/* 105 Series */
+ IWL_DEV_INFO(iwl105_bgn_cfg, iwl105_bgn_name,
+ DEVICE(0x0894)),
+ IWL_DEV_INFO(iwl105_bgn_cfg, iwl105_bgn_name,
+ DEVICE(0x0895), SUBDEV(0x0222)),
+
+/* 135 Series */
+ IWL_DEV_INFO(iwl135_bgn_cfg, iwl135_bgn_name,
+ DEVICE(0x0892)),
+ IWL_DEV_INFO(iwl135_bgn_cfg, iwl135_bgn_name,
+ DEVICE(0x0893), SUBDEV(0x0262)),
+#endif /* CONFIG_IWLDVM */
+
#if IS_ENABLED(CONFIG_IWLMVM)
-/* 9000 */
- IWL_DEV_INFO(0x2526, 0x1550, iwl9260_2ac_cfg, iwl9260_killer_1550_name),
- IWL_DEV_INFO(0x2526, 0x1551, iwl9560_2ac_cfg_soc, iwl9560_killer_1550s_name),
- IWL_DEV_INFO(0x2526, 0x1552, iwl9560_2ac_cfg_soc, iwl9560_killer_1550i_name),
- IWL_DEV_INFO(0x30DC, 0x1551, iwl9560_2ac_cfg_soc, iwl9560_killer_1550s_name),
- IWL_DEV_INFO(0x30DC, 0x1552, iwl9560_2ac_cfg_soc, iwl9560_killer_1550i_name),
- IWL_DEV_INFO(0x31DC, 0x1551, iwl9560_2ac_cfg_soc, iwl9560_killer_1550s_name),
- IWL_DEV_INFO(0x31DC, 0x1552, iwl9560_2ac_cfg_soc, iwl9560_killer_1550i_name),
- IWL_DEV_INFO(0xA370, 0x1551, iwl9560_2ac_cfg_soc, iwl9560_killer_1550s_name),
- IWL_DEV_INFO(0xA370, 0x1552, iwl9560_2ac_cfg_soc, iwl9560_killer_1550i_name),
- IWL_DEV_INFO(0x54F0, 0x1551, iwl9560_2ac_cfg_soc, iwl9560_killer_1550s_160_name),
- IWL_DEV_INFO(0x54F0, 0x1552, iwl9560_2ac_cfg_soc, iwl9560_killer_1550i_name),
- IWL_DEV_INFO(0x51F0, 0x1552, iwl9560_2ac_cfg_soc, iwl9560_killer_1550s_160_name),
- IWL_DEV_INFO(0x51F0, 0x1551, iwl9560_2ac_cfg_soc, iwl9560_killer_1550i_160_name),
- IWL_DEV_INFO(0x51F0, 0x1691, iwlax411_2ax_cfg_so_gf4_a0, iwl_ax411_killer_1690s_name),
- IWL_DEV_INFO(0x51F0, 0x1692, iwlax411_2ax_cfg_so_gf4_a0, iwl_ax411_killer_1690i_name),
- IWL_DEV_INFO(0x51F1, 0x1692, iwlax411_2ax_cfg_so_gf4_a0, iwl_ax411_killer_1690i_name),
- IWL_DEV_INFO(0x54F0, 0x1691, iwlax411_2ax_cfg_so_gf4_a0, iwl_ax411_killer_1690s_name),
- IWL_DEV_INFO(0x54F0, 0x1692, iwlax411_2ax_cfg_so_gf4_a0, iwl_ax411_killer_1690i_name),
- IWL_DEV_INFO(0x7A70, 0x1691, iwlax411_2ax_cfg_so_gf4_a0, iwl_ax411_killer_1690s_name),
- IWL_DEV_INFO(0x7A70, 0x1692, iwlax411_2ax_cfg_so_gf4_a0, iwl_ax411_killer_1690i_name),
- IWL_DEV_INFO(0x7AF0, 0x1691, iwlax411_2ax_cfg_so_gf4_a0, iwl_ax411_killer_1690s_name),
- IWL_DEV_INFO(0x7AF0, 0x1692, iwlax411_2ax_cfg_so_gf4_a0, iwl_ax411_killer_1690i_name),
-
- IWL_DEV_INFO(0x271C, 0x0214, iwl9260_2ac_cfg, iwl9260_1_name),
- IWL_DEV_INFO(0x7E40, 0x1691, iwl_cfg_ma, iwl_ax411_killer_1690s_name),
- IWL_DEV_INFO(0x7E40, 0x1692, iwl_cfg_ma, iwl_ax411_killer_1690i_name),
-
-/* AX200 */
- IWL_DEV_INFO(0x2723, IWL_CFG_ANY, iwl_ax200_cfg_cc, iwl_ax200_name),
- IWL_DEV_INFO(0x2723, 0x1653, iwl_ax200_cfg_cc, iwl_ax200_killer_1650w_name),
- IWL_DEV_INFO(0x2723, 0x1654, iwl_ax200_cfg_cc, iwl_ax200_killer_1650x_name),
-
- /* Qu with Hr */
- IWL_DEV_INFO(0x43F0, 0x0070, iwl_ax201_cfg_qu_hr, NULL),
- IWL_DEV_INFO(0x43F0, 0x0074, iwl_ax201_cfg_qu_hr, NULL),
- IWL_DEV_INFO(0x43F0, 0x0078, iwl_ax201_cfg_qu_hr, NULL),
- IWL_DEV_INFO(0x43F0, 0x007C, iwl_ax201_cfg_qu_hr, NULL),
- IWL_DEV_INFO(0x43F0, 0x1651, killer1650s_2ax_cfg_qu_b0_hr_b0, iwl_ax201_killer_1650s_name),
- IWL_DEV_INFO(0x43F0, 0x1652, killer1650i_2ax_cfg_qu_b0_hr_b0, iwl_ax201_killer_1650i_name),
- IWL_DEV_INFO(0x43F0, 0x2074, iwl_ax201_cfg_qu_hr, NULL),
- IWL_DEV_INFO(0x43F0, 0x4070, iwl_ax201_cfg_qu_hr, NULL),
- IWL_DEV_INFO(0xA0F0, 0x0070, iwl_ax201_cfg_qu_hr, NULL),
- IWL_DEV_INFO(0xA0F0, 0x0074, iwl_ax201_cfg_qu_hr, NULL),
- IWL_DEV_INFO(0xA0F0, 0x0078, iwl_ax201_cfg_qu_hr, NULL),
- IWL_DEV_INFO(0xA0F0, 0x007C, iwl_ax201_cfg_qu_hr, NULL),
- IWL_DEV_INFO(0xA0F0, 0x0A10, iwl_ax201_cfg_qu_hr, NULL),
- IWL_DEV_INFO(0xA0F0, 0x1651, killer1650s_2ax_cfg_qu_b0_hr_b0, NULL),
- IWL_DEV_INFO(0xA0F0, 0x1652, killer1650i_2ax_cfg_qu_b0_hr_b0, NULL),
- IWL_DEV_INFO(0xA0F0, 0x2074, iwl_ax201_cfg_qu_hr, NULL),
- IWL_DEV_INFO(0xA0F0, 0x4070, iwl_ax201_cfg_qu_hr, NULL),
- IWL_DEV_INFO(0xA0F0, 0x6074, iwl_ax201_cfg_qu_hr, NULL),
- IWL_DEV_INFO(0x02F0, 0x0070, iwl_ax201_cfg_quz_hr, NULL),
- IWL_DEV_INFO(0x02F0, 0x0074, iwl_ax201_cfg_quz_hr, NULL),
- IWL_DEV_INFO(0x02F0, 0x6074, iwl_ax201_cfg_quz_hr, NULL),
- IWL_DEV_INFO(0x02F0, 0x0078, iwl_ax201_cfg_quz_hr, NULL),
- IWL_DEV_INFO(0x02F0, 0x007C, iwl_ax201_cfg_quz_hr, NULL),
- IWL_DEV_INFO(0x02F0, 0x0310, iwl_ax201_cfg_quz_hr, NULL),
- IWL_DEV_INFO(0x02F0, 0x1651, iwl_ax1650s_cfg_quz_hr, NULL),
- IWL_DEV_INFO(0x02F0, 0x1652, iwl_ax1650i_cfg_quz_hr, NULL),
- IWL_DEV_INFO(0x02F0, 0x2074, iwl_ax201_cfg_quz_hr, NULL),
- IWL_DEV_INFO(0x02F0, 0x4070, iwl_ax201_cfg_quz_hr, NULL),
- IWL_DEV_INFO(0x06F0, 0x0070, iwl_ax201_cfg_quz_hr, NULL),
- IWL_DEV_INFO(0x06F0, 0x0074, iwl_ax201_cfg_quz_hr, NULL),
- IWL_DEV_INFO(0x06F0, 0x0078, iwl_ax201_cfg_quz_hr, NULL),
- IWL_DEV_INFO(0x06F0, 0x007C, iwl_ax201_cfg_quz_hr, NULL),
- IWL_DEV_INFO(0x06F0, 0x0310, iwl_ax201_cfg_quz_hr, NULL),
- IWL_DEV_INFO(0x06F0, 0x1651, iwl_ax1650s_cfg_quz_hr, NULL),
- IWL_DEV_INFO(0x06F0, 0x1652, iwl_ax1650i_cfg_quz_hr, NULL),
- IWL_DEV_INFO(0x06F0, 0x2074, iwl_ax201_cfg_quz_hr, NULL),
- IWL_DEV_INFO(0x06F0, 0x4070, iwl_ax201_cfg_quz_hr, NULL),
- IWL_DEV_INFO(0x34F0, 0x0070, iwl_ax201_cfg_qu_hr, NULL),
- IWL_DEV_INFO(0x34F0, 0x0074, iwl_ax201_cfg_qu_hr, NULL),
- IWL_DEV_INFO(0x34F0, 0x0078, iwl_ax201_cfg_qu_hr, NULL),
- IWL_DEV_INFO(0x34F0, 0x007C, iwl_ax201_cfg_qu_hr, NULL),
- IWL_DEV_INFO(0x34F0, 0x0310, iwl_ax201_cfg_qu_hr, NULL),
- IWL_DEV_INFO(0x34F0, 0x1651, killer1650s_2ax_cfg_qu_b0_hr_b0, NULL),
- IWL_DEV_INFO(0x34F0, 0x1652, killer1650i_2ax_cfg_qu_b0_hr_b0, NULL),
- IWL_DEV_INFO(0x34F0, 0x2074, iwl_ax201_cfg_qu_hr, NULL),
- IWL_DEV_INFO(0x34F0, 0x4070, iwl_ax201_cfg_qu_hr, NULL),
-
- IWL_DEV_INFO(0x3DF0, 0x0070, iwl_ax201_cfg_qu_hr, NULL),
- IWL_DEV_INFO(0x3DF0, 0x0074, iwl_ax201_cfg_qu_hr, NULL),
- IWL_DEV_INFO(0x3DF0, 0x0078, iwl_ax201_cfg_qu_hr, NULL),
- IWL_DEV_INFO(0x3DF0, 0x007C, iwl_ax201_cfg_qu_hr, NULL),
- IWL_DEV_INFO(0x3DF0, 0x0310, iwl_ax201_cfg_qu_hr, NULL),
- IWL_DEV_INFO(0x3DF0, 0x1651, killer1650s_2ax_cfg_qu_b0_hr_b0, NULL),
- IWL_DEV_INFO(0x3DF0, 0x1652, killer1650i_2ax_cfg_qu_b0_hr_b0, NULL),
- IWL_DEV_INFO(0x3DF0, 0x2074, iwl_ax201_cfg_qu_hr, NULL),
- IWL_DEV_INFO(0x3DF0, 0x4070, iwl_ax201_cfg_qu_hr, NULL),
-
- IWL_DEV_INFO(0x4DF0, 0x0070, iwl_ax201_cfg_qu_hr, NULL),
- IWL_DEV_INFO(0x4DF0, 0x0074, iwl_ax201_cfg_qu_hr, NULL),
- IWL_DEV_INFO(0x4DF0, 0x0078, iwl_ax201_cfg_qu_hr, NULL),
- IWL_DEV_INFO(0x4DF0, 0x007C, iwl_ax201_cfg_qu_hr, NULL),
- IWL_DEV_INFO(0x4DF0, 0x0310, iwl_ax201_cfg_qu_hr, NULL),
- IWL_DEV_INFO(0x4DF0, 0x1651, killer1650s_2ax_cfg_qu_b0_hr_b0, NULL),
- IWL_DEV_INFO(0x4DF0, 0x1652, killer1650i_2ax_cfg_qu_b0_hr_b0, NULL),
- IWL_DEV_INFO(0x4DF0, 0x2074, iwl_ax201_cfg_qu_hr, NULL),
- IWL_DEV_INFO(0x4DF0, 0x4070, iwl_ax201_cfg_qu_hr, NULL),
- IWL_DEV_INFO(0x4DF0, 0x6074, iwl_ax201_cfg_qu_hr, NULL),
-
- /* So with HR */
- IWL_DEV_INFO(0x2725, 0x0090, iwlax211_2ax_cfg_so_gf_a0, NULL),
- IWL_DEV_INFO(0x2725, 0x0020, iwlax210_2ax_cfg_ty_gf_a0, NULL),
- IWL_DEV_INFO(0x2725, 0x2020, iwlax210_2ax_cfg_ty_gf_a0, NULL),
- IWL_DEV_INFO(0x2725, 0x0024, iwlax210_2ax_cfg_ty_gf_a0, NULL),
- IWL_DEV_INFO(0x2725, 0x0310, iwlax210_2ax_cfg_ty_gf_a0, NULL),
- IWL_DEV_INFO(0x2725, 0x0510, iwlax210_2ax_cfg_ty_gf_a0, NULL),
- IWL_DEV_INFO(0x2725, 0x0A10, iwlax210_2ax_cfg_ty_gf_a0, NULL),
- IWL_DEV_INFO(0x2725, 0xE020, iwlax210_2ax_cfg_ty_gf_a0, NULL),
- IWL_DEV_INFO(0x2725, 0xE024, iwlax210_2ax_cfg_ty_gf_a0, NULL),
- IWL_DEV_INFO(0x2725, 0x4020, iwlax210_2ax_cfg_ty_gf_a0, NULL),
- IWL_DEV_INFO(0x2725, 0x6020, iwlax210_2ax_cfg_ty_gf_a0, NULL),
- IWL_DEV_INFO(0x2725, 0x6024, iwlax210_2ax_cfg_ty_gf_a0, NULL),
- IWL_DEV_INFO(0x2725, 0x1673, iwlax210_2ax_cfg_ty_gf_a0, iwl_ax210_killer_1675w_name),
- IWL_DEV_INFO(0x2725, 0x1674, iwlax210_2ax_cfg_ty_gf_a0, iwl_ax210_killer_1675x_name),
- IWL_DEV_INFO(0x7A70, 0x0090, iwlax211_2ax_cfg_so_gf_a0_long, NULL),
- IWL_DEV_INFO(0x7A70, 0x0098, iwlax211_2ax_cfg_so_gf_a0_long, NULL),
- IWL_DEV_INFO(0x7A70, 0x00B0, iwlax411_2ax_cfg_so_gf4_a0_long, NULL),
- IWL_DEV_INFO(0x7A70, 0x0310, iwlax211_2ax_cfg_so_gf_a0_long, NULL),
- IWL_DEV_INFO(0x7A70, 0x0510, iwlax211_2ax_cfg_so_gf_a0_long, NULL),
- IWL_DEV_INFO(0x7A70, 0x0A10, iwlax211_2ax_cfg_so_gf_a0_long, NULL),
- IWL_DEV_INFO(0x7AF0, 0x0090, iwlax211_2ax_cfg_so_gf_a0, NULL),
- IWL_DEV_INFO(0x7AF0, 0x0098, iwlax211_2ax_cfg_so_gf_a0, NULL),
- IWL_DEV_INFO(0x7AF0, 0x00B0, iwlax411_2ax_cfg_so_gf4_a0, NULL),
- IWL_DEV_INFO(0x7AF0, 0x0310, iwlax211_2ax_cfg_so_gf_a0, NULL),
- IWL_DEV_INFO(0x7AF0, 0x0510, iwlax211_2ax_cfg_so_gf_a0, NULL),
- IWL_DEV_INFO(0x7AF0, 0x0A10, iwlax211_2ax_cfg_so_gf_a0, NULL),
-
- /* So with JF */
- IWL_DEV_INFO(0x7A70, 0x1551, iwl9560_2ac_cfg_soc, iwl9560_killer_1550s_160_name),
- IWL_DEV_INFO(0x7A70, 0x1552, iwl9560_2ac_cfg_soc, iwl9560_killer_1550i_160_name),
- IWL_DEV_INFO(0x7AF0, 0x1551, iwl9560_2ac_cfg_soc, iwl9560_killer_1550s_160_name),
- IWL_DEV_INFO(0x7AF0, 0x1552, iwl9560_2ac_cfg_soc, iwl9560_killer_1550i_160_name),
-
- /* SO with GF2 */
- IWL_DEV_INFO(0x2726, 0x1671, iwlax211_2ax_cfg_so_gf_a0, iwl_ax211_killer_1675s_name),
- IWL_DEV_INFO(0x2726, 0x1672, iwlax211_2ax_cfg_so_gf_a0, iwl_ax211_killer_1675i_name),
- IWL_DEV_INFO(0x51F0, 0x1671, iwlax211_2ax_cfg_so_gf_a0, iwl_ax211_killer_1675s_name),
- IWL_DEV_INFO(0x51F0, 0x1672, iwlax211_2ax_cfg_so_gf_a0, iwl_ax211_killer_1675i_name),
- IWL_DEV_INFO(0x51F1, 0x1671, iwlax211_2ax_cfg_so_gf_a0, iwl_ax211_killer_1675s_name),
- IWL_DEV_INFO(0x51F1, 0x1672, iwlax211_2ax_cfg_so_gf_a0, iwl_ax211_killer_1675i_name),
- IWL_DEV_INFO(0x54F0, 0x1671, iwlax211_2ax_cfg_so_gf_a0, iwl_ax211_killer_1675s_name),
- IWL_DEV_INFO(0x54F0, 0x1672, iwlax211_2ax_cfg_so_gf_a0, iwl_ax211_killer_1675i_name),
- IWL_DEV_INFO(0x7A70, 0x1671, iwlax211_2ax_cfg_so_gf_a0, iwl_ax211_killer_1675s_name),
- IWL_DEV_INFO(0x7A70, 0x1672, iwlax211_2ax_cfg_so_gf_a0, iwl_ax211_killer_1675i_name),
- IWL_DEV_INFO(0x7AF0, 0x1671, iwlax211_2ax_cfg_so_gf_a0, iwl_ax211_killer_1675s_name),
- IWL_DEV_INFO(0x7AF0, 0x1672, iwlax211_2ax_cfg_so_gf_a0, iwl_ax211_killer_1675i_name),
- IWL_DEV_INFO(0x7F70, 0x1671, iwlax211_2ax_cfg_so_gf_a0, iwl_ax211_killer_1675s_name),
- IWL_DEV_INFO(0x7F70, 0x1672, iwlax211_2ax_cfg_so_gf_a0, iwl_ax211_killer_1675i_name),
-
- /* MA with GF2 */
- IWL_DEV_INFO(0x7E40, 0x1671, iwl_cfg_ma, iwl_ax211_killer_1675s_name),
- IWL_DEV_INFO(0x7E40, 0x1672, iwl_cfg_ma, iwl_ax211_killer_1675i_name),
-
- _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY,
- IWL_CFG_MAC_TYPE_PU, IWL_CFG_ANY,
- IWL_CFG_RF_TYPE_JF1, IWL_CFG_RF_ID_JF1, IWL_CFG_ANY,
- IWL_CFG_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB,
- iwl9560_2ac_cfg_soc, iwl9461_160_name),
- _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY,
- IWL_CFG_MAC_TYPE_PU, IWL_CFG_ANY,
- IWL_CFG_RF_TYPE_JF1, IWL_CFG_RF_ID_JF1, IWL_CFG_ANY,
- IWL_CFG_NO_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB,
- iwl9560_2ac_cfg_soc, iwl9461_name),
- _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY,
- IWL_CFG_MAC_TYPE_PU, IWL_CFG_ANY,
- IWL_CFG_RF_TYPE_JF1, IWL_CFG_RF_ID_JF1_DIV, IWL_CFG_ANY,
- IWL_CFG_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB,
- iwl9560_2ac_cfg_soc, iwl9462_160_name),
- _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY,
- IWL_CFG_MAC_TYPE_PU, IWL_CFG_ANY,
- IWL_CFG_RF_TYPE_JF1, IWL_CFG_RF_ID_JF1_DIV, IWL_CFG_ANY,
- IWL_CFG_NO_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB,
- iwl9560_2ac_cfg_soc, iwl9462_name),
-
- _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY,
- IWL_CFG_MAC_TYPE_PU, IWL_CFG_ANY,
- IWL_CFG_RF_TYPE_JF2, IWL_CFG_RF_ID_JF, IWL_CFG_ANY,
- IWL_CFG_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB,
- iwl9560_2ac_cfg_soc, iwl9560_160_name),
- _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY,
- IWL_CFG_MAC_TYPE_PU, IWL_CFG_ANY,
- IWL_CFG_RF_TYPE_JF2, IWL_CFG_RF_ID_JF, IWL_CFG_ANY,
- IWL_CFG_NO_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB,
- iwl9560_2ac_cfg_soc, iwl9560_name),
-
- _IWL_DEV_INFO(0x2526, IWL_CFG_ANY,
- IWL_CFG_MAC_TYPE_TH, IWL_CFG_ANY,
- IWL_CFG_RF_TYPE_TH, IWL_CFG_ANY, IWL_CFG_ANY,
- IWL_CFG_160, IWL_CFG_CORES_BT_GNSS, IWL_CFG_NO_CDB,
- iwl9260_2ac_cfg, iwl9270_160_name),
- _IWL_DEV_INFO(0x2526, IWL_CFG_ANY,
- IWL_CFG_MAC_TYPE_TH, IWL_CFG_ANY,
- IWL_CFG_RF_TYPE_TH, IWL_CFG_ANY, IWL_CFG_ANY,
- IWL_CFG_NO_160, IWL_CFG_CORES_BT_GNSS, IWL_CFG_NO_CDB,
- iwl9260_2ac_cfg, iwl9270_name),
-
- _IWL_DEV_INFO(0x271B, IWL_CFG_ANY,
- IWL_CFG_MAC_TYPE_TH, IWL_CFG_ANY,
- IWL_CFG_RF_TYPE_TH1, IWL_CFG_ANY, IWL_CFG_ANY,
- IWL_CFG_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB,
- iwl9260_2ac_cfg, iwl9162_160_name),
- _IWL_DEV_INFO(0x271B, IWL_CFG_ANY,
- IWL_CFG_MAC_TYPE_TH, IWL_CFG_ANY,
- IWL_CFG_RF_TYPE_TH1, IWL_CFG_ANY, IWL_CFG_ANY,
- IWL_CFG_NO_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB,
- iwl9260_2ac_cfg, iwl9162_name),
-
- _IWL_DEV_INFO(0x2526, IWL_CFG_ANY,
- IWL_CFG_MAC_TYPE_TH, IWL_CFG_ANY,
- IWL_CFG_RF_TYPE_TH, IWL_CFG_ANY, IWL_CFG_ANY,
- IWL_CFG_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB,
- iwl9260_2ac_cfg, iwl9260_160_name),
- _IWL_DEV_INFO(0x2526, IWL_CFG_ANY,
- IWL_CFG_MAC_TYPE_TH, IWL_CFG_ANY,
- IWL_CFG_RF_TYPE_TH, IWL_CFG_ANY, IWL_CFG_ANY,
- IWL_CFG_NO_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB,
- iwl9260_2ac_cfg, iwl9260_name),
-
-/* Qu with Jf */
- /* Qu B step */
- _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY,
- IWL_CFG_MAC_TYPE_QU, SILICON_B_STEP,
- IWL_CFG_RF_TYPE_JF1, IWL_CFG_RF_ID_JF1, IWL_CFG_ANY,
- IWL_CFG_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB,
- iwl9560_qu_b0_jf_b0_cfg, iwl9461_160_name),
- _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY,
- IWL_CFG_MAC_TYPE_QU, SILICON_B_STEP,
- IWL_CFG_RF_TYPE_JF1, IWL_CFG_RF_ID_JF1, IWL_CFG_ANY,
- IWL_CFG_NO_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB,
- iwl9560_qu_b0_jf_b0_cfg, iwl9461_name),
- _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY,
- IWL_CFG_MAC_TYPE_QU, SILICON_B_STEP,
- IWL_CFG_RF_TYPE_JF1, IWL_CFG_RF_ID_JF1_DIV, IWL_CFG_ANY,
- IWL_CFG_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB,
- iwl9560_qu_b0_jf_b0_cfg, iwl9462_160_name),
- _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY,
- IWL_CFG_MAC_TYPE_QU, SILICON_B_STEP,
- IWL_CFG_RF_TYPE_JF1, IWL_CFG_RF_ID_JF1_DIV, IWL_CFG_ANY,
- IWL_CFG_NO_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB,
- iwl9560_qu_b0_jf_b0_cfg, iwl9462_name),
-
- _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY,
- IWL_CFG_MAC_TYPE_QU, SILICON_B_STEP,
- IWL_CFG_RF_TYPE_JF2, IWL_CFG_RF_ID_JF, IWL_CFG_ANY,
- IWL_CFG_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB,
- iwl9560_qu_b0_jf_b0_cfg, iwl9560_160_name),
- _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY,
- IWL_CFG_MAC_TYPE_QU, SILICON_B_STEP,
- IWL_CFG_RF_TYPE_JF2, IWL_CFG_RF_ID_JF, IWL_CFG_ANY,
- IWL_CFG_NO_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB,
- iwl9560_qu_b0_jf_b0_cfg, iwl9560_name),
-
- _IWL_DEV_INFO(IWL_CFG_ANY, 0x1551,
- IWL_CFG_MAC_TYPE_QU, SILICON_B_STEP,
- IWL_CFG_RF_TYPE_JF2, IWL_CFG_RF_ID_JF, IWL_CFG_ANY,
- IWL_CFG_NO_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB,
- iwl9560_qu_b0_jf_b0_cfg, iwl9560_killer_1550s_name),
- _IWL_DEV_INFO(IWL_CFG_ANY, 0x1552,
- IWL_CFG_MAC_TYPE_QU, SILICON_B_STEP,
- IWL_CFG_RF_TYPE_JF2, IWL_CFG_RF_ID_JF, IWL_CFG_ANY,
- IWL_CFG_NO_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB,
- iwl9560_qu_b0_jf_b0_cfg, iwl9560_killer_1550i_name),
-
- /* Qu C step */
- _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY,
- IWL_CFG_MAC_TYPE_QU, SILICON_C_STEP,
- IWL_CFG_RF_TYPE_JF1, IWL_CFG_RF_ID_JF1, IWL_CFG_ANY,
- IWL_CFG_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB,
- iwl9560_qu_c0_jf_b0_cfg, iwl9461_160_name),
- _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY,
- IWL_CFG_MAC_TYPE_QU, SILICON_C_STEP,
- IWL_CFG_RF_TYPE_JF1, IWL_CFG_RF_ID_JF1, IWL_CFG_ANY,
- IWL_CFG_NO_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB,
- iwl9560_qu_c0_jf_b0_cfg, iwl9461_name),
- _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY,
- IWL_CFG_MAC_TYPE_QU, SILICON_C_STEP,
- IWL_CFG_RF_TYPE_JF1, IWL_CFG_RF_ID_JF1_DIV, IWL_CFG_ANY,
- IWL_CFG_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB,
- iwl9560_qu_c0_jf_b0_cfg, iwl9462_160_name),
- _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY,
- IWL_CFG_MAC_TYPE_QU, SILICON_C_STEP,
- IWL_CFG_RF_TYPE_JF1, IWL_CFG_RF_ID_JF1_DIV, IWL_CFG_ANY,
- IWL_CFG_NO_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB,
- iwl9560_qu_c0_jf_b0_cfg, iwl9462_name),
-
- _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY,
- IWL_CFG_MAC_TYPE_QU, SILICON_C_STEP,
- IWL_CFG_RF_TYPE_JF2, IWL_CFG_RF_ID_JF, IWL_CFG_ANY,
- IWL_CFG_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB,
- iwl9560_qu_c0_jf_b0_cfg, iwl9560_160_name),
- _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY,
- IWL_CFG_MAC_TYPE_QU, SILICON_C_STEP,
- IWL_CFG_RF_TYPE_JF2, IWL_CFG_RF_ID_JF, IWL_CFG_ANY,
- IWL_CFG_NO_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB,
- iwl9560_qu_c0_jf_b0_cfg, iwl9560_name),
-
- _IWL_DEV_INFO(IWL_CFG_ANY, 0x1551,
- IWL_CFG_MAC_TYPE_QU, SILICON_C_STEP,
- IWL_CFG_RF_TYPE_JF2, IWL_CFG_RF_ID_JF, IWL_CFG_ANY,
- IWL_CFG_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB,
- iwl9560_qu_c0_jf_b0_cfg, iwl9560_killer_1550s_name),
- _IWL_DEV_INFO(IWL_CFG_ANY, 0x1552,
- IWL_CFG_MAC_TYPE_QU, SILICON_C_STEP,
- IWL_CFG_RF_TYPE_JF2, IWL_CFG_RF_ID_JF, IWL_CFG_ANY,
- IWL_CFG_NO_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB,
- iwl9560_qu_c0_jf_b0_cfg, iwl9560_killer_1550i_name),
-
- /* QuZ */
- _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY,
- IWL_CFG_MAC_TYPE_QUZ, IWL_CFG_ANY,
- IWL_CFG_RF_TYPE_JF1, IWL_CFG_RF_ID_JF1, IWL_CFG_ANY,
- IWL_CFG_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB,
- iwl9560_quz_a0_jf_b0_cfg, iwl9461_160_name),
- _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY,
- IWL_CFG_MAC_TYPE_QUZ, IWL_CFG_ANY,
- IWL_CFG_RF_TYPE_JF1, IWL_CFG_RF_ID_JF1, IWL_CFG_ANY,
- IWL_CFG_NO_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB,
- iwl9560_quz_a0_jf_b0_cfg, iwl9461_name),
- _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY,
- IWL_CFG_MAC_TYPE_QUZ, IWL_CFG_ANY,
- IWL_CFG_RF_TYPE_JF1, IWL_CFG_RF_ID_JF1_DIV, IWL_CFG_ANY,
- IWL_CFG_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB,
- iwl9560_quz_a0_jf_b0_cfg, iwl9462_160_name),
- _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY,
- IWL_CFG_MAC_TYPE_QUZ, IWL_CFG_ANY,
- IWL_CFG_RF_TYPE_JF1, IWL_CFG_RF_ID_JF1_DIV, IWL_CFG_ANY,
- IWL_CFG_NO_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB,
- iwl9560_quz_a0_jf_b0_cfg, iwl9462_name),
-
- _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY,
- IWL_CFG_MAC_TYPE_QUZ, IWL_CFG_ANY,
- IWL_CFG_RF_TYPE_JF2, IWL_CFG_RF_ID_JF, IWL_CFG_ANY,
- IWL_CFG_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB,
- iwl9560_quz_a0_jf_b0_cfg, iwl9560_160_name),
- _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY,
- IWL_CFG_MAC_TYPE_QUZ, IWL_CFG_ANY,
- IWL_CFG_RF_TYPE_JF2, IWL_CFG_RF_ID_JF, IWL_CFG_ANY,
- IWL_CFG_NO_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB,
- iwl9560_quz_a0_jf_b0_cfg, iwl9560_name),
-
- _IWL_DEV_INFO(IWL_CFG_ANY, 0x1551,
- IWL_CFG_MAC_TYPE_QUZ, IWL_CFG_ANY,
- IWL_CFG_RF_TYPE_JF2, IWL_CFG_RF_ID_JF, IWL_CFG_ANY,
- IWL_CFG_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB,
- iwl9560_quz_a0_jf_b0_cfg, iwl9560_killer_1550s_name),
- _IWL_DEV_INFO(IWL_CFG_ANY, 0x1552,
- IWL_CFG_MAC_TYPE_QUZ, IWL_CFG_ANY,
- IWL_CFG_RF_TYPE_JF2, IWL_CFG_RF_ID_JF, IWL_CFG_ANY,
- IWL_CFG_NO_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB,
- iwl9560_quz_a0_jf_b0_cfg, iwl9560_killer_1550i_name),
-
-/* Qu with Hr */
- /* Qu B step */
- _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY,
- IWL_CFG_MAC_TYPE_QU, SILICON_B_STEP,
- IWL_CFG_RF_TYPE_HR1, IWL_CFG_ANY, IWL_CFG_ANY,
- IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_NO_CDB,
- iwl_qu_b0_hr1_b0, iwl_ax101_name),
- _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY,
- IWL_CFG_MAC_TYPE_QU, SILICON_B_STEP,
- IWL_CFG_RF_TYPE_HR2, IWL_CFG_ANY, IWL_CFG_ANY,
- IWL_CFG_NO_160, IWL_CFG_ANY, IWL_CFG_NO_CDB,
- iwl_qu_b0_hr_b0, iwl_ax203_name),
-
- /* Qu C step */
- _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY,
- IWL_CFG_MAC_TYPE_QU, SILICON_C_STEP,
- IWL_CFG_RF_TYPE_HR1, IWL_CFG_ANY, IWL_CFG_ANY,
- IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_NO_CDB,
- iwl_qu_c0_hr1_b0, iwl_ax101_name),
- _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY,
- IWL_CFG_MAC_TYPE_QU, SILICON_C_STEP,
- IWL_CFG_RF_TYPE_HR2, IWL_CFG_ANY, IWL_CFG_ANY,
- IWL_CFG_NO_160, IWL_CFG_ANY, IWL_CFG_NO_CDB,
- iwl_qu_c0_hr_b0, iwl_ax203_name),
- _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY,
- IWL_CFG_MAC_TYPE_QU, SILICON_C_STEP,
- IWL_CFG_RF_TYPE_HR2, IWL_CFG_ANY, IWL_CFG_ANY,
- IWL_CFG_160, IWL_CFG_ANY, IWL_CFG_NO_CDB,
- iwl_qu_c0_hr_b0, iwl_ax201_name),
-
- /* QuZ */
- _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY,
- IWL_CFG_MAC_TYPE_QUZ, IWL_CFG_ANY,
- IWL_CFG_RF_TYPE_HR1, IWL_CFG_ANY, IWL_CFG_ANY,
- IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_NO_CDB,
- iwl_quz_a0_hr1_b0, iwl_ax101_name),
- _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY,
- IWL_CFG_MAC_TYPE_QUZ, SILICON_B_STEP,
- IWL_CFG_RF_TYPE_HR2, IWL_CFG_ANY, IWL_CFG_ANY,
- IWL_CFG_NO_160, IWL_CFG_ANY, IWL_CFG_NO_CDB,
- iwl_cfg_quz_a0_hr_b0, iwl_ax203_name),
- _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY,
- IWL_CFG_MAC_TYPE_QUZ, SILICON_B_STEP,
- IWL_CFG_RF_TYPE_HR2, IWL_CFG_ANY, IWL_CFG_ANY,
- IWL_CFG_160, IWL_CFG_ANY, IWL_CFG_NO_CDB,
- iwl_cfg_quz_a0_hr_b0, iwl_ax201_name),
-
-/* Ma */
- _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY,
- IWL_CFG_MAC_TYPE_MA, IWL_CFG_ANY,
- IWL_CFG_RF_TYPE_HR2, IWL_CFG_ANY, IWL_CFG_ANY,
- IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_NO_CDB,
- iwl_cfg_ma, iwl_ax201_name),
- _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY,
- IWL_CFG_MAC_TYPE_MA, IWL_CFG_ANY,
- IWL_CFG_RF_TYPE_GF, IWL_CFG_ANY, IWL_CFG_ANY,
- IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_ANY,
- iwl_cfg_ma, iwl_ax211_name),
- _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY,
- IWL_CFG_MAC_TYPE_MA, IWL_CFG_ANY,
- IWL_CFG_RF_TYPE_FM, IWL_CFG_ANY, IWL_CFG_ANY,
- IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_NO_CDB,
- iwl_cfg_ma, iwl_ax231_name),
-
-/* So with Hr */
- _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY,
- IWL_CFG_MAC_TYPE_SO, IWL_CFG_ANY,
- IWL_CFG_RF_TYPE_HR2, IWL_CFG_ANY, IWL_CFG_ANY,
- IWL_CFG_NO_160, IWL_CFG_ANY, IWL_CFG_NO_CDB,
- iwl_cfg_so_a0_hr_a0, iwl_ax203_name),
- _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY,
- IWL_CFG_MAC_TYPE_SO, IWL_CFG_ANY,
- IWL_CFG_RF_TYPE_HR1, IWL_CFG_ANY, IWL_CFG_ANY,
- IWL_CFG_NO_160, IWL_CFG_ANY, IWL_CFG_NO_CDB,
- iwl_cfg_so_a0_hr_a0, iwl_ax101_name),
- _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY,
- IWL_CFG_MAC_TYPE_SO, IWL_CFG_ANY,
- IWL_CFG_RF_TYPE_HR2, IWL_CFG_ANY, IWL_CFG_ANY,
- IWL_CFG_160, IWL_CFG_ANY, IWL_CFG_NO_CDB,
- iwl_cfg_so_a0_hr_a0, iwl_ax201_name),
-
-/* So-F with Hr */
- _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY,
- IWL_CFG_MAC_TYPE_SOF, IWL_CFG_ANY,
- IWL_CFG_RF_TYPE_HR2, IWL_CFG_ANY, IWL_CFG_ANY,
- IWL_CFG_NO_160, IWL_CFG_ANY, IWL_CFG_NO_CDB,
- iwl_cfg_so_a0_hr_a0, iwl_ax203_name),
- _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY,
- IWL_CFG_MAC_TYPE_SOF, IWL_CFG_ANY,
- IWL_CFG_RF_TYPE_HR1, IWL_CFG_ANY, IWL_CFG_ANY,
- IWL_CFG_NO_160, IWL_CFG_ANY, IWL_CFG_NO_CDB,
- iwl_cfg_so_a0_hr_a0, iwl_ax101_name),
- _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY,
- IWL_CFG_MAC_TYPE_SOF, IWL_CFG_ANY,
- IWL_CFG_RF_TYPE_HR2, IWL_CFG_ANY, IWL_CFG_ANY,
- IWL_CFG_160, IWL_CFG_ANY, IWL_CFG_NO_CDB,
- iwl_cfg_so_a0_hr_a0, iwl_ax201_name),
-
-/* So-F with Gf */
- _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY,
- IWL_CFG_MAC_TYPE_SOF, IWL_CFG_ANY,
- IWL_CFG_RF_TYPE_GF, IWL_CFG_ANY, IWL_CFG_ANY,
- IWL_CFG_160, IWL_CFG_ANY, IWL_CFG_NO_CDB,
- iwlax211_2ax_cfg_so_gf_a0, iwl_ax211_name),
- _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY,
- IWL_CFG_MAC_TYPE_SOF, IWL_CFG_ANY,
- IWL_CFG_RF_TYPE_GF, IWL_CFG_ANY, IWL_CFG_ANY,
- IWL_CFG_160, IWL_CFG_ANY, IWL_CFG_CDB,
- iwlax411_2ax_cfg_so_gf4_a0, iwl_ax411_name),
-
-/* SoF with JF2 */
- _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY,
- IWL_CFG_MAC_TYPE_SOF, IWL_CFG_ANY,
- IWL_CFG_RF_TYPE_JF2, IWL_CFG_RF_ID_JF, IWL_CFG_ANY,
- IWL_CFG_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB,
- iwlax210_2ax_cfg_so_jf_b0, iwl9560_160_name),
- _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY,
- IWL_CFG_MAC_TYPE_SOF, IWL_CFG_ANY,
- IWL_CFG_RF_TYPE_JF2, IWL_CFG_RF_ID_JF, IWL_CFG_ANY,
- IWL_CFG_NO_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB,
- iwlax210_2ax_cfg_so_jf_b0, iwl9560_name),
-
-/* SoF with JF */
- _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY,
- IWL_CFG_MAC_TYPE_SOF, IWL_CFG_ANY,
- IWL_CFG_RF_TYPE_JF1, IWL_CFG_RF_ID_JF1, IWL_CFG_ANY,
- IWL_CFG_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB,
- iwlax210_2ax_cfg_so_jf_b0, iwl9461_160_name),
- _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY,
- IWL_CFG_MAC_TYPE_SOF, IWL_CFG_ANY,
- IWL_CFG_RF_TYPE_JF1, IWL_CFG_RF_ID_JF1_DIV, IWL_CFG_ANY,
- IWL_CFG_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB,
- iwlax210_2ax_cfg_so_jf_b0, iwl9462_160_name),
- _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY,
- IWL_CFG_MAC_TYPE_SOF, IWL_CFG_ANY,
- IWL_CFG_RF_TYPE_JF1, IWL_CFG_RF_ID_JF1, IWL_CFG_ANY,
- IWL_CFG_NO_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB,
- iwlax210_2ax_cfg_so_jf_b0, iwl9461_name),
- _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY,
- IWL_CFG_MAC_TYPE_SOF, IWL_CFG_ANY,
- IWL_CFG_RF_TYPE_JF1, IWL_CFG_RF_ID_JF1_DIV, IWL_CFG_ANY,
- IWL_CFG_NO_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB,
- iwlax210_2ax_cfg_so_jf_b0, iwl9462_name),
-
-/* So with GF */
- _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY,
- IWL_CFG_MAC_TYPE_SO, IWL_CFG_ANY,
- IWL_CFG_RF_TYPE_GF, IWL_CFG_ANY, IWL_CFG_ANY,
- IWL_CFG_160, IWL_CFG_ANY, IWL_CFG_NO_CDB,
- iwlax211_2ax_cfg_so_gf_a0, iwl_ax211_name),
- _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY,
- IWL_CFG_MAC_TYPE_SO, IWL_CFG_ANY,
- IWL_CFG_RF_TYPE_GF, IWL_CFG_ANY, IWL_CFG_ANY,
- IWL_CFG_160, IWL_CFG_ANY, IWL_CFG_CDB,
- iwlax411_2ax_cfg_so_gf4_a0, iwl_ax411_name),
-
-/* So with JF2 */
- _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY,
- IWL_CFG_MAC_TYPE_SO, IWL_CFG_ANY,
- IWL_CFG_RF_TYPE_JF2, IWL_CFG_RF_ID_JF, IWL_CFG_ANY,
- IWL_CFG_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB,
- iwlax210_2ax_cfg_so_jf_b0, iwl9560_160_name),
- _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY,
- IWL_CFG_MAC_TYPE_SO, IWL_CFG_ANY,
- IWL_CFG_RF_TYPE_JF2, IWL_CFG_RF_ID_JF, IWL_CFG_ANY,
- IWL_CFG_NO_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB,
- iwlax210_2ax_cfg_so_jf_b0, iwl9560_name),
-
-/* So with JF */
- _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY,
- IWL_CFG_MAC_TYPE_SO, IWL_CFG_ANY,
- IWL_CFG_RF_TYPE_JF1, IWL_CFG_RF_ID_JF1, IWL_CFG_ANY,
- IWL_CFG_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB,
- iwlax210_2ax_cfg_so_jf_b0, iwl9461_160_name),
- _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY,
- IWL_CFG_MAC_TYPE_SO, IWL_CFG_ANY,
- IWL_CFG_RF_TYPE_JF1, IWL_CFG_RF_ID_JF1_DIV, IWL_CFG_ANY,
- IWL_CFG_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB,
- iwlax210_2ax_cfg_so_jf_b0, iwl9462_160_name),
- _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY,
- IWL_CFG_MAC_TYPE_SO, IWL_CFG_ANY,
- IWL_CFG_RF_TYPE_JF1, IWL_CFG_RF_ID_JF1, IWL_CFG_ANY,
- IWL_CFG_NO_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB,
- iwlax210_2ax_cfg_so_jf_b0, iwl9461_name),
- _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY,
- IWL_CFG_MAC_TYPE_SO, IWL_CFG_ANY,
- IWL_CFG_RF_TYPE_JF1, IWL_CFG_RF_ID_JF1_DIV, IWL_CFG_ANY,
- IWL_CFG_NO_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB,
- iwlax210_2ax_cfg_so_jf_b0, iwl9462_name),
-
-/* Bz */
- _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY,
- IWL_CFG_MAC_TYPE_BZ, IWL_CFG_ANY,
- IWL_CFG_RF_TYPE_HR2, IWL_CFG_ANY, IWL_CFG_ANY,
- IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_ANY,
- iwl_cfg_bz, iwl_ax201_name),
-
- _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY,
- IWL_CFG_MAC_TYPE_BZ, IWL_CFG_ANY,
- IWL_CFG_RF_TYPE_GF, IWL_CFG_ANY, IWL_CFG_ANY,
- IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_ANY,
- iwl_cfg_bz, iwl_ax211_name),
-
- _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY,
- IWL_CFG_MAC_TYPE_BZ, IWL_CFG_ANY,
- IWL_CFG_RF_TYPE_FM, IWL_CFG_ANY, IWL_CFG_ANY,
- IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_ANY,
- iwl_cfg_bz, iwl_fm_name),
-
- _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY,
- IWL_CFG_MAC_TYPE_BZ, IWL_CFG_ANY,
- IWL_CFG_RF_TYPE_WH, IWL_CFG_ANY, IWL_CFG_ANY,
- IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_ANY,
- iwl_cfg_bz, iwl_wh_name),
-
- _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY,
- IWL_CFG_MAC_TYPE_BZ_W, IWL_CFG_ANY,
- IWL_CFG_RF_TYPE_HR2, IWL_CFG_ANY, IWL_CFG_ANY,
- IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_ANY,
- iwl_cfg_bz, iwl_ax201_name),
-
- _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY,
- IWL_CFG_MAC_TYPE_BZ_W, IWL_CFG_ANY,
- IWL_CFG_RF_TYPE_GF, IWL_CFG_ANY, IWL_CFG_ANY,
- IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_ANY,
- iwl_cfg_bz, iwl_ax211_name),
-
- _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY,
- IWL_CFG_MAC_TYPE_BZ_W, IWL_CFG_ANY,
- IWL_CFG_RF_TYPE_FM, IWL_CFG_ANY, IWL_CFG_ANY,
- IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_ANY,
- iwl_cfg_bz, iwl_fm_name),
-
- _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY,
- IWL_CFG_MAC_TYPE_BZ_W, IWL_CFG_ANY,
- IWL_CFG_RF_TYPE_WH, IWL_CFG_ANY, IWL_CFG_ANY,
- IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_ANY,
- iwl_cfg_bz, iwl_wh_name),
-
-/* Ga (Gl) */
- _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY,
- IWL_CFG_MAC_TYPE_GL, IWL_CFG_ANY,
- IWL_CFG_RF_TYPE_FM, IWL_CFG_ANY, IWL_CFG_ANY,
- IWL_CFG_320, IWL_CFG_ANY, IWL_CFG_NO_CDB,
- iwl_cfg_gl, iwl_gl_name),
- _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY,
- IWL_CFG_MAC_TYPE_GL, IWL_CFG_ANY,
- IWL_CFG_RF_TYPE_FM, IWL_CFG_ANY, IWL_CFG_ANY,
- IWL_CFG_NO_320, IWL_CFG_ANY, IWL_CFG_NO_CDB,
- iwl_cfg_gl, iwl_mtp_name),
-
-/* Sc */
- _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY,
- IWL_CFG_MAC_TYPE_SC, IWL_CFG_ANY,
- IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_ANY,
- IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_ANY,
- iwl_cfg_sc, iwl_sc_name),
- _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY,
- IWL_CFG_MAC_TYPE_SC2, IWL_CFG_ANY,
- IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_ANY,
- IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_ANY,
- iwl_cfg_sc2, iwl_sc2_name),
- _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY,
- IWL_CFG_MAC_TYPE_SC2F, IWL_CFG_ANY,
- IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_ANY,
- IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_ANY,
- iwl_cfg_sc2f, iwl_sc2f_name),
-/* Dr */
- _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY,
- IWL_CFG_MAC_TYPE_DR, IWL_CFG_ANY,
- IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_ANY,
- IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_ANY,
- iwl_cfg_dr, iwl_dr_name),
-
-/* Br */
- _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY,
- IWL_CFG_MAC_TYPE_BR, IWL_CFG_ANY,
- IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_ANY,
- IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_ANY,
- iwl_cfg_br, iwl_br_name),
+/* 7260 Series */
+ IWL_DEV_INFO(iwl7260_cfg, iwl7260_2ac_name,
+ DEVICE(0x08B1)), // unlisted ones fall through to here
+ IWL_DEV_INFO(iwl7260_cfg, iwl7260_2n_name,
+ DEVICE(0x08B1), SUBDEV(0x4060)),
+ IWL_DEV_INFO(iwl7260_cfg, iwl7260_2n_name,
+ DEVICE(0x08B1), SUBDEV(0x406A)),
+ IWL_DEV_INFO(iwl7260_cfg, iwl7260_2n_name,
+ DEVICE(0x08B1), SUBDEV(0x4160)),
+ IWL_DEV_INFO(iwl7260_cfg, iwl7260_n_name,
+ DEVICE(0x08B1), SUBDEV(0x4062)),
+ IWL_DEV_INFO(iwl7260_cfg, iwl7260_n_name,
+ DEVICE(0x08B1), SUBDEV(0x4162)),
+ IWL_DEV_INFO(iwl7260_cfg, iwl7260_2n_name,
+ DEVICE(0x08B1), SUBDEV(0x4460)),
+ IWL_DEV_INFO(iwl7260_cfg, iwl7260_2n_name,
+ DEVICE(0x08B1), SUBDEV(0x446A)),
+ IWL_DEV_INFO(iwl7260_cfg, iwl7260_n_name,
+ DEVICE(0x08B1), SUBDEV(0x4462)),
+ IWL_DEV_INFO(iwl7260_high_temp_cfg, iwl7260_2ac_name,
+ DEVICE(0x08B1), SUBDEV(0x4A70)),
+ IWL_DEV_INFO(iwl7260_high_temp_cfg, iwl7260_2ac_name,
+ DEVICE(0x08B1), SUBDEV(0x4A6E)),
+ IWL_DEV_INFO(iwl7260_high_temp_cfg, iwl7260_2ac_name,
+ DEVICE(0x08B1), SUBDEV(0x4A6C)),
+ IWL_DEV_INFO(iwl7260_cfg, iwl7260_2n_name,
+ DEVICE(0x08B1), SUBDEV(0x4560)),
+ IWL_DEV_INFO(iwl7260_cfg, iwl7260_2n_name,
+ DEVICE(0x08B1), SUBDEV(0x4020)),
+ IWL_DEV_INFO(iwl7260_cfg, iwl7260_2n_name,
+ DEVICE(0x08B1), SUBDEV(0x402A)),
+ IWL_DEV_INFO(iwl7260_cfg, iwl7260_2n_name,
+ DEVICE(0x08B1), SUBDEV(0x4420)),
+ IWL_DEV_INFO(iwl7260_cfg, iwl7260_2n_name,
+ DEVICE(0x08B1), SUBDEV(0xC060)),
+ IWL_DEV_INFO(iwl7260_cfg, iwl7260_2n_name,
+ DEVICE(0x08B1), SUBDEV(0xC06A)),
+ IWL_DEV_INFO(iwl7260_cfg, iwl7260_2n_name,
+ DEVICE(0x08B1), SUBDEV(0xC160)),
+ IWL_DEV_INFO(iwl7260_cfg, iwl7260_n_name,
+ DEVICE(0x08B1), SUBDEV(0xC062)),
+ IWL_DEV_INFO(iwl7260_cfg, iwl7260_n_name,
+ DEVICE(0x08B1), SUBDEV(0xC162)),
+ IWL_DEV_INFO(iwl7260_cfg, iwl7260_2n_name,
+ DEVICE(0x08B1), SUBDEV(0xC760)),
+ IWL_DEV_INFO(iwl7260_cfg, iwl7260_2n_name,
+ DEVICE(0x08B1), SUBDEV(0xC460)),
+ IWL_DEV_INFO(iwl7260_cfg, iwl7260_n_name,
+ DEVICE(0x08B1), SUBDEV(0xC462)),
+ IWL_DEV_INFO(iwl7260_cfg, iwl7260_2n_name,
+ DEVICE(0x08B1), SUBDEV(0xC560)),
+ IWL_DEV_INFO(iwl7260_cfg, iwl7260_2n_name,
+ DEVICE(0x08B1), SUBDEV(0xC360)),
+ IWL_DEV_INFO(iwl7260_cfg, iwl7260_2n_name,
+ DEVICE(0x08B1), SUBDEV(0xC020)),
+ IWL_DEV_INFO(iwl7260_cfg, iwl7260_2n_name,
+ DEVICE(0x08B1), SUBDEV(0xC02A)),
+ IWL_DEV_INFO(iwl7260_cfg, iwl7260_2n_name,
+ DEVICE(0x08B1), SUBDEV(0xC420)),
+ IWL_DEV_INFO(iwl7260_cfg, iwl7260_2ac_name,
+ DEVICE(0x08B2), SUBDEV(0x4270)),
+ IWL_DEV_INFO(iwl7260_cfg, iwl7260_2ac_name,
+ DEVICE(0x08B2), SUBDEV(0x4272)),
+ IWL_DEV_INFO(iwl7260_cfg, iwl7260_2n_name,
+ DEVICE(0x08B2), SUBDEV(0x4260)),
+ IWL_DEV_INFO(iwl7260_cfg, iwl7260_2n_name,
+ DEVICE(0x08B2), SUBDEV(0x426A)),
+ IWL_DEV_INFO(iwl7260_cfg, iwl7260_n_name,
+ DEVICE(0x08B2), SUBDEV(0x4262)),
+ IWL_DEV_INFO(iwl7260_cfg, iwl7260_2ac_name,
+ DEVICE(0x08B2), SUBDEV(0x4370)),
+ IWL_DEV_INFO(iwl7260_cfg, iwl7260_2n_name,
+ DEVICE(0x08B2), SUBDEV(0x4360)),
+ IWL_DEV_INFO(iwl7260_cfg, iwl7260_2n_name,
+ DEVICE(0x08B2), SUBDEV(0x4220)),
+ IWL_DEV_INFO(iwl7260_cfg, iwl7260_2ac_name,
+ DEVICE(0x08B2), SUBDEV(0xC270)),
+ IWL_DEV_INFO(iwl7260_cfg, iwl7260_2ac_name,
+ DEVICE(0x08B2), SUBDEV(0xC272)),
+ IWL_DEV_INFO(iwl7260_cfg, iwl7260_2n_name,
+ DEVICE(0x08B2), SUBDEV(0xC260)),
+ IWL_DEV_INFO(iwl7260_cfg, iwl7260_n_name,
+ DEVICE(0x08B2), SUBDEV(0xC26A)),
+ IWL_DEV_INFO(iwl7260_cfg, iwl7260_n_name,
+ DEVICE(0x08B2), SUBDEV(0xC262)),
+ IWL_DEV_INFO(iwl7260_cfg, iwl7260_2ac_name,
+ DEVICE(0x08B2), SUBDEV(0xC370)),
+ IWL_DEV_INFO(iwl7260_cfg, iwl7260_2n_name,
+ DEVICE(0x08B2), SUBDEV(0xC220)),
+
+/* 3160 Series */
+ IWL_DEV_INFO(iwl3160_cfg, iwl3160_2ac_name,
+ DEVICE(0x08B3)),
+
+ IWL_DEV_INFO(iwl3160_cfg, iwl3160_n_name,
+ DEVICE(0x08B3), SUBDEV_MASKED(0x62, 0xFF)),
+ IWL_DEV_INFO(iwl3160_cfg, iwl3160_2n_name,
+ DEVICE(0x08B3), SUBDEV_MASKED(0x60, 0xFF)),
+
+ IWL_DEV_INFO(iwl3160_cfg, iwl3160_2ac_name,
+ DEVICE(0x08B4)),
+
+/* 3165 Series */
+ IWL_DEV_INFO(iwl3165_2ac_cfg, iwl3165_2ac_name,
+ DEVICE(0x3165)),
+ IWL_DEV_INFO(iwl3165_2ac_cfg, iwl3165_2ac_name,
+ DEVICE(0x3166)),
+
+/* 3168 Series */
+ IWL_DEV_INFO(iwl3168_2ac_cfg, iwl3168_2ac_name,
+ DEVICE(0x24FB)),
+
+/* 7265 Series */
+ IWL_DEV_INFO(iwl7265_cfg, iwl7265_2ac_name,
+ DEVICE(0x095A)),
+ IWL_DEV_INFO(iwl7265_cfg, iwl7265_2n_name,
+ DEVICE(0x095A), SUBDEV(0x5000)),
+ IWL_DEV_INFO(iwl7265_cfg, iwl7265_2n_name,
+ DEVICE(0x095A), SUBDEV(0x500A)),
+ IWL_DEV_INFO(iwl7265_cfg, iwl7265_n_name,
+ DEVICE(0x095A), SUBDEV(0x5002)),
+ IWL_DEV_INFO(iwl7265_cfg, iwl7265_n_name,
+ DEVICE(0x095A), SUBDEV(0x5102)),
+ IWL_DEV_INFO(iwl7265_cfg, iwl7265_2n_name,
+ DEVICE(0x095A), SUBDEV(0x5020)),
+ IWL_DEV_INFO(iwl7265_cfg, iwl7265_2n_name,
+ DEVICE(0x095A), SUBDEV(0x502A)),
+ IWL_DEV_INFO(iwl7265_cfg, iwl7265_2n_name,
+ DEVICE(0x095A), SUBDEV(0x5090)),
+ IWL_DEV_INFO(iwl7265_cfg, iwl7265_2n_name,
+ DEVICE(0x095A), SUBDEV(0x5190)),
+ IWL_DEV_INFO(iwl7265_cfg, iwl7265_2n_name,
+ DEVICE(0x095A), SUBDEV(0x5100)),
+ IWL_DEV_INFO(iwl7265_cfg, iwl7265_2n_name,
+ DEVICE(0x095A), SUBDEV(0x5400)),
+ IWL_DEV_INFO(iwl7265_cfg, iwl7265_2n_name,
+ DEVICE(0x095A), SUBDEV(0x5420)),
+ IWL_DEV_INFO(iwl7265_cfg, iwl7265_2n_name,
+ DEVICE(0x095A), SUBDEV(0x5490)),
+ IWL_DEV_INFO(iwl7265_cfg, iwl7265_2n_name,
+ DEVICE(0x095A), SUBDEV(0x5C10)),
+ IWL_DEV_INFO(iwl7265_cfg, iwl7265_2n_name,
+ DEVICE(0x095A), SUBDEV(0x5590)),
+ IWL_DEV_INFO(iwl7265_cfg, iwl7265_2n_name,
+ DEVICE(0x095A), SUBDEV(0x9000)),
+ IWL_DEV_INFO(iwl7265_cfg, iwl7265_2n_name,
+ DEVICE(0x095A), SUBDEV(0x900A)),
+ IWL_DEV_INFO(iwl7265_cfg, iwl7265_2n_name,
+ DEVICE(0x095A), SUBDEV(0x9400)),
+
+ IWL_DEV_INFO(iwl7265_cfg, iwl7265_2ac_name,
+ DEVICE(0x095B)),
+ IWL_DEV_INFO(iwl7265_cfg, iwl7265_2n_name,
+ DEVICE(0x095B), SUBDEV(0x520A)),
+ IWL_DEV_INFO(iwl7265_cfg, iwl7265_n_name,
+ DEVICE(0x095B), SUBDEV(0x5302)),
+ IWL_DEV_INFO(iwl7265_cfg, iwl7265_2n_name,
+ DEVICE(0x095B), SUBDEV(0x5200)),
+ IWL_DEV_INFO(iwl7265_cfg, iwl7265_n_name,
+ DEVICE(0x095B), SUBDEV(0x5202)),
+ IWL_DEV_INFO(iwl7265_cfg, iwl7265_2n_name,
+ DEVICE(0x095B), SUBDEV(0x9200)),
+
+/* 8000 Series */
+ IWL_DEV_INFO(iwl8260_cfg, iwl8260_2ac_name,
+ DEVICE(0x24F3)),
+ IWL_DEV_INFO(iwl8260_cfg, iwl8260_2n_name,
+ DEVICE(0x24F3), SUBDEV(0x0004)),
+ IWL_DEV_INFO(iwl8260_cfg, iwl8260_2n_name,
+ DEVICE(0x24F3), SUBDEV(0x0044)),
+ IWL_DEV_INFO(iwl8265_cfg, iwl8265_2ac_name,
+ DEVICE(0x24FD)),
+ IWL_DEV_INFO(iwl8265_cfg, iwl8275_2ac_name,
+ DEVICE(0x24FD), SUBDEV(0x3E02)),
+ IWL_DEV_INFO(iwl8265_cfg, iwl8275_2ac_name,
+ DEVICE(0x24FD), SUBDEV(0x3E01)),
+ IWL_DEV_INFO(iwl8265_cfg, iwl8275_2ac_name,
+ DEVICE(0x24FD), SUBDEV(0x1012)),
+ IWL_DEV_INFO(iwl8265_cfg, iwl8275_2ac_name,
+ DEVICE(0x24FD), SUBDEV(0x0012)),
+ IWL_DEV_INFO(iwl8265_cfg, iwl_killer_1435i_name,
+ DEVICE(0x24FD), SUBDEV(0x1431)),
+ IWL_DEV_INFO(iwl8265_cfg, iwl_killer_1434_kix_name,
+ DEVICE(0x24FD), SUBDEV(0x1432)),
+
+/* JF1 RF */
+ IWL_DEV_INFO(iwl_rf_jf, iwl9461_160_name,
+ RF_TYPE(JF1)),
+ IWL_DEV_INFO(iwl_rf_jf_80mhz, iwl9461_name,
+ RF_TYPE(JF1), BW_LIMITED),
+ IWL_DEV_INFO(iwl_rf_jf, iwl9462_160_name,
+ RF_TYPE(JF1), RF_ID(JF1_DIV)),
+ IWL_DEV_INFO(iwl_rf_jf_80mhz, iwl9462_name,
+ RF_TYPE(JF1), RF_ID(JF1_DIV), BW_LIMITED),
+/* JF2 RF */
+ IWL_DEV_INFO(iwl_rf_jf, iwl9260_160_name,
+ RF_TYPE(JF2)),
+ IWL_DEV_INFO(iwl_rf_jf_80mhz, iwl9260_name,
+ RF_TYPE(JF2), BW_LIMITED),
+ IWL_DEV_INFO(iwl_rf_jf, iwl9560_160_name,
+ RF_TYPE(JF2), RF_ID(JF)),
+ IWL_DEV_INFO(iwl_rf_jf_80mhz, iwl9560_name,
+ RF_TYPE(JF2), RF_ID(JF), BW_LIMITED),
+
+/* HR RF */
+ IWL_DEV_INFO(iwl_rf_hr, iwl_ax201_name, RF_TYPE(HR2)),
+ IWL_DEV_INFO(iwl_rf_hr_80mhz, iwl_ax101_name, RF_TYPE(HR1)),
+ IWL_DEV_INFO(iwl_rf_hr_80mhz, iwl_ax203_name, RF_TYPE(HR2), BW_LIMITED),
+ IWL_DEV_INFO(iwl_rf_hr, iwl_ax200_name, DEVICE(0x2723)),
+
+/* GF RF */
+ IWL_DEV_INFO(iwl_rf_gf, iwl_ax211_name, RF_TYPE(GF)),
+ IWL_DEV_INFO(iwl_rf_gf, iwl_ax411_name, RF_TYPE(GF), CDB),
+ IWL_DEV_INFO(iwl_rf_gf, iwl_ax210_name, DEVICE(0x2725)),
+
+/* Killer CRFs */
+ IWL_DEV_INFO(iwl_rf_jf, iwl9260_killer_1550_name, SUBDEV(0x1550)),
+ IWL_DEV_INFO(iwl_rf_jf, iwl9560_killer_1550s_name, SUBDEV(0x1551)),
+ IWL_DEV_INFO(iwl_rf_jf, iwl9560_killer_1550i_name, SUBDEV(0x1552)),
+
+ IWL_DEV_INFO(iwl_rf_hr, iwl_ax201_killer_1650s_name, SUBDEV(0x1651)),
+ IWL_DEV_INFO(iwl_rf_hr, iwl_ax201_killer_1650i_name, SUBDEV(0x1652)),
+
+ IWL_DEV_INFO(iwl_rf_gf, iwl_ax211_killer_1675s_name, SUBDEV(0x1671)),
+ IWL_DEV_INFO(iwl_rf_gf, iwl_ax211_killer_1675i_name, SUBDEV(0x1672)),
+ IWL_DEV_INFO(iwl_rf_gf, iwl_ax210_killer_1675w_name, SUBDEV(0x1673)),
+ IWL_DEV_INFO(iwl_rf_gf, iwl_ax210_killer_1675x_name, SUBDEV(0x1674)),
+ IWL_DEV_INFO(iwl_rf_gf, iwl_ax411_killer_1690s_name, SUBDEV(0x1691)),
+ IWL_DEV_INFO(iwl_rf_gf, iwl_ax411_killer_1690i_name, SUBDEV(0x1692)),
+
+/* Killer discrete */
+ IWL_DEV_INFO(iwl_rf_hr, iwl_ax200_killer_1650w_name,
+ DEVICE(0x2723), SUBDEV(0x1653)),
+ IWL_DEV_INFO(iwl_rf_hr, iwl_ax200_killer_1650x_name,
+ DEVICE(0x2723), SUBDEV(0x1654)),
#endif /* CONFIG_IWLMVM */
+#if IS_ENABLED(CONFIG_IWLMLD)
+/* FM RF */
+ IWL_DEV_INFO(iwl_rf_fm, iwl_be201_name, RF_TYPE(FM)),
+ IWL_DEV_INFO(iwl_rf_fm, iwl_be401_name, RF_TYPE(FM), CDB),
+ /* the discrete NICs got the RF B0, it's only for the name anyway */
+ IWL_DEV_INFO(iwl_rf_fm, iwl_be200_name, RF_TYPE(FM),
+ DEVICE(0x272B), RF_STEP(B)),
+ IWL_DEV_INFO(iwl_rf_fm_160mhz, iwl_be202_name,
+ RF_TYPE(FM), BW_LIMITED),
+
+/* Killer CRFs */
+ IWL_DEV_INFO(iwl_rf_fm, iwl_killer_be1750s_name, SUBDEV(0x1771)),
+ IWL_DEV_INFO(iwl_rf_fm, iwl_killer_be1750i_name, SUBDEV(0x1772)),
+ IWL_DEV_INFO(iwl_rf_fm, iwl_killer_be1790s_name, SUBDEV(0x1791)),
+ IWL_DEV_INFO(iwl_rf_fm, iwl_killer_be1790i_name, SUBDEV(0x1792)),
+
+/* Killer discrete */
+ IWL_DEV_INFO(iwl_rf_fm, iwl_killer_be1750w_name,
+ DEVICE(0x272B), SUBDEV(0x1773)),
+ IWL_DEV_INFO(iwl_rf_fm, iwl_killer_be1750x_name,
+ DEVICE(0x272B), SUBDEV(0x1774)),
+
+/* WH RF */
+ IWL_DEV_INFO(iwl_rf_wh, iwl_be211_name, RF_TYPE(WH)),
+ IWL_DEV_INFO(iwl_rf_wh_160mhz, iwl_be213_name, RF_TYPE(WH), BW_LIMITED),
+
+/* PE RF */
+ IWL_DEV_INFO(iwl_rf_pe, iwl_bn201_name, RF_TYPE(PE)),
+ IWL_DEV_INFO(iwl_rf_pe, iwl_be223_name, RF_TYPE(PE), SUBDEV(0x0524)),
+ IWL_DEV_INFO(iwl_rf_pe, iwl_be221_name, RF_TYPE(PE), SUBDEV(0x0324)),
+
+/* Killer */
+ IWL_DEV_INFO(iwl_rf_wh, iwl_killer_be1775s_name, SUBDEV(0x1776)),
+ IWL_DEV_INFO(iwl_rf_wh, iwl_killer_be1775i_name, SUBDEV(0x1775)),
+
+ IWL_DEV_INFO(iwl_rf_pe, iwl_killer_bn1850w2_name, SUBDEV(0x1851)),
+ IWL_DEV_INFO(iwl_rf_pe, iwl_killer_bn1850i_name, SUBDEV(0x1852)),
+#endif /* CONFIG_IWLMLD */
};
EXPORT_SYMBOL_IF_IWLWIFI_KUNIT(iwl_dev_info_table);
@@ -1210,13 +1076,15 @@ EXPORT_SYMBOL_IF_IWLWIFI_KUNIT(iwl_dev_info_table_size);
/*
* Read rf id and cdb info from prph register and store it
*/
-static void get_crf_id(struct iwl_trans *iwl_trans)
+static void get_crf_id(struct iwl_trans *iwl_trans,
+ struct iwl_trans_info *info)
{
u32 sd_reg_ver_addr;
+ u32 hw_wfpm_id;
u32 val = 0;
u8 step;
- if (iwl_trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_AX210)
+ if (iwl_trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_AX210)
sd_reg_ver_addr = SD_REG_VER_GEN2;
else
sd_reg_ver_addr = SD_REG_VER;
@@ -1227,83 +1095,83 @@ static void get_crf_id(struct iwl_trans *iwl_trans)
iwl_write_umac_prph_no_grab(iwl_trans, WFPM_CTRL_REG, val);
/* Read crf info */
- iwl_trans->hw_crf_id = iwl_read_prph_no_grab(iwl_trans, sd_reg_ver_addr);
+ info->hw_crf_id = iwl_read_prph_no_grab(iwl_trans, sd_reg_ver_addr);
/* Read cnv info */
- iwl_trans->hw_cnv_id =
- iwl_read_prph_no_grab(iwl_trans, CNVI_AUX_MISC_CHIP);
+ info->hw_cnv_id = iwl_read_prph_no_grab(iwl_trans, CNVI_AUX_MISC_CHIP);
/* For BZ-W, take B step also when A step is indicated */
- if (CSR_HW_REV_TYPE(iwl_trans->hw_rev) == IWL_CFG_MAC_TYPE_BZ_W)
+ if (CSR_HW_REV_TYPE(info->hw_rev) == IWL_CFG_MAC_TYPE_BZ_W)
step = SILICON_B_STEP;
/* In BZ, the MAC step must be read from the CNVI aux register */
- if (CSR_HW_REV_TYPE(iwl_trans->hw_rev) == IWL_CFG_MAC_TYPE_BZ) {
- step = CNVI_AUX_MISC_CHIP_MAC_STEP(iwl_trans->hw_cnv_id);
+ if (CSR_HW_REV_TYPE(info->hw_rev) == IWL_CFG_MAC_TYPE_BZ) {
+ step = CNVI_AUX_MISC_CHIP_MAC_STEP(info->hw_cnv_id);
/* For BZ-U, take B step also when A step is indicated */
- if ((CNVI_AUX_MISC_CHIP_PROD_TYPE(iwl_trans->hw_cnv_id) ==
+ if ((CNVI_AUX_MISC_CHIP_PROD_TYPE(info->hw_cnv_id) ==
CNVI_AUX_MISC_CHIP_PROD_TYPE_BZ_U) &&
step == SILICON_A_STEP)
step = SILICON_B_STEP;
}
- if (CSR_HW_REV_TYPE(iwl_trans->hw_rev) == IWL_CFG_MAC_TYPE_BZ ||
- CSR_HW_REV_TYPE(iwl_trans->hw_rev) == IWL_CFG_MAC_TYPE_BZ_W) {
- iwl_trans->hw_rev_step = step;
- iwl_trans->hw_rev |= step;
+ if (CSR_HW_REV_TYPE(info->hw_rev) == IWL_CFG_MAC_TYPE_BZ ||
+ CSR_HW_REV_TYPE(info->hw_rev) == IWL_CFG_MAC_TYPE_BZ_W) {
+ info->hw_rev_step = step;
+ info->hw_rev |= step;
}
/* Read cdb info (also contains the jacket info if needed in the future */
- iwl_trans->hw_wfpm_id =
- iwl_read_umac_prph_no_grab(iwl_trans, WFPM_OTP_CFG1_ADDR);
+ hw_wfpm_id = iwl_read_umac_prph_no_grab(iwl_trans, WFPM_OTP_CFG1_ADDR);
IWL_INFO(iwl_trans, "Detected crf-id 0x%x, cnv-id 0x%x wfpm id 0x%x\n",
- iwl_trans->hw_crf_id, iwl_trans->hw_cnv_id,
- iwl_trans->hw_wfpm_id);
+ info->hw_crf_id, info->hw_cnv_id, hw_wfpm_id);
}
/*
* In case that there is no OTP on the NIC, map the rf id and cdb info
* from the prph registers.
*/
-static int map_crf_id(struct iwl_trans *iwl_trans)
+static int map_crf_id(struct iwl_trans *iwl_trans,
+ struct iwl_trans_info *info)
{
int ret = 0;
- u32 val = iwl_trans->hw_crf_id;
+ u32 val = info->hw_crf_id;
u32 step_id = REG_CRF_ID_STEP(val);
u32 slave_id = REG_CRF_ID_SLAVE(val);
- u32 jacket_id_cnv = REG_CRF_ID_SLAVE(iwl_trans->hw_cnv_id);
- u32 jacket_id_wfpm = WFPM_OTP_CFG1_IS_JACKET(iwl_trans->hw_wfpm_id);
- u32 cdb_id_wfpm = WFPM_OTP_CFG1_IS_CDB(iwl_trans->hw_wfpm_id);
+ u32 jacket_id_cnv = REG_CRF_ID_SLAVE(info->hw_cnv_id);
+ u32 hw_wfpm_id = iwl_read_umac_prph_no_grab(iwl_trans,
+ WFPM_OTP_CFG1_ADDR);
+ u32 jacket_id_wfpm = WFPM_OTP_CFG1_IS_JACKET(hw_wfpm_id);
+ u32 cdb_id_wfpm = WFPM_OTP_CFG1_IS_CDB(hw_wfpm_id);
/* Map between crf id to rf id */
switch (REG_CRF_ID_TYPE(val)) {
case REG_CRF_ID_TYPE_JF_1:
- iwl_trans->hw_rf_id = (IWL_CFG_RF_TYPE_JF1 << 12);
+ info->hw_rf_id = (IWL_CFG_RF_TYPE_JF1 << 12);
break;
case REG_CRF_ID_TYPE_JF_2:
- iwl_trans->hw_rf_id = (IWL_CFG_RF_TYPE_JF2 << 12);
+ info->hw_rf_id = (IWL_CFG_RF_TYPE_JF2 << 12);
break;
case REG_CRF_ID_TYPE_HR_NONE_CDB_1X1:
- iwl_trans->hw_rf_id = (IWL_CFG_RF_TYPE_HR1 << 12);
+ info->hw_rf_id = (IWL_CFG_RF_TYPE_HR1 << 12);
break;
case REG_CRF_ID_TYPE_HR_NONE_CDB:
- iwl_trans->hw_rf_id = (IWL_CFG_RF_TYPE_HR2 << 12);
+ info->hw_rf_id = (IWL_CFG_RF_TYPE_HR2 << 12);
break;
case REG_CRF_ID_TYPE_HR_CDB:
- iwl_trans->hw_rf_id = (IWL_CFG_RF_TYPE_HR2 << 12);
+ info->hw_rf_id = (IWL_CFG_RF_TYPE_HR2 << 12);
break;
case REG_CRF_ID_TYPE_GF:
- iwl_trans->hw_rf_id = (IWL_CFG_RF_TYPE_GF << 12);
+ info->hw_rf_id = (IWL_CFG_RF_TYPE_GF << 12);
break;
case REG_CRF_ID_TYPE_FM:
- iwl_trans->hw_rf_id = (IWL_CFG_RF_TYPE_FM << 12);
+ info->hw_rf_id = (IWL_CFG_RF_TYPE_FM << 12);
break;
case REG_CRF_ID_TYPE_WHP:
- iwl_trans->hw_rf_id = (IWL_CFG_RF_TYPE_WH << 12);
+ info->hw_rf_id = (IWL_CFG_RF_TYPE_WH << 12);
break;
case REG_CRF_ID_TYPE_PE:
- iwl_trans->hw_rf_id = (IWL_CFG_RF_TYPE_PE << 12);
+ info->hw_rf_id = (IWL_CFG_RF_TYPE_PE << 12);
break;
default:
ret = -EIO;
@@ -1315,28 +1183,28 @@ static int map_crf_id(struct iwl_trans *iwl_trans)
}
/* Set Step-id */
- iwl_trans->hw_rf_id |= (step_id << 8);
+ info->hw_rf_id |= (step_id << 8);
/* Set CDB capabilities */
if (cdb_id_wfpm || slave_id) {
- iwl_trans->hw_rf_id += BIT(28);
+ info->hw_rf_id += BIT(28);
IWL_INFO(iwl_trans, "Adding cdb to rf id\n");
}
/* Set Jacket capabilities */
if (jacket_id_wfpm || jacket_id_cnv) {
- iwl_trans->hw_rf_id += BIT(29);
+ info->hw_rf_id += BIT(29);
IWL_INFO(iwl_trans, "Adding jacket to rf id\n");
}
IWL_INFO(iwl_trans,
"Detected rf-type 0x%x step-id 0x%x slave-id 0x%x from crf id 0x%x\n",
- REG_CRF_ID_TYPE(val), step_id, slave_id, iwl_trans->hw_rf_id);
+ REG_CRF_ID_TYPE(val), step_id, slave_id, info->hw_rf_id);
IWL_INFO(iwl_trans,
"Detected cdb-id 0x%x jacket-id 0x%x from wfpm id 0x%x\n",
- cdb_id_wfpm, jacket_id_wfpm, iwl_trans->hw_wfpm_id);
+ cdb_id_wfpm, jacket_id_wfpm, hw_wfpm_id);
IWL_INFO(iwl_trans, "Detected jacket-id 0x%x from cnvi id 0x%x\n",
- jacket_id_cnv, iwl_trans->hw_cnv_id);
+ jacket_id_cnv, info->hw_cnv_id);
out:
return ret;
@@ -1346,9 +1214,8 @@ out:
#define PCI_CFG_RETRY_TIMEOUT 0x041
VISIBLE_IF_IWLWIFI_KUNIT const struct iwl_dev_info *
-iwl_pci_find_dev_info(u16 device, u16 subsystem_device,
- u16 mac_type, u8 mac_step, u16 rf_type, u8 cdb,
- u8 jacket, u8 rf_id, u8 no_160, u8 cores, u8 rf_step)
+iwl_pci_find_dev_info(u16 device, u16 subsystem_device, u16 rf_type, u8 cdb,
+ u8 rf_id, u8 bw_limit, u8 rf_step)
{
int num_devices = ARRAY_SIZE(iwl_dev_info_table);
int i;
@@ -1358,49 +1225,32 @@ iwl_pci_find_dev_info(u16 device, u16 subsystem_device,
for (i = num_devices - 1; i >= 0; i--) {
const struct iwl_dev_info *dev_info = &iwl_dev_info_table[i];
+ u16 subdevice_mask;
if (dev_info->device != (u16)IWL_CFG_ANY &&
dev_info->device != device)
continue;
- if (dev_info->subdevice != (u16)IWL_CFG_ANY &&
- dev_info->subdevice != subsystem_device)
- continue;
-
- if (dev_info->mac_type != (u16)IWL_CFG_ANY &&
- dev_info->mac_type != mac_type)
- continue;
-
- if (dev_info->mac_step != (u8)IWL_CFG_ANY &&
- dev_info->mac_step != mac_step)
- continue;
+ subdevice_mask = GENMASK(dev_info->subdevice_m_h,
+ dev_info->subdevice_m_l);
- if (dev_info->rf_type != (u16)IWL_CFG_ANY &&
- dev_info->rf_type != rf_type)
- continue;
-
- if (dev_info->cdb != (u8)IWL_CFG_ANY &&
- dev_info->cdb != cdb)
+ if (dev_info->subdevice != (u16)IWL_CFG_ANY &&
+ dev_info->subdevice != (subsystem_device & subdevice_mask))
continue;
- if (dev_info->jacket != (u8)IWL_CFG_ANY &&
- dev_info->jacket != jacket)
+ if (dev_info->match_rf_type && dev_info->rf_type != rf_type)
continue;
- if (dev_info->rf_id != (u8)IWL_CFG_ANY &&
- dev_info->rf_id != rf_id)
+ if (dev_info->match_cdb && dev_info->cdb != cdb)
continue;
- if (dev_info->no_160 != (u8)IWL_CFG_ANY &&
- dev_info->no_160 != no_160)
+ if (dev_info->match_rf_id && dev_info->rf_id != rf_id)
continue;
- if (dev_info->cores != (u8)IWL_CFG_ANY &&
- dev_info->cores != cores)
+ if (dev_info->match_bw_limit && dev_info->bw_limit != bw_limit)
continue;
- if (dev_info->rf_step != (u8)IWL_CFG_ANY &&
- dev_info->rf_step != rf_step)
+ if (dev_info->match_rf_step && dev_info->rf_step != rf_step)
continue;
return dev_info;
@@ -1412,30 +1262,32 @@ EXPORT_SYMBOL_IF_IWLWIFI_KUNIT(iwl_pci_find_dev_info);
static void iwl_pcie_recheck_me_status(struct work_struct *wk)
{
- struct iwl_trans *trans = container_of(wk, typeof(*trans),
- me_recheck_wk.work);
+ struct iwl_trans_pcie *trans_pcie = container_of(wk,
+ typeof(*trans_pcie),
+ me_recheck_wk.work);
u32 val;
- val = iwl_read32(trans, CSR_HW_IF_CONFIG_REG);
- trans->me_present = !!(val & CSR_HW_IF_CONFIG_REG_IAMT_UP);
+ val = iwl_read32(trans_pcie->trans, CSR_HW_IF_CONFIG_REG);
+ trans_pcie->me_present = !!(val & CSR_HW_IF_CONFIG_REG_IAMT_UP);
}
static void iwl_pcie_check_me_status(struct iwl_trans *trans)
{
+ struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
u32 val;
- trans->me_present = -1;
+ trans_pcie->me_present = -1;
- INIT_DELAYED_WORK(&trans->me_recheck_wk,
+ INIT_DELAYED_WORK(&trans_pcie->me_recheck_wk,
iwl_pcie_recheck_me_status);
/* we don't have a good way of determining this until BZ */
- if (trans->trans_cfg->device_family < IWL_DEVICE_FAMILY_BZ)
+ if (trans->mac_cfg->device_family < IWL_DEVICE_FAMILY_BZ)
return;
val = iwl_read_prph(trans, CNVI_SCU_REG_FOR_ECO_1);
if (val & CNVI_SCU_REG_FOR_ECO_1_WIAMT_KNOWN) {
- trans->me_present =
+ trans_pcie->me_present =
!!(val & CNVI_SCU_REG_FOR_ECO_1_WIAMT_PRESENT);
return;
}
@@ -1443,38 +1295,28 @@ static void iwl_pcie_check_me_status(struct iwl_trans *trans)
val = iwl_read32(trans, CSR_HW_IF_CONFIG_REG);
if (val & (CSR_HW_IF_CONFIG_REG_ME_OWN |
CSR_HW_IF_CONFIG_REG_IAMT_UP)) {
- trans->me_present = 1;
+ trans_pcie->me_present = 1;
return;
}
/* recheck again later, ME might still be initializing */
- schedule_delayed_work(&trans->me_recheck_wk, HZ);
+ schedule_delayed_work(&trans_pcie->me_recheck_wk, HZ);
}
static int iwl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
{
- const struct iwl_cfg_trans_params *trans;
- const struct iwl_cfg *cfg_7265d __maybe_unused = NULL;
+ const struct iwl_mac_cfg *trans;
const struct iwl_dev_info *dev_info;
+ struct iwl_trans_info info = {
+ .hw_id = (pdev->device << 16) + pdev->subsystem_device,
+ };
struct iwl_trans *iwl_trans;
struct iwl_trans_pcie *trans_pcie;
int ret;
- const struct iwl_cfg *cfg;
- trans = (void *)(ent->driver_data & ~TRANS_CFG_MARKER);
+ trans = (void *)ent->driver_data;
- /*
- * This is needed for backwards compatibility with the old
- * tables, so we don't need to change all the config structs
- * at the same time. The cfg is used to compare with the old
- * full cfg structs.
- */
- cfg = (void *)(ent->driver_data & ~TRANS_CFG_MARKER);
-
- /* make sure trans is the first element in iwl_cfg */
- BUILD_BUG_ON(offsetof(struct iwl_cfg, trans));
-
- iwl_trans = iwl_trans_pcie_alloc(pdev, ent, trans);
+ iwl_trans = iwl_trans_pcie_alloc(pdev, trans, &info);
if (IS_ERR(iwl_trans))
return PTR_ERR(iwl_trans);
@@ -1483,6 +1325,9 @@ static int iwl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
iwl_trans_pcie_check_product_reset_status(pdev);
iwl_trans_pcie_check_product_reset_mode(pdev);
+ /* set the things we know so far for the grab NIC access */
+ iwl_trans_set_info(iwl_trans, &info);
+
/*
* Let's try to grab NIC access early here. Sometimes, NICs may
* fail to initialize, and if that happens it's better if we see
@@ -1496,7 +1341,7 @@ static int iwl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
if (ret)
goto out_free_trans;
if (iwl_trans_grab_nic_access(iwl_trans)) {
- get_crf_id(iwl_trans);
+ get_crf_id(iwl_trans, &info);
/* all good */
iwl_trans_release_nic_access(iwl_trans);
} else {
@@ -1505,38 +1350,32 @@ static int iwl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
}
}
- iwl_trans->hw_rf_id = iwl_read32(iwl_trans, CSR_HW_RF_ID);
+ info.hw_rf_id = iwl_read32(iwl_trans, CSR_HW_RF_ID);
/*
* The RF_ID is set to zero in blank OTP so read version to
* extract the RF_ID.
* This is relevant only for family 9000 and up.
*/
- if (iwl_trans->trans_cfg->rf_id &&
- iwl_trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_9000 &&
- !CSR_HW_RFID_TYPE(iwl_trans->hw_rf_id) && map_crf_id(iwl_trans)) {
+ if (iwl_trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_9000 &&
+ !CSR_HW_RFID_TYPE(info.hw_rf_id) && map_crf_id(iwl_trans, &info)) {
ret = -EINVAL;
goto out_free_trans;
}
IWL_INFO(iwl_trans, "PCI dev %04x/%04x, rev=0x%x, rfid=0x%x\n",
pdev->device, pdev->subsystem_device,
- iwl_trans->hw_rev, iwl_trans->hw_rf_id);
+ info.hw_rev, info.hw_rf_id);
dev_info = iwl_pci_find_dev_info(pdev->device, pdev->subsystem_device,
- CSR_HW_REV_TYPE(iwl_trans->hw_rev),
- iwl_trans->hw_rev_step,
- CSR_HW_RFID_TYPE(iwl_trans->hw_rf_id),
- CSR_HW_RFID_IS_CDB(iwl_trans->hw_rf_id),
- CSR_HW_RFID_IS_JACKET(iwl_trans->hw_rf_id),
+ CSR_HW_RFID_TYPE(info.hw_rf_id),
+ CSR_HW_RFID_IS_CDB(info.hw_rf_id),
IWL_SUBDEVICE_RF_ID(pdev->subsystem_device),
- IWL_SUBDEVICE_NO_160(pdev->subsystem_device),
- IWL_SUBDEVICE_CORES(pdev->subsystem_device),
- CSR_HW_RFID_STEP(iwl_trans->hw_rf_id));
+ IWL_SUBDEVICE_BW_LIM(pdev->subsystem_device),
+ CSR_HW_RFID_STEP(info.hw_rf_id));
if (dev_info) {
iwl_trans->cfg = dev_info->cfg;
- iwl_trans->name = dev_info->name;
- iwl_trans->no_160 = dev_info->no_160 == IWL_CFG_NO_160;
+ info.name = dev_info->name;
}
#if IS_ENABLED(CONFIG_IWLMVM)
@@ -1547,82 +1386,41 @@ static int iwl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
* all the parameters that the transport uses must, until that is
* changed, be identical to the ones in the 7265D configuration.
*/
- if (cfg == &iwl7265_2ac_cfg)
- cfg_7265d = &iwl7265d_2ac_cfg;
- else if (cfg == &iwl7265_2n_cfg)
- cfg_7265d = &iwl7265d_2n_cfg;
- else if (cfg == &iwl7265_n_cfg)
- cfg_7265d = &iwl7265d_n_cfg;
- if (cfg_7265d &&
- (iwl_trans->hw_rev & CSR_HW_REV_TYPE_MSK) == CSR_HW_REV_TYPE_7265D)
- iwl_trans->cfg = cfg_7265d;
-
- /*
- * This is a hack to switch from Qu B0 to Qu C0. We need to
- * do this for all cfgs that use Qu B0, except for those using
- * Jf, which have already been moved to the new table. The
- * rest must be removed once we convert Qu with Hr as well.
- */
- if (iwl_trans->hw_rev == CSR_HW_REV_TYPE_QU_C0) {
- if (iwl_trans->cfg == &iwl_ax201_cfg_qu_hr)
- iwl_trans->cfg = &iwl_ax201_cfg_qu_c0_hr_b0;
- else if (iwl_trans->cfg == &killer1650s_2ax_cfg_qu_b0_hr_b0)
- iwl_trans->cfg = &killer1650s_2ax_cfg_qu_c0_hr_b0;
- else if (iwl_trans->cfg == &killer1650i_2ax_cfg_qu_b0_hr_b0)
- iwl_trans->cfg = &killer1650i_2ax_cfg_qu_c0_hr_b0;
- }
-
- /* same thing for QuZ... */
- if (iwl_trans->hw_rev == CSR_HW_REV_TYPE_QUZ) {
- if (iwl_trans->cfg == &iwl_ax201_cfg_qu_hr)
- iwl_trans->cfg = &iwl_ax201_cfg_quz_hr;
- else if (iwl_trans->cfg == &killer1650s_2ax_cfg_qu_b0_hr_b0)
- iwl_trans->cfg = &iwl_ax1650s_cfg_quz_hr;
- else if (iwl_trans->cfg == &killer1650i_2ax_cfg_qu_b0_hr_b0)
- iwl_trans->cfg = &iwl_ax1650i_cfg_quz_hr;
- }
-
+ if (iwl_trans->cfg == &iwl7265_cfg &&
+ (info.hw_rev & CSR_HW_REV_TYPE_MSK) == CSR_HW_REV_TYPE_7265D)
+ iwl_trans->cfg = &iwl7265d_cfg;
#endif
- /*
- * If we didn't set the cfg yet, the PCI ID table entry should have
- * been a full config - if yes, use it, otherwise fail.
- */
if (!iwl_trans->cfg) {
- if (ent->driver_data & TRANS_CFG_MARKER) {
- pr_err("No config found for PCI dev %04x/%04x, rev=0x%x, rfid=0x%x\n",
- pdev->device, pdev->subsystem_device,
- iwl_trans->hw_rev, iwl_trans->hw_rf_id);
- ret = -EINVAL;
- goto out_free_trans;
- }
- iwl_trans->cfg = cfg;
+ pr_err("No config found for PCI dev %04x/%04x, rev=0x%x, rfid=0x%x\n",
+ pdev->device, pdev->subsystem_device,
+ info.hw_rev, info.hw_rf_id);
+ ret = -EINVAL;
+ goto out_free_trans;
}
- /* if we don't have a name yet, copy name from the old cfg */
- if (!iwl_trans->name)
- iwl_trans->name = iwl_trans->cfg->name;
-
- IWL_INFO(iwl_trans, "Detected %s\n", iwl_trans->name);
+ IWL_INFO(iwl_trans, "Detected %s\n", info.name);
- if (iwl_trans->trans_cfg->mq_rx_supported) {
+ if (iwl_trans->mac_cfg->mq_rx_supported) {
if (WARN_ON(!iwl_trans->cfg->num_rbds)) {
ret = -EINVAL;
goto out_free_trans;
}
- trans_pcie->num_rx_bufs = iwl_trans->cfg->num_rbds;
+ trans_pcie->num_rx_bufs = iwl_trans_get_num_rbds(iwl_trans);
} else {
trans_pcie->num_rx_bufs = RX_QUEUE_SIZE;
}
- if (!iwl_trans->trans_cfg->integrated) {
+ if (!iwl_trans->mac_cfg->integrated) {
u16 link_status;
pcie_capability_read_word(pdev, PCI_EXP_LNKSTA, &link_status);
- iwl_trans->pcie_link_speed =
+ info.pcie_link_speed =
u16_get_bits(link_status, PCI_EXP_LNKSTA_CLS);
}
+ iwl_trans_set_info(iwl_trans, &info);
+
ret = iwl_trans_init(iwl_trans);
if (ret)
goto out_free_trans;
@@ -1654,11 +1452,12 @@ out_free_trans:
static void iwl_pci_remove(struct pci_dev *pdev)
{
struct iwl_trans *trans = pci_get_drvdata(pdev);
+ struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
if (!trans)
return;
- cancel_delayed_work_sync(&trans->me_recheck_wk);
+ cancel_delayed_work_sync(&trans_pcie->me_recheck_wk);
iwl_drv_stop(trans->drv);
@@ -1704,7 +1503,7 @@ static int _iwl_pci_resume(struct device *device, bool restore)
* Note: MAC (bits 0:7) will be cleared upon suspend even with wowlan,
* so assume that any bits there mean that the device is usable.
*/
- if (trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_BZ &&
+ if (trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_BZ &&
!iwl_read32(trans, CSR_FUNC_SCRATCH))
device_was_powered_off = true;
diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/internal.h b/drivers/net/wireless/intel/iwlwifi/pcie/internal.h
index 856b7e9f717d..3b7c12fc4f9e 100644
--- a/drivers/net/wireless/intel/iwlwifi/pcie/internal.h
+++ b/drivers/net/wireless/intel/iwlwifi/pcie/internal.h
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
/*
- * Copyright (C) 2003-2015, 2018-2024 Intel Corporation
+ * Copyright (C) 2003-2015, 2018-2025 Intel Corporation
* Copyright (C) 2013-2015 Intel Mobile Communications GmbH
* Copyright (C) 2016-2017 Intel Deutschland GmbH
*/
@@ -194,7 +194,7 @@ struct iwl_rb_allocator {
static inline u16 iwl_get_closed_rb_stts(struct iwl_trans *trans,
struct iwl_rxq *rxq)
{
- if (trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_AX210) {
+ if (trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_AX210) {
__le16 *rb_stts = rxq->rb_stts;
return le16_to_cpu(READ_ONCE(*rb_stts));
@@ -269,6 +269,7 @@ enum iwl_pcie_fw_reset_state {
FW_RESET_REQUESTED,
FW_RESET_OK,
FW_RESET_ERROR,
+ FW_RESET_TOP_REQUESTED,
};
/**
@@ -288,22 +289,14 @@ enum iwl_pcie_imr_status {
/**
* struct iwl_pcie_txqs - TX queues data
*
- * @bc_table_dword: true if the BC table expects DWORD (as opposed to bytes)
- * @page_offs: offset from skb->cb to mac header page pointer
- * @dev_cmd_offs: offset from skb->cb to iwl_device_tx_cmd pointer
* @queue_used: bit mask of used queues
* @queue_stopped: bit mask of stopped queues
* @txq: array of TXQ data structures representing the TXQs
* @scd_bc_tbls: gen1 pointer to the byte count table of the scheduler
- * @queue_alloc_cmd_ver: queue allocation command version
* @bc_pool: bytecount DMA allocations pool
* @bc_tbl_size: bytecount table size
* @tso_hdr_page: page allocated (per CPU) for A-MSDU headers when doing TSO
* (and similar usage)
- * @cmd: command queue data
- * @cmd.fifo: FIFO number
- * @cmd.q_id: queue ID
- * @cmd.wdg_timeout: watchdog timeout
* @tfd: TFD data
* @tfd.max_tbs: max number of buffers per TFD
* @tfd.size: TFD size
@@ -315,26 +308,15 @@ struct iwl_pcie_txqs {
struct iwl_txq *txq[IWL_MAX_TVQM_QUEUES];
struct dma_pool *bc_pool;
size_t bc_tbl_size;
- bool bc_table_dword;
- u8 page_offs;
- u8 dev_cmd_offs;
struct iwl_tso_hdr_page __percpu *tso_hdr_page;
struct {
- u8 fifo;
- u8 q_id;
- unsigned int wdg_timeout;
- } cmd;
-
- struct {
u8 max_tbs;
u16 size;
u8 addr_size;
} tfd;
struct iwl_dma_ptr scd_bc_tbls;
-
- u8 queue_alloc_cmd_ver;
};
/**
@@ -344,7 +326,7 @@ struct iwl_pcie_txqs {
* @global_table: table mapping received VID from hw to rxb
* @rba: allocator for RX replenishing
* @ctxt_info: context information for FW self init
- * @ctxt_info_gen3: context information for gen3 devices
+ * @ctxt_info_v2: context information for v1 devices
* @prph_info: prph info for self init
* @prph_scratch: prph scratch for self init
* @ctxt_info_dma_addr: dma addr of context information
@@ -352,6 +334,7 @@ struct iwl_pcie_txqs {
* @prph_scratch_dma_addr: dma addr of prph scratch
* @ctxt_info_dma_addr: dma addr of context information
* @iml: image loader image virtual address
+ * @iml_len: image loader image size
* @iml_dma_addr: image loader image DMA address
* @trans: pointer to the generic transport area
* @scd_base_addr: scheduler sram base address in SRAM
@@ -363,9 +346,6 @@ struct iwl_pcie_txqs {
* @hw_base: pci hardware address support
* @ucode_write_complete: indicates that the ucode has been copied.
* @ucode_write_waitq: wait queue for uCode load
- * @cmd_queue - command queue number
- * @rx_buf_size: Rx buffer size
- * @scd_set_active: should the transport configure the SCD for HCMD queue
* @rx_page_order: page order for receive buffer size
* @rx_buf_bytes: RX buffer (RB) size in bytes
* @reg_lock: protect hw register access
@@ -406,19 +386,20 @@ struct iwl_pcie_txqs {
* @pcie_dbg_dumped_once: indicates PCIe regs were dumped already
* @opmode_down: indicates opmode went away
* @num_rx_bufs: number of RX buffers to allocate/use
- * @no_reclaim_cmds: special commands not using reclaim flow
- * (firmware workaround)
- * @n_no_reclaim_cmds: number of special commands not using reclaim flow
* @affinity_mask: IRQ affinity mask for each RX queue
* @debug_rfkill: RF-kill debugging state, -1 for unset, 0/1 for radio
* enable/disable
- * @fw_reset_handshake: indicates FW reset handshake is needed
* @fw_reset_state: state of FW reset handshake
* @fw_reset_waitq: waitqueue for FW reset handshake
* @is_down: indicates the NIC is down
* @isr_stats: interrupt statistics
* @napi_dev: (fake) netdev for NAPI registration
* @txqs: transport tx queues data.
+ * @me_present: WiAMT/CSME is detected as present (1), not present (0)
+ * or unknown (-1, so can still use it as a boolean safely)
+ * @me_recheck_wk: worker to recheck WiAMT/CSME presence
+ * @invalid_tx_cmd: invalid TX command buffer
+ * @wait_command_queue: wait queue for sync commands
*/
struct iwl_trans_pcie {
struct iwl_rxq *rxq;
@@ -427,11 +408,12 @@ struct iwl_trans_pcie {
struct iwl_rb_allocator rba;
union {
struct iwl_context_info *ctxt_info;
- struct iwl_context_info_gen3 *ctxt_info_gen3;
+ struct iwl_context_info_v2 *ctxt_info_v2;
};
struct iwl_prph_info *prph_info;
struct iwl_prph_scratch *prph_scratch;
void *iml;
+ size_t iml_len;
dma_addr_t ctxt_info_dma_addr;
dma_addr_t prph_info_dma_addr;
dma_addr_t prph_scratch_dma_addr;
@@ -470,12 +452,8 @@ struct iwl_trans_pcie {
wait_queue_head_t ucode_write_waitq;
wait_queue_head_t sx_waitq;
- u8 n_no_reclaim_cmds;
- u8 no_reclaim_cmds[MAX_NO_RECLAIM_CMDS];
u16 num_rx_bufs;
- enum iwl_amsdu_size rx_buf_size;
- bool scd_set_active;
bool pcie_dbg_dumped_once;
u32 rx_page_order;
u32 rx_buf_bytes;
@@ -510,7 +488,6 @@ struct iwl_trans_pcie {
void *base_rb_stts;
dma_addr_t base_rb_stts_dma;
- bool fw_reset_handshake;
enum iwl_pcie_fw_reset_state fw_reset_state;
wait_queue_head_t fw_reset_waitq;
enum iwl_pcie_imr_status imr_status;
@@ -518,6 +495,13 @@ struct iwl_trans_pcie {
char rf_name[32];
struct iwl_pcie_txqs txqs;
+
+ s8 me_present;
+ struct delayed_work me_recheck_wk;
+
+ struct iwl_dma_ptr invalid_tx_cmd;
+
+ wait_queue_head_t wait_command_queue;
};
static inline struct iwl_trans_pcie *
@@ -552,16 +536,16 @@ iwl_trans_pcie_get_trans(struct iwl_trans_pcie *trans_pcie)
*/
struct iwl_trans
*iwl_trans_pcie_alloc(struct pci_dev *pdev,
- const struct pci_device_id *ent,
- const struct iwl_cfg_trans_params *cfg_trans);
+ const struct iwl_mac_cfg *mac_cfg,
+ struct iwl_trans_info *info);
void iwl_trans_pcie_free(struct iwl_trans *trans);
void iwl_trans_pcie_free_pnvm_dram_regions(struct iwl_dram_regions *dram_regions,
struct device *dev);
-bool __iwl_trans_pcie_grab_nic_access(struct iwl_trans *trans);
-#define _iwl_trans_pcie_grab_nic_access(trans) \
+bool __iwl_trans_pcie_grab_nic_access(struct iwl_trans *trans, bool silent);
+#define _iwl_trans_pcie_grab_nic_access(trans, silent) \
__cond_lock(nic_access_nobh, \
- likely(__iwl_trans_pcie_grab_nic_access(trans)))
+ likely(__iwl_trans_pcie_grab_nic_access(trans, silent)))
void iwl_trans_pcie_check_product_reset_status(struct pci_dev *pdev);
void iwl_trans_pcie_check_product_reset_mode(struct pci_dev *pdev);
@@ -623,7 +607,7 @@ struct iwl_tso_page_info {
IWL_TSO_PAGE_DATA_SIZE))
int iwl_pcie_tx_init(struct iwl_trans *trans);
-void iwl_pcie_tx_start(struct iwl_trans *trans, u32 scd_base_addr);
+void iwl_pcie_tx_start(struct iwl_trans *trans);
int iwl_pcie_tx_stop(struct iwl_trans *trans);
void iwl_pcie_tx_free(struct iwl_trans *trans);
bool iwl_trans_pcie_txq_enable(struct iwl_trans *trans, int queue, u16 ssn,
@@ -646,7 +630,8 @@ dma_addr_t iwl_pcie_get_sgt_tb_phys(struct sg_table *sgt, unsigned int offset,
unsigned int len);
struct sg_table *iwl_pcie_prep_tso(struct iwl_trans *trans, struct sk_buff *skb,
struct iwl_cmd_meta *cmd_meta,
- u8 **hdr, unsigned int hdr_room);
+ u8 **hdr, unsigned int hdr_room,
+ unsigned int offset);
void iwl_pcie_free_tso_pages(struct iwl_trans *trans, struct sk_buff *skb,
struct iwl_cmd_meta *cmd_meta);
@@ -678,7 +663,7 @@ static inline void *iwl_txq_get_tfd(struct iwl_trans *trans,
{
struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
- if (trans->trans_cfg->gen2)
+ if (trans->mac_cfg->gen2)
idx = iwl_txq_get_cmd_index(txq, idx);
return (u8 *)txq->tfds + trans_pcie->txqs.tfd.size * idx;
@@ -717,7 +702,7 @@ static inline void iwl_txq_stop(struct iwl_trans *trans, struct iwl_txq *txq)
static inline int iwl_txq_inc_wrap(struct iwl_trans *trans, int index)
{
return ++index &
- (trans->trans_cfg->base_params->max_tfd_queue_size - 1);
+ (trans->mac_cfg->base->max_tfd_queue_size - 1);
}
/**
@@ -728,7 +713,7 @@ static inline int iwl_txq_inc_wrap(struct iwl_trans *trans, int index)
static inline int iwl_txq_dec_wrap(struct iwl_trans *trans, int index)
{
return --index &
- (trans->trans_cfg->base_params->max_tfd_queue_size - 1);
+ (trans->mac_cfg->base->max_tfd_queue_size - 1);
}
void iwl_txq_log_scd_error(struct iwl_trans *trans, struct iwl_txq *txq);
@@ -751,10 +736,12 @@ int iwl_txq_gen2_set_tb(struct iwl_trans *trans,
static inline void iwl_txq_set_tfd_invalid_gen2(struct iwl_trans *trans,
struct iwl_tfh_tfd *tfd)
{
+ struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+
tfd->num_tbs = 0;
- iwl_txq_gen2_set_tb(trans, tfd, trans->invalid_tx_cmd.dma,
- trans->invalid_tx_cmd.size);
+ iwl_txq_gen2_set_tb(trans, tfd, trans_pcie->invalid_tx_cmd.dma,
+ trans_pcie->invalid_tx_cmd.size);
}
void iwl_txq_gen2_tfd_unmap(struct iwl_trans *trans,
@@ -781,7 +768,7 @@ static inline u16 iwl_txq_gen1_tfd_tb_get_len(struct iwl_trans *trans,
struct iwl_tfd *tfd;
struct iwl_tfd_tb *tb;
- if (trans->trans_cfg->gen2) {
+ if (trans->mac_cfg->gen2) {
struct iwl_tfh_tfd *tfh_tfd = _tfd;
struct iwl_tfh_tb *tfh_tb = &tfh_tfd->tbs[idx];
@@ -939,11 +926,13 @@ static inline void iwl_enable_fw_load_int(struct iwl_trans *trans)
}
}
-static inline void iwl_enable_fw_load_int_ctx_info(struct iwl_trans *trans)
+static inline void iwl_enable_fw_load_int_ctx_info(struct iwl_trans *trans,
+ bool top_reset)
{
struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
- IWL_DEBUG_ISR(trans, "Enabling ALIVE interrupt only\n");
+ IWL_DEBUG_ISR(trans, "Enabling %s interrupt only\n",
+ top_reset ? "RESET" : "ALIVE");
if (!trans_pcie->msix_enabled) {
/*
@@ -953,11 +942,20 @@ static inline void iwl_enable_fw_load_int_ctx_info(struct iwl_trans *trans)
* RX interrupt which will allow us to receive the ALIVE
* notification (which is Rx) and continue the flow.
*/
- trans_pcie->inta_mask = CSR_INT_BIT_ALIVE | CSR_INT_BIT_FH_RX;
+ if (top_reset)
+ trans_pcie->inta_mask = CSR_INT_BIT_RESET_DONE;
+ else
+ trans_pcie->inta_mask = CSR_INT_BIT_ALIVE |
+ CSR_INT_BIT_FH_RX;
iwl_write32(trans, CSR_INT_MASK, trans_pcie->inta_mask);
} else {
- iwl_enable_hw_int_msk_msix(trans,
- MSIX_HW_INT_CAUSES_REG_ALIVE);
+ u32 val = top_reset ? MSIX_HW_INT_CAUSES_REG_RESET_DONE
+ : MSIX_HW_INT_CAUSES_REG_ALIVE;
+
+ iwl_enable_hw_int_msk_msix(trans, val);
+
+ if (top_reset)
+ return;
/*
* Leave all the FH causes enabled to get the ALIVE
* notification.
@@ -1004,7 +1002,7 @@ static inline void iwl_enable_rfkill_int(struct iwl_trans *trans)
MSIX_HW_INT_CAUSES_REG_RF_KILL);
}
- if (trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_9000) {
+ if (trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_9000) {
/*
* On 9000-series devices this bit isn't enabled by default, so
* when we power down the device we need set the bit to allow it
@@ -1075,8 +1073,7 @@ static inline void iwl_trans_pcie_dbgfs_register(struct iwl_trans *trans) { }
void iwl_pcie_rx_allocator_work(struct work_struct *data);
/* common trans ops for all generations transports */
-void iwl_trans_pcie_configure(struct iwl_trans *trans,
- const struct iwl_trans_config *trans_cfg);
+void iwl_trans_pcie_op_mode_enter(struct iwl_trans *trans);
int iwl_trans_pcie_start_hw(struct iwl_trans *trans);
void iwl_trans_pcie_op_mode_leave(struct iwl_trans *trans);
void iwl_trans_pcie_write8(struct iwl_trans *trans, u32 ofs, u8 val);
@@ -1104,12 +1101,16 @@ void iwl_trans_pcie_set_bits_mask(struct iwl_trans *trans, u32 reg,
int iwl_trans_pcie_read_config32(struct iwl_trans *trans, u32 ofs,
u32 *val);
bool iwl_trans_pcie_grab_nic_access(struct iwl_trans *trans);
-void iwl_trans_pcie_release_nic_access(struct iwl_trans *trans);
+void __releases(nic_access_nobh)
+iwl_trans_pcie_release_nic_access(struct iwl_trans *trans);
+void iwl_pcie_alloc_fw_monitor(struct iwl_trans *trans, u8 max_power);
/* transport gen 1 exported functions */
-void iwl_trans_pcie_fw_alive(struct iwl_trans *trans, u32 scd_addr);
+void iwl_trans_pcie_fw_alive(struct iwl_trans *trans);
int iwl_trans_pcie_start_fw(struct iwl_trans *trans,
- const struct fw_img *fw, bool run_in_rfkill);
+ const struct iwl_fw *fw,
+ const struct fw_img *img,
+ bool run_in_rfkill);
void iwl_trans_pcie_stop_device(struct iwl_trans *trans);
/* common functions that are used by gen2 transport */
@@ -1127,15 +1128,12 @@ int iwl_pcie_alloc_dma_ptr(struct iwl_trans *trans,
void iwl_pcie_free_dma_ptr(struct iwl_trans *trans, struct iwl_dma_ptr *ptr);
void iwl_pcie_apply_destination(struct iwl_trans *trans);
-/* common functions that are used by gen3 transport */
-void iwl_pcie_alloc_fw_monitor(struct iwl_trans *trans, u8 max_power);
-
/* transport gen 2 exported functions */
int iwl_trans_pcie_gen2_start_fw(struct iwl_trans *trans,
- const struct fw_img *fw, bool run_in_rfkill);
+ const struct iwl_fw *fw,
+ const struct fw_img *img,
+ bool run_in_rfkill);
void iwl_trans_pcie_gen2_fw_alive(struct iwl_trans *trans);
-int iwl_trans_pcie_gen2_send_hcmd(struct iwl_trans *trans,
- struct iwl_host_cmd *cmd);
void iwl_trans_pcie_gen2_stop_device(struct iwl_trans *trans);
int iwl_pcie_gen2_enqueue_hcmd(struct iwl_trans *trans,
struct iwl_host_cmd *cmd);
diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/rx.c b/drivers/net/wireless/intel/iwlwifi/pcie/rx.c
index 4a442d03d8d2..f0405eddc367 100644
--- a/drivers/net/wireless/intel/iwlwifi/pcie/rx.c
+++ b/drivers/net/wireless/intel/iwlwifi/pcie/rx.c
@@ -12,7 +12,8 @@
#include "iwl-io.h"
#include "internal.h"
#include "iwl-op-mode.h"
-#include "iwl-context-info-gen3.h"
+#include "iwl-context-info-v2.h"
+#include "fw/dbg.h"
/******************************************************************************
*
@@ -143,12 +144,12 @@ static inline __le32 iwl_pcie_dma_addr2rbd_ptr(dma_addr_t dma_addr)
*/
int iwl_pcie_rx_stop(struct iwl_trans *trans)
{
- if (trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_AX210) {
+ if (trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_AX210) {
/* TODO: remove this once fw does it */
- iwl_write_umac_prph(trans, RFH_RXF_DMA_CFG_GEN3, 0);
- return iwl_poll_umac_prph_bit(trans, RFH_GEN_STATUS_GEN3,
+ iwl_write_umac_prph(trans, RFH_RXF_DMA_CFG_AX210, 0);
+ return iwl_poll_umac_prph_bit(trans, RFH_GEN_STATUS_AX210,
RXF_DMA_IDLE, RXF_DMA_IDLE, 1000);
- } else if (trans->trans_cfg->mq_rx_supported) {
+ } else if (trans->mac_cfg->mq_rx_supported) {
iwl_write_prph(trans, RFH_RXF_DMA_CFG, 0);
return iwl_poll_prph_bit(trans, RFH_GEN_STATUS,
RXF_DMA_IDLE, RXF_DMA_IDLE, 1000);
@@ -175,7 +176,7 @@ static void iwl_pcie_rxq_inc_wr_ptr(struct iwl_trans *trans,
* 1. shadow registers aren't enabled
* 2. there is a chance that the NIC is asleep
*/
- if (!trans->trans_cfg->base_params->shadow_reg_enable &&
+ if (!trans->mac_cfg->base->shadow_reg_enable &&
test_bit(STATUS_TPOWER_PMI, &trans->status)) {
reg = iwl_read32(trans, CSR_UCODE_DRV_GP1);
@@ -190,9 +191,9 @@ static void iwl_pcie_rxq_inc_wr_ptr(struct iwl_trans *trans,
}
rxq->write_actual = round_down(rxq->write, 8);
- if (!trans->trans_cfg->mq_rx_supported)
+ if (!trans->mac_cfg->mq_rx_supported)
iwl_write32(trans, FH_RSCSR_CHNL0_WPTR, rxq->write_actual);
- else if (trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_BZ)
+ else if (trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_BZ)
iwl_write32(trans, HBUS_TARG_WRPTR, rxq->write_actual |
HBUS_TARG_WRPTR_RX_Q(rxq->id));
else
@@ -205,7 +206,7 @@ static void iwl_pcie_rxq_check_wrptr(struct iwl_trans *trans)
struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
int i;
- for (i = 0; i < trans->num_rx_queues; i++) {
+ for (i = 0; i < trans->info.num_rxqs; i++) {
struct iwl_rxq *rxq = &trans_pcie->rxq[i];
if (!rxq->need_update)
@@ -221,7 +222,7 @@ static void iwl_pcie_restock_bd(struct iwl_trans *trans,
struct iwl_rxq *rxq,
struct iwl_rx_mem_buffer *rxb)
{
- if (trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_AX210) {
+ if (trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_AX210) {
struct iwl_rx_transfer_desc *bd = rxq->bd;
BUILD_BUG_ON(sizeof(*bd) != 2 * sizeof(u64));
@@ -348,7 +349,7 @@ static void iwl_pcie_rxsq_restock(struct iwl_trans *trans,
static
void iwl_pcie_rxq_restock(struct iwl_trans *trans, struct iwl_rxq *rxq)
{
- if (trans->trans_cfg->mq_rx_supported)
+ if (trans->mac_cfg->mq_rx_supported)
iwl_pcie_rxmq_restock(trans, rxq);
else
iwl_pcie_rxsq_restock(trans, rxq);
@@ -362,8 +363,8 @@ static struct page *iwl_pcie_rx_alloc_page(struct iwl_trans *trans,
u32 *offset, gfp_t priority)
{
struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
- unsigned int rbsize = iwl_trans_get_rb_size(trans_pcie->rx_buf_size);
unsigned int allocsize = PAGE_SIZE << trans_pcie->rx_page_order;
+ unsigned int rbsize = trans_pcie->rx_buf_bytes;
struct page *page;
gfp_t gfp_mask = priority;
@@ -657,19 +658,19 @@ void iwl_pcie_rx_allocator_work(struct work_struct *data)
static int iwl_pcie_free_bd_size(struct iwl_trans *trans)
{
- if (trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_AX210)
+ if (trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_AX210)
return sizeof(struct iwl_rx_transfer_desc);
- return trans->trans_cfg->mq_rx_supported ?
+ return trans->mac_cfg->mq_rx_supported ?
sizeof(__le64) : sizeof(__le32);
}
static int iwl_pcie_used_bd_size(struct iwl_trans *trans)
{
- if (trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_BZ)
+ if (trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_BZ)
return sizeof(struct iwl_rx_completion_desc_bz);
- if (trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_AX210)
+ if (trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_AX210)
return sizeof(struct iwl_rx_completion_desc);
return sizeof(__le32);
@@ -701,7 +702,7 @@ static void iwl_pcie_free_rxq_dma(struct iwl_trans *trans,
static size_t iwl_pcie_rb_stts_size(struct iwl_trans *trans)
{
- bool use_rx_td = (trans->trans_cfg->device_family >=
+ bool use_rx_td = (trans->mac_cfg->device_family >=
IWL_DEVICE_FAMILY_AX210);
if (use_rx_td)
@@ -720,8 +721,8 @@ static int iwl_pcie_alloc_rxq_dma(struct iwl_trans *trans,
int free_size;
spin_lock_init(&rxq->lock);
- if (trans->trans_cfg->mq_rx_supported)
- rxq->queue_size = trans->cfg->num_rbds;
+ if (trans->mac_cfg->mq_rx_supported)
+ rxq->queue_size = iwl_trans_get_num_rbds(trans);
else
rxq->queue_size = RX_QUEUE_SIZE;
@@ -736,7 +737,7 @@ static int iwl_pcie_alloc_rxq_dma(struct iwl_trans *trans,
if (!rxq->bd)
goto err;
- if (trans->trans_cfg->mq_rx_supported) {
+ if (trans->mac_cfg->mq_rx_supported) {
rxq->used_bd = dma_alloc_coherent(dev,
iwl_pcie_used_bd_size(trans) *
rxq->queue_size,
@@ -753,7 +754,7 @@ static int iwl_pcie_alloc_rxq_dma(struct iwl_trans *trans,
return 0;
err:
- for (i = 0; i < trans->num_rx_queues; i++) {
+ for (i = 0; i < trans->info.num_rxqs; i++) {
struct iwl_rxq *rxq = &trans_pcie->rxq[i];
iwl_pcie_free_rxq_dma(trans, rxq);
@@ -772,7 +773,7 @@ static int iwl_pcie_rx_alloc(struct iwl_trans *trans)
if (WARN_ON(trans_pcie->rxq))
return -EINVAL;
- trans_pcie->rxq = kcalloc(trans->num_rx_queues, sizeof(struct iwl_rxq),
+ trans_pcie->rxq = kcalloc(trans->info.num_rxqs, sizeof(struct iwl_rxq),
GFP_KERNEL);
trans_pcie->rx_pool = kcalloc(RX_POOL_SIZE(trans_pcie->num_rx_bufs),
sizeof(trans_pcie->rx_pool[0]),
@@ -795,7 +796,7 @@ static int iwl_pcie_rx_alloc(struct iwl_trans *trans)
*/
trans_pcie->base_rb_stts =
dma_alloc_coherent(trans->dev,
- rb_stts_size * trans->num_rx_queues,
+ rb_stts_size * trans->info.num_rxqs,
&trans_pcie->base_rb_stts_dma,
GFP_KERNEL);
if (!trans_pcie->base_rb_stts) {
@@ -803,7 +804,7 @@ static int iwl_pcie_rx_alloc(struct iwl_trans *trans)
goto err;
}
- for (i = 0; i < trans->num_rx_queues; i++) {
+ for (i = 0; i < trans->info.num_rxqs; i++) {
struct iwl_rxq *rxq = &trans_pcie->rxq[i];
rxq->id = i;
@@ -816,7 +817,7 @@ static int iwl_pcie_rx_alloc(struct iwl_trans *trans)
err:
if (trans_pcie->base_rb_stts) {
dma_free_coherent(trans->dev,
- rb_stts_size * trans->num_rx_queues,
+ rb_stts_size * trans->info.num_rxqs,
trans_pcie->base_rb_stts,
trans_pcie->base_rb_stts_dma);
trans_pcie->base_rb_stts = NULL;
@@ -834,11 +835,10 @@ err:
static void iwl_pcie_rx_hw_init(struct iwl_trans *trans, struct iwl_rxq *rxq)
{
- struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
u32 rb_size;
const u32 rfdnlog = RX_QUEUE_SIZE_LOG; /* 256 RBDs */
- switch (trans_pcie->rx_buf_size) {
+ switch (trans->conf.rx_buf_size) {
case IWL_AMSDU_4K:
rb_size = FH_RCSR_RX_CONFIG_REG_VAL_RB_SIZE_4K;
break;
@@ -906,7 +906,7 @@ static void iwl_pcie_rx_mq_hw_init(struct iwl_trans *trans)
u32 rb_size, enabled = 0;
int i;
- switch (trans_pcie->rx_buf_size) {
+ switch (trans->conf.rx_buf_size) {
case IWL_AMSDU_2K:
rb_size = RFH_RXF_DMA_RB_SIZE_2K;
break;
@@ -932,7 +932,7 @@ static void iwl_pcie_rx_mq_hw_init(struct iwl_trans *trans)
/* disable free amd used rx queue operation */
iwl_write_prph_no_grab(trans, RFH_RXF_RXQ_ACTIVE, 0);
- for (i = 0; i < trans->num_rx_queues; i++) {
+ for (i = 0; i < trans->info.num_rxqs; i++) {
/* Tell device where to find RBD free table in DRAM */
iwl_write_prph64_no_grab(trans,
RFH_Q_FRBDCB_BA_LSB(i),
@@ -976,7 +976,7 @@ static void iwl_pcie_rx_mq_hw_init(struct iwl_trans *trans)
RFH_GEN_CFG_VAL(DEFAULT_RXQ_NUM, 0) |
RFH_GEN_CFG_SERVICE_DMA_SNOOP |
RFH_GEN_CFG_VAL(RB_CHUNK_SIZE,
- trans->trans_cfg->integrated ?
+ trans->mac_cfg->integrated ?
RFH_GEN_CFG_RB_CHUNK_SIZE_64 :
RFH_GEN_CFG_RB_CHUNK_SIZE_128));
/* Enable the relevant rx queues */
@@ -1072,7 +1072,7 @@ void iwl_pcie_rx_napi_sync(struct iwl_trans *trans)
if (unlikely(!trans_pcie->rxq))
return;
- for (i = 0; i < trans->num_rx_queues; i++) {
+ for (i = 0; i < trans->info.num_rxqs; i++) {
struct iwl_rxq *rxq = &trans_pcie->rxq[i];
if (rxq && rxq->napi.poll)
@@ -1109,7 +1109,7 @@ static int _iwl_pcie_rx_init(struct iwl_trans *trans)
for (i = 0; i < RX_QUEUE_SIZE; i++)
def_rxq->queue[i] = NULL;
- for (i = 0; i < trans->num_rx_queues; i++) {
+ for (i = 0; i < trans->info.num_rxqs; i++) {
struct iwl_rxq *rxq = &trans_pcie->rxq[i];
spin_lock_bh(&rxq->lock);
@@ -1122,7 +1122,7 @@ static int _iwl_pcie_rx_init(struct iwl_trans *trans)
rxq->write = 0;
rxq->write_actual = 0;
memset(rxq->rb_stts, 0,
- (trans->trans_cfg->device_family >=
+ (trans->mac_cfg->device_family >=
IWL_DEVICE_FAMILY_AX210) ?
sizeof(__le16) : sizeof(struct iwl_rb_status));
@@ -1144,9 +1144,9 @@ static int _iwl_pcie_rx_init(struct iwl_trans *trans)
}
/* move the pool to the default queue and allocator ownerships */
- queue_size = trans->trans_cfg->mq_rx_supported ?
+ queue_size = trans->mac_cfg->mq_rx_supported ?
trans_pcie->num_rx_bufs - 1 : RX_QUEUE_SIZE;
- allocator_pool_size = trans->num_rx_queues *
+ allocator_pool_size = trans->info.num_rxqs *
(RX_CLAIM_REQ_ALLOC - RX_POST_REQ_ALLOC);
num_alloc = queue_size + allocator_pool_size;
@@ -1175,7 +1175,7 @@ int iwl_pcie_rx_init(struct iwl_trans *trans)
if (ret)
return ret;
- if (trans->trans_cfg->mq_rx_supported)
+ if (trans->mac_cfg->mq_rx_supported)
iwl_pcie_rx_mq_hw_init(trans);
else
iwl_pcie_rx_hw_init(trans, trans_pcie->rxq);
@@ -1223,14 +1223,14 @@ void iwl_pcie_rx_free(struct iwl_trans *trans)
if (trans_pcie->base_rb_stts) {
dma_free_coherent(trans->dev,
- rb_stts_size * trans->num_rx_queues,
+ rb_stts_size * trans->info.num_rxqs,
trans_pcie->base_rb_stts,
trans_pcie->base_rb_stts_dma);
trans_pcie->base_rb_stts = NULL;
trans_pcie->base_rb_stts_dma = 0;
}
- for (i = 0; i < trans->num_rx_queues; i++) {
+ for (i = 0; i < trans->info.num_rxqs; i++) {
struct iwl_rxq *rxq = &trans_pcie->rxq[i];
iwl_pcie_free_rxq_dma(trans, rxq);
@@ -1301,7 +1301,7 @@ static void iwl_pcie_rx_handle_rb(struct iwl_trans *trans,
int i)
{
struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
- struct iwl_txq *txq = trans_pcie->txqs.txq[trans_pcie->txqs.cmd.q_id];
+ struct iwl_txq *txq = trans_pcie->txqs.txq[trans->conf.cmd_queue];
bool page_stolen = false;
int max_len = trans_pcie->rx_buf_bytes;
u32 offset = 0;
@@ -1368,8 +1368,8 @@ static void iwl_pcie_rx_handle_rb(struct iwl_trans *trans,
if (reclaim && !pkt->hdr.group_id) {
int i;
- for (i = 0; i < trans_pcie->n_no_reclaim_cmds; i++) {
- if (trans_pcie->no_reclaim_cmds[i] ==
+ for (i = 0; i < trans->conf.n_no_reclaim_cmds; i++) {
+ if (trans->conf.no_reclaim_cmds[i] ==
pkt->hdr.cmd) {
reclaim = false;
break;
@@ -1408,7 +1408,7 @@ static void iwl_pcie_rx_handle_rb(struct iwl_trans *trans,
}
page_stolen |= rxcb._page_stolen;
- if (trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_AX210)
+ if (trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_AX210)
break;
}
@@ -1454,18 +1454,18 @@ static struct iwl_rx_mem_buffer *iwl_pcie_get_rxb(struct iwl_trans *trans,
BUILD_BUG_ON(sizeof(struct iwl_rx_completion_desc) != 32);
BUILD_BUG_ON(sizeof(struct iwl_rx_completion_desc_bz) != 4);
- if (!trans->trans_cfg->mq_rx_supported) {
+ if (!trans->mac_cfg->mq_rx_supported) {
rxb = rxq->queue[i];
rxq->queue[i] = NULL;
return rxb;
}
- if (trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_BZ) {
+ if (trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_BZ) {
struct iwl_rx_completion_desc_bz *cd = rxq->used_bd;
vid = le16_to_cpu(cd[i].rbid);
*join = cd[i].flags & IWL_RX_CD_FLAGS_FRAGMENTED;
- } else if (trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_AX210) {
+ } else if (trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_AX210) {
struct iwl_rx_completion_desc *cd = rxq->used_bd;
vid = le16_to_cpu(cd[i].rbid);
@@ -1648,7 +1648,7 @@ irqreturn_t iwl_pcie_irq_rx_msix_handler(int irq, void *dev_id)
trace_iwlwifi_dev_irq_msix(trans->dev, entry, false, 0, 0);
- if (WARN_ON(entry->entry >= trans->num_rx_queues))
+ if (WARN_ON(entry->entry >= trans->info.num_rxqs))
return IRQ_NONE;
if (!trans_pcie->rxq) {
@@ -1683,21 +1683,21 @@ static void iwl_pcie_irq_handle_error(struct iwl_trans *trans)
/* W/A for WiFi/WiMAX coex and WiMAX own the RF */
if (trans->cfg->internal_wimax_coex &&
- !trans->cfg->apmg_not_supported &&
+ !trans->mac_cfg->base->apmg_not_supported &&
(!(iwl_read_prph(trans, APMG_CLK_CTRL_REG) &
APMS_CLK_VAL_MRB_FUNC_MODE) ||
(iwl_read_prph(trans, APMG_PS_CTRL_REG) &
APMG_PS_CTRL_VAL_RESET_REQ))) {
clear_bit(STATUS_SYNC_HCMD_ACTIVE, &trans->status);
iwl_op_mode_wimax_active(trans->op_mode);
- wake_up(&trans->wait_command_queue);
+ wake_up(&trans_pcie->wait_command_queue);
return;
}
- for (i = 0; i < trans->trans_cfg->base_params->num_of_queues; i++) {
+ for (i = 0; i < trans->mac_cfg->base->num_of_queues; i++) {
if (!trans_pcie->txqs.txq[i])
continue;
- del_timer(&trans_pcie->txqs.txq[i]->stuck_timer);
+ timer_delete(&trans_pcie->txqs.txq[i]->stuck_timer);
}
/* The STATUS_FW_ERROR bit is set in this function. This must happen
@@ -1705,7 +1705,7 @@ static void iwl_pcie_irq_handle_error(struct iwl_trans *trans)
iwl_trans_fw_error(trans, IWL_ERR_TYPE_IRQ);
clear_bit(STATUS_SYNC_HCMD_ACTIVE, &trans->status);
- wake_up(&trans->wait_command_queue);
+ wake_up(&trans_pcie->wait_command_queue);
}
static u32 iwl_pcie_int_cause_non_ict(struct iwl_trans *trans)
@@ -1820,7 +1820,7 @@ void iwl_pcie_handle_rfkill_irq(struct iwl_trans *trans, bool from_irq)
&trans->status))
IWL_DEBUG_RF_KILL(trans,
"Rfkill while SYNC HCMD in flight\n");
- wake_up(&trans->wait_command_queue);
+ wake_up(&trans_pcie->wait_command_queue);
} else {
clear_bit(STATUS_RFKILL_HW, &trans->status);
if (trans_pcie->opmode_down)
@@ -1828,6 +1828,54 @@ void iwl_pcie_handle_rfkill_irq(struct iwl_trans *trans, bool from_irq)
}
}
+static void iwl_trans_pcie_handle_reset_interrupt(struct iwl_trans *trans)
+{
+ struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+ u32 state;
+
+ if (trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_SC) {
+ u32 val = iwl_read32(trans, CSR_IPC_STATE);
+
+ state = u32_get_bits(val, CSR_IPC_STATE_RESET);
+ IWL_DEBUG_ISR(trans, "IPC state = 0x%x/%d\n", val, state);
+ } else {
+ state = CSR_IPC_STATE_RESET_SW_READY;
+ }
+
+ switch (state) {
+ case CSR_IPC_STATE_RESET_SW_READY:
+ if (trans_pcie->fw_reset_state == FW_RESET_REQUESTED) {
+ IWL_DEBUG_ISR(trans, "Reset flow completed\n");
+ trans_pcie->fw_reset_state = FW_RESET_OK;
+ wake_up(&trans_pcie->fw_reset_waitq);
+ break;
+ }
+ fallthrough;
+ case CSR_IPC_STATE_RESET_TOP_READY:
+ /* FIXME: handle this case when requesting TOP reset */
+ fallthrough;
+ case CSR_IPC_STATE_RESET_NONE:
+ IWL_FW_CHECK_FAILED(trans,
+ "Invalid reset interrupt (state=%d)!\n",
+ state);
+ break;
+ case CSR_IPC_STATE_RESET_TOP_FOLLOWER:
+ if (trans_pcie->fw_reset_state == FW_RESET_REQUESTED) {
+ /* if we were in reset, wake that up */
+ IWL_INFO(trans,
+ "TOP reset from BT while doing reset\n");
+ trans_pcie->fw_reset_state = FW_RESET_OK;
+ wake_up(&trans_pcie->fw_reset_waitq);
+ } else {
+ IWL_INFO(trans, "TOP reset from BT\n");
+ trans->state = IWL_TRANS_NO_FW;
+ iwl_trans_schedule_reset(trans,
+ IWL_ERR_TYPE_TOP_RESET_BY_BT);
+ }
+ break;
+ }
+}
+
irqreturn_t iwl_pcie_irq_handler(int irq, void *dev_id)
{
struct iwl_trans *trans = dev_id;
@@ -1936,7 +1984,7 @@ irqreturn_t iwl_pcie_irq_handler(int irq, void *dev_id)
if (inta & CSR_INT_BIT_ALIVE) {
IWL_DEBUG_ISR(trans, "Alive interrupt\n");
isr_stats->alive++;
- if (trans->trans_cfg->gen2) {
+ if (trans->mac_cfg->gen2) {
/*
* We can restock, since firmware configured
* the RFH
@@ -1947,6 +1995,11 @@ irqreturn_t iwl_pcie_irq_handler(int irq, void *dev_id)
handled |= CSR_INT_BIT_ALIVE;
}
+ if (inta & CSR_INT_BIT_RESET_DONE) {
+ iwl_trans_pcie_handle_reset_interrupt(trans);
+ handled |= CSR_INT_BIT_RESET_DONE;
+ }
+
/* Safely ignore these bits for debug checks below */
inta &= ~(CSR_INT_BIT_SCD | CSR_INT_BIT_ALIVE);
@@ -1968,7 +2021,12 @@ irqreturn_t iwl_pcie_irq_handler(int irq, void *dev_id)
IWL_ERR(trans, "Microcode SW error detected. "
" Restarting 0x%X.\n", inta);
isr_stats->sw++;
- iwl_pcie_irq_handle_error(trans);
+ if (trans_pcie->fw_reset_state == FW_RESET_REQUESTED) {
+ trans_pcie->fw_reset_state = FW_RESET_ERROR;
+ wake_up(&trans_pcie->fw_reset_waitq);
+ } else {
+ iwl_pcie_irq_handle_error(trans);
+ }
handled |= CSR_INT_BIT_SW_ERR;
}
@@ -2074,7 +2132,7 @@ irqreturn_t iwl_pcie_irq_handler(int irq, void *dev_id)
iwl_enable_rfkill_int(trans);
/* Re-enable the ALIVE / Rx interrupt if it occurred */
else if (handled & (CSR_INT_BIT_ALIVE | CSR_INT_BIT_FH_RX))
- iwl_enable_fw_load_int_ctx_info(trans);
+ iwl_enable_fw_load_int_ctx_info(trans, false);
spin_unlock_bh(&trans_pcie->irq_lock);
}
@@ -2289,7 +2347,7 @@ irqreturn_t iwl_pcie_irq_msix_handler(int irq, void *dev_id)
}
}
- if (trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_BZ)
+ if (trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_BZ)
sw_err = inta_hw & MSIX_HW_INT_CAUSES_REG_SW_ERR_BZ;
else
sw_err = inta_hw & MSIX_HW_INT_CAUSES_REG_SW_ERR;
@@ -2297,9 +2355,13 @@ irqreturn_t iwl_pcie_irq_msix_handler(int irq, void *dev_id)
if (inta_hw & MSIX_HW_INT_CAUSES_REG_TOP_FATAL_ERR) {
IWL_ERR(trans, "TOP Fatal error detected, inta_hw=0x%x.\n",
inta_hw);
- if (trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_BZ)
- iwl_trans_pcie_reset(trans,
- IWL_RESET_MODE_PROD_RESET);
+ if (trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_BZ) {
+ trans->request_top_reset = 1;
+ iwl_op_mode_nic_error(trans->op_mode,
+ IWL_ERR_TYPE_TOP_FATAL_ERROR);
+ iwl_trans_schedule_reset(trans,
+ IWL_ERR_TYPE_TOP_FATAL_ERROR);
+ }
}
/* Error detected by uCode */
@@ -2338,7 +2400,7 @@ irqreturn_t iwl_pcie_irq_msix_handler(int irq, void *dev_id)
if (inta_hw & MSIX_HW_INT_CAUSES_REG_ALIVE) {
IWL_DEBUG_ISR(trans, "Alive interrupt\n");
isr_stats->alive++;
- if (trans->trans_cfg->gen2) {
+ if (trans->mac_cfg->gen2) {
/* We can restock, since firmware configured the RFH */
iwl_pcie_rxmq_restock(trans, trans_pcie->rxq);
}
@@ -2388,11 +2450,8 @@ irqreturn_t iwl_pcie_irq_msix_handler(int irq, void *dev_id)
iwl_pcie_irq_handle_error(trans);
}
- if (inta_hw & MSIX_HW_INT_CAUSES_REG_RESET_DONE) {
- IWL_DEBUG_ISR(trans, "Reset flow completed\n");
- trans_pcie->fw_reset_state = FW_RESET_OK;
- wake_up(&trans_pcie->fw_reset_waitq);
- }
+ if (inta_hw & MSIX_HW_INT_CAUSES_REG_RESET_DONE)
+ iwl_trans_pcie_handle_reset_interrupt(trans);
if (!polling)
iwl_pcie_clear_irq(trans, entry->entry);
diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/trans-gen2.c b/drivers/net/wireless/intel/iwlwifi/pcie/trans-gen2.c
index 793514a1852a..38ad719161e6 100644
--- a/drivers/net/wireless/intel/iwlwifi/pcie/trans-gen2.c
+++ b/drivers/net/wireless/intel/iwlwifi/pcie/trans-gen2.c
@@ -1,12 +1,12 @@
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
/*
* Copyright (C) 2017 Intel Deutschland GmbH
- * Copyright (C) 2018-2024 Intel Corporation
+ * Copyright (C) 2018-2025 Intel Corporation
*/
#include "iwl-trans.h"
#include "iwl-prph.h"
#include "iwl-context-info.h"
-#include "iwl-context-info-gen3.h"
+#include "iwl-context-info-v2.h"
#include "internal.h"
#include "fw/dbg.h"
@@ -81,13 +81,13 @@ static void iwl_pcie_gen2_apm_stop(struct iwl_trans *trans, bool op_mode_leave)
/* Stop device's DMA activity */
iwl_pcie_apm_stop_master(trans);
- iwl_trans_sw_reset(trans, false);
+ iwl_trans_pcie_sw_reset(trans, false);
/*
* Clear "initialization complete" bit to move adapter from
* D0A* (powered-up Active) --> D0U* (Uninitialized) state.
*/
- if (trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_BZ)
+ if (trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_BZ)
iwl_clear_bit(trans, CSR_GP_CNTRL,
CSR_GP_CNTRL_REG_FLAG_MAC_INIT);
else
@@ -95,17 +95,17 @@ static void iwl_pcie_gen2_apm_stop(struct iwl_trans *trans, bool op_mode_leave)
CSR_GP_CNTRL_REG_FLAG_INIT_DONE);
}
-static void iwl_trans_pcie_fw_reset_handshake(struct iwl_trans *trans)
+void iwl_trans_pcie_fw_reset_handshake(struct iwl_trans *trans)
{
struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
int ret;
trans_pcie->fw_reset_state = FW_RESET_REQUESTED;
- if (trans->trans_cfg->device_family < IWL_DEVICE_FAMILY_AX210)
+ if (trans->mac_cfg->device_family < IWL_DEVICE_FAMILY_AX210)
iwl_write_umac_prph(trans, UREG_NIC_SET_NMI_DRIVER,
UREG_NIC_SET_NMI_DRIVER_RESET_HANDSHAKE);
- else if (trans->trans_cfg->device_family == IWL_DEVICE_FAMILY_AX210)
+ else if (trans->mac_cfg->device_family == IWL_DEVICE_FAMILY_AX210)
iwl_write_umac_prph(trans, UREG_DOORBELL_TO_ISR6,
UREG_DOORBELL_TO_ISR6_RESET_HANDSHAKE);
else
@@ -117,13 +117,23 @@ static void iwl_trans_pcie_fw_reset_handshake(struct iwl_trans *trans)
trans_pcie->fw_reset_state != FW_RESET_REQUESTED,
FW_RESET_TIMEOUT);
if (!ret || trans_pcie->fw_reset_state == FW_RESET_ERROR) {
- u32 inta_hw = iwl_read32(trans, CSR_MSIX_HW_INT_CAUSES_AD);
+ bool reset_done;
+ u32 inta_hw;
+
+ if (trans_pcie->msix_enabled) {
+ inta_hw = iwl_read32(trans, CSR_MSIX_HW_INT_CAUSES_AD);
+ reset_done =
+ inta_hw & MSIX_HW_INT_CAUSES_REG_RESET_DONE;
+ } else {
+ inta_hw = iwl_read32(trans, CSR_INT_MASK);
+ reset_done = inta_hw & CSR_INT_BIT_RESET_DONE;
+ }
IWL_ERR(trans,
- "timeout waiting for FW reset ACK (inta_hw=0x%x)\n",
- inta_hw);
+ "timeout waiting for FW reset ACK (inta_hw=0x%x, reset_done %d)\n",
+ inta_hw, reset_done);
- if (!(inta_hw & MSIX_HW_INT_CAUSES_REG_RESET_DONE)) {
+ if (!reset_done) {
struct iwl_fw_error_dump_mode mode = {
.type = IWL_ERR_TYPE_RESET_HS_TIMEOUT,
.context = IWL_ERR_CONTEXT_FROM_OPMODE,
@@ -147,8 +157,14 @@ static void _iwl_trans_pcie_gen2_stop_device(struct iwl_trans *trans)
return;
if (trans->state >= IWL_TRANS_FW_STARTED &&
- trans_pcie->fw_reset_handshake)
+ trans->conf.fw_reset_handshake) {
+ /*
+ * Reset handshake can dump firmware on timeout, but that
+ * should assume that the firmware is already dead.
+ */
+ trans->state = IWL_TRANS_NO_FW;
iwl_trans_pcie_fw_reset_handshake(trans);
+ }
trans_pcie->is_down = true;
@@ -175,8 +191,8 @@ static void _iwl_trans_pcie_gen2_stop_device(struct iwl_trans *trans)
}
iwl_pcie_ctxt_info_free_paging(trans);
- if (trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_AX210)
- iwl_pcie_ctxt_info_gen3_free(trans, false);
+ if (trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_AX210)
+ iwl_pcie_ctxt_info_v2_free(trans, false);
else
iwl_pcie_ctxt_info_free(trans);
@@ -184,7 +200,7 @@ static void _iwl_trans_pcie_gen2_stop_device(struct iwl_trans *trans)
iwl_pcie_gen2_apm_stop(trans, false);
/* re-take ownership to prevent other users from stealing the device */
- iwl_trans_sw_reset(trans, true);
+ iwl_trans_pcie_sw_reset(trans, true);
/*
* Upon stop, the IVAR table gets erased, so msi-x won't
@@ -237,7 +253,7 @@ static int iwl_pcie_gen2_nic_init(struct iwl_trans *trans)
{
struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
int queue_size = max_t(u32, IWL_CMD_QUEUE_SIZE,
- trans->cfg->min_txq_size);
+ trans->mac_cfg->base->min_txq_size);
int ret;
/* TODO: most of the logic can be removed in A0 - but not in Z0 */
@@ -254,7 +270,7 @@ static int iwl_pcie_gen2_nic_init(struct iwl_trans *trans)
return -ENOMEM;
/* Allocate or reset and init all Tx and Command queues */
- if (iwl_txq_gen2_init(trans, trans_pcie->txqs.cmd.q_id, queue_size))
+ if (iwl_txq_gen2_init(trans, trans->conf.cmd_queue, queue_size))
return -ENOMEM;
/* enable shadow regs in HW */
@@ -275,7 +291,7 @@ static void iwl_pcie_get_rf_name(struct iwl_trans *trans)
if (buf[0])
return;
- switch (CSR_HW_RFID_TYPE(trans->hw_rf_id)) {
+ switch (CSR_HW_RFID_TYPE(trans->info.hw_rf_id)) {
case CSR_HW_RFID_TYPE(CSR_HW_RF_ID_TYPE_JF):
pos = scnprintf(buf, buflen, "JF");
break;
@@ -299,7 +315,7 @@ static void iwl_pcie_get_rf_name(struct iwl_trans *trans)
break;
case CSR_HW_RFID_TYPE(CSR_HW_RF_ID_TYPE_WP):
if (SILICON_Z_STEP ==
- CSR_HW_RFID_STEP(trans->hw_rf_id))
+ CSR_HW_RFID_STEP(trans->info.hw_rf_id))
pos = scnprintf(buf, buflen, "WHTC");
else
pos = scnprintf(buf, buflen, "WH");
@@ -308,7 +324,7 @@ static void iwl_pcie_get_rf_name(struct iwl_trans *trans)
return;
}
- switch (CSR_HW_RFID_TYPE(trans->hw_rf_id)) {
+ switch (CSR_HW_RFID_TYPE(trans->info.hw_rf_id)) {
case CSR_HW_RFID_TYPE(CSR_HW_RF_ID_TYPE_HR):
case CSR_HW_RFID_TYPE(CSR_HW_RF_ID_TYPE_HR1):
case CSR_HW_RFID_TYPE(CSR_HW_RF_ID_TYPE_HRCDB):
@@ -331,7 +347,7 @@ static void iwl_pcie_get_rf_name(struct iwl_trans *trans)
}
pos += scnprintf(buf + pos, buflen - pos, ", rfid=0x%x",
- trans->hw_rf_id);
+ trans->info.hw_rf_id);
IWL_INFO(trans, "Detected RF %s\n", buf);
@@ -358,8 +374,8 @@ void iwl_trans_pcie_gen2_fw_alive(struct iwl_trans *trans)
/* now that we got alive we can free the fw image & the context info.
* paging memory cannot be freed included since FW will still use it
*/
- if (trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_AX210)
- iwl_pcie_ctxt_info_gen3_free(trans, true);
+ if (trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_AX210)
+ iwl_pcie_ctxt_info_v2_free(trans, true);
else
iwl_pcie_ctxt_info_free(trans);
@@ -373,6 +389,11 @@ void iwl_trans_pcie_gen2_fw_alive(struct iwl_trans *trans)
iwl_pcie_get_rf_name(trans);
mutex_unlock(&trans_pcie->mutex);
+
+ if (trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_BZ)
+ trans->step_urm = !!(iwl_read_umac_prph(trans,
+ CNVI_PMU_STEP_FLOW) &
+ CNVI_PMU_STEP_FLOW_FORCE_URM);
}
static bool iwl_pcie_set_ltr(struct iwl_trans *trans)
@@ -392,21 +413,21 @@ static bool iwl_pcie_set_ltr(struct iwl_trans *trans)
* initialize the LTR to ~250 usec (see ltr_val above).
* The firmware initializes this again later (to a smaller value).
*/
- if ((trans->trans_cfg->device_family == IWL_DEVICE_FAMILY_AX210 ||
- trans->trans_cfg->device_family == IWL_DEVICE_FAMILY_22000) &&
- !trans->trans_cfg->integrated) {
+ if ((trans->mac_cfg->device_family == IWL_DEVICE_FAMILY_AX210 ||
+ trans->mac_cfg->device_family == IWL_DEVICE_FAMILY_22000) &&
+ !trans->mac_cfg->integrated) {
iwl_write32(trans, CSR_LTR_LONG_VAL_AD, ltr_val);
return true;
}
- if (trans->trans_cfg->integrated &&
- trans->trans_cfg->device_family == IWL_DEVICE_FAMILY_22000) {
+ if (trans->mac_cfg->integrated &&
+ trans->mac_cfg->device_family == IWL_DEVICE_FAMILY_22000) {
iwl_write_prph(trans, HPM_MAC_LTR_CSR, HPM_MAC_LRT_ENABLE_ALL);
iwl_write_prph(trans, HPM_UMAC_LTR, ltr_val);
return true;
}
- if (trans->trans_cfg->device_family == IWL_DEVICE_FAMILY_AX210) {
+ if (trans->mac_cfg->device_family == IWL_DEVICE_FAMILY_AX210) {
/* First clear the interrupt, just in case */
iwl_write32(trans, CSR_MSIX_HW_INT_CAUSES_AD,
MSIX_HW_INT_CAUSES_REG_IML);
@@ -463,16 +484,22 @@ static void iwl_pcie_spin_for_iml(struct iwl_trans *trans)
}
int iwl_trans_pcie_gen2_start_fw(struct iwl_trans *trans,
- const struct fw_img *fw, bool run_in_rfkill)
+ const struct iwl_fw *fw,
+ const struct fw_img *img,
+ bool run_in_rfkill)
{
struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
bool hw_rfkill, keep_ram_busy;
+ bool top_reset_done = false;
int ret;
+ mutex_lock(&trans_pcie->mutex);
+again:
/* This may fail if AMT took ownership of the device */
if (iwl_pcie_prepare_card_hw(trans)) {
IWL_WARN(trans, "Exit HW not ready\n");
- return -EIO;
+ ret = -EIO;
+ goto out;
}
iwl_enable_rfkill_int(trans);
@@ -489,8 +516,6 @@ int iwl_trans_pcie_gen2_start_fw(struct iwl_trans *trans,
/* Make sure it finished running */
iwl_pcie_synchronize_irqs(trans);
- mutex_lock(&trans_pcie->mutex);
-
/* If platform's RF_KILL switch is NOT set to KILL */
hw_rfkill = iwl_pcie_check_hw_rf_kill(trans);
if (hw_rfkill && !run_in_rfkill) {
@@ -520,22 +545,37 @@ int iwl_trans_pcie_gen2_start_fw(struct iwl_trans *trans,
goto out;
}
- if (trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_AX210)
- ret = iwl_pcie_ctxt_info_gen3_init(trans, fw);
- else
- ret = iwl_pcie_ctxt_info_init(trans, fw);
- if (ret)
- goto out;
+ if (WARN_ON(trans->do_top_reset &&
+ trans->mac_cfg->device_family < IWL_DEVICE_FAMILY_SC))
+ return -EINVAL;
+
+ /* we need to wait later - set state */
+ if (trans->do_top_reset)
+ trans_pcie->fw_reset_state = FW_RESET_TOP_REQUESTED;
+
+ if (trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_AX210) {
+ if (!top_reset_done) {
+ ret = iwl_pcie_ctxt_info_v2_alloc(trans, fw, img);
+ if (ret)
+ goto out;
+ }
+
+ iwl_pcie_ctxt_info_v2_kick(trans);
+ } else {
+ ret = iwl_pcie_ctxt_info_init(trans, img);
+ if (ret)
+ goto out;
+ }
keep_ram_busy = !iwl_pcie_set_ltr(trans);
- if (trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_BZ) {
+ if (trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_BZ) {
IWL_DEBUG_POWER(trans, "function scratch register value is 0x%08x\n",
iwl_read32(trans, CSR_FUNC_SCRATCH));
iwl_write32(trans, CSR_FUNC_SCRATCH, CSR_FUNC_SCRATCH_INIT_VALUE);
iwl_set_bit(trans, CSR_GP_CNTRL,
CSR_GP_CNTRL_REG_FLAG_ROM_START);
- } else if (trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_AX210) {
+ } else if (trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_AX210) {
iwl_write_umac_prph(trans, UREG_CPU_INIT_RUN, 1);
} else {
iwl_write_prph(trans, UREG_CPU_INIT_RUN, 1);
@@ -544,6 +584,38 @@ int iwl_trans_pcie_gen2_start_fw(struct iwl_trans *trans,
if (keep_ram_busy)
iwl_pcie_spin_for_iml(trans);
+ if (trans->do_top_reset) {
+ trans->do_top_reset = 0;
+
+#define FW_TOP_RESET_TIMEOUT (HZ / 4)
+ ret = wait_event_timeout(trans_pcie->fw_reset_waitq,
+ trans_pcie->fw_reset_state != FW_RESET_TOP_REQUESTED,
+ FW_TOP_RESET_TIMEOUT);
+
+ if (trans_pcie->fw_reset_state != FW_RESET_OK) {
+ if (trans_pcie->fw_reset_state != FW_RESET_TOP_REQUESTED)
+ IWL_ERR(trans,
+ "TOP reset interrupted by error (state %d)!\n",
+ trans_pcie->fw_reset_state);
+ else
+ IWL_ERR(trans, "TOP reset timed out!\n");
+ iwl_op_mode_nic_error(trans->op_mode,
+ IWL_ERR_TYPE_TOP_RESET_FAILED);
+ iwl_trans_schedule_reset(trans,
+ IWL_ERR_TYPE_TOP_RESET_FAILED);
+ ret = -EIO;
+ goto out;
+ }
+
+ msleep(10);
+ IWL_INFO(trans, "TOP reset successful, reinit now\n");
+ /* now load the firmware again properly */
+ trans_pcie->prph_scratch->ctrl_cfg.control.control_flags &=
+ ~cpu_to_le32(IWL_PRPH_SCRATCH_TOP_RESET);
+ top_reset_done = true;
+ goto again;
+ }
+
/* re-check RF-Kill state since we may have missed the interrupt */
hw_rfkill = iwl_pcie_check_hw_rf_kill(trans);
if (hw_rfkill && !run_in_rfkill)
diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/trans.c b/drivers/net/wireless/intel/iwlwifi/pcie/trans.c
index c917ed4c19bc..cc4d289b110d 100644
--- a/drivers/net/wireless/intel/iwlwifi/pcie/trans.c
+++ b/drivers/net/wireless/intel/iwlwifi/pcie/trans.c
@@ -28,7 +28,7 @@
#include "mei/iwl-mei.h"
#include "internal.h"
#include "iwl-fh.h"
-#include "iwl-context-info-gen3.h"
+#include "iwl-context-info-v2.h"
/* extended range in FW SRAM */
#define IWL_FW_MEM_EXTENDED_START 0x40000
@@ -131,7 +131,7 @@ out:
int iwl_trans_pcie_sw_reset(struct iwl_trans *trans, bool retake_ownership)
{
/* Reset entire device - do controller reset (results in SHRD_HW_RST) */
- if (trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_BZ) {
+ if (trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_BZ) {
iwl_set_bit(trans, CSR_GP_CNTRL,
CSR_GP_CNTRL_REG_FLAG_SW_RESET);
usleep_range(10000, 20000);
@@ -237,7 +237,7 @@ static void iwl_trans_pcie_write_shr(struct iwl_trans *trans, u32 reg, u32 val)
static void iwl_pcie_set_pwr(struct iwl_trans *trans, bool vaux)
{
- if (trans->cfg->apmg_not_supported)
+ if (trans->mac_cfg->base->apmg_not_supported)
return;
if (vaux && pci_pme_capable(to_pci_dev(trans->dev), PCI_D3cold))
@@ -293,7 +293,7 @@ static int iwl_pcie_apm_init(struct iwl_trans *trans)
*/
/* Disable L0S exit timer (platform NMI Work/Around) */
- if (trans->trans_cfg->device_family < IWL_DEVICE_FAMILY_8000)
+ if (trans->mac_cfg->device_family < IWL_DEVICE_FAMILY_8000)
iwl_set_bit(trans, CSR_GIO_CHICKEN_BITS,
CSR_GIO_CHICKEN_BITS_REG_BIT_DIS_L0S_EXIT_TIMER);
@@ -317,7 +317,7 @@ static int iwl_pcie_apm_init(struct iwl_trans *trans)
iwl_pcie_apm_config(trans);
/* Configure analog phase-lock-loop before activating to D0A */
- if (trans->trans_cfg->base_params->pll_cfg)
+ if (trans->mac_cfg->base->pll_cfg)
iwl_set_bit(trans, CSR_ANA_PLL_CFG, CSR50_ANA_PLL_CFG_VAL);
ret = iwl_finish_nic_init(trans);
@@ -353,7 +353,7 @@ static int iwl_pcie_apm_init(struct iwl_trans *trans)
* bits do not disable clocks. This preserves any hardware
* bits already set by default in "CLK_CTRL_REG" after reset.
*/
- if (!trans->cfg->apmg_not_supported) {
+ if (!trans->mac_cfg->base->apmg_not_supported) {
iwl_write_prph(trans, APMG_CLK_EN_REG,
APMG_CLK_VAL_DMA_CLK_RQT);
udelay(20);
@@ -469,7 +469,7 @@ void iwl_pcie_apm_stop_master(struct iwl_trans *trans)
/* stop device's busmaster DMA activity */
- if (trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_BZ) {
+ if (trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_BZ) {
iwl_set_bit(trans, CSR_GP_CNTRL,
CSR_GP_CNTRL_REG_FLAG_BUS_MASTER_DISABLE_REQ);
@@ -501,10 +501,10 @@ static void iwl_pcie_apm_stop(struct iwl_trans *trans, bool op_mode_leave)
iwl_pcie_apm_init(trans);
/* inform ME that we are leaving */
- if (trans->trans_cfg->device_family == IWL_DEVICE_FAMILY_7000)
+ if (trans->mac_cfg->device_family == IWL_DEVICE_FAMILY_7000)
iwl_set_bits_prph(trans, APMG_PCIDEV_STT_REG,
APMG_PCIDEV_STT_VAL_WAKE_ME);
- else if (trans->trans_cfg->device_family >=
+ else if (trans->mac_cfg->device_family >=
IWL_DEVICE_FAMILY_8000) {
iwl_set_bit(trans, CSR_DBG_LINK_PWR_MGMT_REG,
CSR_RESET_LINK_PWR_MGMT_DISABLED);
@@ -565,7 +565,7 @@ static int iwl_pcie_nic_init(struct iwl_trans *trans)
return -ENOMEM;
}
- if (trans->trans_cfg->base_params->shadow_reg_enable) {
+ if (trans->mac_cfg->base->shadow_reg_enable) {
/* enable shadow regs in HW */
iwl_set_bit(trans, CSR_MAC_SHADOW_REG_CTRL, 0x800FFFFF);
IWL_DEBUG_INFO(trans, "Enabling shadow registers in device\n");
@@ -634,7 +634,7 @@ int iwl_pcie_prepare_card_hw(struct iwl_trans *trans)
IWL_DEBUG_INFO(trans,
"Couldn't prepare the card but SAP is connected\n");
trans->csme_own = true;
- if (trans->trans_cfg->device_family !=
+ if (trans->mac_cfg->device_family !=
IWL_DEVICE_FAMILY_9000)
IWL_ERR(trans,
"SAP not supported for this NIC family\n");
@@ -819,7 +819,7 @@ static int iwl_pcie_load_cpu_sections_8000(struct iwl_trans *trans,
iwl_enable_interrupts(trans);
- if (trans->trans_cfg->gen2) {
+ if (trans->mac_cfg->gen2) {
if (cpu == 1)
iwl_write_prph(trans, UREG_UCODE_LOAD_STATUS,
0xFFFF);
@@ -977,7 +977,7 @@ monitor:
if (dest->monitor_mode == EXTERNAL_MODE && fw_mon->size) {
iwl_write_prph(trans, le32_to_cpu(dest->base_reg),
fw_mon->physical >> dest->base_shift);
- if (trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_8000)
+ if (trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_8000)
iwl_write_prph(trans, le32_to_cpu(dest->end_reg),
(fw_mon->physical + fw_mon->size -
256) >> dest->end_shift);
@@ -1153,7 +1153,7 @@ static void iwl_pcie_map_non_rx_causes(struct iwl_trans *trans)
*/
iwl_pcie_map_list(trans, causes_list_common,
ARRAY_SIZE(causes_list_common), val);
- if (trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_BZ)
+ if (trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_BZ)
iwl_pcie_map_list(trans, causes_list_bz,
ARRAY_SIZE(causes_list_bz), val);
else
@@ -1175,7 +1175,7 @@ static void iwl_pcie_map_rx_causes(struct iwl_trans *trans)
* the other (N - 2) interrupt vectors.
*/
val = BIT(MSIX_FH_INT_CAUSES_Q(0));
- for (idx = 1; idx < trans->num_rx_queues; idx++) {
+ for (idx = 1; idx < trans->info.num_rxqs; idx++) {
iwl_write8(trans, CSR_MSIX_RX_IVAR(idx),
MSIX_FH_INT_CAUSES_Q(idx - offset));
val |= BIT(MSIX_FH_INT_CAUSES_Q(idx));
@@ -1196,7 +1196,7 @@ void iwl_pcie_conf_msix_hw(struct iwl_trans_pcie *trans_pcie)
struct iwl_trans *trans = trans_pcie->trans;
if (!trans_pcie->msix_enabled) {
- if (trans->trans_cfg->mq_rx_supported &&
+ if (trans->mac_cfg->mq_rx_supported &&
test_bit(STATUS_DEVICE_ENABLED, &trans->status))
iwl_write_umac_prph(trans, UREG_CHICK,
UREG_CHICK_MSI_ENABLE);
@@ -1271,7 +1271,7 @@ static void _iwl_trans_pcie_stop_device(struct iwl_trans *trans, bool from_irq)
iwl_pcie_rx_stop(trans);
/* Power-down device's busmaster DMA clocks */
- if (!trans->cfg->apmg_not_supported) {
+ if (!trans->mac_cfg->base->apmg_not_supported) {
iwl_write_prph(trans, APMG_CLK_DIS_REG,
APMG_CLK_VAL_DMA_CLK_RQT);
udelay(5);
@@ -1279,7 +1279,7 @@ static void _iwl_trans_pcie_stop_device(struct iwl_trans *trans, bool from_irq)
}
/* Make sure (redundant) we've released our request to stay awake */
- if (trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_BZ)
+ if (trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_BZ)
iwl_clear_bit(trans, CSR_GP_CNTRL,
CSR_GP_CNTRL_REG_FLAG_BZ_MAC_ACCESS_REQ);
else
@@ -1337,7 +1337,9 @@ void iwl_pcie_synchronize_irqs(struct iwl_trans *trans)
}
int iwl_trans_pcie_start_fw(struct iwl_trans *trans,
- const struct fw_img *fw, bool run_in_rfkill)
+ const struct iwl_fw *fw,
+ const struct fw_img *img,
+ bool run_in_rfkill)
{
struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
bool hw_rfkill;
@@ -1408,10 +1410,10 @@ int iwl_trans_pcie_start_fw(struct iwl_trans *trans,
iwl_write32(trans, CSR_UCODE_DRV_GP1_CLR, CSR_UCODE_SW_BIT_RFKILL);
/* Load the given image to the HW */
- if (trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_8000)
- ret = iwl_pcie_load_given_ucode_8000(trans, fw);
+ if (trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_8000)
+ ret = iwl_pcie_load_given_ucode_8000(trans, img);
else
- ret = iwl_pcie_load_given_ucode(trans, fw);
+ ret = iwl_pcie_load_given_ucode(trans, img);
/* re-check RF-Kill state since we may have missed the interrupt */
hw_rfkill = iwl_pcie_check_hw_rf_kill(trans);
@@ -1423,10 +1425,10 @@ out:
return ret;
}
-void iwl_trans_pcie_fw_alive(struct iwl_trans *trans, u32 scd_addr)
+void iwl_trans_pcie_fw_alive(struct iwl_trans *trans)
{
iwl_pcie_reset_ict(trans);
- iwl_pcie_tx_start(trans, scd_addr);
+ iwl_pcie_tx_start(trans);
}
void iwl_trans_pcie_handle_stop_rfkill(struct iwl_trans *trans,
@@ -1485,7 +1487,7 @@ void iwl_trans_pcie_rf_kill(struct iwl_trans *trans, bool state, bool from_irq)
IWL_WARN(trans, "reporting RF_KILL (radio %s)\n",
state ? "disabled" : "enabled");
if (iwl_op_mode_hw_rf_kill(trans->op_mode, state) &&
- !WARN_ON(trans->trans_cfg->gen2))
+ !WARN_ON(trans->mac_cfg->gen2))
_iwl_trans_pcie_stop_device(trans, from_irq);
}
@@ -1505,7 +1507,7 @@ static void iwl_pcie_d3_complete_suspend(struct iwl_trans *trans,
iwl_pcie_synchronize_irqs(trans);
- if (trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_BZ) {
+ if (trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_BZ) {
iwl_clear_bit(trans, CSR_GP_CNTRL,
CSR_GP_CNTRL_REG_FLAG_BZ_MAC_ACCESS_REQ);
iwl_clear_bit(trans, CSR_GP_CNTRL,
@@ -1534,11 +1536,11 @@ static int iwl_pcie_d3_handshake(struct iwl_trans *trans, bool suspend)
struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
int ret;
- if (trans->trans_cfg->device_family == IWL_DEVICE_FAMILY_AX210)
+ if (trans->mac_cfg->device_family == IWL_DEVICE_FAMILY_AX210)
iwl_write_umac_prph(trans, UREG_DOORBELL_TO_ISR6,
suspend ? UREG_DOORBELL_TO_ISR6_SUSPEND :
UREG_DOORBELL_TO_ISR6_RESUME);
- else if (trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_BZ)
+ else if (trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_BZ)
iwl_write32(trans, CSR_IPC_SLEEP_CONTROL,
suspend ? CSR_IPC_SLEEP_CONTROL_SUSPEND :
CSR_IPC_SLEEP_CONTROL_RESUME);
@@ -1593,7 +1595,7 @@ int iwl_trans_pcie_d3_resume(struct iwl_trans *trans,
goto out;
}
- if (trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_BZ)
+ if (trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_BZ)
iwl_set_bit(trans, CSR_GP_CNTRL,
CSR_GP_CNTRL_REG_FLAG_BZ_MAC_ACCESS_REQ);
else
@@ -1653,17 +1655,18 @@ out:
static void
iwl_pcie_set_interrupt_capa(struct pci_dev *pdev,
struct iwl_trans *trans,
- const struct iwl_cfg_trans_params *cfg_trans)
+ const struct iwl_mac_cfg *mac_cfg,
+ struct iwl_trans_info *info)
{
struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
int max_irqs, num_irqs, i, ret;
u16 pci_cmd;
u32 max_rx_queues = IWL_MAX_RX_HW_QUEUES;
- if (!cfg_trans->mq_rx_supported)
+ if (!mac_cfg->mq_rx_supported)
goto enable_msi;
- if (cfg_trans->device_family <= IWL_DEVICE_FAMILY_9000)
+ if (mac_cfg->device_family <= IWL_DEVICE_FAMILY_9000)
max_rx_queues = IWL_9000_MAX_RX_HW_QUEUES;
max_irqs = min_t(u32, num_online_cpus() + 2, max_rx_queues);
@@ -1693,27 +1696,28 @@ iwl_pcie_set_interrupt_capa(struct pci_dev *pdev,
* More than two interrupts: we will use fewer RSS queues.
*/
if (num_irqs <= max_irqs - 2) {
- trans_pcie->trans->num_rx_queues = num_irqs + 1;
+ info->num_rxqs = num_irqs + 1;
trans_pcie->shared_vec_mask = IWL_SHARED_IRQ_NON_RX |
IWL_SHARED_IRQ_FIRST_RSS;
} else if (num_irqs == max_irqs - 1) {
- trans_pcie->trans->num_rx_queues = num_irqs;
+ info->num_rxqs = num_irqs;
trans_pcie->shared_vec_mask = IWL_SHARED_IRQ_NON_RX;
} else {
- trans_pcie->trans->num_rx_queues = num_irqs - 1;
+ info->num_rxqs = num_irqs - 1;
}
IWL_DEBUG_INFO(trans,
"MSI-X enabled with rx queues %d, vec mask 0x%x\n",
- trans_pcie->trans->num_rx_queues, trans_pcie->shared_vec_mask);
+ info->num_rxqs, trans_pcie->shared_vec_mask);
- WARN_ON(trans_pcie->trans->num_rx_queues > IWL_MAX_RX_HW_QUEUES);
+ WARN_ON(info->num_rxqs > IWL_MAX_RX_HW_QUEUES);
trans_pcie->alloc_vecs = num_irqs;
trans_pcie->msix_enabled = true;
return;
enable_msi:
+ info->num_rxqs = 1;
ret = pci_enable_msi(pdev);
if (ret) {
dev_err(&pdev->dev, "pci_enable_msi failed - %d\n", ret);
@@ -1726,14 +1730,15 @@ enable_msi:
}
}
-static void iwl_pcie_irq_set_affinity(struct iwl_trans *trans)
+static void iwl_pcie_irq_set_affinity(struct iwl_trans *trans,
+ struct iwl_trans_info *info)
{
#if defined(CONFIG_SMP)
int iter_rx_q, i, ret, cpu, offset;
struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
i = trans_pcie->shared_vec_mask & IWL_SHARED_IRQ_FIRST_RSS ? 0 : 1;
- iter_rx_q = trans_pcie->trans->num_rx_queues - 1 + i;
+ iter_rx_q = info->num_rxqs - 1 + i;
offset = 1 + i;
for (; i < iter_rx_q ; i++) {
/*
@@ -1753,7 +1758,8 @@ static void iwl_pcie_irq_set_affinity(struct iwl_trans *trans)
}
static int iwl_pcie_init_msix_handler(struct pci_dev *pdev,
- struct iwl_trans_pcie *trans_pcie)
+ struct iwl_trans_pcie *trans_pcie,
+ struct iwl_trans_info *info)
{
int i;
@@ -1782,7 +1788,7 @@ static int iwl_pcie_init_msix_handler(struct pci_dev *pdev,
return ret;
}
}
- iwl_pcie_irq_set_affinity(trans_pcie->trans);
+ iwl_pcie_irq_set_affinity(trans_pcie->trans, info);
return 0;
}
@@ -1791,7 +1797,7 @@ static int iwl_trans_pcie_clear_persistence_bit(struct iwl_trans *trans)
{
u32 hpm, wprot;
- switch (trans->trans_cfg->device_family) {
+ switch (trans->mac_cfg->device_family) {
case IWL_DEVICE_FAMILY_9000:
wprot = PREG_PRPH_WPROT_9000;
break;
@@ -1860,8 +1866,8 @@ static int _iwl_trans_pcie_start_hw(struct iwl_trans *trans)
if (err)
return err;
- if (trans->trans_cfg->device_family == IWL_DEVICE_FAMILY_22000 &&
- trans->trans_cfg->integrated) {
+ if (trans->mac_cfg->device_family == IWL_DEVICE_FAMILY_22000 &&
+ trans->mac_cfg->integrated) {
err = iwl_pcie_gen2_force_power_gating(trans);
if (err)
return err;
@@ -1936,7 +1942,7 @@ u32 iwl_trans_pcie_read32(struct iwl_trans *trans, u32 ofs)
static u32 iwl_trans_pcie_prph_msk(struct iwl_trans *trans)
{
- if (trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_AX210)
+ if (trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_AX210)
return 0x00FFFFFF;
else
return 0x000FFFFF;
@@ -1960,45 +1966,17 @@ void iwl_trans_pcie_write_prph(struct iwl_trans *trans, u32 addr, u32 val)
iwl_trans_pcie_write32(trans, HBUS_TARG_PRPH_WDAT, val);
}
-void iwl_trans_pcie_configure(struct iwl_trans *trans,
- const struct iwl_trans_config *trans_cfg)
+void iwl_trans_pcie_op_mode_enter(struct iwl_trans *trans)
{
struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
/* free all first - we might be reconfigured for a different size */
iwl_pcie_free_rbs_pool(trans);
- trans_pcie->txqs.cmd.q_id = trans_cfg->cmd_queue;
- trans_pcie->txqs.cmd.fifo = trans_cfg->cmd_fifo;
- trans_pcie->txqs.page_offs = trans_cfg->cb_data_offs;
- trans_pcie->txqs.dev_cmd_offs = trans_cfg->cb_data_offs + sizeof(void *);
- trans_pcie->txqs.queue_alloc_cmd_ver = trans_cfg->queue_alloc_cmd_ver;
-
- if (WARN_ON(trans_cfg->n_no_reclaim_cmds > MAX_NO_RECLAIM_CMDS))
- trans_pcie->n_no_reclaim_cmds = 0;
- else
- trans_pcie->n_no_reclaim_cmds = trans_cfg->n_no_reclaim_cmds;
- if (trans_pcie->n_no_reclaim_cmds)
- memcpy(trans_pcie->no_reclaim_cmds, trans_cfg->no_reclaim_cmds,
- trans_pcie->n_no_reclaim_cmds * sizeof(u8));
-
- trans_pcie->rx_buf_size = trans_cfg->rx_buf_size;
trans_pcie->rx_page_order =
- iwl_trans_get_rb_size_order(trans_pcie->rx_buf_size);
+ iwl_trans_get_rb_size_order(trans->conf.rx_buf_size);
trans_pcie->rx_buf_bytes =
- iwl_trans_get_rb_size(trans_pcie->rx_buf_size);
- trans_pcie->supported_dma_mask = DMA_BIT_MASK(12);
- if (trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_AX210)
- trans_pcie->supported_dma_mask = DMA_BIT_MASK(11);
-
- trans_pcie->txqs.bc_table_dword = trans_cfg->bc_table_dword;
- trans_pcie->scd_set_active = trans_cfg->scd_set_active;
-
- trans->command_groups = trans_cfg->command_groups;
- trans->command_groups_size = trans_cfg->command_groups_size;
-
-
- trans_pcie->fw_reset_handshake = trans_cfg->fw_reset_handshake;
+ iwl_trans_get_rb_size(trans->conf.rx_buf_size);
}
void iwl_trans_pcie_free_pnvm_dram_regions(struct iwl_dram_regions *dram_regions,
@@ -2026,11 +2004,14 @@ void iwl_trans_pcie_free_pnvm_dram_regions(struct iwl_dram_regions *dram_regions
static void iwl_pcie_free_invalid_tx_cmd(struct iwl_trans *trans)
{
- iwl_pcie_free_dma_ptr(trans, &trans->invalid_tx_cmd);
+ struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+
+ iwl_pcie_free_dma_ptr(trans, &trans_pcie->invalid_tx_cmd);
}
static int iwl_pcie_alloc_invalid_tx_cmd(struct iwl_trans *trans)
{
+ struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
struct iwl_cmd_header_wide bad_cmd = {
.cmd = INVALID_WR_PTR_CMD,
.group_id = DEBUG_GROUP,
@@ -2040,11 +2021,11 @@ static int iwl_pcie_alloc_invalid_tx_cmd(struct iwl_trans *trans)
};
int ret;
- ret = iwl_pcie_alloc_dma_ptr(trans, &trans->invalid_tx_cmd,
+ ret = iwl_pcie_alloc_dma_ptr(trans, &trans_pcie->invalid_tx_cmd,
sizeof(bad_cmd));
if (ret)
return ret;
- memcpy(trans->invalid_tx_cmd.addr, &bad_cmd, sizeof(bad_cmd));
+ memcpy(trans_pcie->invalid_tx_cmd.addr, &bad_cmd, sizeof(bad_cmd));
return 0;
}
@@ -2055,7 +2036,7 @@ void iwl_trans_pcie_free(struct iwl_trans *trans)
iwl_pcie_synchronize_irqs(trans);
- if (trans->trans_cfg->gen2)
+ if (trans->mac_cfg->gen2)
iwl_txq_gen2_tx_free(trans);
else
iwl_pcie_tx_free(trans);
@@ -2348,18 +2329,20 @@ out:
void iwl_trans_pcie_reset(struct iwl_trans *trans, enum iwl_reset_mode mode)
{
+ struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
struct iwl_trans_pcie_removal *removal;
char _msg = 0, *msg = &_msg;
- if (WARN_ON(mode < IWL_RESET_MODE_REMOVE_ONLY))
+ if (WARN_ON(mode < IWL_RESET_MODE_REMOVE_ONLY ||
+ mode == IWL_RESET_MODE_BACKOFF))
return;
if (test_bit(STATUS_TRANS_DEAD, &trans->status))
return;
- if (trans->me_present && mode == IWL_RESET_MODE_PROD_RESET) {
+ if (trans_pcie->me_present && mode == IWL_RESET_MODE_PROD_RESET) {
mode = IWL_RESET_MODE_FUNC_RESET;
- if (trans->me_present < 0)
+ if (trans_pcie->me_present < 0)
msg = " instead of product reset as ME may be present";
else
msg = " instead of product reset as ME is present";
@@ -2394,7 +2377,7 @@ void iwl_trans_pcie_reset(struct iwl_trans *trans, enum iwl_reset_mode mode)
removal->pdev = to_pci_dev(trans->dev);
removal->mode = mode;
- removal->integrated = trans->trans_cfg->integrated;
+ removal->integrated = trans->mac_cfg->integrated;
INIT_WORK(&removal->work, iwl_trans_pcie_removal_wk);
pci_dev_get(removal->pdev);
schedule_work(&removal->work);
@@ -2405,7 +2388,7 @@ EXPORT_SYMBOL(iwl_trans_pcie_reset);
* This version doesn't disable BHs but rather assumes they're
* already disabled.
*/
-bool __iwl_trans_pcie_grab_nic_access(struct iwl_trans *trans)
+bool __iwl_trans_pcie_grab_nic_access(struct iwl_trans *trans, bool silent)
{
int ret;
struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
@@ -2422,7 +2405,7 @@ bool __iwl_trans_pcie_grab_nic_access(struct iwl_trans *trans)
if (trans_pcie->cmd_hold_nic_awake)
goto out;
- if (trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_BZ) {
+ if (trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_BZ) {
write = CSR_GP_CNTRL_REG_FLAG_BZ_MAC_ACCESS_REQ;
mask = CSR_GP_CNTRL_REG_FLAG_MAC_STATUS;
poll = CSR_GP_CNTRL_REG_FLAG_MAC_STATUS;
@@ -2430,7 +2413,7 @@ bool __iwl_trans_pcie_grab_nic_access(struct iwl_trans *trans)
/* this bit wakes up the NIC */
__iwl_trans_pcie_set_bit(trans, CSR_GP_CNTRL, write);
- if (trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_8000)
+ if (trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_8000)
udelay(2);
/*
@@ -2457,6 +2440,11 @@ bool __iwl_trans_pcie_grab_nic_access(struct iwl_trans *trans)
if (unlikely(ret < 0)) {
u32 cntrl = iwl_read32(trans, CSR_GP_CNTRL);
+ if (silent) {
+ spin_unlock(&trans_pcie->reg_lock);
+ return false;
+ }
+
WARN_ONCE(1,
"Timeout waiting for hardware access (CSR_GP_CNTRL 0x%08x)\n",
cntrl);
@@ -2488,7 +2476,7 @@ bool iwl_trans_pcie_grab_nic_access(struct iwl_trans *trans)
bool ret;
local_bh_disable();
- ret = __iwl_trans_pcie_grab_nic_access(trans);
+ ret = __iwl_trans_pcie_grab_nic_access(trans, false);
if (ret) {
/* keep BHs disabled until iwl_trans_pcie_release_nic_access */
return ret;
@@ -2497,7 +2485,8 @@ bool iwl_trans_pcie_grab_nic_access(struct iwl_trans *trans)
return false;
}
-void iwl_trans_pcie_release_nic_access(struct iwl_trans *trans)
+void __releases(nic_access_nobh)
+iwl_trans_pcie_release_nic_access(struct iwl_trans *trans)
{
struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
@@ -2511,7 +2500,7 @@ void iwl_trans_pcie_release_nic_access(struct iwl_trans *trans)
if (trans_pcie->cmd_hold_nic_awake)
goto out;
- if (trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_BZ)
+ if (trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_BZ)
__iwl_trans_pcie_clear_bit(trans, CSR_GP_CNTRL,
CSR_GP_CNTRL_REG_FLAG_BZ_MAC_ACCESS_REQ);
else
@@ -2524,6 +2513,7 @@ void iwl_trans_pcie_release_nic_access(struct iwl_trans *trans)
* scheduled on different CPUs (after we drop reg_lock).
*/
out:
+ __release(nic_access_nobh);
spin_unlock_bh(&trans_pcie->reg_lock);
}
@@ -2609,7 +2599,7 @@ int iwl_trans_pcie_rxq_dma_data(struct iwl_trans *trans, int queue,
{
struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
- if (queue >= trans->num_rx_queues || !trans_pcie->rxq)
+ if (queue >= trans->info.num_rxqs || !trans_pcie->rxq)
return -EINVAL;
data->fr_bd_cb = trans_pcie->rxq[queue].bd_dma;
@@ -2690,10 +2680,10 @@ int iwl_trans_pcie_wait_txqs_empty(struct iwl_trans *trans, u32 txq_bm)
/* waiting for all the tx frames complete might take a while */
for (cnt = 0;
- cnt < trans->trans_cfg->base_params->num_of_queues;
+ cnt < trans->mac_cfg->base->num_of_queues;
cnt++) {
- if (cnt == trans_pcie->txqs.cmd.q_id)
+ if (cnt == trans->conf.cmd_queue)
continue;
if (!test_bit(cnt, trans_pcie->txqs.queue_used))
continue;
@@ -2834,7 +2824,7 @@ static void *iwl_dbgfs_tx_queue_seq_start(struct seq_file *seq, loff_t *pos)
struct iwl_dbgfs_tx_queue_priv *priv = seq->private;
struct iwl_dbgfs_tx_queue_state *state;
- if (*pos >= priv->trans->trans_cfg->base_params->num_of_queues)
+ if (*pos >= priv->trans->mac_cfg->base->num_of_queues)
return NULL;
state = kmalloc(sizeof(*state), GFP_KERNEL);
@@ -2852,7 +2842,7 @@ static void *iwl_dbgfs_tx_queue_seq_next(struct seq_file *seq,
*pos = ++state->pos;
- if (*pos >= priv->trans->trans_cfg->base_params->num_of_queues)
+ if (*pos >= priv->trans->mac_cfg->base->num_of_queues)
return NULL;
return state;
@@ -2884,7 +2874,7 @@ static int iwl_dbgfs_tx_queue_seq_show(struct seq_file *seq, void *v)
else
seq_puts(seq, "(unallocated)");
- if (state->pos == trans_pcie->txqs.cmd.q_id)
+ if (state->pos == trans->conf.cmd_queue)
seq_puts(seq, " (HCMD)");
seq_puts(seq, "\n");
@@ -2922,7 +2912,7 @@ static ssize_t iwl_dbgfs_rx_queue_read(struct file *file,
int pos = 0, i, ret;
size_t bufsz;
- bufsz = sizeof(char) * 121 * trans->num_rx_queues;
+ bufsz = sizeof(char) * 121 * trans->info.num_rxqs;
if (!trans_pcie->rxq)
return -EAGAIN;
@@ -2931,9 +2921,11 @@ static ssize_t iwl_dbgfs_rx_queue_read(struct file *file,
if (!buf)
return -ENOMEM;
- for (i = 0; i < trans->num_rx_queues && pos < bufsz; i++) {
+ for (i = 0; i < trans->info.num_rxqs && pos < bufsz; i++) {
struct iwl_rxq *rxq = &trans_pcie->rxq[i];
+ spin_lock_bh(&rxq->lock);
+
pos += scnprintf(buf + pos, bufsz - pos, "queue#: %2d\n",
i);
pos += scnprintf(buf + pos, bufsz - pos, "\tread: %u\n",
@@ -2954,6 +2946,7 @@ static ssize_t iwl_dbgfs_rx_queue_read(struct file *file,
pos += scnprintf(buf + pos, bufsz - pos,
"\tclosed_rb_num: Not Allocated\n");
}
+ spin_unlock_bh(&rxq->lock);
}
ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos);
kfree(buf);
@@ -3258,8 +3251,9 @@ static ssize_t iwl_dbgfs_reset_write(struct file *file,
{
struct iwl_trans *trans = file->private_data;
static const char * const modes[] = {
- [IWL_RESET_MODE_SW_RESET] = "n/a",
- [IWL_RESET_MODE_REPROBE] = "n/a",
+ [IWL_RESET_MODE_SW_RESET] = "sw",
+ [IWL_RESET_MODE_REPROBE] = "reprobe",
+ [IWL_RESET_MODE_TOP_RESET] = "top",
[IWL_RESET_MODE_REMOVE_ONLY] = "remove",
[IWL_RESET_MODE_RESCAN] = "rescan",
[IWL_RESET_MODE_FUNC_RESET] = "function",
@@ -3278,8 +3272,18 @@ static ssize_t iwl_dbgfs_reset_write(struct file *file,
if (mode < 0)
return mode;
- if (mode < IWL_RESET_MODE_REMOVE_ONLY)
- return -EINVAL;
+ if (mode < IWL_RESET_MODE_REMOVE_ONLY) {
+ if (!test_bit(STATUS_DEVICE_ENABLED, &trans->status))
+ return -EINVAL;
+ if (mode == IWL_RESET_MODE_TOP_RESET) {
+ if (trans->mac_cfg->device_family < IWL_DEVICE_FAMILY_SC)
+ return -EINVAL;
+ trans->request_top_reset = 1;
+ }
+ iwl_op_mode_nic_error(trans->op_mode, IWL_ERR_TYPE_DEBUGFS);
+ iwl_trans_schedule_reset(trans, IWL_ERR_TYPE_DEBUGFS);
+ return count;
+ }
iwl_trans_pcie_reset(trans, mode);
@@ -3420,7 +3424,7 @@ static u32 iwl_trans_pcie_fh_regs_dump(struct iwl_trans *trans,
(*data)->len = cpu_to_le32(fh_regs_len);
val = (void *)(*data)->data;
- if (!trans->trans_cfg->gen2)
+ if (!trans->mac_cfg->gen2)
for (i = FH_MEM_LOWER_BOUND; i < FH_MEM_UPPER_BOUND;
i += sizeof(u32))
*val++ = cpu_to_le32(iwl_trans_pcie_read32(trans, i));
@@ -3467,7 +3471,7 @@ iwl_trans_pcie_dump_pointers(struct iwl_trans *trans,
{
u32 base, base_high, write_ptr, write_ptr_val, wrap_cnt;
- if (trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_AX210) {
+ if (trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_AX210) {
base = DBGC_CUR_DBGBUF_BASE_ADDR_LSB;
base_high = DBGC_CUR_DBGBUF_BASE_ADDR_MSB;
write_ptr = DBGC_CUR_DBGBUF_STATUS;
@@ -3487,7 +3491,7 @@ iwl_trans_pcie_dump_pointers(struct iwl_trans *trans,
cpu_to_le32(iwl_read_prph(trans, wrap_cnt));
fw_mon_data->fw_mon_base_ptr =
cpu_to_le32(iwl_read_prph(trans, base));
- if (trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_AX210) {
+ if (trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_AX210) {
fw_mon_data->fw_mon_base_high_ptr =
cpu_to_le32(iwl_read_prph(trans, base_high));
write_ptr_val &= DBGC_CUR_DBGBUF_STATUS_OFFSET_MSK;
@@ -3507,8 +3511,8 @@ iwl_trans_pcie_dump_monitor(struct iwl_trans *trans,
if (trans->dbg.dest_tlv ||
(fw_mon->size &&
- (trans->trans_cfg->device_family == IWL_DEVICE_FAMILY_7000 ||
- trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_AX210))) {
+ (trans->mac_cfg->device_family == IWL_DEVICE_FAMILY_7000 ||
+ trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_AX210))) {
struct iwl_fw_error_dump_fw_mon *fw_mon_data;
(*data)->type = cpu_to_le32(IWL_FW_ERROR_DUMP_FW_MONITOR);
@@ -3531,14 +3535,14 @@ iwl_trans_pcie_dump_monitor(struct iwl_trans *trans,
IWL_LDBG_M2S_BUF_BA_MSK) <<
trans->dbg.dest_tlv->base_shift;
base *= IWL_M2S_UNIT_SIZE;
- base += trans->cfg->smem_offset;
+ base += trans->mac_cfg->base->smem_offset;
} else {
base = iwl_read_prph(trans, base) <<
trans->dbg.dest_tlv->base_shift;
}
- iwl_trans_read_mem(trans, base, fw_mon_data->data,
- monitor_len / sizeof(u32));
+ iwl_trans_pcie_read_mem(trans, base, fw_mon_data->data,
+ monitor_len / sizeof(u32));
} else if (trans->dbg.dest_tlv->monitor_mode == MARBH_MODE) {
monitor_len =
iwl_trans_pci_dump_marbh_monitor(trans,
@@ -3572,7 +3576,7 @@ static int iwl_trans_get_fw_monitor_len(struct iwl_trans *trans, u32 *len)
base = (cfg_reg & IWL_LDBG_M2S_BUF_BA_MSK) <<
trans->dbg.dest_tlv->base_shift;
base *= IWL_M2S_UNIT_SIZE;
- base += trans->cfg->smem_offset;
+ base += trans->mac_cfg->base->smem_offset;
monitor_len =
(cfg_reg & IWL_LDBG_M2S_BUF_SIZE_MSK) >>
@@ -3588,7 +3592,7 @@ static int iwl_trans_get_fw_monitor_len(struct iwl_trans *trans, u32 *len)
trans->dbg.dest_tlv->end_shift;
/* Make "end" point to the actual end */
- if (trans->trans_cfg->device_family >=
+ if (trans->mac_cfg->device_family >=
IWL_DEVICE_FAMILY_8000 ||
trans->dbg.dest_tlv->monitor_mode == MARBH_MODE)
end += (1 << trans->dbg.dest_tlv->end_shift);
@@ -3609,13 +3613,13 @@ iwl_trans_pcie_dump_data(struct iwl_trans *trans, u32 dump_mask,
{
struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
struct iwl_fw_error_dump_data *data;
- struct iwl_txq *cmdq = trans_pcie->txqs.txq[trans_pcie->txqs.cmd.q_id];
+ struct iwl_txq *cmdq = trans_pcie->txqs.txq[trans->conf.cmd_queue];
struct iwl_fw_error_dump_txcmd *txcmd;
struct iwl_trans_dump_data *dump_data;
u32 len, num_rbs = 0, monitor_len = 0;
int i, ptr;
bool dump_rbs = test_bit(STATUS_FW_ERROR, &trans->status) &&
- !trans->trans_cfg->mq_rx_supported &&
+ !trans->mac_cfg->mq_rx_supported &&
dump_mask & BIT(IWL_FW_ERROR_DUMP_RB);
if (!dump_mask)
@@ -3640,7 +3644,7 @@ iwl_trans_pcie_dump_data(struct iwl_trans *trans, u32 dump_mask,
/* FH registers */
if (dump_mask & BIT(IWL_FW_ERROR_DUMP_FH_REGS)) {
- if (trans->trans_cfg->gen2)
+ if (trans->mac_cfg->gen2)
len += sizeof(*data) +
(iwl_umac_prph(trans, FH_MEM_UPPER_BOUND_GEN2) -
iwl_umac_prph(trans, FH_MEM_LOWER_BOUND_GEN2));
@@ -3654,15 +3658,18 @@ iwl_trans_pcie_dump_data(struct iwl_trans *trans, u32 dump_mask,
/* Dump RBs is supported only for pre-9000 devices (1 queue) */
struct iwl_rxq *rxq = &trans_pcie->rxq[0];
/* RBs */
+ spin_lock_bh(&rxq->lock);
num_rbs = iwl_get_closed_rb_stts(trans, rxq);
num_rbs = (num_rbs - rxq->read) & RX_QUEUE_MASK;
+ spin_unlock_bh(&rxq->lock);
+
len += num_rbs * (sizeof(*data) +
sizeof(struct iwl_fw_error_dump_rb) +
(PAGE_SIZE << trans_pcie->rx_page_order));
}
/* Paged memory for gen2 HW */
- if (trans->trans_cfg->gen2 && dump_mask & BIT(IWL_FW_ERROR_DUMP_PAGING))
+ if (trans->mac_cfg->gen2 && dump_mask & BIT(IWL_FW_ERROR_DUMP_PAGING))
for (i = 0; i < trans->init_dram.paging_cnt; i++)
len += sizeof(*data) +
sizeof(struct iwl_fw_error_dump_paging) +
@@ -3687,7 +3694,7 @@ iwl_trans_pcie_dump_data(struct iwl_trans *trans, u32 dump_mask,
u8 tfdidx;
u32 caplen, cmdlen;
- if (trans->trans_cfg->gen2)
+ if (trans->mac_cfg->gen2)
tfdidx = idx;
else
tfdidx = ptr;
@@ -3727,7 +3734,7 @@ iwl_trans_pcie_dump_data(struct iwl_trans *trans, u32 dump_mask,
len += iwl_trans_pcie_dump_rbs(trans, &data, num_rbs);
/* Paged memory for gen2 HW */
- if (trans->trans_cfg->gen2 &&
+ if (trans->mac_cfg->gen2 &&
dump_mask & BIT(IWL_FW_ERROR_DUMP_PAGING)) {
for (i = 0; i < trans->init_dram.paging_cnt; i++) {
struct iwl_fw_error_dump_paging *paging;
@@ -3767,7 +3774,7 @@ void iwl_trans_pcie_sync_nmi(struct iwl_trans *trans)
if (trans_pcie->msix_enabled) {
inta_addr = CSR_MSIX_HW_INT_CAUSES_AD;
- if (trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_BZ)
+ if (trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_BZ)
sw_err_bit = MSIX_HW_INT_CAUSES_REG_SW_ERR_BZ;
else
sw_err_bit = MSIX_HW_INT_CAUSES_REG_SW_ERR;
@@ -3779,12 +3786,14 @@ void iwl_trans_pcie_sync_nmi(struct iwl_trans *trans)
iwl_trans_sync_nmi_with_addr(trans, inta_addr, sw_err_bit);
}
-struct iwl_trans *iwl_trans_pcie_alloc(struct pci_dev *pdev,
- const struct pci_device_id *ent,
- const struct iwl_cfg_trans_params *cfg_trans)
+struct iwl_trans *
+iwl_trans_pcie_alloc(struct pci_dev *pdev,
+ const struct iwl_mac_cfg *mac_cfg,
+ struct iwl_trans_info *info)
{
struct iwl_trans_pcie *trans_pcie, **priv;
struct iwl_trans *trans;
+ unsigned int bc_tbl_n_entries;
int ret, addr_size;
u32 bar0;
@@ -3801,13 +3810,16 @@ struct iwl_trans *iwl_trans_pcie_alloc(struct pci_dev *pdev,
return ERR_PTR(ret);
trans = iwl_trans_alloc(sizeof(struct iwl_trans_pcie), &pdev->dev,
- cfg_trans);
+ mac_cfg);
if (!trans)
return ERR_PTR(-ENOMEM);
trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
- if (trans->trans_cfg->gen2) {
+ /* Initialize the wait queue for commands */
+ init_waitqueue_head(&trans_pcie->wait_command_queue);
+
+ if (trans->mac_cfg->gen2) {
trans_pcie->txqs.tfd.addr_size = 64;
trans_pcie->txqs.tfd.max_tbs = IWL_TFH_NUM_TBS;
trans_pcie->txqs.tfd.size = sizeof(struct iwl_tfh_tfd);
@@ -3816,10 +3828,12 @@ struct iwl_trans *iwl_trans_pcie_alloc(struct pci_dev *pdev,
trans_pcie->txqs.tfd.max_tbs = IWL_NUM_OF_TBS;
trans_pcie->txqs.tfd.size = sizeof(struct iwl_tfd);
}
- trans->max_skb_frags = IWL_TRANS_PCIE_MAX_FRAGS(trans_pcie);
- /* Set a short watchdog for the command queue */
- trans_pcie->txqs.cmd.wdg_timeout = IWL_DEF_WD_TIMEOUT;
+ trans_pcie->supported_dma_mask = (u32)DMA_BIT_MASK(12);
+ if (trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_AX210)
+ trans_pcie->supported_dma_mask = (u32)DMA_BIT_MASK(11);
+
+ info->max_skb_frags = IWL_TRANS_PCIE_MAX_FRAGS(trans_pcie);
trans_pcie->txqs.tso_hdr_page = alloc_percpu(struct iwl_tso_hdr_page);
if (!trans_pcie->txqs.tso_hdr_page) {
@@ -3827,20 +3841,21 @@ struct iwl_trans *iwl_trans_pcie_alloc(struct pci_dev *pdev,
goto out_free_trans;
}
- if (trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_BZ)
- trans_pcie->txqs.bc_tbl_size =
- sizeof(struct iwl_gen3_bc_tbl_entry) * TFD_QUEUE_BC_SIZE_GEN3_BZ;
- else if (trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_AX210)
- trans_pcie->txqs.bc_tbl_size =
- sizeof(struct iwl_gen3_bc_tbl_entry) * TFD_QUEUE_BC_SIZE_GEN3_AX210;
+ if (trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_BZ)
+ bc_tbl_n_entries = TFD_QUEUE_BC_SIZE_BZ;
+ else if (trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_AX210)
+ bc_tbl_n_entries = TFD_QUEUE_BC_SIZE_AX210;
else
- trans_pcie->txqs.bc_tbl_size = sizeof(struct iwlagn_scd_bc_tbl);
+ bc_tbl_n_entries = TFD_QUEUE_BC_SIZE;
+
+ trans_pcie->txqs.bc_tbl_size =
+ sizeof(struct iwl_bc_tbl_entry) * bc_tbl_n_entries;
/*
* For gen2 devices, we use a single allocation for each byte-count
* table, but they're pretty small (1k) so use a DMA pool that we
* allocate here.
*/
- if (trans->trans_cfg->gen2) {
+ if (trans->mac_cfg->gen2) {
trans_pcie->txqs.bc_pool =
dmam_pool_create("iwlwifi:bc", trans->dev,
trans_pcie->txqs.bc_tbl_size,
@@ -3853,7 +3868,7 @@ struct iwl_trans *iwl_trans_pcie_alloc(struct pci_dev *pdev,
/* Some things must not change even if the config does */
WARN_ON(trans_pcie->txqs.tfd.addr_size !=
- (trans->trans_cfg->gen2 ? 64 : 36));
+ (trans->mac_cfg->gen2 ? 64 : 36));
/* Initialize NAPI here - it should be before registering to mac80211
* in the opmode but after the HW struct is allocated.
@@ -3887,7 +3902,7 @@ struct iwl_trans *iwl_trans_pcie_alloc(struct pci_dev *pdev,
trans_pcie->debug_rfkill = -1;
- if (!cfg_trans->base_params->pcie_l1_allowed) {
+ if (!mac_cfg->base->pcie_l1_allowed) {
/*
* W/A - seems to solve weird behavior. We need to remove this
* if we don't want to stay in L1 all the time. This wastes a
@@ -3931,8 +3946,8 @@ struct iwl_trans *iwl_trans_pcie_alloc(struct pci_dev *pdev,
trans_pcie->pci_dev = pdev;
iwl_disable_interrupts(trans);
- trans->hw_rev = iwl_read32(trans, CSR_HW_REV);
- if (trans->hw_rev == 0xffffffff) {
+ info->hw_rev = iwl_read32(trans, CSR_HW_REV);
+ if (info->hw_rev == 0xffffffff) {
dev_err(&pdev->dev, "HW_REV=0xFFFFFFFF, PCI issues?\n");
ret = -EIO;
goto out_no_pci;
@@ -3944,17 +3959,14 @@ struct iwl_trans *iwl_trans_pcie_alloc(struct pci_dev *pdev,
* "dash" value). To keep hw_rev backwards compatible - we'll store it
* in the old format.
*/
- if (cfg_trans->device_family >= IWL_DEVICE_FAMILY_8000)
- trans->hw_rev_step = trans->hw_rev & 0xF;
+ if (mac_cfg->device_family >= IWL_DEVICE_FAMILY_8000)
+ info->hw_rev_step = info->hw_rev & 0xF;
else
- trans->hw_rev_step = (trans->hw_rev & 0xC) >> 2;
+ info->hw_rev_step = (info->hw_rev & 0xC) >> 2;
- IWL_DEBUG_INFO(trans, "HW REV: 0x%0x\n", trans->hw_rev);
+ IWL_DEBUG_INFO(trans, "HW REV: 0x%0x\n", info->hw_rev);
- iwl_pcie_set_interrupt_capa(pdev, trans, cfg_trans);
- trans->hw_id = (pdev->device << 16) + pdev->subsystem_device;
- snprintf(trans->hw_id_str, sizeof(trans->hw_id_str),
- "PCI ID: 0x%04X:0x%04X", pdev->device, pdev->subsystem_device);
+ iwl_pcie_set_interrupt_capa(pdev, trans, mac_cfg, info);
init_waitqueue_head(&trans_pcie->sx_waitq);
@@ -3963,7 +3975,7 @@ struct iwl_trans *iwl_trans_pcie_alloc(struct pci_dev *pdev,
goto out_no_pci;
if (trans_pcie->msix_enabled) {
- ret = iwl_pcie_init_msix_handler(pdev, trans_pcie);
+ ret = iwl_pcie_init_msix_handler(pdev, trans_pcie, info);
if (ret)
goto out_no_pci;
} else {
diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/tx-gen2.c b/drivers/net/wireless/intel/iwlwifi/pcie/tx-gen2.c
index 1f483f15c238..df0545f09da9 100644
--- a/drivers/net/wireless/intel/iwlwifi/pcie/tx-gen2.c
+++ b/drivers/net/wireless/intel/iwlwifi/pcie/tx-gen2.c
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
/*
* Copyright (C) 2017 Intel Deutschland GmbH
- * Copyright (C) 2018-2020, 2023-2024 Intel Corporation
+ * Copyright (C) 2018-2020, 2023-2025 Intel Corporation
*/
#include <net/tso.h>
#include <linux/tcp.h>
@@ -18,13 +18,12 @@
static struct page *get_workaround_page(struct iwl_trans *trans,
struct sk_buff *skb)
{
- struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
struct iwl_tso_page_info *info;
struct page **page_ptr;
struct page *ret;
dma_addr_t phys;
- page_ptr = (void *)((u8 *)skb->cb + trans_pcie->txqs.page_offs);
+ page_ptr = (void *)((u8 *)skb->cb + trans->conf.cb_data_offs);
ret = alloc_page(GFP_ATOMIC);
if (!ret)
@@ -164,7 +163,7 @@ static int iwl_txq_gen2_build_amsdu(struct iwl_trans *trans,
struct iwl_device_tx_cmd *dev_cmd)
{
#ifdef CONFIG_INET
- struct iwl_tx_cmd_gen2 *tx_cmd = (void *)dev_cmd->payload;
+ struct iwl_tx_cmd_v9 *tx_cmd = (void *)dev_cmd->payload;
struct ieee80211_hdr *hdr = (void *)skb->data;
unsigned int snap_ip_tcp_hdrlen, ip_hdrlen, total_len, hdr_room;
unsigned int mss = skb_shinfo(skb)->gso_size;
@@ -188,7 +187,8 @@ static int iwl_txq_gen2_build_amsdu(struct iwl_trans *trans,
(3 + snap_ip_tcp_hdrlen + sizeof(struct ethhdr));
/* Our device supports 9 segments at most, it will fit in 1 page */
- sgt = iwl_pcie_prep_tso(trans, skb, out_meta, &start_hdr, hdr_room);
+ sgt = iwl_pcie_prep_tso(trans, skb, out_meta, &start_hdr, hdr_room,
+ snap_ip_tcp_hdrlen + hdr_len);
if (!sgt)
return -ENOMEM;
@@ -347,6 +347,7 @@ iwl_tfh_tfd *iwl_txq_gen2_build_tx_amsdu(struct iwl_trans *trans,
return tfd;
out_err:
+ iwl_pcie_free_tso_pages(trans, skb, out_meta);
iwl_txq_gen2_tfd_unmap(trans, out_meta, tfd);
return NULL;
}
@@ -489,21 +490,21 @@ struct iwl_tfh_tfd *iwl_txq_gen2_build_tfd(struct iwl_trans *trans,
bool amsdu;
/* There must be data left over for TB1 or this code must be changed */
- BUILD_BUG_ON(sizeof(struct iwl_tx_cmd_gen2) < IWL_FIRST_TB_SIZE);
+ BUILD_BUG_ON(sizeof(struct iwl_tx_cmd_v9) < IWL_FIRST_TB_SIZE);
BUILD_BUG_ON(sizeof(struct iwl_cmd_header) +
- offsetofend(struct iwl_tx_cmd_gen2, dram_info) >
+ offsetofend(struct iwl_tx_cmd_v9, dram_info) >
IWL_FIRST_TB_SIZE);
- BUILD_BUG_ON(sizeof(struct iwl_tx_cmd_gen3) < IWL_FIRST_TB_SIZE);
+ BUILD_BUG_ON(sizeof(struct iwl_tx_cmd) < IWL_FIRST_TB_SIZE);
BUILD_BUG_ON(sizeof(struct iwl_cmd_header) +
- offsetofend(struct iwl_tx_cmd_gen3, dram_info) >
+ offsetofend(struct iwl_tx_cmd, dram_info) >
IWL_FIRST_TB_SIZE);
memset(tfd, 0, sizeof(*tfd));
- if (trans->trans_cfg->device_family < IWL_DEVICE_FAMILY_AX210)
- len = sizeof(struct iwl_tx_cmd_gen2);
+ if (trans->mac_cfg->device_family < IWL_DEVICE_FAMILY_AX210)
+ len = sizeof(struct iwl_tx_cmd_v9);
else
- len = sizeof(struct iwl_tx_cmd_gen3);
+ len = sizeof(struct iwl_tx_cmd);
amsdu = ieee80211_is_data_qos(hdr->frame_control) &&
(*ieee80211_get_qos_ctl(hdr) &
@@ -534,17 +535,17 @@ int iwl_txq_space(struct iwl_trans *trans, const struct iwl_txq *q)
* If q->n_window is smaller than max_tfd_queue_size, there is no need
* to reserve any queue entries for this purpose.
*/
- if (q->n_window < trans->trans_cfg->base_params->max_tfd_queue_size)
+ if (q->n_window < trans->mac_cfg->base->max_tfd_queue_size)
max = q->n_window;
else
- max = trans->trans_cfg->base_params->max_tfd_queue_size - 1;
+ max = trans->mac_cfg->base->max_tfd_queue_size - 1;
/*
* max_tfd_queue_size is a power of 2, so the following is equivalent to
* modulo by max_tfd_queue_size and is well defined.
*/
used = (q->write_ptr - q->read_ptr) &
- (trans->trans_cfg->base_params->max_tfd_queue_size - 1);
+ (trans->mac_cfg->base->max_tfd_queue_size - 1);
if (WARN_ON(used > max))
return 0;
@@ -559,8 +560,8 @@ static void iwl_pcie_gen2_update_byte_tbl(struct iwl_trans *trans,
struct iwl_txq *txq, u16 byte_cnt,
int num_tbs)
{
- struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
int idx = iwl_txq_get_cmd_index(txq, txq->write_ptr);
+ struct iwl_bc_tbl_entry *scd_bc_tbl = txq->bc_tbl.addr;
u8 filled_tfd_size, num_fetch_chunks;
u16 len = byte_cnt;
__le16 bc_ent;
@@ -580,24 +581,16 @@ static void iwl_pcie_gen2_update_byte_tbl(struct iwl_trans *trans,
*/
num_fetch_chunks = DIV_ROUND_UP(filled_tfd_size, 64) - 1;
- if (trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_AX210) {
- struct iwl_gen3_bc_tbl_entry *scd_bc_tbl_gen3 = txq->bc_tbl.addr;
-
- /* Starting from AX210, the HW expects bytes */
- WARN_ON(trans_pcie->txqs.bc_table_dword);
+ if (trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_AX210) {
WARN_ON(len > 0x3FFF);
bc_ent = cpu_to_le16(len | (num_fetch_chunks << 14));
- scd_bc_tbl_gen3[idx].tfd_offset = bc_ent;
} else {
- struct iwlagn_scd_bc_tbl *scd_bc_tbl = txq->bc_tbl.addr;
-
- /* Before AX210, the HW expects DW */
- WARN_ON(!trans_pcie->txqs.bc_table_dword);
len = DIV_ROUND_UP(len, 4);
WARN_ON(len > 0xFFF);
bc_ent = cpu_to_le16(len | (num_fetch_chunks << 12));
- scd_bc_tbl->tfd_offset[idx] = bc_ent;
}
+
+ scd_bc_tbl[idx].tfd_offset = bc_ent;
}
static u8 iwl_txq_gen2_get_num_tbs(struct iwl_tfh_tfd *tfd)
@@ -754,7 +747,8 @@ int iwl_txq_gen2_tx(struct iwl_trans *trans, struct sk_buff *skb,
struct iwl_device_tx_cmd **dev_cmd_ptr;
dev_cmd_ptr = (void *)((u8 *)skb->cb +
- trans_pcie->txqs.dev_cmd_offs);
+ trans->conf.cb_data_offs +
+ sizeof(void *));
*dev_cmd_ptr = dev_cmd;
__skb_queue_tail(&txq->overflow_q, skb);
@@ -783,16 +777,16 @@ int iwl_txq_gen2_tx(struct iwl_trans *trans, struct sk_buff *skb,
return -1;
}
- if (trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_AX210) {
- struct iwl_tx_cmd_gen3 *tx_cmd_gen3 =
+ if (trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_AX210) {
+ struct iwl_tx_cmd *tx_cmd =
(void *)dev_cmd->payload;
- cmd_len = le16_to_cpu(tx_cmd_gen3->len);
+ cmd_len = le16_to_cpu(tx_cmd->len);
} else {
- struct iwl_tx_cmd_gen2 *tx_cmd_gen2 =
+ struct iwl_tx_cmd_v9 *tx_cmd_v9 =
(void *)dev_cmd->payload;
- cmd_len = le16_to_cpu(tx_cmd_gen2->len);
+ cmd_len = le16_to_cpu(tx_cmd_v9->len);
}
/* Set up entry for this TFD in Tx byte-count array */
@@ -830,7 +824,7 @@ static void iwl_txq_gen2_unmap(struct iwl_trans *trans, int txq_id)
IWL_DEBUG_TX_REPLY(trans, "Q %d Free %d\n",
txq_id, txq->read_ptr);
- if (txq_id != trans_pcie->txqs.cmd.q_id) {
+ if (txq_id != trans->conf.cmd_queue) {
int idx = iwl_txq_get_cmd_index(txq, txq->read_ptr);
struct iwl_cmd_meta *cmd_meta = &txq->entries[idx].meta;
struct sk_buff *skb = txq->entries[idx].skb;
@@ -904,12 +898,12 @@ static void iwl_txq_gen2_free(struct iwl_trans *trans, int txq_id)
iwl_txq_gen2_unmap(trans, txq_id);
/* De-alloc array of command/tx buffers */
- if (txq_id == trans_pcie->txqs.cmd.q_id)
+ if (txq_id == trans->conf.cmd_queue)
for (i = 0; i < txq->n_window; i++) {
kfree_sensitive(txq->entries[i].cmd);
kfree_sensitive(txq->entries[i].free_buf);
}
- del_timer_sync(&txq->stuck_timer);
+ timer_delete_sync(&txq->stuck_timer);
iwl_txq_gen2_free_memory(trans, txq);
@@ -1005,7 +999,7 @@ static int iwl_pcie_txq_alloc_response(struct iwl_trans *trans,
txq->id = qid;
trans_pcie->txqs.txq[qid] = txq;
- wr_ptr &= (trans->trans_cfg->base_params->max_tfd_queue_size - 1);
+ wr_ptr &= (trans->mac_cfg->base->max_tfd_queue_size - 1);
/* Place first TFD at index corresponding to start sequence number */
txq->read_ptr = wr_ptr;
@@ -1041,8 +1035,8 @@ int iwl_txq_dyn_alloc(struct iwl_trans *trans, u32 flags, u32 sta_mask,
/* but must be power of 2 values for calculating read/write pointers */
size = rounddown_pow_of_two(size);
- if (trans->trans_cfg->device_family == IWL_DEVICE_FAMILY_BZ &&
- trans->hw_rev_step == SILICON_A_STEP) {
+ if (trans->mac_cfg->device_family == IWL_DEVICE_FAMILY_BZ &&
+ trans->info.hw_rev_step == SILICON_A_STEP) {
size = 4096;
txq = iwl_txq_dyn_alloc_dma(trans, size, timeout);
} else {
@@ -1062,7 +1056,7 @@ int iwl_txq_dyn_alloc(struct iwl_trans *trans, u32 flags, u32 sta_mask,
if (IS_ERR(txq))
return PTR_ERR(txq);
- if (trans_pcie->txqs.queue_alloc_cmd_ver == 0) {
+ if (trans->conf.queue_alloc_cmd_ver == 0) {
memset(&cmd.old, 0, sizeof(cmd.old));
cmd.old.tfdq_addr = cpu_to_le64(txq->dma_addr);
cmd.old.byte_cnt_addr = cpu_to_le64(txq->bc_tbl.dma);
@@ -1079,7 +1073,7 @@ int iwl_txq_dyn_alloc(struct iwl_trans *trans, u32 flags, u32 sta_mask,
hcmd.id = SCD_QUEUE_CFG;
hcmd.len[0] = sizeof(cmd.old);
hcmd.data[0] = &cmd.old;
- } else if (trans_pcie->txqs.queue_alloc_cmd_ver == 3) {
+ } else if (trans->conf.queue_alloc_cmd_ver == 3) {
memset(&cmd.new, 0, sizeof(cmd.new));
cmd.new.operation = cpu_to_le32(IWL_SCD_QUEUE_ADD);
cmd.new.u.add.tfdq_dram_addr = cpu_to_le64(txq->dma_addr);
@@ -1174,7 +1168,7 @@ int iwl_txq_gen2_init(struct iwl_trans *trans, int txq_id, int queue_size)
}
ret = iwl_txq_init(trans, queue, queue_size,
- (txq_id == trans_pcie->txqs.cmd.q_id));
+ (txq_id == trans->conf.cmd_queue));
if (ret) {
IWL_ERR(trans, "Tx %d queue alloc failed\n", txq_id);
goto error;
@@ -1204,7 +1198,7 @@ int iwl_pcie_gen2_enqueue_hcmd(struct iwl_trans *trans,
struct iwl_host_cmd *cmd)
{
struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
- struct iwl_txq *txq = trans_pcie->txqs.txq[trans_pcie->txqs.cmd.q_id];
+ struct iwl_txq *txq = trans_pcie->txqs.txq[trans->conf.cmd_queue];
struct iwl_device_cmd *out_cmd;
struct iwl_cmd_meta *out_meta;
void *dup_buf = NULL;
@@ -1321,7 +1315,7 @@ int iwl_pcie_gen2_enqueue_hcmd(struct iwl_trans *trans,
cpu_to_le16(cmd_size - sizeof(struct iwl_cmd_header_wide));
out_cmd->hdr_wide.reserved = 0;
out_cmd->hdr_wide.sequence =
- cpu_to_le16(QUEUE_TO_SEQ(trans_pcie->txqs.cmd.q_id) |
+ cpu_to_le16(QUEUE_TO_SEQ(trans->conf.cmd_queue) |
INDEX_TO_SEQ(txq->write_ptr));
cmd_pos = sizeof(struct iwl_cmd_header_wide);
@@ -1369,7 +1363,7 @@ int iwl_pcie_gen2_enqueue_hcmd(struct iwl_trans *trans,
"Sending command %s (%.2x.%.2x), seq: 0x%04X, %d bytes at %d[%d]:%d\n",
iwl_get_cmd_string(trans, cmd->id), group_id,
out_cmd->hdr.cmd, le16_to_cpu(out_cmd->hdr.sequence),
- cmd_size, txq->write_ptr, idx, trans_pcie->txqs.cmd.q_id);
+ cmd_size, txq->write_ptr, idx, trans->conf.cmd_queue);
/* start the TFD with the minimum copy bytes */
tb0_size = min_t(int, copy_size, IWL_FIRST_TB_SIZE);
diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/tx.c b/drivers/net/wireless/intel/iwlwifi/pcie/tx.c
index 334ebd4c12fa..7abd7c7daa89 100644
--- a/drivers/net/wireless/intel/iwlwifi/pcie/tx.c
+++ b/drivers/net/wireless/intel/iwlwifi/pcie/tx.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
/*
- * Copyright (C) 2003-2014, 2018-2021, 2023-2024 Intel Corporation
+ * Copyright (C) 2003-2014, 2018-2021, 2023-2025 Intel Corporation
* Copyright (C) 2013-2015 Intel Mobile Communications GmbH
* Copyright (C) 2016-2017 Intel Deutschland GmbH
*/
@@ -78,7 +78,6 @@ void iwl_pcie_free_dma_ptr(struct iwl_trans *trans, struct iwl_dma_ptr *ptr)
static void iwl_pcie_txq_inc_wr_ptr(struct iwl_trans *trans,
struct iwl_txq *txq)
{
- struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
u32 reg = 0;
int txq_id = txq->id;
@@ -90,8 +89,8 @@ static void iwl_pcie_txq_inc_wr_ptr(struct iwl_trans *trans,
* 2. NIC is woken up for CMD regardless of shadow outside this function
* 3. there is a chance that the NIC is asleep
*/
- if (!trans->trans_cfg->base_params->shadow_reg_enable &&
- txq_id != trans_pcie->txqs.cmd.q_id &&
+ if (!trans->mac_cfg->base->shadow_reg_enable &&
+ txq_id != trans->conf.cmd_queue &&
test_bit(STATUS_TPOWER_PMI, &trans->status)) {
/*
* wake up nic if it's powered down ...
@@ -125,7 +124,7 @@ void iwl_pcie_txq_check_wrptrs(struct iwl_trans *trans)
struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
int i;
- for (i = 0; i < trans->trans_cfg->base_params->num_of_queues; i++) {
+ for (i = 0; i < trans->mac_cfg->base->num_of_queues; i++) {
struct iwl_txq *txq = trans_pcie->txqs.txq[i];
if (!test_bit(i, trans_pcie->txqs.queue_used))
@@ -193,7 +192,7 @@ static void iwl_pcie_clear_cmd_in_flight(struct iwl_trans *trans)
{
struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
- if (!trans->trans_cfg->base_params->apmg_wake_up_wa)
+ if (!trans->mac_cfg->base->apmg_wake_up_wa)
return;
spin_lock(&trans_pcie->reg_lock);
@@ -226,11 +225,10 @@ static void iwl_pcie_free_and_unmap_tso_page(struct iwl_trans *trans,
void iwl_pcie_free_tso_pages(struct iwl_trans *trans, struct sk_buff *skb,
struct iwl_cmd_meta *cmd_meta)
{
- struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
struct page **page_ptr;
struct page *next;
- page_ptr = (void *)((u8 *)skb->cb + trans_pcie->txqs.page_offs);
+ page_ptr = (void *)((u8 *)skb->cb + trans->conf.cb_data_offs);
next = *page_ptr;
*page_ptr = NULL;
@@ -280,10 +278,12 @@ iwl_txq_gen1_tfd_tb_get_addr(struct iwl_tfd *tfd, u8 idx)
static void iwl_txq_set_tfd_invalid_gen1(struct iwl_trans *trans,
struct iwl_tfd *tfd)
{
+ struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+
tfd->num_tbs = 0;
- iwl_pcie_gen1_tfd_set_tb(tfd, 0, trans->invalid_tx_cmd.dma,
- trans->invalid_tx_cmd.size);
+ iwl_pcie_gen1_tfd_set_tb(tfd, 0, trans_pcie->invalid_tx_cmd.dma,
+ trans_pcie->invalid_tx_cmd.size);
}
static void iwl_txq_gen1_tfd_unmap(struct iwl_trans *trans,
@@ -355,7 +355,7 @@ static void iwl_txq_free_tfd(struct iwl_trans *trans, struct iwl_txq *txq,
/* We have only q->n_window txq->entries, but we use
* TFD_QUEUE_SIZE_MAX tfds
*/
- if (trans->trans_cfg->gen2)
+ if (trans->mac_cfg->gen2)
iwl_txq_gen2_tfd_unmap(trans, &txq->entries[idx].meta,
iwl_txq_get_tfd(trans, txq, read_ptr));
else
@@ -394,7 +394,7 @@ static void iwl_pcie_txq_unmap(struct iwl_trans *trans, int txq_id)
IWL_DEBUG_TX_REPLY(trans, "Q %d Free %d\n",
txq_id, txq->read_ptr);
- if (txq_id != trans_pcie->txqs.cmd.q_id) {
+ if (txq_id != trans->conf.cmd_queue) {
struct sk_buff *skb = txq->entries[txq->read_ptr].skb;
struct iwl_cmd_meta *cmd_meta =
&txq->entries[txq->read_ptr].meta;
@@ -408,7 +408,7 @@ static void iwl_pcie_txq_unmap(struct iwl_trans *trans, int txq_id)
txq->read_ptr = iwl_txq_inc_wrap(trans, txq->read_ptr);
if (txq->read_ptr == txq->write_ptr &&
- txq_id == trans_pcie->txqs.cmd.q_id)
+ txq_id == trans->conf.cmd_queue)
iwl_pcie_clear_cmd_in_flight(trans);
}
@@ -446,7 +446,7 @@ static void iwl_pcie_txq_free(struct iwl_trans *trans, int txq_id)
iwl_pcie_txq_unmap(trans, txq_id);
/* De-alloc array of command/tx buffers */
- if (txq_id == trans_pcie->txqs.cmd.q_id)
+ if (txq_id == trans->conf.cmd_queue)
for (i = 0; i < txq->n_window; i++) {
kfree_sensitive(txq->entries[i].cmd);
kfree_sensitive(txq->entries[i].free_buf);
@@ -456,7 +456,7 @@ static void iwl_pcie_txq_free(struct iwl_trans *trans, int txq_id)
if (txq->tfds) {
dma_free_coherent(dev,
trans_pcie->txqs.tfd.size *
- trans->trans_cfg->base_params->max_tfd_queue_size,
+ trans->mac_cfg->base->max_tfd_queue_size,
txq->tfds, txq->dma_addr);
txq->dma_addr = 0;
txq->tfds = NULL;
@@ -469,16 +469,16 @@ static void iwl_pcie_txq_free(struct iwl_trans *trans, int txq_id)
kfree(txq->entries);
txq->entries = NULL;
- del_timer_sync(&txq->stuck_timer);
+ timer_delete_sync(&txq->stuck_timer);
/* 0-fill queue descriptor structure */
memset(txq, 0, sizeof(*txq));
}
-void iwl_pcie_tx_start(struct iwl_trans *trans, u32 scd_base_addr)
+void iwl_pcie_tx_start(struct iwl_trans *trans)
{
struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
- int nq = trans->trans_cfg->base_params->num_of_queues;
+ int nq = trans->mac_cfg->base->num_of_queues;
int chan;
u32 reg_val;
int clear_dwords = (SCD_TRANS_TBL_OFFSET_QUEUE(nq) -
@@ -493,13 +493,10 @@ void iwl_pcie_tx_start(struct iwl_trans *trans, u32 scd_base_addr)
trans_pcie->scd_base_addr =
iwl_read_prph(trans, SCD_SRAM_BASE_ADDR);
- WARN_ON(scd_base_addr != 0 &&
- scd_base_addr != trans_pcie->scd_base_addr);
-
/* reset context data, TX status and translation data */
- iwl_trans_write_mem(trans, trans_pcie->scd_base_addr +
- SCD_CONTEXT_MEM_LOWER_BOUND,
- NULL, clear_dwords);
+ iwl_trans_pcie_write_mem(trans, trans_pcie->scd_base_addr +
+ SCD_CONTEXT_MEM_LOWER_BOUND,
+ NULL, clear_dwords);
iwl_write_prph(trans, SCD_DRAM_BASE_ADDR,
trans_pcie->txqs.scd_bc_tbls.dma >> 10);
@@ -507,12 +504,12 @@ void iwl_pcie_tx_start(struct iwl_trans *trans, u32 scd_base_addr)
/* The chain extension of the SCD doesn't work well. This feature is
* enabled by default by the HW, so we need to disable it manually.
*/
- if (trans->trans_cfg->base_params->scd_chain_ext_wa)
+ if (trans->mac_cfg->base->scd_chain_ext_wa)
iwl_write_prph(trans, SCD_CHAINEXT_EN, 0);
- iwl_trans_ac_txq_enable(trans, trans_pcie->txqs.cmd.q_id,
- trans_pcie->txqs.cmd.fifo,
- trans_pcie->txqs.cmd.wdg_timeout);
+ iwl_trans_ac_txq_enable(trans, trans->conf.cmd_queue,
+ trans->conf.cmd_fifo,
+ IWL_DEF_WD_TIMEOUT);
/* Activate all Tx DMA/FIFO channels */
iwl_scd_activate_fifos(trans);
@@ -529,7 +526,7 @@ void iwl_pcie_tx_start(struct iwl_trans *trans, u32 scd_base_addr)
reg_val | FH_TX_CHICKEN_BITS_SCD_AUTO_RETRY_EN);
/* Enable L1-Active */
- if (trans->trans_cfg->device_family < IWL_DEVICE_FAMILY_8000)
+ if (trans->mac_cfg->device_family < IWL_DEVICE_FAMILY_8000)
iwl_clear_bits_prph(trans, APMG_PCIDEV_STT_REG,
APMG_PCIDEV_STT_VAL_L1_ACT_DIS);
}
@@ -543,13 +540,13 @@ void iwl_trans_pcie_tx_reset(struct iwl_trans *trans)
* we should never get here in gen2 trans mode return early to avoid
* having invalid accesses
*/
- if (WARN_ON_ONCE(trans->trans_cfg->gen2))
+ if (WARN_ON_ONCE(trans->mac_cfg->gen2))
return;
- for (txq_id = 0; txq_id < trans->trans_cfg->base_params->num_of_queues;
+ for (txq_id = 0; txq_id < trans->mac_cfg->base->num_of_queues;
txq_id++) {
struct iwl_txq *txq = trans_pcie->txqs.txq[txq_id];
- if (trans->trans_cfg->gen2)
+ if (trans->mac_cfg->gen2)
iwl_write_direct64(trans,
FH_MEM_CBBC_QUEUE(trans, txq_id),
txq->dma_addr);
@@ -571,7 +568,7 @@ void iwl_trans_pcie_tx_reset(struct iwl_trans *trans)
* while we were in WoWLAN in which case SCD_SRAM_BASE_ADDR will
* contain garbage.
*/
- iwl_pcie_tx_start(trans, 0);
+ iwl_pcie_tx_start(trans);
}
static void iwl_pcie_tx_stop_fh(struct iwl_trans *trans)
@@ -633,7 +630,7 @@ int iwl_pcie_tx_stop(struct iwl_trans *trans)
return 0;
/* Unmap DMA from host system and free skb's */
- for (txq_id = 0; txq_id < trans->trans_cfg->base_params->num_of_queues;
+ for (txq_id = 0; txq_id < trans->mac_cfg->base->num_of_queues;
txq_id++)
iwl_pcie_txq_unmap(trans, txq_id);
@@ -656,7 +653,7 @@ void iwl_pcie_tx_free(struct iwl_trans *trans)
/* Tx queues */
if (trans_pcie->txq_memory) {
for (txq_id = 0;
- txq_id < trans->trans_cfg->base_params->num_of_queues;
+ txq_id < trans->mac_cfg->base->num_of_queues;
txq_id++) {
iwl_pcie_txq_free(trans, txq_id);
trans_pcie->txqs.txq[txq_id] = NULL;
@@ -678,7 +675,7 @@ void iwl_txq_log_scd_error(struct iwl_trans *trans, struct iwl_txq *txq)
bool active;
u8 fifo;
- if (trans->trans_cfg->gen2) {
+ if (trans->mac_cfg->gen2) {
IWL_ERR(trans, "Queue %d is stuck %d %d\n", txq_id,
txq->read_ptr, txq->write_ptr);
/* TODO: access new SCD registers and dump them */
@@ -695,9 +692,9 @@ void iwl_txq_log_scd_error(struct iwl_trans *trans, struct iwl_txq *txq)
jiffies_to_msecs(txq->wd_timeout),
txq->read_ptr, txq->write_ptr,
iwl_read_prph(trans, SCD_QUEUE_RDPTR(txq_id)) &
- (trans->trans_cfg->base_params->max_tfd_queue_size - 1),
+ (trans->mac_cfg->base->max_tfd_queue_size - 1),
iwl_read_prph(trans, SCD_QUEUE_WRPTR(txq_id)) &
- (trans->trans_cfg->base_params->max_tfd_queue_size - 1),
+ (trans->mac_cfg->base->max_tfd_queue_size - 1),
iwl_read_direct32(trans, FH_TX_TRB_REG(fifo)));
}
@@ -723,8 +720,8 @@ int iwl_pcie_txq_alloc(struct iwl_trans *trans, struct iwl_txq *txq,
int slots_num, bool cmd_queue)
{
struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
- size_t num_entries = trans->trans_cfg->gen2 ?
- slots_num : trans->trans_cfg->base_params->max_tfd_queue_size;
+ size_t num_entries = trans->mac_cfg->gen2 ?
+ slots_num : trans->mac_cfg->base->max_tfd_queue_size;
size_t tfd_sz;
size_t tb0_buf_sz;
int i;
@@ -779,7 +776,7 @@ int iwl_pcie_txq_alloc(struct iwl_trans *trans, struct iwl_txq *txq,
for (i = 0; i < num_entries; i++) {
void *tfd = iwl_txq_get_tfd(trans, txq, i);
- if (trans->trans_cfg->gen2)
+ if (trans->mac_cfg->gen2)
iwl_txq_set_tfd_invalid_gen2(trans, tfd);
else
iwl_txq_set_tfd_invalid_gen1(trans, tfd);
@@ -799,6 +796,8 @@ error:
return -ENOMEM;
}
+#define BC_TABLE_SIZE (sizeof(struct iwl_bc_tbl_entry) * TFD_QUEUE_BC_SIZE)
+
/*
* iwl_pcie_tx_alloc - allocate TX context
* Allocate all Tx DMA structures and initialize them
@@ -808,12 +807,12 @@ static int iwl_pcie_tx_alloc(struct iwl_trans *trans)
int ret;
int txq_id, slots_num;
struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
- u16 bc_tbls_size = trans->trans_cfg->base_params->num_of_queues;
+ u16 bc_tbls_size = trans->mac_cfg->base->num_of_queues;
- if (WARN_ON(trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_AX210))
+ if (WARN_ON(trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_AX210))
return -EINVAL;
- bc_tbls_size *= sizeof(struct iwlagn_scd_bc_tbl);
+ bc_tbls_size *= BC_TABLE_SIZE;
/*It is not allowed to alloc twice, so warn when this happens.
* We cannot rely on the previous allocation, so free and fail */
@@ -837,7 +836,7 @@ static int iwl_pcie_tx_alloc(struct iwl_trans *trans)
}
trans_pcie->txq_memory =
- kcalloc(trans->trans_cfg->base_params->num_of_queues,
+ kcalloc(trans->mac_cfg->base->num_of_queues,
sizeof(struct iwl_txq), GFP_KERNEL);
if (!trans_pcie->txq_memory) {
IWL_ERR(trans, "Not enough memory for txq\n");
@@ -846,16 +845,16 @@ static int iwl_pcie_tx_alloc(struct iwl_trans *trans)
}
/* Alloc and init all Tx queues, including the command queue (#4/#9) */
- for (txq_id = 0; txq_id < trans->trans_cfg->base_params->num_of_queues;
+ for (txq_id = 0; txq_id < trans->mac_cfg->base->num_of_queues;
txq_id++) {
- bool cmd_queue = (txq_id == trans_pcie->txqs.cmd.q_id);
+ bool cmd_queue = (txq_id == trans->conf.cmd_queue);
if (cmd_queue)
slots_num = max_t(u32, IWL_CMD_QUEUE_SIZE,
- trans->cfg->min_txq_size);
+ trans->mac_cfg->base->min_txq_size);
else
slots_num = max_t(u32, IWL_DEFAULT_QUEUE_SIZE,
- trans->cfg->min_ba_txq_size);
+ trans->mac_cfg->base->min_ba_txq_size);
trans_pcie->txqs.txq[txq_id] = &trans_pcie->txq_memory[txq_id];
ret = iwl_pcie_txq_alloc(trans, trans_pcie->txqs.txq[txq_id],
slots_num, cmd_queue);
@@ -905,7 +904,7 @@ int iwl_txq_init(struct iwl_trans *trans, struct iwl_txq *txq,
int slots_num, bool cmd_queue)
{
u32 tfd_queue_max_size =
- trans->trans_cfg->base_params->max_tfd_queue_size;
+ trans->mac_cfg->base->max_tfd_queue_size;
int ret;
txq->need_update = false;
@@ -963,16 +962,16 @@ int iwl_pcie_tx_init(struct iwl_trans *trans)
spin_unlock_bh(&trans_pcie->irq_lock);
/* Alloc and init all Tx queues, including the command queue (#4/#9) */
- for (txq_id = 0; txq_id < trans->trans_cfg->base_params->num_of_queues;
+ for (txq_id = 0; txq_id < trans->mac_cfg->base->num_of_queues;
txq_id++) {
- bool cmd_queue = (txq_id == trans_pcie->txqs.cmd.q_id);
+ bool cmd_queue = (txq_id == trans->conf.cmd_queue);
if (cmd_queue)
slots_num = max_t(u32, IWL_CMD_QUEUE_SIZE,
- trans->cfg->min_txq_size);
+ trans->mac_cfg->base->min_txq_size);
else
slots_num = max_t(u32, IWL_DEFAULT_QUEUE_SIZE,
- trans->cfg->min_ba_txq_size);
+ trans->mac_cfg->base->min_ba_txq_size);
ret = iwl_txq_init(trans, trans_pcie->txqs.txq[txq_id], slots_num,
cmd_queue);
if (ret) {
@@ -991,7 +990,7 @@ int iwl_pcie_tx_init(struct iwl_trans *trans)
}
iwl_set_bits_prph(trans, SCD_GP_CTRL, SCD_GP_CTRL_AUTO_ACTIVE_MODE);
- if (trans->trans_cfg->base_params->num_of_queues > 20)
+ if (trans->mac_cfg->base->num_of_queues > 20)
iwl_set_bits_prph(trans, SCD_GP_CTRL,
SCD_GP_CTRL_ENABLE_31_QUEUES);
@@ -1012,7 +1011,7 @@ static int iwl_pcie_set_cmd_in_flight(struct iwl_trans *trans,
if (test_bit(STATUS_TRANS_DEAD, &trans->status))
return -ENODEV;
- if (!trans->trans_cfg->base_params->apmg_wake_up_wa)
+ if (!trans->mac_cfg->base->apmg_wake_up_wa)
return 0;
/*
@@ -1021,7 +1020,7 @@ static int iwl_pcie_set_cmd_in_flight(struct iwl_trans *trans,
* returned. This needs to be done only on NICs that have
* apmg_wake_up_wa set (see above.)
*/
- if (!_iwl_trans_pcie_grab_nic_access(trans))
+ if (!_iwl_trans_pcie_grab_nic_access(trans, false))
return -EIO;
/*
@@ -1054,7 +1053,7 @@ static void iwl_txq_progress(struct iwl_txq *txq)
* since we're making progress on this queue
*/
if (txq->read_ptr == txq->write_ptr)
- del_timer(&txq->stuck_timer);
+ timer_delete(&txq->stuck_timer);
else
mod_timer(&txq->stuck_timer, jiffies + txq->wd_timeout);
}
@@ -1090,12 +1089,12 @@ static void iwl_pcie_cmdq_reclaim(struct iwl_trans *trans, int txq_id, int idx)
idx = iwl_txq_get_cmd_index(txq, idx);
r = iwl_txq_get_cmd_index(txq, txq->read_ptr);
- if (idx >= trans->trans_cfg->base_params->max_tfd_queue_size ||
+ if (idx >= trans->mac_cfg->base->max_tfd_queue_size ||
(!iwl_txq_used(txq, idx, txq->read_ptr, txq->write_ptr))) {
WARN_ONCE(test_bit(txq_id, trans_pcie->txqs.queue_used),
"%s: Read index for DMA queue txq id (%d), index %d is out of range [0-%d] %d %d.\n",
__func__, txq_id, idx,
- trans->trans_cfg->base_params->max_tfd_queue_size,
+ trans->mac_cfg->base->max_tfd_queue_size,
txq->write_ptr, txq->read_ptr);
return;
}
@@ -1164,15 +1163,15 @@ bool iwl_trans_pcie_txq_enable(struct iwl_trans *trans, int txq_id, u16 ssn,
fifo = cfg->fifo;
/* Disable the scheduler prior configuring the cmd queue */
- if (txq_id == trans_pcie->txqs.cmd.q_id &&
- trans_pcie->scd_set_active)
+ if (txq_id == trans->conf.cmd_queue &&
+ trans->conf.scd_set_active)
iwl_scd_enable_set_active(trans, 0);
/* Stop this Tx queue before configuring it */
iwl_scd_txq_set_inactive(trans, txq_id);
/* Set this queue as a chain-building queue unless it is CMD */
- if (txq_id != trans_pcie->txqs.cmd.q_id)
+ if (txq_id != trans->conf.cmd_queue)
iwl_scd_txq_set_chain(trans, txq_id);
if (cfg->aggregate) {
@@ -1206,7 +1205,7 @@ bool iwl_trans_pcie_txq_enable(struct iwl_trans *trans, int txq_id, u16 ssn,
* this sad hardware issue.
* This bug has been fixed on devices 9000 and up.
*/
- scd_bug = !trans->trans_cfg->mq_rx_supported &&
+ scd_bug = !trans->mac_cfg->mq_rx_supported &&
!((ssn - txq->write_ptr) & 0x3f) &&
(ssn != txq->write_ptr);
if (scd_bug)
@@ -1242,8 +1241,8 @@ bool iwl_trans_pcie_txq_enable(struct iwl_trans *trans, int txq_id, u16 ssn,
SCD_QUEUE_STTS_REG_MSK);
/* enable the scheduler for this queue (only) */
- if (txq_id == trans_pcie->txqs.cmd.q_id &&
- trans_pcie->scd_set_active)
+ if (txq_id == trans->conf.cmd_queue &&
+ trans->conf.scd_set_active)
iwl_scd_enable_set_active(trans, BIT(txq_id));
IWL_DEBUG_TX_QUEUES(trans,
@@ -1293,8 +1292,9 @@ void iwl_trans_pcie_txq_disable(struct iwl_trans *trans, int txq_id,
if (configure_scd) {
iwl_scd_txq_set_inactive(trans, txq_id);
- iwl_trans_write_mem(trans, stts_addr, (const void *)zero_val,
- ARRAY_SIZE(zero_val));
+ iwl_trans_pcie_write_mem(trans, stts_addr,
+ (const void *)zero_val,
+ ARRAY_SIZE(zero_val));
}
iwl_pcie_txq_unmap(trans, txq_id);
@@ -1310,10 +1310,10 @@ static void iwl_trans_pcie_block_txq_ptrs(struct iwl_trans *trans, bool block)
struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
int i;
- for (i = 0; i < trans->trans_cfg->base_params->num_of_queues; i++) {
+ for (i = 0; i < trans->mac_cfg->base->num_of_queues; i++) {
struct iwl_txq *txq = trans_pcie->txqs.txq[i];
- if (i == trans_pcie->txqs.cmd.q_id)
+ if (i == trans->conf.cmd_queue)
continue;
/* we skip the command queue (obviously) so it's OK to nest */
@@ -1346,7 +1346,7 @@ int iwl_pcie_enqueue_hcmd(struct iwl_trans *trans,
struct iwl_host_cmd *cmd)
{
struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
- struct iwl_txq *txq = trans_pcie->txqs.txq[trans_pcie->txqs.cmd.q_id];
+ struct iwl_txq *txq = trans_pcie->txqs.txq[trans->conf.cmd_queue];
struct iwl_device_cmd *out_cmd;
struct iwl_cmd_meta *out_meta;
void *dup_buf = NULL;
@@ -1361,7 +1361,7 @@ int iwl_pcie_enqueue_hcmd(struct iwl_trans *trans,
u16 cmdlen[IWL_MAX_CMD_TBS_PER_TFD];
unsigned long flags;
- if (WARN(!trans->wide_cmd_header &&
+ if (WARN(!trans->conf.wide_cmd_header &&
group_id > IWL_ALWAYS_LONG_GROUP,
"unsupported wide command %#x\n", cmd->id))
return -EINVAL;
@@ -1475,7 +1475,7 @@ int iwl_pcie_enqueue_hcmd(struct iwl_trans *trans,
sizeof(struct iwl_cmd_header_wide));
out_cmd->hdr_wide.reserved = 0;
out_cmd->hdr_wide.sequence =
- cpu_to_le16(QUEUE_TO_SEQ(trans_pcie->txqs.cmd.q_id) |
+ cpu_to_le16(QUEUE_TO_SEQ(trans->conf.cmd_queue) |
INDEX_TO_SEQ(txq->write_ptr));
cmd_pos = sizeof(struct iwl_cmd_header_wide);
@@ -1483,7 +1483,7 @@ int iwl_pcie_enqueue_hcmd(struct iwl_trans *trans,
} else {
out_cmd->hdr.cmd = iwl_cmd_opcode(cmd->id);
out_cmd->hdr.sequence =
- cpu_to_le16(QUEUE_TO_SEQ(trans_pcie->txqs.cmd.q_id) |
+ cpu_to_le16(QUEUE_TO_SEQ(trans->conf.cmd_queue) |
INDEX_TO_SEQ(txq->write_ptr));
out_cmd->hdr.group_id = 0;
@@ -1534,7 +1534,7 @@ int iwl_pcie_enqueue_hcmd(struct iwl_trans *trans,
iwl_get_cmd_string(trans, cmd->id),
group_id, out_cmd->hdr.cmd,
le16_to_cpu(out_cmd->hdr.sequence),
- cmd_size, txq->write_ptr, idx, trans_pcie->txqs.cmd.q_id);
+ cmd_size, txq->write_ptr, idx, trans->conf.cmd_queue);
/* start the TFD with the minimum copy bytes */
tb0_size = min_t(int, copy_size, IWL_FIRST_TB_SIZE);
@@ -1633,14 +1633,14 @@ void iwl_pcie_hcmd_complete(struct iwl_trans *trans,
struct iwl_device_cmd *cmd;
struct iwl_cmd_meta *meta;
struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
- struct iwl_txq *txq = trans_pcie->txqs.txq[trans_pcie->txqs.cmd.q_id];
+ struct iwl_txq *txq = trans_pcie->txqs.txq[trans->conf.cmd_queue];
/* If a Tx command is being handled and it isn't in the actual
* command queue then there a command routing bug has been introduced
* in the queue management code. */
- if (WARN(txq_id != trans_pcie->txqs.cmd.q_id,
+ if (WARN(txq_id != trans->conf.cmd_queue,
"wrong command queue %d (should be %d), sequence 0x%X readp=%d writep=%d\n",
- txq_id, trans_pcie->txqs.cmd.q_id, sequence, txq->read_ptr,
+ txq_id, trans->conf.cmd_queue, sequence, txq->read_ptr,
txq->write_ptr)) {
iwl_print_hex_error(trans, pkt, 32);
return;
@@ -1654,7 +1654,7 @@ void iwl_pcie_hcmd_complete(struct iwl_trans *trans,
group_id = cmd->hdr.group_id;
cmd_id = WIDE_ID(group_id, cmd->hdr.cmd);
- if (trans->trans_cfg->gen2)
+ if (trans->mac_cfg->gen2)
iwl_txq_gen2_tfd_unmap(trans, meta,
iwl_txq_get_tfd(trans, txq, index));
else
@@ -1683,7 +1683,7 @@ void iwl_pcie_hcmd_complete(struct iwl_trans *trans,
clear_bit(STATUS_SYNC_HCMD_ACTIVE, &trans->status);
IWL_DEBUG_INFO(trans, "Clearing HCMD_ACTIVE for command %s\n",
iwl_get_cmd_string(trans, cmd_id));
- wake_up(&trans->wait_command_queue);
+ wake_up(&trans_pcie->wait_command_queue);
}
meta->flags = 0;
@@ -1753,7 +1753,7 @@ static void *iwl_pcie_get_page_hdr(struct iwl_trans *trans,
dma_addr_t phys;
void *ret;
- page_ptr = (void *)((u8 *)skb->cb + trans_pcie->txqs.page_offs);
+ page_ptr = (void *)((u8 *)skb->cb + trans->conf.cb_data_offs);
if (WARN_ON(*page_ptr))
return NULL;
@@ -1855,6 +1855,7 @@ dma_addr_t iwl_pcie_get_sgt_tb_phys(struct sg_table *sgt, unsigned int offset,
* @cmd_meta: command meta to store the scatter list information for unmapping
* @hdr: output argument for TSO headers
* @hdr_room: requested length for TSO headers
+ * @offset: offset into the data from which mapping should start
*
* Allocate space for a scatter gather list and TSO headers and map the SKB
* using the scatter gather list. The SKB is unmapped again when the page is
@@ -1864,9 +1865,12 @@ dma_addr_t iwl_pcie_get_sgt_tb_phys(struct sg_table *sgt, unsigned int offset,
*/
struct sg_table *iwl_pcie_prep_tso(struct iwl_trans *trans, struct sk_buff *skb,
struct iwl_cmd_meta *cmd_meta,
- u8 **hdr, unsigned int hdr_room)
+ u8 **hdr, unsigned int hdr_room,
+ unsigned int offset)
{
struct sg_table *sgt;
+ unsigned int n_segments = skb_shinfo(skb)->nr_frags + 1;
+ int orig_nents;
if (WARN_ON_ONCE(skb_has_frag_list(skb)))
return NULL;
@@ -1874,8 +1878,7 @@ struct sg_table *iwl_pcie_prep_tso(struct iwl_trans *trans, struct sk_buff *skb,
*hdr = iwl_pcie_get_page_hdr(trans,
hdr_room + __alignof__(struct sg_table) +
sizeof(struct sg_table) +
- (skb_shinfo(skb)->nr_frags + 1) *
- sizeof(struct scatterlist),
+ n_segments * sizeof(struct scatterlist),
skb);
if (!*hdr)
return NULL;
@@ -1883,14 +1886,15 @@ struct sg_table *iwl_pcie_prep_tso(struct iwl_trans *trans, struct sk_buff *skb,
sgt = (void *)PTR_ALIGN(*hdr + hdr_room, __alignof__(struct sg_table));
sgt->sgl = (void *)(sgt + 1);
- sg_init_table(sgt->sgl, skb_shinfo(skb)->nr_frags + 1);
+ sg_init_table(sgt->sgl, n_segments);
/* Only map the data, not the header (it is copied to the TSO page) */
- sgt->orig_nents = skb_to_sgvec(skb, sgt->sgl, skb_headlen(skb),
- skb->data_len);
- if (WARN_ON_ONCE(sgt->orig_nents <= 0))
+ orig_nents = skb_to_sgvec(skb, sgt->sgl, offset, skb->len - offset);
+ if (WARN_ON_ONCE(orig_nents <= 0))
return NULL;
+ sgt->orig_nents = orig_nents;
+
/* And map the entire SKB */
if (dma_map_sgtable(trans->dev, sgt, DMA_TO_DEVICE, 0) < 0)
return NULL;
@@ -1908,7 +1912,7 @@ static int iwl_fill_data_tbs_amsdu(struct iwl_trans *trans, struct sk_buff *skb,
u16 tb1_len)
{
struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
- struct iwl_tx_cmd *tx_cmd = (void *)dev_cmd->payload;
+ struct iwl_tx_cmd_v6 *tx_cmd = (void *)dev_cmd->payload;
struct ieee80211_hdr *hdr = (void *)skb->data;
unsigned int snap_ip_tcp_hdrlen, ip_hdrlen, total_len, hdr_room;
unsigned int mss = skb_shinfo(skb)->gso_size;
@@ -1939,7 +1943,8 @@ static int iwl_fill_data_tbs_amsdu(struct iwl_trans *trans, struct sk_buff *skb,
(3 + snap_ip_tcp_hdrlen + sizeof(struct ethhdr)) + iv_len;
/* Our device supports 9 segments at most, it will fit in 1 page */
- sgt = iwl_pcie_prep_tso(trans, skb, out_meta, &start_hdr, hdr_room);
+ sgt = iwl_pcie_prep_tso(trans, skb, out_meta, &start_hdr, hdr_room,
+ snap_ip_tcp_hdrlen + hdr_len + iv_len);
if (!sgt)
return -ENOMEM;
@@ -2062,14 +2067,14 @@ static void iwl_txq_gen1_update_byte_cnt_tbl(struct iwl_trans *trans,
int num_tbs)
{
struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
- struct iwlagn_scd_bc_tbl *scd_bc_tbl;
+ struct iwl_bc_tbl_entry *scd_bc_tbl;
int write_ptr = txq->write_ptr;
int txq_id = txq->id;
u8 sec_ctl = 0;
u16 len = byte_cnt + IWL_TX_CRC_SIZE + IWL_TX_DELIMITER_SIZE;
__le16 bc_ent;
struct iwl_device_tx_cmd *dev_cmd = txq->entries[txq->write_ptr].cmd;
- struct iwl_tx_cmd *tx_cmd = (void *)dev_cmd->payload;
+ struct iwl_tx_cmd_v6 *tx_cmd = (void *)dev_cmd->payload;
u8 sta_id = tx_cmd->sta_id;
scd_bc_tbl = trans_pcie->txqs.scd_bc_tbls.addr;
@@ -2087,7 +2092,8 @@ static void iwl_txq_gen1_update_byte_cnt_tbl(struct iwl_trans *trans,
len += IEEE80211_WEP_IV_LEN + IEEE80211_WEP_ICV_LEN;
break;
}
- if (trans_pcie->txqs.bc_table_dword)
+
+ if (trans->mac_cfg->device_family < IWL_DEVICE_FAMILY_AX210)
len = DIV_ROUND_UP(len, 4);
if (WARN_ON(len > 0xFFF || write_ptr >= TFD_QUEUE_SIZE_MAX))
@@ -2095,10 +2101,10 @@ static void iwl_txq_gen1_update_byte_cnt_tbl(struct iwl_trans *trans,
bc_ent = cpu_to_le16(len | (sta_id << 12));
- scd_bc_tbl[txq_id].tfd_offset[write_ptr] = bc_ent;
+ scd_bc_tbl[txq_id * BC_TABLE_SIZE + write_ptr].tfd_offset = bc_ent;
if (write_ptr < TFD_QUEUE_SIZE_BC_DUP)
- scd_bc_tbl[txq_id].tfd_offset[TFD_QUEUE_SIZE_MAX + write_ptr] =
+ scd_bc_tbl[txq_id * BC_TABLE_SIZE + TFD_QUEUE_SIZE_MAX + write_ptr].tfd_offset =
bc_ent;
}
@@ -2107,7 +2113,7 @@ int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb,
{
struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
struct ieee80211_hdr *hdr;
- struct iwl_tx_cmd *tx_cmd = (struct iwl_tx_cmd *)dev_cmd->payload;
+ struct iwl_tx_cmd_v6 *tx_cmd = (struct iwl_tx_cmd_v6 *)dev_cmd->payload;
struct iwl_cmd_meta *out_meta;
struct iwl_txq *txq;
dma_addr_t tb0_phys, tb1_phys, scratch_phys;
@@ -2148,7 +2154,8 @@ int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb,
struct iwl_device_tx_cmd **dev_cmd_ptr;
dev_cmd_ptr = (void *)((u8 *)skb->cb +
- trans_pcie->txqs.dev_cmd_offs);
+ trans->conf.cb_data_offs +
+ sizeof(void *));
*dev_cmd_ptr = dev_cmd;
__skb_queue_tail(&txq->overflow_q, skb);
@@ -2179,7 +2186,7 @@ int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb,
tb0_phys = iwl_txq_get_first_tb_dma(txq, txq->write_ptr);
scratch_phys = tb0_phys + sizeof(struct iwl_cmd_header) +
- offsetof(struct iwl_tx_cmd, scratch);
+ offsetof(struct iwl_tx_cmd_v6, scratch);
tx_cmd->dram_lsb_ptr = cpu_to_le32(scratch_phys);
tx_cmd->dram_msb_ptr = iwl_get_dma_hi_addr(scratch_phys);
@@ -2194,7 +2201,7 @@ int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb,
* (This calculation modifies the TX command, so do it before the
* setup of the first TB)
*/
- len = sizeof(struct iwl_tx_cmd) + sizeof(struct iwl_cmd_header) +
+ len = sizeof(struct iwl_tx_cmd_v6) + sizeof(struct iwl_cmd_header) +
hdr_len - IWL_FIRST_TB_SIZE;
/* do not align A-MSDU to dword as the subframe header aligns it */
amsdu = ieee80211_is_data_qos(fc) &&
@@ -2217,9 +2224,9 @@ int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb,
IWL_FIRST_TB_SIZE, true);
/* there must be data left over for TB1 or this code must be changed */
- BUILD_BUG_ON(sizeof(struct iwl_tx_cmd) < IWL_FIRST_TB_SIZE);
+ BUILD_BUG_ON(sizeof(struct iwl_tx_cmd_v6) < IWL_FIRST_TB_SIZE);
BUILD_BUG_ON(sizeof(struct iwl_cmd_header) +
- offsetofend(struct iwl_tx_cmd, scratch) >
+ offsetofend(struct iwl_tx_cmd_v6, scratch) >
IWL_FIRST_TB_SIZE);
/* map the data for TB1 */
@@ -2307,24 +2314,24 @@ static void iwl_txq_gen1_inval_byte_cnt_tbl(struct iwl_trans *trans,
int read_ptr)
{
struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
- struct iwlagn_scd_bc_tbl *scd_bc_tbl = trans_pcie->txqs.scd_bc_tbls.addr;
+ struct iwl_bc_tbl_entry *scd_bc_tbl = trans_pcie->txqs.scd_bc_tbls.addr;
int txq_id = txq->id;
u8 sta_id = 0;
__le16 bc_ent;
struct iwl_device_tx_cmd *dev_cmd = txq->entries[read_ptr].cmd;
- struct iwl_tx_cmd *tx_cmd = (void *)dev_cmd->payload;
+ struct iwl_tx_cmd_v6 *tx_cmd = (void *)dev_cmd->payload;
WARN_ON(read_ptr >= TFD_QUEUE_SIZE_MAX);
- if (txq_id != trans_pcie->txqs.cmd.q_id)
+ if (txq_id != trans->conf.cmd_queue)
sta_id = tx_cmd->sta_id;
bc_ent = cpu_to_le16(1 | (sta_id << 12));
- scd_bc_tbl[txq_id].tfd_offset[read_ptr] = bc_ent;
+ scd_bc_tbl[txq_id * BC_TABLE_SIZE + read_ptr].tfd_offset = bc_ent;
if (read_ptr < TFD_QUEUE_SIZE_BC_DUP)
- scd_bc_tbl[txq_id].tfd_offset[TFD_QUEUE_SIZE_MAX + read_ptr] =
+ scd_bc_tbl[txq_id * BC_TABLE_SIZE + TFD_QUEUE_SIZE_MAX + read_ptr].tfd_offset =
bc_ent;
}
@@ -2338,7 +2345,7 @@ void iwl_pcie_reclaim(struct iwl_trans *trans, int txq_id, int ssn,
int txq_read_ptr, txq_write_ptr;
/* This function is not meant to release cmd queue*/
- if (WARN_ON(txq_id == trans_pcie->txqs.cmd.q_id))
+ if (WARN_ON(txq_id == trans->conf.cmd_queue))
return;
if (WARN_ON(!txq))
@@ -2380,7 +2387,7 @@ void iwl_pcie_reclaim(struct iwl_trans *trans, int txq_id, int ssn,
IWL_ERR(trans,
"%s: Read index for txq id (%d), last_to_free %d is out of range [0-%d] %d %d.\n",
__func__, txq_id, last_to_free,
- trans->trans_cfg->base_params->max_tfd_queue_size,
+ trans->mac_cfg->base->max_tfd_queue_size,
txq_write_ptr, txq_read_ptr);
iwl_op_mode_time_point(trans->op_mode,
@@ -2409,7 +2416,7 @@ void iwl_pcie_reclaim(struct iwl_trans *trans, int txq_id, int ssn,
txq->entries[read_ptr].skb = NULL;
- if (!trans->trans_cfg->gen2)
+ if (!trans->mac_cfg->gen2)
iwl_txq_gen1_inval_byte_cnt_tbl(trans, txq,
txq_read_ptr);
@@ -2451,7 +2458,8 @@ void iwl_pcie_reclaim(struct iwl_trans *trans, int txq_id, int ssn,
struct iwl_device_tx_cmd *dev_cmd_ptr;
dev_cmd_ptr = *(void **)((u8 *)skb->cb +
- trans_pcie->txqs.dev_cmd_offs);
+ trans->conf.cb_data_offs +
+ sizeof(void *));
/*
* Note that we can very well be overflowing again.
@@ -2524,7 +2532,7 @@ void iwl_pcie_freeze_txq_timer(struct iwl_trans *trans,
/* remember how long until the timer fires */
txq->frozen_expiry_remainder =
txq->stuck_timer.expires - now;
- del_timer(&txq->stuck_timer);
+ timer_delete(&txq->stuck_timer);
goto next_queue;
}
@@ -2543,11 +2551,11 @@ next_queue:
#define HOST_COMPLETE_TIMEOUT (2 * HZ)
static int iwl_trans_pcie_send_hcmd_sync(struct iwl_trans *trans,
- struct iwl_host_cmd *cmd)
+ struct iwl_host_cmd *cmd,
+ const char *cmd_str)
{
struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
- const char *cmd_str = iwl_get_cmd_string(trans, cmd->id);
- struct iwl_txq *txq = trans_pcie->txqs.txq[trans_pcie->txqs.cmd.q_id];
+ struct iwl_txq *txq = trans_pcie->txqs.txq[trans->conf.cmd_queue];
int cmd_idx;
int ret;
@@ -2560,7 +2568,7 @@ static int iwl_trans_pcie_send_hcmd_sync(struct iwl_trans *trans,
IWL_DEBUG_INFO(trans, "Setting HCMD_ACTIVE for command %s\n", cmd_str);
- if (trans->trans_cfg->gen2)
+ if (trans->mac_cfg->gen2)
cmd_idx = iwl_pcie_gen2_enqueue_hcmd(trans, cmd);
else
cmd_idx = iwl_pcie_enqueue_hcmd(trans, cmd);
@@ -2573,7 +2581,7 @@ static int iwl_trans_pcie_send_hcmd_sync(struct iwl_trans *trans,
return ret;
}
- ret = wait_event_timeout(trans->wait_command_queue,
+ ret = wait_event_timeout(trans_pcie->wait_command_queue,
!test_bit(STATUS_SYNC_HCMD_ACTIVE,
&trans->status),
HOST_COMPLETE_TIMEOUT);
@@ -2589,7 +2597,7 @@ static int iwl_trans_pcie_send_hcmd_sync(struct iwl_trans *trans,
cmd_str);
ret = -ETIMEDOUT;
- iwl_trans_sync_nmi(trans);
+ iwl_trans_pcie_sync_nmi(trans);
goto cancel;
}
@@ -2640,6 +2648,8 @@ cancel:
int iwl_trans_pcie_send_hcmd(struct iwl_trans *trans,
struct iwl_host_cmd *cmd)
{
+ const char *cmd_str = iwl_get_cmd_string(trans, cmd->id);
+
/* Make sure the NIC is still alive in the bus */
if (test_bit(STATUS_TRANS_DEAD, &trans->status))
return -ENODEV;
@@ -2651,20 +2661,16 @@ int iwl_trans_pcie_send_hcmd(struct iwl_trans *trans,
return -ERFKILL;
}
- if (unlikely(trans->system_pm_mode == IWL_PLAT_PM_MODE_D3 &&
- !(cmd->flags & CMD_SEND_IN_D3))) {
- IWL_DEBUG_WOWLAN(trans, "Dropping CMD 0x%x: D3\n", cmd->id);
- return -EHOSTDOWN;
- }
-
if (cmd->flags & CMD_ASYNC) {
int ret;
+ IWL_DEBUG_INFO(trans, "Sending async command %s\n", cmd_str);
+
/* An asynchronous command can not expect an SKB to be set. */
if (WARN_ON(cmd->flags & CMD_WANT_SKB))
return -EINVAL;
- if (trans->trans_cfg->gen2)
+ if (trans->mac_cfg->gen2)
ret = iwl_pcie_gen2_enqueue_hcmd(trans, cmd);
else
ret = iwl_pcie_enqueue_hcmd(trans, cmd);
@@ -2678,6 +2684,5 @@ int iwl_trans_pcie_send_hcmd(struct iwl_trans *trans,
return 0;
}
- return iwl_trans_pcie_send_hcmd_sync(trans, cmd);
+ return iwl_trans_pcie_send_hcmd_sync(trans, cmd, cmd_str);
}
-IWL_EXPORT_SYMBOL(iwl_trans_pcie_send_hcmd);
diff --git a/drivers/net/wireless/intel/iwlwifi/tests/devinfo.c b/drivers/net/wireless/intel/iwlwifi/tests/devinfo.c
index d0bda23c628a..784433bb246a 100644
--- a/drivers/net/wireless/intel/iwlwifi/tests/devinfo.c
+++ b/drivers/net/wireless/intel/iwlwifi/tests/devinfo.c
@@ -2,7 +2,7 @@
/*
* KUnit tests for the iwlwifi device info table
*
- * Copyright (C) 2023-2024 Intel Corporation
+ * Copyright (C) 2023-2025 Intel Corporation
*/
#include <kunit/test.h>
#include <linux/pci.h>
@@ -13,10 +13,50 @@ MODULE_IMPORT_NS("EXPORTED_FOR_KUNIT_TESTING");
static void iwl_pci_print_dev_info(const char *pfx, const struct iwl_dev_info *di)
{
- printk(KERN_DEBUG "%sdev=%.4x,subdev=%.4x,mac_type=%.4x,mac_step=%.4x,rf_type=%.4x,cdb=%d,jacket=%d,rf_id=%.2x,no_160=%d,cores=%.2x\n",
- pfx, di->device, di->subdevice, di->mac_type, di->mac_step,
- di->rf_type, di->cdb, di->jacket, di->rf_id, di->no_160,
- di->cores);
+ u16 subdevice_mask = GENMASK(di->subdevice_m_h, di->subdevice_m_l);
+ char buf[100] = {};
+ int pos = 0;
+
+ if (di->match_rf_type)
+ pos += scnprintf(buf + pos, sizeof(buf) - pos,
+ " rf_type=%03x", di->rf_type);
+ else
+ pos += scnprintf(buf + pos, sizeof(buf) - pos,
+ " rf_type=*");
+
+ if (di->match_bw_limit)
+ pos += scnprintf(buf + pos, sizeof(buf) - pos,
+ " bw_limit=%d", di->bw_limit);
+ else
+ pos += scnprintf(buf + pos, sizeof(buf) - pos,
+ " bw_limit=*");
+
+ if (di->match_rf_step)
+ pos += scnprintf(buf + pos, sizeof(buf) - pos,
+ " rf_step=%c",
+ di->rf_step == SILICON_Z_STEP ? 'Z' :
+ 'A' + di->rf_step);
+ else
+ pos += scnprintf(buf + pos, sizeof(buf) - pos,
+ " rf_step=*");
+
+ if (di->match_rf_id)
+ pos += scnprintf(buf + pos, sizeof(buf) - pos,
+ " rf_id=0x%x", di->rf_id);
+ else
+ pos += scnprintf(buf + pos, sizeof(buf) - pos,
+ " rf_id=*");
+
+ if (di->match_cdb)
+ pos += scnprintf(buf + pos, sizeof(buf) - pos,
+ " cdb=%d", di->cdb);
+ else
+ pos += scnprintf(buf + pos, sizeof(buf) - pos,
+ " cdb=*");
+
+
+ printk(KERN_DEBUG "%sdev=%04x subdev=%04x/%04x%s\n",
+ pfx, di->device, di->subdevice, subdevice_mask, buf);
}
static void devinfo_table_order(struct kunit *test)
@@ -28,11 +68,14 @@ static void devinfo_table_order(struct kunit *test)
const struct iwl_dev_info *ret;
ret = iwl_pci_find_dev_info(di->device, di->subdevice,
- di->mac_type, di->mac_step,
di->rf_type, di->cdb,
- di->jacket, di->rf_id,
- di->no_160, di->cores, di->rf_step);
- if (ret != di) {
+ di->rf_id, di->bw_limit,
+ di->rf_step);
+ if (!ret) {
+ iwl_pci_print_dev_info("No entry found for: ", di);
+ KUNIT_FAIL(test,
+ "No entry found for entry at index %d\n", idx);
+ } else if (ret != di) {
iwl_pci_print_dev_info("searched: ", di);
iwl_pci_print_dev_info("found: ", ret);
KUNIT_FAIL(test,
@@ -42,6 +85,92 @@ static void devinfo_table_order(struct kunit *test)
}
}
+static void devinfo_names(struct kunit *test)
+{
+ int idx;
+
+ for (idx = 0; idx < iwl_dev_info_table_size; idx++) {
+ const struct iwl_dev_info *di = &iwl_dev_info_table[idx];
+
+ KUNIT_ASSERT_TRUE(test, di->name);
+ }
+}
+
+static void devinfo_no_cfg_dups(struct kunit *test)
+{
+ for (int i = 0; i < iwl_dev_info_table_size; i++) {
+ const struct iwl_rf_cfg *cfg_i = iwl_dev_info_table[i].cfg;
+
+ for (int j = 0; j < i; j++) {
+ const struct iwl_rf_cfg *cfg_j = iwl_dev_info_table[j].cfg;
+
+ if (cfg_i == cfg_j)
+ continue;
+
+ KUNIT_EXPECT_NE_MSG(test, memcmp(cfg_i, cfg_j,
+ sizeof(*cfg_i)), 0,
+ "identical configs: %ps and %ps\n",
+ cfg_i, cfg_j);
+ }
+ }
+}
+
+static void devinfo_no_name_dups(struct kunit *test)
+{
+ for (int i = 0; i < iwl_dev_info_table_size; i++) {
+ for (int j = 0; j < i; j++) {
+ if (iwl_dev_info_table[i].name == iwl_dev_info_table[j].name)
+ continue;
+
+ KUNIT_EXPECT_NE_MSG(test,
+ strcmp(iwl_dev_info_table[i].name,
+ iwl_dev_info_table[j].name),
+ 0,
+ "name dup: %ps/%ps",
+ iwl_dev_info_table[i].name,
+ iwl_dev_info_table[j].name);
+ }
+ }
+}
+
+static void devinfo_check_subdev_match(struct kunit *test)
+{
+ for (int i = 0; i < iwl_dev_info_table_size; i++) {
+ const struct iwl_dev_info *di = &iwl_dev_info_table[i];
+ u16 subdevice_mask = GENMASK(di->subdevice_m_h,
+ di->subdevice_m_l);
+
+ /* if BW limit bit is matched then must have a limit */
+ if (di->match_bw_limit == 1 && di->bw_limit == 1)
+ KUNIT_EXPECT_NE(test, di->cfg->bw_limit, 0);
+
+ /* if subdevice is ANY we can have RF ID/BW limit */
+ if (di->subdevice == (u16)IWL_CFG_ANY)
+ continue;
+
+ /* same if the subdevice mask doesn't overlap them */
+ if (IWL_SUBDEVICE_RF_ID(subdevice_mask) == 0 &&
+ IWL_SUBDEVICE_BW_LIM(subdevice_mask) == 0)
+ continue;
+
+ /* but otherwise they shouldn't be used */
+ KUNIT_EXPECT_EQ(test, (int)di->match_rf_id, 0);
+ KUNIT_EXPECT_EQ(test, (int)di->match_bw_limit, 0);
+ }
+}
+
+static void devinfo_check_killer_subdev(struct kunit *test)
+{
+ for (int i = 0; i < iwl_dev_info_table_size; i++) {
+ const struct iwl_dev_info *di = &iwl_dev_info_table[i];
+
+ if (!strstr(di->name, "Killer"))
+ continue;
+
+ KUNIT_EXPECT_NE(test, di->subdevice, (u16)IWL_CFG_ANY);
+ }
+}
+
static void devinfo_pci_ids(struct kunit *test)
{
struct pci_dev *dev;
@@ -64,9 +193,36 @@ static void devinfo_pci_ids(struct kunit *test)
}
}
+static void devinfo_no_mac_cfg_dups(struct kunit *test)
+{
+ for (int i = 0; iwl_hw_card_ids[i].vendor; i++) {
+ const struct iwl_mac_cfg *cfg_i =
+ (void *)iwl_hw_card_ids[i].driver_data;
+
+ for (int j = 0; j < i; j++) {
+ const struct iwl_mac_cfg *cfg_j =
+ (void *)iwl_hw_card_ids[j].driver_data;
+
+ if (cfg_i == cfg_j)
+ continue;
+
+ KUNIT_EXPECT_NE_MSG(test, memcmp(cfg_j, cfg_i,
+ sizeof(*cfg_i)), 0,
+ "identical configs: %ps and %ps\n",
+ cfg_i, cfg_j);
+ }
+ }
+}
+
static struct kunit_case devinfo_test_cases[] = {
KUNIT_CASE(devinfo_table_order),
+ KUNIT_CASE(devinfo_names),
+ KUNIT_CASE(devinfo_no_cfg_dups),
+ KUNIT_CASE(devinfo_no_name_dups),
+ KUNIT_CASE(devinfo_check_subdev_match),
+ KUNIT_CASE(devinfo_check_killer_subdev),
KUNIT_CASE(devinfo_pci_ids),
+ KUNIT_CASE(devinfo_no_mac_cfg_dups),
{}
};
diff --git a/drivers/net/wireless/intersil/p54/fwio.c b/drivers/net/wireless/intersil/p54/fwio.c
index 772084a9bd8d..3baf8ab01e22 100644
--- a/drivers/net/wireless/intersil/p54/fwio.c
+++ b/drivers/net/wireless/intersil/p54/fwio.c
@@ -231,6 +231,7 @@ int p54_download_eeprom(struct p54_common *priv, void *buf,
mutex_lock(&priv->eeprom_mutex);
priv->eeprom = buf;
+ priv->eeprom_slice_size = len;
eeprom_hdr = skb_put(skb, eeprom_hdr_size + len);
if (priv->fw_var < 0x509) {
@@ -253,6 +254,7 @@ int p54_download_eeprom(struct p54_common *priv, void *buf,
ret = -EBUSY;
}
priv->eeprom = NULL;
+ priv->eeprom_slice_size = 0;
mutex_unlock(&priv->eeprom_mutex);
return ret;
}
diff --git a/drivers/net/wireless/intersil/p54/p54.h b/drivers/net/wireless/intersil/p54/p54.h
index 522656de4159..aeb5e40cc5ef 100644
--- a/drivers/net/wireless/intersil/p54/p54.h
+++ b/drivers/net/wireless/intersil/p54/p54.h
@@ -258,6 +258,7 @@ struct p54_common {
/* eeprom handling */
void *eeprom;
+ size_t eeprom_slice_size;
struct completion eeprom_comp;
struct mutex eeprom_mutex;
};
diff --git a/drivers/net/wireless/intersil/p54/txrx.c b/drivers/net/wireless/intersil/p54/txrx.c
index 8414aa208655..2deb1bb54f24 100644
--- a/drivers/net/wireless/intersil/p54/txrx.c
+++ b/drivers/net/wireless/intersil/p54/txrx.c
@@ -496,14 +496,19 @@ static void p54_rx_eeprom_readback(struct p54_common *priv,
return ;
if (priv->fw_var >= 0x509) {
- memcpy(priv->eeprom, eeprom->v2.data,
- le16_to_cpu(eeprom->v2.len));
+ if (le16_to_cpu(eeprom->v2.len) != priv->eeprom_slice_size)
+ return;
+
+ memcpy(priv->eeprom, eeprom->v2.data, priv->eeprom_slice_size);
} else {
- memcpy(priv->eeprom, eeprom->v1.data,
- le16_to_cpu(eeprom->v1.len));
+ if (le16_to_cpu(eeprom->v1.len) != priv->eeprom_slice_size)
+ return;
+
+ memcpy(priv->eeprom, eeprom->v1.data, priv->eeprom_slice_size);
}
priv->eeprom = NULL;
+ priv->eeprom_slice_size = 0;
tmp = p54_find_and_unlink_skb(priv, hdr->req_id);
dev_kfree_skb_any(tmp);
complete(&priv->eeprom_comp);
diff --git a/drivers/net/wireless/marvell/libertas/cmd.c b/drivers/net/wireless/marvell/libertas/cmd.c
index 5a525da434c2..21fde876bb0d 100644
--- a/drivers/net/wireless/marvell/libertas/cmd.c
+++ b/drivers/net/wireless/marvell/libertas/cmd.c
@@ -453,46 +453,6 @@ out:
}
/**
- * lbs_get_snmp_mib - Get an SNMP MIB value
- *
- * @priv: A pointer to &struct lbs_private structure
- * @oid: The OID to retrieve from the firmware
- * @out_val: Location for the returned value
- *
- * returns: 0 on success, error on failure
- */
-int lbs_get_snmp_mib(struct lbs_private *priv, u32 oid, u16 *out_val)
-{
- struct cmd_ds_802_11_snmp_mib cmd;
- int ret;
-
- memset(&cmd, 0, sizeof (cmd));
- cmd.hdr.size = cpu_to_le16(sizeof(cmd));
- cmd.action = cpu_to_le16(CMD_ACT_GET);
- cmd.oid = cpu_to_le16(oid);
-
- ret = lbs_cmd_with_response(priv, CMD_802_11_SNMP_MIB, &cmd);
- if (ret)
- goto out;
-
- switch (le16_to_cpu(cmd.bufsize)) {
- case sizeof(u8):
- *out_val = cmd.value[0];
- break;
- case sizeof(u16):
- *out_val = le16_to_cpu(*((__le16 *)(&cmd.value)));
- break;
- default:
- lbs_deb_cmd("SNMP_CMD: (get) unhandled OID 0x%x size %d\n",
- oid, le16_to_cpu(cmd.bufsize));
- break;
- }
-
-out:
- return ret;
-}
-
-/**
* lbs_get_tx_power - Get the min, max, and current TX power
*
* @priv: A pointer to &struct lbs_private structure
@@ -525,31 +485,6 @@ int lbs_get_tx_power(struct lbs_private *priv, s16 *curlevel, s16 *minlevel,
}
/**
- * lbs_set_tx_power - Set the TX power
- *
- * @priv: A pointer to &struct lbs_private structure
- * @dbm: The desired power level in dBm
- *
- * returns: 0 on success, error on failure
- */
-int lbs_set_tx_power(struct lbs_private *priv, s16 dbm)
-{
- struct cmd_ds_802_11_rf_tx_power cmd;
- int ret;
-
- memset(&cmd, 0, sizeof(cmd));
- cmd.hdr.size = cpu_to_le16(sizeof(cmd));
- cmd.action = cpu_to_le16(CMD_ACT_SET);
- cmd.curlevel = cpu_to_le16(dbm);
-
- lbs_deb_cmd("SET_RF_TX_POWER: %d dBm\n", dbm);
-
- ret = lbs_cmd_with_response(priv, CMD_802_11_RF_TX_POWER, &cmd);
-
- return ret;
-}
-
-/**
* lbs_set_monitor_mode - Enable or disable monitor mode
* (only implemented on OLPC usb8388 FW)
*
@@ -968,10 +903,6 @@ static void lbs_submit_command(struct lbs_private *priv,
}
if (command == CMD_802_11_DEEP_SLEEP) {
- if (priv->is_auto_deep_sleep_enabled) {
- priv->wakeup_dev_required = 1;
- priv->dnld_sent = 0;
- }
priv->is_deep_sleep = 1;
lbs_complete_command(priv, cmdnode, 0);
} else {
@@ -1440,70 +1371,6 @@ void lbs_ps_confirm_sleep(struct lbs_private *priv)
}
-/**
- * lbs_set_tpc_cfg - Configures the transmission power control functionality
- *
- * @priv: A pointer to &struct lbs_private structure
- * @enable: Transmission power control enable
- * @p0: Power level when link quality is good (dBm).
- * @p1: Power level when link quality is fair (dBm).
- * @p2: Power level when link quality is poor (dBm).
- * @usesnr: Use Signal to Noise Ratio in TPC
- *
- * returns: 0 on success
- */
-int lbs_set_tpc_cfg(struct lbs_private *priv, int enable, int8_t p0, int8_t p1,
- int8_t p2, int usesnr)
-{
- struct cmd_ds_802_11_tpc_cfg cmd;
- int ret;
-
- memset(&cmd, 0, sizeof(cmd));
- cmd.hdr.size = cpu_to_le16(sizeof(cmd));
- cmd.action = cpu_to_le16(CMD_ACT_SET);
- cmd.enable = !!enable;
- cmd.usesnr = !!usesnr;
- cmd.P0 = p0;
- cmd.P1 = p1;
- cmd.P2 = p2;
-
- ret = lbs_cmd_with_response(priv, CMD_802_11_TPC_CFG, &cmd);
-
- return ret;
-}
-
-/**
- * lbs_set_power_adapt_cfg - Configures the power adaptation settings
- *
- * @priv: A pointer to &struct lbs_private structure
- * @enable: Power adaptation enable
- * @p0: Power level for 1, 2, 5.5 and 11 Mbps (dBm).
- * @p1: Power level for 6, 9, 12, 18, 22, 24 and 36 Mbps (dBm).
- * @p2: Power level for 48 and 54 Mbps (dBm).
- *
- * returns: 0 on Success
- */
-
-int lbs_set_power_adapt_cfg(struct lbs_private *priv, int enable, int8_t p0,
- int8_t p1, int8_t p2)
-{
- struct cmd_ds_802_11_pa_cfg cmd;
- int ret;
-
- memset(&cmd, 0, sizeof(cmd));
- cmd.hdr.size = cpu_to_le16(sizeof(cmd));
- cmd.action = cpu_to_le16(CMD_ACT_SET);
- cmd.enable = !!enable;
- cmd.P0 = p0;
- cmd.P1 = p1;
- cmd.P2 = p2;
-
- ret = lbs_cmd_with_response(priv, CMD_802_11_PA_CFG , &cmd);
-
- return ret;
-}
-
-
struct cmd_ctrl_node *__lbs_cmd_async(struct lbs_private *priv,
uint16_t command, struct cmd_header *in_cmd, int in_cmd_size,
int (*callback)(struct lbs_private *, unsigned long, struct cmd_header *),
@@ -1520,12 +1387,10 @@ struct cmd_ctrl_node *__lbs_cmd_async(struct lbs_private *priv,
/* No commands are allowed in Deep Sleep until we toggle the GPIO
* to wake up the card and it has signaled that it's ready.
*/
- if (!priv->is_auto_deep_sleep_enabled) {
- if (priv->is_deep_sleep) {
- lbs_deb_cmd("command not allowed in deep sleep\n");
- cmdnode = ERR_PTR(-EBUSY);
- goto done;
- }
+ if (priv->is_deep_sleep) {
+ lbs_deb_cmd("command not allowed in deep sleep\n");
+ cmdnode = ERR_PTR(-EBUSY);
+ goto done;
}
cmdnode = lbs_get_free_cmd_node(priv);
diff --git a/drivers/net/wireless/marvell/libertas/cmd.h b/drivers/net/wireless/marvell/libertas/cmd.h
index d7be232f5739..a95c2651e67f 100644
--- a/drivers/net/wireless/marvell/libertas/cmd.h
+++ b/drivers/net/wireless/marvell/libertas/cmd.h
@@ -105,19 +105,9 @@ int lbs_get_tx_power(struct lbs_private *priv, s16 *curlevel, s16 *minlevel,
int lbs_set_snmp_mib(struct lbs_private *priv, u32 oid, u16 val);
-int lbs_get_snmp_mib(struct lbs_private *priv, u32 oid, u16 *out_val);
-
/* Commands only used in wext.c, assoc. and scan.c */
-int lbs_set_power_adapt_cfg(struct lbs_private *priv, int enable, int8_t p0,
- int8_t p1, int8_t p2);
-
-int lbs_set_tpc_cfg(struct lbs_private *priv, int enable, int8_t p0, int8_t p1,
- int8_t p2, int usesnr);
-
-int lbs_set_tx_power(struct lbs_private *priv, s16 dbm);
-
int lbs_set_deep_sleep(struct lbs_private *priv, int deep_sleep);
int lbs_set_host_sleep(struct lbs_private *priv, int host_sleep);
diff --git a/drivers/net/wireless/marvell/libertas/cmdresp.c b/drivers/net/wireless/marvell/libertas/cmdresp.c
index f2aa659e7714..9742d3dba31c 100644
--- a/drivers/net/wireless/marvell/libertas/cmdresp.c
+++ b/drivers/net/wireless/marvell/libertas/cmdresp.c
@@ -119,7 +119,7 @@ int lbs_process_command_response(struct lbs_private *priv, u8 *data, u32 len)
}
/* Now we got response from FW, cancel the command timer */
- del_timer(&priv->command_timer);
+ timer_delete(&priv->command_timer);
priv->cmd_timed_out = 0;
if (respcmd == CMD_RET(CMD_802_11_PS_MODE)) {
@@ -279,7 +279,6 @@ void lbs_process_event(struct lbs_private *priv, u32 event)
priv->reset_deep_sleep_wakeup(priv);
lbs_deb_cmd("EVENT: ds awake\n");
priv->is_deep_sleep = 0;
- priv->wakeup_dev_required = 0;
wake_up_interruptible(&priv->ds_awake_q);
break;
diff --git a/drivers/net/wireless/marvell/libertas/decl.h b/drivers/net/wireless/marvell/libertas/decl.h
index c1e0388ef01d..ea69007a2958 100644
--- a/drivers/net/wireless/marvell/libertas/decl.h
+++ b/drivers/net/wireless/marvell/libertas/decl.h
@@ -64,11 +64,7 @@ int lbs_resume(struct lbs_private *priv);
void lbs_queue_event(struct lbs_private *priv, u32 event);
void lbs_notify_command_response(struct lbs_private *priv, u8 resp_idx);
-int lbs_enter_auto_deep_sleep(struct lbs_private *priv);
-int lbs_exit_auto_deep_sleep(struct lbs_private *priv);
-
u32 lbs_fw_index_to_data_rate(u8 index);
-u8 lbs_data_rate_to_fw_index(u32 rate);
int lbs_get_firmware(struct device *dev, u32 card_model,
const struct lbs_fw_table *fw_table,
diff --git a/drivers/net/wireless/marvell/libertas/dev.h b/drivers/net/wireless/marvell/libertas/dev.h
index 4b6e05a8e5d5..c4708ce4eb83 100644
--- a/drivers/net/wireless/marvell/libertas/dev.h
+++ b/drivers/net/wireless/marvell/libertas/dev.h
@@ -83,12 +83,8 @@ struct lbs_private {
/* Deep sleep */
int is_deep_sleep;
int deep_sleep_required;
- int is_auto_deep_sleep_enabled;
- int wakeup_dev_required;
int is_activity_detected;
- int auto_deep_sleep_timeout; /* in ms */
wait_queue_head_t ds_awake_q;
- struct timer_list auto_deepsleep_timer;
/* Host sleep*/
int is_host_sleep_configured;
diff --git a/drivers/net/wireless/marvell/libertas/if_usb.c b/drivers/net/wireless/marvell/libertas/if_usb.c
index 2240b4db8c03..ea3cc2eaec36 100644
--- a/drivers/net/wireless/marvell/libertas/if_usb.c
+++ b/drivers/net/wireless/marvell/libertas/if_usb.c
@@ -897,7 +897,7 @@ restart:
/* ... and wait for the process to complete */
wait_event_interruptible(cardp->fw_wq, cardp->surprise_removed || cardp->fwdnldover);
- del_timer_sync(&cardp->fw_timeout);
+ timer_delete_sync(&cardp->fw_timeout);
usb_kill_urb(cardp->rx_urb);
if (!cardp->fwdnldover) {
diff --git a/drivers/net/wireless/marvell/libertas/main.c b/drivers/net/wireless/marvell/libertas/main.c
index 78e8b5aecec0..26d13e9b3c95 100644
--- a/drivers/net/wireless/marvell/libertas/main.c
+++ b/drivers/net/wireless/marvell/libertas/main.c
@@ -79,26 +79,6 @@ u32 lbs_fw_index_to_data_rate(u8 idx)
return fw_data_rates[idx];
}
-/**
- * lbs_data_rate_to_fw_index - use rate to get the index
- *
- * @rate: data rate
- * returns: index or 0
- */
-u8 lbs_data_rate_to_fw_index(u32 rate)
-{
- u8 i;
-
- if (!rate)
- return 0;
-
- for (i = 0; i < sizeof(fw_data_rates); i++) {
- if (rate == fw_data_rates[i])
- return i;
- }
- return 0;
-}
-
int lbs_set_iface_type(struct lbs_private *priv, enum nl80211_iftype type)
{
int ret = 0;
@@ -222,7 +202,7 @@ int lbs_stop_iface(struct lbs_private *priv)
spin_unlock_irqrestore(&priv->driver_lock, flags);
cancel_work_sync(&priv->mcast_work);
- del_timer_sync(&priv->tx_lockup_timer);
+ timer_delete_sync(&priv->tx_lockup_timer);
/* Disable command processing, and wait for all commands to complete */
lbs_deb_main("waiting for commands to complete\n");
@@ -270,14 +250,13 @@ void lbs_host_to_card_done(struct lbs_private *priv)
unsigned long flags;
spin_lock_irqsave(&priv->driver_lock, flags);
- del_timer(&priv->tx_lockup_timer);
+ timer_delete(&priv->tx_lockup_timer);
priv->dnld_sent = DNLD_RES_RECEIVED;
/* Wake main thread if commands are pending */
if (!priv->cur_cmd || priv->tx_pending_len > 0) {
- if (!priv->wakeup_dev_required)
- wake_up(&priv->waitq);
+ wake_up(&priv->waitq);
}
spin_unlock_irqrestore(&priv->driver_lock, flags);
@@ -468,8 +447,7 @@ static int lbs_thread(void *data)
shouldsleep = 0; /* We have a command response */
else if (priv->cur_cmd)
shouldsleep = 1; /* Can't send a command; one already running */
- else if (!list_empty(&priv->cmdpendingq) &&
- !(priv->wakeup_dev_required))
+ else if (!list_empty(&priv->cmdpendingq))
shouldsleep = 0; /* We have a command to send */
else if (kfifo_len(&priv->event_fifo))
shouldsleep = 0; /* We have an event to process */
@@ -536,14 +514,6 @@ static int lbs_thread(void *data)
}
spin_unlock_irq(&priv->driver_lock);
- if (priv->wakeup_dev_required) {
- lbs_deb_thread("Waking up device...\n");
- /* Wake up device */
- if (priv->exit_deep_sleep(priv))
- lbs_deb_thread("Wakeup device failed\n");
- continue;
- }
-
/* command timeout stuff */
if (priv->cmd_timed_out && priv->cur_cmd) {
struct cmd_ctrl_node *cmdnode = priv->cur_cmd;
@@ -624,9 +594,8 @@ static int lbs_thread(void *data)
spin_unlock_irq(&priv->driver_lock);
}
- del_timer(&priv->command_timer);
- del_timer(&priv->tx_lockup_timer);
- del_timer(&priv->auto_deepsleep_timer);
+ timer_delete(&priv->command_timer);
+ timer_delete(&priv->tx_lockup_timer);
return 0;
}
@@ -773,55 +742,6 @@ static void lbs_tx_lockup_handler(struct timer_list *t)
spin_unlock_irqrestore(&priv->driver_lock, flags);
}
-/**
- * auto_deepsleep_timer_fn - put the device back to deep sleep mode when
- * timer expires and no activity (command, event, data etc.) is detected.
- * @t: Context from which to retrieve a &struct lbs_private pointer
- * returns: N/A
- */
-static void auto_deepsleep_timer_fn(struct timer_list *t)
-{
- struct lbs_private *priv = from_timer(priv, t, auto_deepsleep_timer);
-
- if (priv->is_activity_detected) {
- priv->is_activity_detected = 0;
- } else {
- if (priv->is_auto_deep_sleep_enabled &&
- (!priv->wakeup_dev_required) &&
- (priv->connect_status != LBS_CONNECTED)) {
- struct cmd_header cmd;
-
- lbs_deb_main("Entering auto deep sleep mode...\n");
- memset(&cmd, 0, sizeof(cmd));
- cmd.size = cpu_to_le16(sizeof(cmd));
- lbs_cmd_async(priv, CMD_802_11_DEEP_SLEEP, &cmd,
- sizeof(cmd));
- }
- }
- mod_timer(&priv->auto_deepsleep_timer , jiffies +
- (priv->auto_deep_sleep_timeout * HZ)/1000);
-}
-
-int lbs_enter_auto_deep_sleep(struct lbs_private *priv)
-{
- priv->is_auto_deep_sleep_enabled = 1;
- if (priv->is_deep_sleep)
- priv->wakeup_dev_required = 1;
- mod_timer(&priv->auto_deepsleep_timer ,
- jiffies + (priv->auto_deep_sleep_timeout * HZ)/1000);
-
- return 0;
-}
-
-int lbs_exit_auto_deep_sleep(struct lbs_private *priv)
-{
- priv->is_auto_deep_sleep_enabled = 0;
- priv->auto_deep_sleep_timeout = 0;
- del_timer(&priv->auto_deepsleep_timer);
-
- return 0;
-}
-
static int lbs_init_adapter(struct lbs_private *priv)
{
int ret;
@@ -835,9 +755,7 @@ static int lbs_init_adapter(struct lbs_private *priv)
priv->psmode = LBS802_11POWERMODECAM;
priv->psstate = PS_STATE_FULL_POWER;
priv->is_deep_sleep = 0;
- priv->is_auto_deep_sleep_enabled = 0;
priv->deep_sleep_required = 0;
- priv->wakeup_dev_required = 0;
init_waitqueue_head(&priv->ds_awake_q);
init_waitqueue_head(&priv->scan_q);
priv->authtype_auto = 1;
@@ -849,7 +767,6 @@ static int lbs_init_adapter(struct lbs_private *priv)
timer_setup(&priv->command_timer, lbs_cmd_timeout_handler, 0);
timer_setup(&priv->tx_lockup_timer, lbs_tx_lockup_handler, 0);
- timer_setup(&priv->auto_deepsleep_timer, auto_deepsleep_timer_fn, 0);
INIT_LIST_HEAD(&priv->cmdfreeq);
INIT_LIST_HEAD(&priv->cmdpendingq);
@@ -881,9 +798,8 @@ static void lbs_free_adapter(struct lbs_private *priv)
{
lbs_free_cmd_buffer(priv);
kfifo_free(&priv->event_fifo);
- del_timer(&priv->command_timer);
- del_timer(&priv->tx_lockup_timer);
- del_timer(&priv->auto_deepsleep_timer);
+ timer_delete(&priv->command_timer);
+ timer_delete(&priv->tx_lockup_timer);
}
static const struct net_device_ops lbs_netdev_ops = {
diff --git a/drivers/net/wireless/marvell/libertas_tf/cmd.c b/drivers/net/wireless/marvell/libertas_tf/cmd.c
index efb98304555a..7fc1bdb6c458 100644
--- a/drivers/net/wireless/marvell/libertas_tf/cmd.c
+++ b/drivers/net/wireless/marvell/libertas_tf/cmd.c
@@ -757,7 +757,7 @@ int lbtf_process_rx_command(struct lbtf_private *priv)
}
/* Now we got response from FW, cancel the command timer */
- del_timer(&priv->command_timer);
+ timer_delete(&priv->command_timer);
priv->cmd_timed_out = 0;
if (priv->nr_retries)
priv->nr_retries = 0;
diff --git a/drivers/net/wireless/marvell/libertas_tf/if_usb.c b/drivers/net/wireless/marvell/libertas_tf/if_usb.c
index 1750f5e93de2..7c413dc81f9a 100644
--- a/drivers/net/wireless/marvell/libertas_tf/if_usb.c
+++ b/drivers/net/wireless/marvell/libertas_tf/if_usb.c
@@ -875,7 +875,7 @@ restart:
wait_event_interruptible(cardp->fw_wq, cardp->priv->surpriseremoved ||
cardp->fwdnldover);
- del_timer_sync(&cardp->fw_timeout);
+ timer_delete_sync(&cardp->fw_timeout);
usb_kill_urb(cardp->rx_urb);
if (!cardp->fwdnldover) {
diff --git a/drivers/net/wireless/marvell/libertas_tf/main.c b/drivers/net/wireless/marvell/libertas_tf/main.c
index b47a832b9ae2..a57a11be57d8 100644
--- a/drivers/net/wireless/marvell/libertas_tf/main.c
+++ b/drivers/net/wireless/marvell/libertas_tf/main.c
@@ -174,7 +174,7 @@ static void lbtf_free_adapter(struct lbtf_private *priv)
{
lbtf_deb_enter(LBTF_DEB_MAIN);
lbtf_free_cmd_buffer(priv);
- del_timer(&priv->command_timer);
+ timer_delete(&priv->command_timer);
lbtf_deb_leave(LBTF_DEB_MAIN);
}
@@ -642,7 +642,7 @@ int lbtf_remove_card(struct lbtf_private *priv)
lbtf_deb_enter(LBTF_DEB_MAIN);
priv->surpriseremoved = 1;
- del_timer(&priv->command_timer);
+ timer_delete(&priv->command_timer);
lbtf_free_adapter(priv);
priv->hw = NULL;
ieee80211_unregister_hw(hw);
diff --git a/drivers/net/wireless/marvell/mwifiex/11n.c b/drivers/net/wireless/marvell/mwifiex/11n.c
index 66f0f5377ac1..738bafc3749b 100644
--- a/drivers/net/wireless/marvell/mwifiex/11n.c
+++ b/drivers/net/wireless/marvell/mwifiex/11n.c
@@ -403,12 +403,14 @@ mwifiex_cmd_append_11n_tlv(struct mwifiex_private *priv,
if (sband->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40 &&
bss_desc->bcn_ht_oper->ht_param &
- IEEE80211_HT_PARAM_CHAN_WIDTH_ANY)
+ IEEE80211_HT_PARAM_CHAN_WIDTH_ANY) {
+ chan_list->chan_scan_param[0].radio_type |=
+ CHAN_BW_40MHZ << 2;
SET_SECONDARYCHAN(chan_list->chan_scan_param[0].
radio_type,
(bss_desc->bcn_ht_oper->ht_param &
IEEE80211_HT_PARAM_CHA_SEC_OFFSET));
-
+ }
*buffer += struct_size(chan_list, chan_scan_param, 1);
ret_len += struct_size(chan_list, chan_scan_param, 1);
}
diff --git a/drivers/net/wireless/marvell/mwifiex/11n_rxreorder.c b/drivers/net/wireless/marvell/mwifiex/11n_rxreorder.c
index cb948ca34373..8aff1df09b40 100644
--- a/drivers/net/wireless/marvell/mwifiex/11n_rxreorder.c
+++ b/drivers/net/wireless/marvell/mwifiex/11n_rxreorder.c
@@ -206,7 +206,7 @@ mwifiex_del_rx_reorder_entry(struct mwifiex_private *priv,
start_win = (tbl->start_win + tbl->win_size) & (MAX_TID_VALUE - 1);
mwifiex_11n_dispatch_pkt_until_start_win(priv, tbl, start_win);
- del_timer_sync(&tbl->timer_context.timer);
+ timer_delete_sync(&tbl->timer_context.timer);
tbl->timer_context.timer_is_set = false;
spin_lock_bh(&priv->rx_reorder_tbl_lock);
diff --git a/drivers/net/wireless/marvell/mwifiex/cfg80211.c b/drivers/net/wireless/marvell/mwifiex/cfg80211.c
index a099fdaafa45..60c12328c2f3 100644
--- a/drivers/net/wireless/marvell/mwifiex/cfg80211.c
+++ b/drivers/net/wireless/marvell/mwifiex/cfg80211.c
@@ -1126,7 +1126,7 @@ mwifiex_change_vif_to_p2p(struct net_device *dev,
HostCmd_ACT_GEN_SET, 0, NULL, true))
return -1;
- if (mwifiex_sta_init_cmd(priv, false, false))
+ if (mwifiex_sta_init_cmd(priv, false))
return -1;
return 0;
@@ -1167,7 +1167,7 @@ mwifiex_change_vif_to_sta_adhoc(struct net_device *dev,
if (mwifiex_send_cmd(priv, HostCmd_CMD_SET_BSS_MODE,
HostCmd_ACT_GEN_SET, 0, NULL, true))
return -1;
- if (mwifiex_sta_init_cmd(priv, false, false))
+ if (mwifiex_sta_init_cmd(priv, false))
return -1;
return 0;
@@ -1204,7 +1204,7 @@ mwifiex_change_vif_to_ap(struct net_device *dev,
if (mwifiex_send_cmd(priv, HostCmd_CMD_SET_BSS_MODE,
HostCmd_ACT_GEN_SET, 0, NULL, true))
return -1;
- if (mwifiex_sta_init_cmd(priv, false, false))
+ if (mwifiex_sta_init_cmd(priv, false))
return -1;
return 0;
@@ -2906,16 +2906,12 @@ mwifiex_setup_ht_caps(struct ieee80211_sta_ht_cap *ht_info,
struct mwifiex_private *priv)
{
int rx_mcs_supp;
- struct ieee80211_mcs_info mcs_set;
- u8 *mcs = (u8 *)&mcs_set;
struct mwifiex_adapter *adapter = priv->adapter;
ht_info->ht_supported = true;
ht_info->ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K;
ht_info->ampdu_density = IEEE80211_HT_MPDU_DENSITY_NONE;
- memset(&ht_info->mcs, 0, sizeof(ht_info->mcs));
-
/* Fill HT capability information */
if (ISSUPP_CHANWIDTH40(adapter->hw_dot_11n_dev_cap))
ht_info->cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40;
@@ -2961,17 +2957,15 @@ mwifiex_setup_ht_caps(struct ieee80211_sta_ht_cap *ht_info,
ht_info->cap |= IEEE80211_HT_CAP_SM_PS;
rx_mcs_supp = GET_RXMCSSUPP(adapter->user_dev_mcs_support);
+
+ memset(&ht_info->mcs, 0, sizeof(ht_info->mcs));
/* Set MCS for 1x1/2x2 */
- memset(mcs, 0xff, rx_mcs_supp);
- /* Clear all the other values */
- memset(&mcs[rx_mcs_supp], 0,
- sizeof(struct ieee80211_mcs_info) - rx_mcs_supp);
+ memset(ht_info->mcs.rx_mask, 0xff, rx_mcs_supp);
+
if (priv->bss_mode == NL80211_IFTYPE_STATION ||
ISSUPP_CHANWIDTH40(adapter->hw_dot_11n_dev_cap))
/* Set MCS32 for infra mode or ad-hoc mode with 40MHz support */
- SETHT_MCS32(mcs_set.rx_mask);
-
- memcpy((u8 *) &ht_info->mcs, mcs, sizeof(struct ieee80211_mcs_info));
+ SETHT_MCS32(ht_info->mcs.rx_mask);
ht_info->mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED;
}
@@ -3013,7 +3007,6 @@ struct wireless_dev *mwifiex_add_virtual_intf(struct wiphy *wiphy,
return ERR_PTR(-EFAULT);
}
- priv->wdev.wiphy = wiphy;
priv->wdev.iftype = NL80211_IFTYPE_STATION;
if (type == NL80211_IFTYPE_UNSPECIFIED)
@@ -3022,8 +3015,6 @@ struct wireless_dev *mwifiex_add_virtual_intf(struct wiphy *wiphy,
priv->bss_mode = type;
priv->bss_type = MWIFIEX_BSS_TYPE_STA;
- priv->frame_type = MWIFIEX_DATA_FRAME_TYPE_ETH_II;
- priv->bss_priority = 0;
priv->bss_role = MWIFIEX_BSS_ROLE_STA;
break;
@@ -3043,14 +3034,10 @@ struct wireless_dev *mwifiex_add_virtual_intf(struct wiphy *wiphy,
return ERR_PTR(-EFAULT);
}
- priv->wdev.wiphy = wiphy;
priv->wdev.iftype = NL80211_IFTYPE_AP;
priv->bss_type = MWIFIEX_BSS_TYPE_UAP;
- priv->frame_type = MWIFIEX_DATA_FRAME_TYPE_ETH_II;
- priv->bss_priority = 0;
priv->bss_role = MWIFIEX_BSS_ROLE_UAP;
- priv->bss_started = 0;
priv->bss_mode = type;
break;
@@ -3070,7 +3057,6 @@ struct wireless_dev *mwifiex_add_virtual_intf(struct wiphy *wiphy,
return ERR_PTR(-EFAULT);
}
- priv->wdev.wiphy = wiphy;
/* At start-up, wpa_supplicant tries to change the interface
* to NL80211_IFTYPE_STATION if it is not managed mode.
*/
@@ -3083,10 +3069,7 @@ struct wireless_dev *mwifiex_add_virtual_intf(struct wiphy *wiphy,
*/
priv->bss_type = MWIFIEX_BSS_TYPE_P2P;
- priv->frame_type = MWIFIEX_DATA_FRAME_TYPE_ETH_II;
- priv->bss_priority = 0;
priv->bss_role = MWIFIEX_BSS_ROLE_STA;
- priv->bss_started = 0;
if (mwifiex_cfg80211_init_p2p_client(priv)) {
memset(&priv->wdev, 0, sizeof(priv->wdev));
@@ -3100,6 +3083,11 @@ struct wireless_dev *mwifiex_add_virtual_intf(struct wiphy *wiphy,
return ERR_PTR(-EINVAL);
}
+ priv->wdev.wiphy = wiphy;
+ priv->bss_priority = 0;
+ priv->bss_started = 0;
+ priv->frame_type = MWIFIEX_DATA_FRAME_TYPE_ETH_II;
+
dev = alloc_netdev_mqs(sizeof(struct mwifiex_private *), name,
name_assign_type, ether_setup,
IEEE80211_NUM_ACS, 1);
@@ -3122,7 +3110,7 @@ struct wireless_dev *mwifiex_add_virtual_intf(struct wiphy *wiphy,
if (ret)
goto err_set_bss_mode;
- ret = mwifiex_sta_init_cmd(priv, false, false);
+ ret = mwifiex_sta_init_cmd(priv, false);
if (ret)
goto err_sta_init;
}
@@ -4703,7 +4691,7 @@ int mwifiex_register_cfg80211(struct mwifiex_adapter *adapter)
void *wdev_priv;
struct wiphy *wiphy;
struct mwifiex_private *priv = adapter->priv[MWIFIEX_BSS_TYPE_STA];
- u8 *country_code;
+ const u8 *country_code;
u32 thr, retry;
struct cfg80211_ops *ops;
diff --git a/drivers/net/wireless/marvell/mwifiex/cfp.c b/drivers/net/wireless/marvell/mwifiex/cfp.c
index d39092b99212..686bf12b6c26 100644
--- a/drivers/net/wireless/marvell/mwifiex/cfp.c
+++ b/drivers/net/wireless/marvell/mwifiex/cfp.c
@@ -150,10 +150,10 @@ static const u16 ac_mcs_rate_nss2[8][10] = {
struct region_code_mapping {
u8 code;
- u8 region[IEEE80211_COUNTRY_STRING_LEN];
+ u8 region[IEEE80211_COUNTRY_STRING_LEN] __nonstring;
};
-static struct region_code_mapping region_code_mapping_t[] = {
+static const struct region_code_mapping region_code_mapping_t[] = {
{ 0x10, "US " }, /* US FCC */
{ 0x20, "CA " }, /* IC Canada */
{ 0x30, "FR " }, /* France */
@@ -165,7 +165,7 @@ static struct region_code_mapping region_code_mapping_t[] = {
};
/* This function converts integer code to region string */
-u8 *mwifiex_11d_code_2_region(u8 code)
+const u8 *mwifiex_11d_code_2_region(u8 code)
{
u8 i;
diff --git a/drivers/net/wireless/marvell/mwifiex/cmdevt.c b/drivers/net/wireless/marvell/mwifiex/cmdevt.c
index b30ed321c625..3bf27efe4537 100644
--- a/drivers/net/wireless/marvell/mwifiex/cmdevt.c
+++ b/drivers/net/wireless/marvell/mwifiex/cmdevt.c
@@ -159,11 +159,9 @@ static int mwifiex_cmd_host_cmd(struct mwifiex_private *priv,
* sending. Afterwards, it logs the command ID and action for debugging
* and sets up the command timeout timer.
*/
-static int mwifiex_dnld_cmd_to_fw(struct mwifiex_private *priv,
+static int mwifiex_dnld_cmd_to_fw(struct mwifiex_adapter *adapter,
struct cmd_ctrl_node *cmd_node)
{
-
- struct mwifiex_adapter *adapter = priv->adapter;
int ret;
struct host_cmd_ds_command *host_cmd;
uint16_t cmd_code;
@@ -367,8 +365,7 @@ static int mwifiex_dnld_sleep_confirm_cmd(struct mwifiex_adapter *adapter)
(test_bit(MWIFIEX_IS_HS_CONFIGURED, &adapter->work_flags) &&
!adapter->sleep_period.period)) {
adapter->pm_wakeup_card_req = true;
- mwifiex_hs_activated_event(mwifiex_get_priv
- (adapter, MWIFIEX_BSS_ROLE_ANY), true);
+ mwifiex_hs_activated_event(adapter, true);
}
return ret;
@@ -473,8 +470,7 @@ void mwifiex_free_cmd_buffer(struct mwifiex_adapter *adapter)
int mwifiex_process_event(struct mwifiex_adapter *adapter)
{
int ret, i;
- struct mwifiex_private *priv =
- mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY);
+ struct mwifiex_private *priv;
struct sk_buff *skb = adapter->event_skb;
u32 eventcause;
struct mwifiex_rxinfo *rx_info;
@@ -744,7 +740,6 @@ mwifiex_insert_cmd_to_pending_q(struct mwifiex_adapter *adapter,
*/
int mwifiex_exec_next_cmd(struct mwifiex_adapter *adapter)
{
- struct mwifiex_private *priv;
struct cmd_ctrl_node *cmd_node;
int ret = 0;
struct host_cmd_ds_command *host_cmd;
@@ -768,7 +763,6 @@ int mwifiex_exec_next_cmd(struct mwifiex_adapter *adapter)
struct cmd_ctrl_node, list);
host_cmd = (struct host_cmd_ds_command *) (cmd_node->cmd_skb->data);
- priv = cmd_node->priv;
if (adapter->ps_state != PS_STATE_AWAKE) {
mwifiex_dbg(adapter, ERROR,
@@ -783,18 +777,17 @@ int mwifiex_exec_next_cmd(struct mwifiex_adapter *adapter)
spin_unlock_bh(&adapter->cmd_pending_q_lock);
spin_unlock_bh(&adapter->mwifiex_cmd_lock);
- ret = mwifiex_dnld_cmd_to_fw(priv, cmd_node);
- priv = mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY);
+ ret = mwifiex_dnld_cmd_to_fw(adapter, cmd_node);
+
/* Any command sent to the firmware when host is in sleep
* mode should de-configure host sleep. We should skip the
* host sleep configuration command itself though
*/
- if (priv && (host_cmd->command !=
- cpu_to_le16(HostCmd_CMD_802_11_HS_CFG_ENH))) {
+ if (host_cmd->command != cpu_to_le16(HostCmd_CMD_802_11_HS_CFG_ENH)) {
if (adapter->hs_activated) {
clear_bit(MWIFIEX_IS_HS_CONFIGURED,
&adapter->work_flags);
- mwifiex_hs_activated_event(priv, false);
+ mwifiex_hs_activated_event(adapter, false);
}
}
@@ -810,8 +803,7 @@ int mwifiex_exec_next_cmd(struct mwifiex_adapter *adapter)
int mwifiex_process_cmdresp(struct mwifiex_adapter *adapter)
{
struct host_cmd_ds_command *resp;
- struct mwifiex_private *priv =
- mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY);
+ struct mwifiex_private *priv;
int ret = 0;
uint16_t orig_cmdresp_no;
uint16_t cmdresp_no;
@@ -836,7 +828,7 @@ int mwifiex_process_cmdresp(struct mwifiex_adapter *adapter)
return -1;
}
/* Now we got response from FW, cancel the command timer */
- del_timer_sync(&adapter->cmd_timer);
+ timer_delete_sync(&adapter->cmd_timer);
clear_bit(MWIFIEX_IS_CMD_TIMEDOUT, &adapter->work_flags);
if (adapter->curr_cmd->cmd_flag & CMD_F_HOSTCMD) {
@@ -900,18 +892,6 @@ int mwifiex_process_cmdresp(struct mwifiex_adapter *adapter)
ret = mwifiex_process_sta_cmdresp(priv, cmdresp_no, resp);
}
- /* Check init command response */
- if (adapter->hw_status == MWIFIEX_HW_STATUS_INITIALIZING) {
- if (ret) {
- mwifiex_dbg(adapter, ERROR,
- "%s: cmd %#x failed during\t"
- "initialization\n", __func__, cmdresp_no);
- mwifiex_init_fw_complete(adapter);
- return -1;
- } else if (adapter->last_init_cmd == cmdresp_no)
- adapter->hw_status = MWIFIEX_HW_STATUS_INIT_DONE;
- }
-
if (adapter->curr_cmd) {
if (adapter->curr_cmd->wait_q_enabled)
adapter->cmd_wait_q.status = ret;
@@ -1030,10 +1010,6 @@ mwifiex_cmd_timeout_func(struct timer_list *t)
mwifiex_cancel_pending_ioctl(adapter);
}
}
- if (adapter->hw_status == MWIFIEX_HW_STATUS_INITIALIZING) {
- mwifiex_init_fw_complete(adapter);
- return;
- }
if (adapter->if_ops.device_dump)
adapter->if_ops.device_dump(adapter);
@@ -1160,27 +1136,27 @@ mwifiex_check_ps_cond(struct mwifiex_adapter *adapter)
* This event is generated by the driver, with a blank event body.
*/
void
-mwifiex_hs_activated_event(struct mwifiex_private *priv, u8 activated)
+mwifiex_hs_activated_event(struct mwifiex_adapter *adapter, u8 activated)
{
if (activated) {
if (test_bit(MWIFIEX_IS_HS_CONFIGURED,
- &priv->adapter->work_flags)) {
- priv->adapter->hs_activated = true;
- mwifiex_update_rxreor_flags(priv->adapter,
+ &adapter->work_flags)) {
+ adapter->hs_activated = true;
+ mwifiex_update_rxreor_flags(adapter,
RXREOR_FORCE_NO_DROP);
- mwifiex_dbg(priv->adapter, EVENT,
+ mwifiex_dbg(adapter, EVENT,
"event: hs_activated\n");
- priv->adapter->hs_activate_wait_q_woken = true;
+ adapter->hs_activate_wait_q_woken = true;
wake_up_interruptible(
- &priv->adapter->hs_activate_wait_q);
+ &adapter->hs_activate_wait_q);
} else {
- mwifiex_dbg(priv->adapter, EVENT,
+ mwifiex_dbg(adapter, EVENT,
"event: HS not configured\n");
}
} else {
- mwifiex_dbg(priv->adapter, EVENT,
+ mwifiex_dbg(adapter, EVENT,
"event: hs_deactivated\n");
- priv->adapter->hs_activated = false;
+ adapter->hs_activated = false;
}
}
@@ -1204,7 +1180,7 @@ int mwifiex_ret_802_11_hs_cfg(struct mwifiex_private *priv,
if (phs_cfg->action == cpu_to_le16(HS_ACTIVATE) &&
adapter->iface_type != MWIFIEX_USB) {
- mwifiex_hs_activated_event(priv, true);
+ mwifiex_hs_activated_event(adapter, true);
return 0;
} else {
mwifiex_dbg(adapter, CMD,
@@ -1217,11 +1193,11 @@ int mwifiex_ret_802_11_hs_cfg(struct mwifiex_private *priv,
if (conditions != HS_CFG_CANCEL) {
set_bit(MWIFIEX_IS_HS_CONFIGURED, &adapter->work_flags);
if (adapter->iface_type == MWIFIEX_USB)
- mwifiex_hs_activated_event(priv, true);
+ mwifiex_hs_activated_event(adapter, true);
} else {
clear_bit(MWIFIEX_IS_HS_CONFIGURED, &adapter->work_flags);
if (adapter->hs_activated)
- mwifiex_hs_activated_event(priv, false);
+ mwifiex_hs_activated_event(adapter, false);
}
return 0;
@@ -1250,9 +1226,7 @@ mwifiex_process_hs_config(struct mwifiex_adapter *adapter)
adapter->hs_activated = false;
clear_bit(MWIFIEX_IS_HS_CONFIGURED, &adapter->work_flags);
clear_bit(MWIFIEX_IS_SUSPENDED, &adapter->work_flags);
- mwifiex_hs_activated_event(mwifiex_get_priv(adapter,
- MWIFIEX_BSS_ROLE_ANY),
- false);
+ mwifiex_hs_activated_event(adapter, false);
}
EXPORT_SYMBOL_GPL(mwifiex_process_hs_config);
@@ -1302,9 +1276,7 @@ mwifiex_process_sleep_confirm_resp(struct mwifiex_adapter *adapter,
}
adapter->pm_wakeup_card_req = true;
if (test_bit(MWIFIEX_IS_HS_CONFIGURED, &adapter->work_flags))
- mwifiex_hs_activated_event(mwifiex_get_priv
- (adapter, MWIFIEX_BSS_ROLE_ANY),
- true);
+ mwifiex_hs_activated_event(adapter, true);
adapter->ps_state = PS_STATE_SLEEP;
cmd->command = cpu_to_le16(command);
cmd->seq_num = cpu_to_le16(seq_num);
diff --git a/drivers/net/wireless/marvell/mwifiex/fw.h b/drivers/net/wireless/marvell/mwifiex/fw.h
index 4a96281792cc..91458f3bd14a 100644
--- a/drivers/net/wireless/marvell/mwifiex/fw.h
+++ b/drivers/net/wireless/marvell/mwifiex/fw.h
@@ -454,6 +454,11 @@ enum mwifiex_channel_flags {
#define HostCmd_RET_BIT 0x8000
#define HostCmd_ACT_GEN_GET 0x0000
#define HostCmd_ACT_GEN_SET 0x0001
+#define HOST_CMD_ACT_GEN_SET 0x0001
+/* Add this non-CamelCase-style macro to comply with checkpatch requirements.
+ * This macro will eventually replace all existing CamelCase-style macros in
+ * the future for consistency.
+ */
#define HostCmd_ACT_GEN_REMOVE 0x0004
#define HostCmd_ACT_BITWISE_SET 0x0002
#define HostCmd_ACT_BITWISE_CLR 0x0003
@@ -2352,6 +2357,14 @@ struct host_cmd_ds_add_station {
u8 tlv[];
} __packed;
+#define MWIFIEX_CFG_TYPE_CAL 0x2
+
+struct host_cmd_ds_802_11_cfg_data {
+ __le16 action;
+ __le16 type;
+ __le16 data_len;
+} __packed;
+
struct host_cmd_ds_command {
__le16 command;
__le16 size;
@@ -2431,6 +2444,7 @@ struct host_cmd_ds_command {
struct host_cmd_ds_pkt_aggr_ctrl pkt_aggr_ctrl;
struct host_cmd_ds_sta_configure sta_cfg;
struct host_cmd_ds_add_station sta_info;
+ struct host_cmd_ds_802_11_cfg_data cfg_data;
} params;
} __packed;
diff --git a/drivers/net/wireless/marvell/mwifiex/init.c b/drivers/net/wireless/marvell/mwifiex/init.c
index 8b61e45cd667..32c374e47794 100644
--- a/drivers/net/wireless/marvell/mwifiex/init.c
+++ b/drivers/net/wireless/marvell/mwifiex/init.c
@@ -390,7 +390,7 @@ static void mwifiex_invalidate_lists(struct mwifiex_adapter *adapter)
static void
mwifiex_adapter_cleanup(struct mwifiex_adapter *adapter)
{
- del_timer(&adapter->wakeup_timer);
+ timer_delete(&adapter->wakeup_timer);
cancel_delayed_work_sync(&adapter->devdump_work);
mwifiex_cancel_all_pending_cmd(adapter);
wake_up_interruptible(&adapter->cmd_wait_q.wait);
@@ -480,14 +480,12 @@ int mwifiex_init_lock_list(struct mwifiex_adapter *adapter)
* - Initialize the private structure
* - Add BSS priority tables to the adapter structure
* - For each interface, send the init commands to firmware
- * - Send the first command in command pending queue, if available
*/
int mwifiex_init_fw(struct mwifiex_adapter *adapter)
{
int ret;
struct mwifiex_private *priv;
u8 i, first_sta = true;
- int is_cmd_pend_q_empty;
adapter->hw_status = MWIFIEX_HW_STATUS_INITIALIZING;
@@ -509,11 +507,10 @@ int mwifiex_init_fw(struct mwifiex_adapter *adapter)
}
if (adapter->mfg_mode) {
adapter->hw_status = MWIFIEX_HW_STATUS_READY;
- ret = -EINPROGRESS;
} else {
for (i = 0; i < adapter->priv_num; i++) {
ret = mwifiex_sta_init_cmd(adapter->priv[i],
- first_sta, true);
+ first_sta);
if (ret == -1)
return -1;
@@ -522,17 +519,15 @@ int mwifiex_init_fw(struct mwifiex_adapter *adapter)
}
spin_lock_bh(&adapter->cmd_pending_q_lock);
- is_cmd_pend_q_empty = list_empty(&adapter->cmd_pending_q);
+ WARN_ON(!list_empty(&adapter->cmd_pending_q));
spin_unlock_bh(&adapter->cmd_pending_q_lock);
- if (!is_cmd_pend_q_empty) {
- /* Send the first command in queue and return */
- if (mwifiex_main_process(adapter) != -1)
- ret = -EINPROGRESS;
- } else {
- adapter->hw_status = MWIFIEX_HW_STATUS_READY;
- }
- return ret;
+ adapter->hw_status = MWIFIEX_HW_STATUS_READY;
+
+ if (adapter->if_ops.init_fw_port)
+ adapter->if_ops.init_fw_port(adapter);
+
+ return 0;
}
/*
@@ -613,7 +608,7 @@ mwifiex_shutdown_drv(struct mwifiex_adapter *adapter)
if (adapter->curr_cmd) {
mwifiex_dbg(adapter, WARN,
"curr_cmd is still in processing\n");
- del_timer_sync(&adapter->cmd_timer);
+ timer_delete_sync(&adapter->cmd_timer);
mwifiex_recycle_cmd_node(adapter, adapter->curr_cmd);
adapter->curr_cmd = NULL;
}
diff --git a/drivers/net/wireless/marvell/mwifiex/main.c b/drivers/net/wireless/marvell/mwifiex/main.c
index 855019fe5485..7b50a88a18e5 100644
--- a/drivers/net/wireless/marvell/mwifiex/main.c
+++ b/drivers/net/wireless/marvell/mwifiex/main.c
@@ -6,6 +6,7 @@
*/
#include <linux/suspend.h>
+#include <net/sock.h>
#include "main.h"
#include "wmm.h"
@@ -54,7 +55,7 @@ const u16 mwifiex_1d_to_wmm_queue[8] = { 1, 0, 0, 1, 2, 2, 3, 3 };
* proper cleanup before exiting.
*/
static int mwifiex_register(void *card, struct device *dev,
- struct mwifiex_if_ops *if_ops, void **padapter)
+ const struct mwifiex_if_ops *if_ops, void **padapter)
{
struct mwifiex_adapter *adapter;
int i;
@@ -307,7 +308,7 @@ process_start:
if (IS_CARD_RX_RCVD(adapter)) {
adapter->data_received = false;
adapter->pm_wakeup_fw_try = false;
- del_timer(&adapter->wakeup_timer);
+ timer_delete(&adapter->wakeup_timer);
if (adapter->ps_state == PS_STATE_SLEEP)
adapter->ps_state = PS_STATE_AWAKE;
} else {
@@ -354,13 +355,6 @@ process_start:
if (adapter->cmd_resp_received) {
adapter->cmd_resp_received = false;
mwifiex_process_cmdresp(adapter);
-
- /* call mwifiex back when init_fw is done */
- if (adapter->hw_status == MWIFIEX_HW_STATUS_INIT_DONE) {
- adapter->hw_status = MWIFIEX_HW_STATUS_READY;
- mwifiex_init_fw_complete(adapter);
- maybe_quirk_fw_disable_ds(adapter);
- }
}
/* Check if we need to confirm Sleep Request
@@ -415,10 +409,7 @@ process_start:
if (adapter->hs_activated) {
clear_bit(MWIFIEX_IS_HS_CONFIGURED,
&adapter->work_flags);
- mwifiex_hs_activated_event
- (mwifiex_get_priv
- (adapter, MWIFIEX_BSS_ROLE_ANY),
- false);
+ mwifiex_hs_activated_event(adapter, false);
}
}
@@ -438,10 +429,7 @@ process_start:
if (adapter->hs_activated) {
clear_bit(MWIFIEX_IS_HS_CONFIGURED,
&adapter->work_flags);
- mwifiex_hs_activated_event
- (mwifiex_get_priv
- (adapter, MWIFIEX_BSS_ROLE_ANY),
- false);
+ mwifiex_hs_activated_event(adapter, false);
}
}
@@ -460,10 +448,7 @@ process_start:
if (adapter->hs_activated) {
clear_bit(MWIFIEX_IS_HS_CONFIGURED,
&adapter->work_flags);
- mwifiex_hs_activated_event
- (mwifiex_get_priv
- (adapter, MWIFIEX_BSS_ROLE_ANY),
- false);
+ mwifiex_hs_activated_event(adapter, false);
}
}
@@ -587,21 +572,11 @@ static int _mwifiex_fw_dpc(const struct firmware *firmware, void *context)
goto err_dnld_fw;
}
- adapter->init_wait_q_woken = false;
ret = mwifiex_init_fw(adapter);
- if (ret == -1) {
+ if (ret == -1)
goto err_init_fw;
- } else if (!ret) {
- adapter->hw_status = MWIFIEX_HW_STATUS_READY;
- goto done;
- }
- /* Wait for mwifiex_init to complete */
- if (!adapter->mfg_mode) {
- wait_event_interruptible(adapter->init_wait_q,
- adapter->init_wait_q_woken);
- if (adapter->hw_status != MWIFIEX_HW_STATUS_READY)
- goto err_init_fw;
- }
+
+ maybe_quirk_fw_disable_ds(adapter);
if (!adapter->wiphy) {
if (mwifiex_register_cfg80211(adapter)) {
@@ -691,10 +666,6 @@ err_dnld_fw:
init_failed = true;
done:
- if (adapter->cal_data) {
- release_firmware(adapter->cal_data);
- adapter->cal_data = NULL;
- }
if (adapter->firmware) {
release_firmware(adapter->firmware);
adapter->firmware = NULL;
@@ -942,8 +913,7 @@ mwifiex_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
multicast = is_multicast_ether_addr(skb->data);
- if (unlikely(!multicast && skb->sk &&
- skb_shinfo(skb)->tx_flags & SKBTX_WIFI_STATUS &&
+ if (unlikely(!multicast && sk_requests_wifi_status(skb->sk) &&
priv->adapter->fw_api_ver == MWIFIEX_FW_V15))
skb = mwifiex_clone_skb_for_tx_status(priv,
skb,
@@ -1562,7 +1532,6 @@ mwifiex_reinit_sw(struct mwifiex_adapter *adapter)
adapter->hw_status = MWIFIEX_HW_STATUS_INITIALIZING;
clear_bit(MWIFIEX_SURPRISE_REMOVED, &adapter->work_flags);
- init_waitqueue_head(&adapter->init_wait_q);
clear_bit(MWIFIEX_IS_SUSPENDED, &adapter->work_flags);
adapter->hs_activated = false;
clear_bit(MWIFIEX_IS_CMD_TIMEDOUT, &adapter->work_flags);
@@ -1713,7 +1682,7 @@ err_exit:
*/
int
mwifiex_add_card(void *card, struct completion *fw_done,
- struct mwifiex_if_ops *if_ops, u8 iface_type,
+ const struct mwifiex_if_ops *if_ops, u8 iface_type,
struct device *dev)
{
struct mwifiex_adapter *adapter;
@@ -1730,7 +1699,6 @@ mwifiex_add_card(void *card, struct completion *fw_done,
adapter->hw_status = MWIFIEX_HW_STATUS_INITIALIZING;
clear_bit(MWIFIEX_SURPRISE_REMOVED, &adapter->work_flags);
- init_waitqueue_head(&adapter->init_wait_q);
clear_bit(MWIFIEX_IS_SUSPENDED, &adapter->work_flags);
adapter->hs_activated = false;
init_waitqueue_head(&adapter->hs_activate_wait_q);
diff --git a/drivers/net/wireless/marvell/mwifiex/main.h b/drivers/net/wireless/marvell/mwifiex/main.h
index 0674dcf7a537..9ac36bef980e 100644
--- a/drivers/net/wireless/marvell/mwifiex/main.h
+++ b/drivers/net/wireless/marvell/mwifiex/main.h
@@ -239,7 +239,6 @@ struct mwifiex_dbg {
enum MWIFIEX_HARDWARE_STATUS {
MWIFIEX_HW_STATUS_READY,
MWIFIEX_HW_STATUS_INITIALIZING,
- MWIFIEX_HW_STATUS_INIT_DONE,
MWIFIEX_HW_STATUS_RESET,
MWIFIEX_HW_STATUS_NOT_READY
};
@@ -865,8 +864,6 @@ struct mwifiex_adapter {
unsigned long work_flags;
u32 fw_release_number;
u8 intf_hdr_len;
- u16 init_wait_q_woken;
- wait_queue_head_t init_wait_q;
void *card;
struct mwifiex_if_ops if_ops;
atomic_t bypass_tx_pending;
@@ -919,7 +916,6 @@ struct mwifiex_adapter {
struct cmd_ctrl_node *curr_cmd;
/* spin lock for command */
spinlock_t mwifiex_cmd_lock;
- u16 last_init_cmd;
struct timer_list cmd_timer;
struct list_head cmd_free_q;
/* spin lock for cmd_free_q */
@@ -1060,8 +1056,6 @@ void mwifiex_free_priv(struct mwifiex_private *priv);
int mwifiex_init_fw(struct mwifiex_adapter *adapter);
-int mwifiex_init_fw_complete(struct mwifiex_adapter *adapter);
-
void mwifiex_shutdown_drv(struct mwifiex_adapter *adapter);
int mwifiex_dnld_fw(struct mwifiex_adapter *, struct mwifiex_fw_image *);
@@ -1125,7 +1119,7 @@ int mwifiex_ret_enh_power_mode(struct mwifiex_private *priv,
struct host_cmd_ds_command *resp,
struct mwifiex_ds_pm_cfg *pm_cfg);
void mwifiex_process_hs_config(struct mwifiex_adapter *adapter);
-void mwifiex_hs_activated_event(struct mwifiex_private *priv,
+void mwifiex_hs_activated_event(struct mwifiex_adapter *adapter,
u8 activated);
int mwifiex_set_hs_params(struct mwifiex_private *priv, u16 action,
int cmd_type, struct mwifiex_ds_hs_cfg *hs_cfg);
@@ -1156,7 +1150,7 @@ void mwifiex_process_sta_txpd(struct mwifiex_private *priv,
struct sk_buff *skb);
void mwifiex_process_uap_txpd(struct mwifiex_private *priv,
struct sk_buff *skb);
-int mwifiex_sta_init_cmd(struct mwifiex_private *, u8 first_sta, bool init);
+int mwifiex_sta_init_cmd(struct mwifiex_private *, u8 first_sta);
int mwifiex_cmd_802_11_scan(struct host_cmd_ds_command *cmd,
struct mwifiex_scan_cmd_config *scan_cfg);
void mwifiex_queue_scan_cmd(struct mwifiex_private *priv,
@@ -1470,7 +1464,7 @@ int mwifiex_init_shutdown_fw(struct mwifiex_private *priv,
u32 func_init_shutdown);
int mwifiex_add_card(void *card, struct completion *fw_done,
- struct mwifiex_if_ops *if_ops, u8 iface_type,
+ const struct mwifiex_if_ops *if_ops, u8 iface_type,
struct device *dev);
int mwifiex_remove_card(struct mwifiex_adapter *adapter);
@@ -1565,14 +1559,12 @@ int mwifiex_add_wowlan_magic_pkt_filter(struct mwifiex_adapter *adapter);
int mwifiex_set_mgmt_ies(struct mwifiex_private *priv,
struct cfg80211_beacon_data *data);
int mwifiex_del_mgmt_ies(struct mwifiex_private *priv);
-u8 *mwifiex_11d_code_2_region(u8 code);
+const u8 *mwifiex_11d_code_2_region(u8 code);
void mwifiex_uap_set_channel(struct mwifiex_private *priv,
struct mwifiex_uap_bss_param *bss_cfg,
struct cfg80211_chan_def chandef);
int mwifiex_config_start_uap(struct mwifiex_private *priv,
struct mwifiex_uap_bss_param *bss_cfg);
-void mwifiex_uap_del_sta_data(struct mwifiex_private *priv,
- struct mwifiex_sta_node *node);
void mwifiex_config_uap_11d(struct mwifiex_private *priv,
struct cfg80211_beacon_data *beacon_data);
@@ -1600,7 +1592,6 @@ mwifiex_add_sta_entry(struct mwifiex_private *priv, const u8 *mac);
struct mwifiex_sta_node *
mwifiex_get_sta_entry(struct mwifiex_private *priv, const u8 *mac);
u8 mwifiex_is_tdls_chan_switching(struct mwifiex_private *priv);
-u8 mwifiex_is_tdls_off_chan(struct mwifiex_private *priv);
u8 mwifiex_is_send_cmd_allowed(struct mwifiex_private *priv);
int mwifiex_send_tdls_data_frame(struct mwifiex_private *priv, const u8 *peer,
u8 action_code, u8 dialog_token,
diff --git a/drivers/net/wireless/marvell/mwifiex/pcie.c b/drivers/net/wireless/marvell/mwifiex/pcie.c
index 5f997becdbaa..a760de191fce 100644
--- a/drivers/net/wireless/marvell/mwifiex/pcie.c
+++ b/drivers/net/wireless/marvell/mwifiex/pcie.c
@@ -21,7 +21,7 @@
#define PCIE_VERSION "1.0"
#define DRV_NAME "Marvell mwifiex PCIe"
-static struct mwifiex_if_ops pcie_ops;
+static const struct mwifiex_if_ops pcie_ops;
static const struct mwifiex_pcie_card_reg mwifiex_reg_8766 = {
.cmd_addr_lo = PCIE_SCRATCH_0_REG,
@@ -2437,7 +2437,7 @@ static void mwifiex_interrupt_status(struct mwifiex_adapter *adapter,
*/
adapter->ps_state = PS_STATE_AWAKE;
adapter->pm_wakeup_fw_try = false;
- del_timer(&adapter->wakeup_timer);
+ timer_delete(&adapter->wakeup_timer);
}
spin_lock_irqsave(&adapter->int_lock, flags);
@@ -2527,7 +2527,7 @@ static int mwifiex_process_int_status(struct mwifiex_adapter *adapter)
adapter->ps_state == PS_STATE_SLEEP) {
adapter->ps_state = PS_STATE_AWAKE;
adapter->pm_wakeup_fw_try = false;
- del_timer(&adapter->wakeup_timer);
+ timer_delete(&adapter->wakeup_timer);
}
}
}
@@ -2971,7 +2971,7 @@ static int mwifiex_init_pcie(struct mwifiex_adapter *adapter)
goto err_iomap2;
}
- pr_notice("PCI memory map Virt0: %pK PCI memory map Virt2: %pK\n",
+ pr_notice("PCI memory map Virt0: %p PCI memory map Virt2: %p\n",
card->pci_mmap, card->pci_mmap1);
ret = mwifiex_pcie_alloc_buffers(adapter);
@@ -3240,7 +3240,7 @@ static void mwifiex_pcie_down_dev(struct mwifiex_adapter *adapter)
mwifiex_pcie_free_buffers(adapter);
}
-static struct mwifiex_if_ops pcie_ops = {
+static const struct mwifiex_if_ops pcie_ops = {
.init_if = mwifiex_init_pcie,
.cleanup_if = mwifiex_cleanup_pcie,
.check_fw_status = mwifiex_check_fw_status,
diff --git a/drivers/net/wireless/marvell/mwifiex/sdio.c b/drivers/net/wireless/marvell/mwifiex/sdio.c
index 490ffd981164..c1fe48448839 100644
--- a/drivers/net/wireless/marvell/mwifiex/sdio.c
+++ b/drivers/net/wireless/marvell/mwifiex/sdio.c
@@ -21,7 +21,7 @@
static void mwifiex_sdio_work(struct work_struct *work);
-static struct mwifiex_if_ops sdio_ops;
+static const struct mwifiex_if_ops sdio_ops;
static const struct mwifiex_sdio_card_reg mwifiex_reg_sd87xx = {
.start_rd_port = 1,
@@ -3167,7 +3167,7 @@ static void mwifiex_sdio_up_dev(struct mwifiex_adapter *adapter)
dev_err(&card->func->dev, "error enabling SDIO port\n");
}
-static struct mwifiex_if_ops sdio_ops = {
+static const struct mwifiex_if_ops sdio_ops = {
.init_if = mwifiex_init_sdio,
.cleanup_if = mwifiex_cleanup_sdio,
.check_fw_status = mwifiex_check_fw_status,
diff --git a/drivers/net/wireless/marvell/mwifiex/sta_cmd.c b/drivers/net/wireless/marvell/mwifiex/sta_cmd.c
index e2800a831c8e..c93281f5a47c 100644
--- a/drivers/net/wireless/marvell/mwifiex/sta_cmd.c
+++ b/drivers/net/wireless/marvell/mwifiex/sta_cmd.c
@@ -157,7 +157,7 @@ mwifiex_cmd_802_11_get_log(struct host_cmd_ds_command *cmd)
*/
static int mwifiex_cmd_tx_rate_cfg(struct mwifiex_private *priv,
struct host_cmd_ds_command *cmd,
- u16 cmd_action, u16 *pbitmap_rates)
+ u16 cmd_action, const u16 *pbitmap_rates)
{
struct host_cmd_ds_tx_rate_cfg *rate_cfg = &cmd->params.tx_rate_cfg;
struct mwifiex_rate_scope *rate_scope;
@@ -174,34 +174,19 @@ static int mwifiex_cmd_tx_rate_cfg(struct mwifiex_private *priv,
rate_scope->type = cpu_to_le16(TLV_TYPE_RATE_SCOPE);
rate_scope->length = cpu_to_le16
(sizeof(*rate_scope) - sizeof(struct mwifiex_ie_types_header));
- if (pbitmap_rates != NULL) {
- rate_scope->hr_dsss_rate_bitmap = cpu_to_le16(pbitmap_rates[0]);
- rate_scope->ofdm_rate_bitmap = cpu_to_le16(pbitmap_rates[1]);
- for (i = 0; i < ARRAY_SIZE(rate_scope->ht_mcs_rate_bitmap); i++)
- rate_scope->ht_mcs_rate_bitmap[i] =
- cpu_to_le16(pbitmap_rates[2 + i]);
- if (priv->adapter->fw_api_ver == MWIFIEX_FW_V15) {
- for (i = 0;
- i < ARRAY_SIZE(rate_scope->vht_mcs_rate_bitmap);
- i++)
- rate_scope->vht_mcs_rate_bitmap[i] =
- cpu_to_le16(pbitmap_rates[10 + i]);
- }
- } else {
- rate_scope->hr_dsss_rate_bitmap =
- cpu_to_le16(priv->bitmap_rates[0]);
- rate_scope->ofdm_rate_bitmap =
- cpu_to_le16(priv->bitmap_rates[1]);
- for (i = 0; i < ARRAY_SIZE(rate_scope->ht_mcs_rate_bitmap); i++)
- rate_scope->ht_mcs_rate_bitmap[i] =
- cpu_to_le16(priv->bitmap_rates[2 + i]);
- if (priv->adapter->fw_api_ver == MWIFIEX_FW_V15) {
- for (i = 0;
- i < ARRAY_SIZE(rate_scope->vht_mcs_rate_bitmap);
- i++)
- rate_scope->vht_mcs_rate_bitmap[i] =
- cpu_to_le16(priv->bitmap_rates[10 + i]);
- }
+ if (!pbitmap_rates)
+ pbitmap_rates = priv->bitmap_rates;
+
+ rate_scope->hr_dsss_rate_bitmap = cpu_to_le16(pbitmap_rates[0]);
+ rate_scope->ofdm_rate_bitmap = cpu_to_le16(pbitmap_rates[1]);
+
+ for (i = 0; i < ARRAY_SIZE(rate_scope->ht_mcs_rate_bitmap); i++)
+ rate_scope->ht_mcs_rate_bitmap[i] = cpu_to_le16(pbitmap_rates[2 + i]);
+
+ if (priv->adapter->fw_api_ver == MWIFIEX_FW_V15) {
+ for (i = 0; i < ARRAY_SIZE(rate_scope->vht_mcs_rate_bitmap); i++)
+ rate_scope->vht_mcs_rate_bitmap[i] =
+ cpu_to_le16(pbitmap_rates[10 + i]);
}
rate_drop = (struct mwifiex_rate_drop_pattern *) ((u8 *) rate_scope +
@@ -1507,6 +1492,7 @@ static int mwifiex_cmd_cfg_data(struct mwifiex_private *priv,
u32 len;
u8 *data = (u8 *)cmd + S_DS_GEN;
int ret;
+ struct host_cmd_ds_802_11_cfg_data *pcfg_data;
if (prop) {
len = prop->length;
@@ -1514,12 +1500,20 @@ static int mwifiex_cmd_cfg_data(struct mwifiex_private *priv,
data, len);
if (ret)
return ret;
+
+ cmd->size = cpu_to_le16(S_DS_GEN + len);
mwifiex_dbg(adapter, INFO,
"download cfg_data from device tree: %s\n",
prop->name);
} else if (adapter->cal_data->data && adapter->cal_data->size > 0) {
len = mwifiex_parse_cal_cfg((u8 *)adapter->cal_data->data,
- adapter->cal_data->size, data);
+ adapter->cal_data->size,
+ data + sizeof(*pcfg_data));
+ pcfg_data = &cmd->params.cfg_data;
+ pcfg_data->action = cpu_to_le16(HOST_CMD_ACT_GEN_SET);
+ pcfg_data->type = cpu_to_le16(MWIFIEX_CFG_TYPE_CAL);
+ pcfg_data->data_len = cpu_to_le16(len);
+ cmd->size = cpu_to_le16(S_DS_GEN + sizeof(*pcfg_data) + len);
mwifiex_dbg(adapter, INFO,
"download cfg_data from config file\n");
} else {
@@ -1527,7 +1521,6 @@ static int mwifiex_cmd_cfg_data(struct mwifiex_private *priv,
}
cmd->command = cpu_to_le16(HostCmd_CMD_CFG_DATA);
- cmd->size = cpu_to_le16(S_DS_GEN + len);
return 0;
}
@@ -2250,7 +2243,7 @@ int mwifiex_sta_prepare_cmd(struct mwifiex_private *priv, uint16_t cmd_no,
* - Set 11d control
* - Set MAC control (this must be the last command to initialize firmware)
*/
-int mwifiex_sta_init_cmd(struct mwifiex_private *priv, u8 first_sta, bool init)
+int mwifiex_sta_init_cmd(struct mwifiex_private *priv, u8 first_sta)
{
struct mwifiex_adapter *adapter = priv->adapter;
int ret;
@@ -2293,9 +2286,13 @@ int mwifiex_sta_init_cmd(struct mwifiex_private *priv, u8 first_sta, bool init)
"marvell,caldata");
}
- if (adapter->cal_data)
+ if (adapter->cal_data) {
mwifiex_send_cmd(priv, HostCmd_CMD_CFG_DATA,
HostCmd_ACT_GEN_SET, 0, NULL, true);
+ release_firmware(adapter->cal_data);
+ adapter->cal_data = NULL;
+ }
+
/* Read MAC address from HW */
ret = mwifiex_send_cmd(priv, HostCmd_CMD_GET_HW_SPEC,
@@ -2421,11 +2418,5 @@ int mwifiex_sta_init_cmd(struct mwifiex_private *priv, u8 first_sta, bool init)
ret = mwifiex_send_cmd(priv, HostCmd_CMD_11N_CFG,
HostCmd_ACT_GEN_SET, 0, &tx_cfg, true);
- if (init) {
- /* set last_init_cmd before sending the command */
- priv->adapter->last_init_cmd = HostCmd_CMD_11N_CFG;
- ret = -EINPROGRESS;
- }
-
return ret;
}
diff --git a/drivers/net/wireless/marvell/mwifiex/sta_event.c b/drivers/net/wireless/marvell/mwifiex/sta_event.c
index 400348abeee5..fecd88967ceb 100644
--- a/drivers/net/wireless/marvell/mwifiex/sta_event.c
+++ b/drivers/net/wireless/marvell/mwifiex/sta_event.c
@@ -789,7 +789,7 @@ int mwifiex_process_sta_event(struct mwifiex_private *priv)
adapter->ps_state = PS_STATE_AWAKE;
adapter->pm_wakeup_card_req = false;
adapter->pm_wakeup_fw_try = false;
- del_timer(&adapter->wakeup_timer);
+ timer_delete(&adapter->wakeup_timer);
break;
}
if (!mwifiex_send_null_packet
@@ -804,7 +804,7 @@ int mwifiex_process_sta_event(struct mwifiex_private *priv)
adapter->ps_state = PS_STATE_AWAKE;
adapter->pm_wakeup_card_req = false;
adapter->pm_wakeup_fw_try = false;
- del_timer(&adapter->wakeup_timer);
+ timer_delete(&adapter->wakeup_timer);
break;
diff --git a/drivers/net/wireless/marvell/mwifiex/tdls.c b/drivers/net/wireless/marvell/mwifiex/tdls.c
index 0a5f340876c3..18e8c04d14c4 100644
--- a/drivers/net/wireless/marvell/mwifiex/tdls.c
+++ b/drivers/net/wireless/marvell/mwifiex/tdls.c
@@ -1490,7 +1490,7 @@ void mwifiex_clean_auto_tdls(struct mwifiex_private *priv)
priv->adapter->auto_tdls &&
priv->bss_type == MWIFIEX_BSS_TYPE_STA) {
priv->auto_tdls_timer_active = false;
- del_timer(&priv->auto_tdls_timer);
+ timer_delete(&priv->auto_tdls_timer);
mwifiex_flush_auto_tdls_list(priv);
}
}
diff --git a/drivers/net/wireless/marvell/mwifiex/txrx.c b/drivers/net/wireless/marvell/mwifiex/txrx.c
index bd91678d26b4..f44e22f24511 100644
--- a/drivers/net/wireless/marvell/mwifiex/txrx.c
+++ b/drivers/net/wireless/marvell/mwifiex/txrx.c
@@ -24,8 +24,7 @@
int mwifiex_handle_rx_packet(struct mwifiex_adapter *adapter,
struct sk_buff *skb)
{
- struct mwifiex_private *priv =
- mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY);
+ struct mwifiex_private *priv;
struct rxpd *local_rx_pd;
struct mwifiex_rxinfo *rx_info = MWIFIEX_SKB_RXCB(skb);
int ret;
diff --git a/drivers/net/wireless/marvell/mwifiex/uap_event.c b/drivers/net/wireless/marvell/mwifiex/uap_event.c
index 58ef5020a46a..245cb99a3daa 100644
--- a/drivers/net/wireless/marvell/mwifiex/uap_event.c
+++ b/drivers/net/wireless/marvell/mwifiex/uap_event.c
@@ -325,19 +325,3 @@ int mwifiex_process_uap_event(struct mwifiex_private *priv)
return 0;
}
-
-/* This function deletes station entry from associated station list.
- * Also if both AP and STA are 11n enabled, RxReorder tables and TxBA stream
- * tables created for this station are deleted.
- */
-void mwifiex_uap_del_sta_data(struct mwifiex_private *priv,
- struct mwifiex_sta_node *node)
-{
- if (priv->ap_11n_enabled && node->is_11n_enabled) {
- mwifiex_11n_del_rx_reorder_tbl_by_ta(priv, node->mac_addr);
- mwifiex_del_tx_ba_stream_tbl_by_ra(priv, node->mac_addr);
- }
- mwifiex_del_sta_entry(priv, node->mac_addr);
-
- return;
-}
diff --git a/drivers/net/wireless/marvell/mwifiex/usb.c b/drivers/net/wireless/marvell/mwifiex/usb.c
index 6085cd50970d..2f565397cf36 100644
--- a/drivers/net/wireless/marvell/mwifiex/usb.c
+++ b/drivers/net/wireless/marvell/mwifiex/usb.c
@@ -10,7 +10,7 @@
#define USB_VERSION "1.0"
-static struct mwifiex_if_ops usb_ops;
+static const struct mwifiex_if_ops usb_ops;
static const struct usb_device_id mwifiex_usb_table[] = {
/* 8766 */
@@ -877,7 +877,7 @@ static int mwifiex_usb_prepare_tx_aggr_skb(struct mwifiex_adapter *adapter,
* write complete, delete the tx_aggr timer
*/
if (port->tx_aggr.timer_cnxt.is_hold_timer_set) {
- del_timer(&port->tx_aggr.timer_cnxt.hold_timer);
+ timer_delete(&port->tx_aggr.timer_cnxt.hold_timer);
port->tx_aggr.timer_cnxt.is_hold_timer_set = false;
port->tx_aggr.timer_cnxt.hold_tmo_msecs = 0;
}
@@ -1354,7 +1354,7 @@ static void mwifiex_usb_cleanup_tx_aggr(struct mwifiex_adapter *adapter)
mwifiex_write_data_complete(adapter, skb_tmp,
0, -1);
if (port->tx_aggr.timer_cnxt.hold_timer.function)
- del_timer_sync(&port->tx_aggr.timer_cnxt.hold_timer);
+ timer_delete_sync(&port->tx_aggr.timer_cnxt.hold_timer);
port->tx_aggr.timer_cnxt.is_hold_timer_set = false;
port->tx_aggr.timer_cnxt.hold_tmo_msecs = 0;
}
@@ -1557,7 +1557,7 @@ static int mwifiex_pm_wakeup_card(struct mwifiex_adapter *adapter)
{
/* Simulation of HS_AWAKE event */
adapter->pm_wakeup_fw_try = false;
- del_timer(&adapter->wakeup_timer);
+ timer_delete(&adapter->wakeup_timer);
adapter->pm_wakeup_card_req = false;
adapter->ps_state = PS_STATE_AWAKE;
@@ -1585,7 +1585,7 @@ mwifiex_pm_wakeup_card_complete(struct mwifiex_adapter *adapter)
return 0;
}
-static struct mwifiex_if_ops usb_ops = {
+static const struct mwifiex_if_ops usb_ops = {
.register_dev = mwifiex_register_dev,
.unregister_dev = mwifiex_unregister_dev,
.wakeup = mwifiex_pm_wakeup_card,
diff --git a/drivers/net/wireless/marvell/mwifiex/util.c b/drivers/net/wireless/marvell/mwifiex/util.c
index 1f1f6280a0f2..4c5b1de0e936 100644
--- a/drivers/net/wireless/marvell/mwifiex/util.c
+++ b/drivers/net/wireless/marvell/mwifiex/util.c
@@ -116,24 +116,6 @@ static struct mwifiex_debug_data items[] = {
static int num_of_items = ARRAY_SIZE(items);
/*
- * Firmware initialization complete callback handler.
- *
- * This function wakes up the function waiting on the init
- * wait queue for the firmware initialization to complete.
- */
-int mwifiex_init_fw_complete(struct mwifiex_adapter *adapter)
-{
-
- if (adapter->hw_status == MWIFIEX_HW_STATUS_READY)
- if (adapter->if_ops.init_fw_port)
- adapter->if_ops.init_fw_port(adapter);
-
- adapter->init_wait_q_woken = true;
- wake_up_interruptible(&adapter->init_wait_q);
- return 0;
-}
-
-/*
* This function sends init/shutdown command
* to firmware.
*/
@@ -663,7 +645,7 @@ u8 mwifiex_is_tdls_chan_switching(struct mwifiex_private *priv)
return false;
}
-u8 mwifiex_is_tdls_off_chan(struct mwifiex_private *priv)
+static u8 mwifiex_is_tdls_off_chan(struct mwifiex_private *priv)
{
struct mwifiex_sta_node *sta_ptr;
diff --git a/drivers/net/wireless/marvell/mwifiex/wmm.c b/drivers/net/wireless/marvell/mwifiex/wmm.c
index bcb61dab7dc8..1b1222c73728 100644
--- a/drivers/net/wireless/marvell/mwifiex/wmm.c
+++ b/drivers/net/wireless/marvell/mwifiex/wmm.c
@@ -1428,13 +1428,13 @@ mwifiex_dequeue_tx_packet(struct mwifiex_adapter *adapter)
}
if (!ptr->is_11n_enabled ||
- ptr->ba_status ||
- priv->wps.session_enable) {
+ ptr->ba_status ||
+ priv->wps.session_enable) {
if (ptr->is_11n_enabled &&
- ptr->ba_status &&
- ptr->amsdu_in_ampdu &&
- mwifiex_is_amsdu_allowed(priv, tid) &&
- mwifiex_is_11n_aggragation_possible(priv, ptr,
+ ptr->ba_status &&
+ ptr->amsdu_in_ampdu &&
+ mwifiex_is_amsdu_allowed(priv, tid) &&
+ mwifiex_is_11n_aggragation_possible(priv, ptr,
adapter->tx_buf_size))
mwifiex_11n_aggregate_pkt(priv, ptr, ptr_index);
/* ra_list_spinlock has been freed in
diff --git a/drivers/net/wireless/mediatek/mt76/channel.c b/drivers/net/wireless/mediatek/mt76/channel.c
index 6a35c6ebd823..cc2d888e3f17 100644
--- a/drivers/net/wireless/mediatek/mt76/channel.c
+++ b/drivers/net/wireless/mediatek/mt76/channel.c
@@ -293,6 +293,7 @@ struct mt76_vif_link *mt76_get_vif_phy_link(struct mt76_phy *phy,
kfree(mlink);
return ERR_PTR(ret);
}
+ rcu_assign_pointer(mvif->offchannel_link, mlink);
return mlink;
}
@@ -301,10 +302,14 @@ void mt76_put_vif_phy_link(struct mt76_phy *phy, struct ieee80211_vif *vif,
struct mt76_vif_link *mlink)
{
struct mt76_dev *dev = phy->dev;
+ struct mt76_vif_data *mvif;
if (IS_ERR_OR_NULL(mlink) || !mlink->offchannel)
return;
+ mvif = mlink->mvif;
+
+ rcu_assign_pointer(mvif->offchannel_link, NULL);
dev->drv->vif_link_remove(phy, vif, &vif->bss_conf, mlink);
kfree(mlink);
}
diff --git a/drivers/net/wireless/mediatek/mt76/dma.c b/drivers/net/wireless/mediatek/mt76/dma.c
index 844af16ee551..35b4ec91979e 100644
--- a/drivers/net/wireless/mediatek/mt76/dma.c
+++ b/drivers/net/wireless/mediatek/mt76/dma.c
@@ -1011,6 +1011,7 @@ void mt76_dma_cleanup(struct mt76_dev *dev)
int i;
mt76_worker_disable(&dev->tx_worker);
+ napi_disable(&dev->tx_napi);
netif_napi_del(&dev->tx_napi);
for (i = 0; i < ARRAY_SIZE(dev->phys); i++) {
diff --git a/drivers/net/wireless/mediatek/mt76/eeprom.c b/drivers/net/wireless/mediatek/mt76/eeprom.c
index 0bc66cc19acd..443517d06c9f 100644
--- a/drivers/net/wireless/mediatek/mt76/eeprom.c
+++ b/drivers/net/wireless/mediatek/mt76/eeprom.c
@@ -95,6 +95,10 @@ int mt76_get_of_data_from_mtd(struct mt76_dev *dev, void *eep, int offset, int l
#ifdef CONFIG_NL80211_TESTMODE
dev->test_mtd.name = devm_kstrdup(dev->dev, part, GFP_KERNEL);
+ if (!dev->test_mtd.name) {
+ ret = -ENOMEM;
+ goto out_put_node;
+ }
dev->test_mtd.offset = offset;
#endif
diff --git a/drivers/net/wireless/mediatek/mt76/mac80211.c b/drivers/net/wireless/mediatek/mt76/mac80211.c
index 508b472408c2..45c8db939d55 100644
--- a/drivers/net/wireless/mediatek/mt76/mac80211.c
+++ b/drivers/net/wireless/mediatek/mt76/mac80211.c
@@ -449,8 +449,10 @@ mt76_phy_init(struct mt76_phy *phy, struct ieee80211_hw *hw)
wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_AIRTIME_FAIRNESS);
wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_AQL);
- wiphy->available_antennas_tx = phy->antenna_mask;
- wiphy->available_antennas_rx = phy->antenna_mask;
+ if (!wiphy->available_antennas_tx)
+ wiphy->available_antennas_tx = phy->antenna_mask;
+ if (!wiphy->available_antennas_rx)
+ wiphy->available_antennas_rx = phy->antenna_mask;
wiphy->sar_capa = &mt76_sar_capa;
phy->frp = devm_kcalloc(dev->dev, wiphy->sar_capa->num_freq_ranges,
@@ -816,8 +818,8 @@ void mt76_free_device(struct mt76_dev *dev)
}
EXPORT_SYMBOL_GPL(mt76_free_device);
-static struct mt76_phy *
-mt76_vif_phy(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
+struct mt76_phy *mt76_vif_phy(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif)
{
struct mt76_vif_link *mlink = (struct mt76_vif_link *)vif->drv_priv;
struct mt76_chanctx *ctx;
@@ -831,6 +833,7 @@ mt76_vif_phy(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
ctx = (struct mt76_chanctx *)mlink->ctx->drv_priv;
return ctx->phy;
}
+EXPORT_SYMBOL_GPL(mt76_vif_phy);
static void mt76_rx_release_amsdu(struct mt76_phy *phy, enum mt76_rxq_id q)
{
@@ -1697,6 +1700,17 @@ void mt76_wcid_add_poll(struct mt76_dev *dev, struct mt76_wcid *wcid)
}
EXPORT_SYMBOL_GPL(mt76_wcid_add_poll);
+s8 mt76_get_power_bound(struct mt76_phy *phy, s8 txpower)
+{
+ int n_chains = hweight16(phy->chainmask);
+
+ txpower = mt76_get_sar_power(phy, phy->chandef.chan, txpower * 2);
+ txpower -= mt76_tx_power_path_delta(n_chains);
+
+ return txpower;
+}
+EXPORT_SYMBOL_GPL(mt76_get_power_bound);
+
int mt76_get_txpower(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
unsigned int link_id, int *dbm)
{
@@ -1707,7 +1721,7 @@ int mt76_get_txpower(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
return -EINVAL;
n_chains = hweight16(phy->chainmask);
- delta = mt76_tx_power_nss_delta(n_chains);
+ delta = mt76_tx_power_path_delta(n_chains);
*dbm = DIV_ROUND_UP(phy->txpower_cur + delta, 2);
return 0;
diff --git a/drivers/net/wireless/mediatek/mt76/mt76.h b/drivers/net/wireless/mediatek/mt76/mt76.h
index 132148f7b107..5f8d81cda6cd 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76.h
+++ b/drivers/net/wireless/mediatek/mt76/mt76.h
@@ -162,6 +162,16 @@ enum mt76_dfs_state {
MT_DFS_STATE_ACTIVE,
};
+#define MT76_RNR_SCAN_MAX_BSSIDS 16
+struct mt76_scan_rnr_param {
+ u8 bssid[MT76_RNR_SCAN_MAX_BSSIDS][ETH_ALEN];
+ u8 channel[MT76_RNR_SCAN_MAX_BSSIDS];
+ u8 random_mac[ETH_ALEN];
+ u8 seq_num;
+ u8 bssid_num;
+ u32 sreq_flag;
+};
+
struct mt76_queue_buf {
dma_addr_t addr;
u16 len:15,
@@ -351,6 +361,7 @@ struct mt76_wcid {
u8 hw_key_idx;
u8 hw_key_idx2;
+ u8 offchannel:1;
u8 sta:1;
u8 sta_disabled:1;
u8 amsdu:1;
@@ -491,6 +502,7 @@ struct mt76_hw_cap {
#define MT_DRV_RX_DMA_HDR BIT(3)
#define MT_DRV_HW_MGMT_TXQ BIT(4)
#define MT_DRV_AMSDU_OFFLOAD BIT(5)
+#define MT_DRV_IGNORE_TXS_FAILED BIT(6)
struct mt76_driver_ops {
u32 drv_flags;
@@ -769,6 +781,7 @@ struct mt76_testmode_data {
struct mt76_vif_link {
u8 idx;
+ u8 link_idx;
u8 omac_idx;
u8 band_idx;
u8 wmm_idx;
@@ -786,6 +799,7 @@ struct mt76_vif_link {
struct mt76_vif_data {
struct mt76_vif_link __rcu *link[IEEE80211_MLD_MAX_NUM_LINKS];
+ struct mt76_vif_link __rcu *offchannel_link;
struct mt76_phy *roc_phy;
u16 valid_links;
@@ -937,6 +951,8 @@ struct mt76_dev {
char alpha2[3];
enum nl80211_dfs_regions region;
+ struct mt76_scan_rnr_param rnr;
+
u32 debugfs_reg;
u8 csa_complete;
@@ -1224,6 +1240,8 @@ struct mt76_phy *mt76_alloc_phy(struct mt76_dev *dev, unsigned int size,
u8 band_idx);
int mt76_register_phy(struct mt76_phy *phy, bool vht,
struct ieee80211_rate *rates, int n_rates);
+struct mt76_phy *mt76_vif_phy(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif);
struct dentry *mt76_register_debugfs_fops(struct mt76_phy *phy,
const struct file_operations *ops);
@@ -1380,12 +1398,12 @@ static inline bool mt76_is_skb_pktid(u8 pktid)
return pktid >= MT_PACKET_ID_FIRST;
}
-static inline u8 mt76_tx_power_nss_delta(u8 nss)
+static inline u8 mt76_tx_power_path_delta(u8 path)
{
- static const u8 nss_delta[4] = { 0, 6, 9, 12 };
- u8 idx = nss - 1;
+ static const u8 path_delta[5] = { 0, 6, 9, 12, 14 };
+ u8 idx = path - 1;
- return (idx < ARRAY_SIZE(nss_delta)) ? nss_delta[idx] : 0;
+ return (idx < ARRAY_SIZE(path_delta)) ? path_delta[idx] : 0;
}
static inline bool mt76_testmode_enabled(struct mt76_phy *phy)
@@ -1482,6 +1500,8 @@ void mt76_sta_pre_rcu_remove(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
int mt76_get_min_avg_rssi(struct mt76_dev *dev, u8 phy_idx);
+s8 mt76_get_power_bound(struct mt76_phy *phy, s8 txpower);
+
int mt76_get_txpower(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
unsigned int link_id, int *dbm);
int mt76_init_sar_power(struct ieee80211_hw *hw,
diff --git a/drivers/net/wireless/mediatek/mt76/mt7615/init.c b/drivers/net/wireless/mediatek/mt76/mt7615/init.c
index 66ba3be27343..aae80005a3c1 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7615/init.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7615/init.c
@@ -273,7 +273,7 @@ void mt7615_init_txpower(struct mt7615_dev *dev,
struct ieee80211_supported_band *sband)
{
int i, n_chains = hweight8(dev->mphy.antenna_mask), target_chains;
- int delta_idx, delta = mt76_tx_power_nss_delta(n_chains);
+ int delta_idx, delta = mt76_tx_power_path_delta(n_chains);
u8 *eep = (u8 *)dev->mt76.eeprom.data;
enum nl80211_band band = sband->band;
struct mt76_power_limits limits;
diff --git a/drivers/net/wireless/mediatek/mt76/mt7615/main.c b/drivers/net/wireless/mediatek/mt76/mt7615/main.c
index 2e7b05eeef7a..c54005df08ca 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7615/main.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7615/main.c
@@ -97,7 +97,7 @@ static void mt7615_stop(struct ieee80211_hw *hw, bool suspend)
struct mt7615_phy *phy = mt7615_hw_phy(hw);
cancel_delayed_work_sync(&phy->mt76->mac_work);
- del_timer_sync(&phy->roc_timer);
+ timer_delete_sync(&phy->roc_timer);
cancel_work_sync(&phy->roc_work);
cancel_delayed_work_sync(&dev->pm.ps_work);
@@ -1194,7 +1194,7 @@ static int mt7615_cancel_remain_on_channel(struct ieee80211_hw *hw,
if (!test_and_clear_bit(MT76_STATE_ROC, &phy->mt76->state))
return 0;
- del_timer_sync(&phy->roc_timer);
+ timer_delete_sync(&phy->roc_timer);
cancel_work_sync(&phy->roc_work);
mt7615_mutex_acquire(phy->dev);
diff --git a/drivers/net/wireless/mediatek/mt76/mt7615/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7615/mcu.c
index b8fcd4eb3fbb..4064e193d4de 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7615/mcu.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7615/mcu.c
@@ -2067,7 +2067,7 @@ static void mt7615_mcu_set_txpower_sku(struct mt7615_phy *phy, u8 *sku)
};
tx_power = mt76_get_sar_power(mphy, mphy->chandef.chan, tx_power);
- tx_power -= mt76_tx_power_nss_delta(n_chains);
+ tx_power -= mt76_tx_power_path_delta(n_chains);
tx_power = mt76_get_rate_power_limits(mphy, mphy->chandef.chan,
&limits, tx_power);
mphy->txpower_cur = tx_power;
@@ -2084,8 +2084,8 @@ static void mt7615_mcu_set_txpower_sku(struct mt7615_phy *phy, u8 *sku)
int delta = 0;
if (i < n_chains - 1)
- delta = mt76_tx_power_nss_delta(n_chains) -
- mt76_tx_power_nss_delta(i + 1);
+ delta = mt76_tx_power_path_delta(n_chains) -
+ mt76_tx_power_path_delta(i + 1);
sku[MT_SKU_1SS_DELTA + i] = delta;
}
}
diff --git a/drivers/net/wireless/mediatek/mt76/mt7615/pci_mac.c b/drivers/net/wireless/mediatek/mt76/mt7615/pci_mac.c
index c2e4e6aabd9f..b795d11d943d 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7615/pci_mac.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7615/pci_mac.c
@@ -220,12 +220,12 @@ void mt7615_mac_reset_work(struct work_struct *work)
set_bit(MT76_MCU_RESET, &dev->mphy.state);
wake_up(&dev->mt76.mcu.wait);
cancel_delayed_work_sync(&dev->mphy.mac_work);
- del_timer_sync(&dev->phy.roc_timer);
+ timer_delete_sync(&dev->phy.roc_timer);
cancel_work_sync(&dev->phy.roc_work);
if (phy2) {
set_bit(MT76_RESET, &phy2->mt76->state);
cancel_delayed_work_sync(&phy2->mt76->mac_work);
- del_timer_sync(&phy2->roc_timer);
+ timer_delete_sync(&phy2->roc_timer);
cancel_work_sync(&phy2->roc_work);
}
diff --git a/drivers/net/wireless/mediatek/mt76/mt7615/usb.c b/drivers/net/wireless/mediatek/mt76/mt7615/usb.c
index 4aa9fa1c4a23..d96e06b4fee1 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7615/usb.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7615/usb.c
@@ -85,7 +85,7 @@ static void mt7663u_stop(struct ieee80211_hw *hw, bool suspend)
struct mt7615_dev *dev = hw->priv;
clear_bit(MT76_STATE_RUNNING, &dev->mphy.state);
- del_timer_sync(&phy->roc_timer);
+ timer_delete_sync(&phy->roc_timer);
cancel_work_sync(&phy->roc_work);
cancel_delayed_work_sync(&phy->scan_work);
cancel_delayed_work_sync(&phy->mt76->mac_work);
diff --git a/drivers/net/wireless/mediatek/mt76/mt76_connac.h b/drivers/net/wireless/mediatek/mt76/mt76_connac.h
index 455979476d11..192dcc374a64 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76_connac.h
+++ b/drivers/net/wireless/mediatek/mt76/mt76_connac.h
@@ -232,9 +232,14 @@ static inline bool is_mt7992(struct mt76_dev *dev)
return mt76_chip(dev) == 0x7992;
}
+static inline bool is_mt7990(struct mt76_dev *dev)
+{
+ return mt76_chip(dev) == 0x7993;
+}
+
static inline bool is_mt799x(struct mt76_dev *dev)
{
- return is_mt7996(dev) || is_mt7992(dev);
+ return is_mt7996(dev) || is_mt7992(dev) || is_mt7990(dev);
}
static inline bool is_mt7622(struct mt76_dev *dev)
diff --git a/drivers/net/wireless/mediatek/mt76/mt76_connac3_mac.h b/drivers/net/wireless/mediatek/mt76/mt76_connac3_mac.h
index db0c29e65185..1013cad57a7f 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76_connac3_mac.h
+++ b/drivers/net/wireless/mediatek/mt76/mt76_connac3_mac.h
@@ -273,6 +273,7 @@ enum tx_frag_idx {
#define MT_TXD6_TX_RATE GENMASK(21, 16)
#define MT_TXD6_TIMESTAMP_OFS_EN BIT(15)
#define MT_TXD6_TIMESTAMP_OFS_IDX GENMASK(14, 10)
+#define MT_TXD6_TID_ADDBA GENMASK(10, 8)
#define MT_TXD6_MSDU_CNT GENMASK(9, 4)
#define MT_TXD6_MSDU_CNT_V2 GENMASK(15, 10)
#define MT_TXD6_DIS_MAT BIT(3)
@@ -314,6 +315,9 @@ enum tx_frag_idx {
#define MT_TXFREE_INFO_COUNT GENMASK(27, 24)
#define MT_TXFREE_INFO_STAT GENMASK(29, 28)
+#define MT_TXS_HDR_SIZE 4 /* Unit: DW */
+#define MT_TXS_SIZE 12 /* Unit: DW */
+
#define MT_TXS0_BW GENMASK(31, 29)
#define MT_TXS0_TID GENMASK(28, 26)
#define MT_TXS0_AMPDU BIT(25)
diff --git a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c
index f30cf9e71610..cb13d0a76878 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c
+++ b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c
@@ -67,8 +67,7 @@ int mt76_connac_mcu_init_download(struct mt76_dev *dev, u32 addr, u32 len,
if ((!is_connac_v1(dev) && addr == MCU_PATCH_ADDRESS) ||
(is_mt7921(dev) && addr == 0x900000) ||
(is_mt7925(dev) && (addr == 0x900000 || addr == 0xe0002800)) ||
- (is_mt7996(dev) && addr == 0x900000) ||
- (is_mt7992(dev) && addr == 0x900000))
+ (is_mt799x(dev) && addr == 0x900000))
cmd = MCU_CMD(PATCH_START_REQ);
else
cmd = MCU_CMD(TARGET_ADDRESS_LEN_REQ);
@@ -1168,7 +1167,7 @@ int mt76_connac_mcu_uni_add_dev(struct mt76_phy *phy,
.tag = cpu_to_le16(DEV_INFO_ACTIVE),
.len = cpu_to_le16(sizeof(struct req_tlv)),
.active = enable,
- .link_idx = mvif->idx,
+ .link_idx = mvif->link_idx,
},
};
struct {
@@ -1191,7 +1190,7 @@ int mt76_connac_mcu_uni_add_dev(struct mt76_phy *phy,
.bmc_tx_wlan_idx = cpu_to_le16(wcid->idx),
.sta_idx = cpu_to_le16(wcid->idx),
.conn_state = 1,
- .link_idx = mvif->idx,
+ .link_idx = mvif->link_idx,
},
};
int err, idx, cmd, len;
@@ -1667,6 +1666,44 @@ int mt76_connac_mcu_uni_add_bss(struct mt76_phy *phy,
}
EXPORT_SYMBOL_GPL(mt76_connac_mcu_uni_add_bss);
+void mt76_connac_mcu_build_rnr_scan_param(struct mt76_dev *mdev,
+ struct cfg80211_scan_request *sreq)
+{
+ struct ieee80211_channel **scan_list = sreq->channels;
+ int i, bssid_index = 0;
+
+ /* clear 6G active Scan BSSID table */
+ memset(&mdev->rnr, 0, sizeof(mdev->rnr));
+
+ for (i = 0; i < sreq->n_6ghz_params; i++) {
+ u8 ch_idx = sreq->scan_6ghz_params[i].channel_idx;
+ int k = 0;
+
+ /* Remove the duplicated BSSID */
+ for (k = 0; k < bssid_index; k++) {
+ if (!memcmp(&mdev->rnr.bssid[k],
+ sreq->scan_6ghz_params[i].bssid,
+ ETH_ALEN))
+ break;
+ }
+
+ if (k == bssid_index &&
+ bssid_index < MT76_RNR_SCAN_MAX_BSSIDS) {
+ memcpy(&mdev->rnr.bssid[bssid_index++],
+ sreq->scan_6ghz_params[i].bssid, ETH_ALEN);
+ mdev->rnr.channel[k] = scan_list[ch_idx]->hw_value;
+ }
+ }
+
+ mdev->rnr.bssid_num = bssid_index;
+
+ if (sreq->flags & NL80211_SCAN_FLAG_RANDOM_ADDR) {
+ memcpy(mdev->rnr.random_mac, sreq->mac_addr, ETH_ALEN);
+ mdev->rnr.sreq_flag = sreq->flags;
+ }
+}
+EXPORT_SYMBOL_GPL(mt76_connac_mcu_build_rnr_scan_param);
+
#define MT76_CONNAC_SCAN_CHANNEL_TIME 60
int mt76_connac_mcu_hw_scan(struct mt76_phy *phy, struct ieee80211_vif *vif,
struct ieee80211_scan_request *scan_req)
diff --git a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h
index 43237e518373..27daf419560a 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h
+++ b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h
@@ -869,6 +869,7 @@ enum {
#define NETWORK_WDS BIT(21)
#define SCAN_FUNC_RANDOM_MAC BIT(0)
+#define SCAN_FUNC_RNR_SCAN BIT(3)
#define SCAN_FUNC_SPLIT_SCAN BIT(5)
#define CONNECTION_INFRA_STA (STA_TYPE_STA | NETWORK_INFRA)
@@ -1065,6 +1066,7 @@ enum {
MCU_UNI_EVENT_WED_RRO = 0x57,
MCU_UNI_EVENT_PER_STA_INFO = 0x6d,
MCU_UNI_EVENT_ALL_STA_INFO = 0x6e,
+ MCU_UNI_EVENT_SDO = 0x83,
};
#define MCU_UNI_CMD_EVENT BIT(1)
@@ -1182,6 +1184,11 @@ enum {
#define MCU_UNI_CMD(_t) (__MCU_CMD_FIELD_UNI | \
FIELD_PREP(__MCU_CMD_FIELD_ID, \
MCU_UNI_CMD_##_t))
+
+#define MCU_UNI_QUERY(_t) (__MCU_CMD_FIELD_UNI | __MCU_CMD_FIELD_QUERY | \
+ FIELD_PREP(__MCU_CMD_FIELD_ID, \
+ MCU_UNI_CMD_##_t))
+
#define MCU_CE_CMD(_t) (__MCU_CMD_FIELD_CE | \
FIELD_PREP(__MCU_CMD_FIELD_ID, \
MCU_CE_CMD_##_t))
@@ -1287,16 +1294,20 @@ enum {
MCU_UNI_CMD_EFUSE_CTRL = 0x2d,
MCU_UNI_CMD_RA = 0x2f,
MCU_UNI_CMD_MURU = 0x31,
+ MCU_UNI_CMD_TESTMODE_RX_STAT = 0x32,
MCU_UNI_CMD_BF = 0x33,
MCU_UNI_CMD_CHANNEL_SWITCH = 0x34,
MCU_UNI_CMD_THERMAL = 0x35,
MCU_UNI_CMD_VOW = 0x37,
MCU_UNI_CMD_FIXED_RATE_TABLE = 0x40,
+ MCU_UNI_CMD_TESTMODE_CTRL = 0x46,
MCU_UNI_CMD_RRO = 0x57,
MCU_UNI_CMD_OFFCH_SCAN_CTRL = 0x58,
MCU_UNI_CMD_PER_STA_INFO = 0x6d,
MCU_UNI_CMD_ALL_STA_INFO = 0x6e,
MCU_UNI_CMD_ASSERT_DUMP = 0x6f,
+ MCU_UNI_CMD_RADIO_STATUS = 0x80,
+ MCU_UNI_CMD_SDO = 0x88,
};
enum {
@@ -1369,6 +1380,7 @@ enum {
UNI_BSS_INFO_OFFLOAD = 25,
UNI_BSS_INFO_MLD = 26,
UNI_BSS_INFO_PM_DISABLE = 27,
+ UNI_BSS_INFO_EHT = 30,
};
enum {
@@ -1969,6 +1981,8 @@ int mt76_connac_mcu_start_patch(struct mt76_dev *dev);
int mt76_connac_mcu_patch_sem_ctrl(struct mt76_dev *dev, bool get);
int mt76_connac_mcu_start_firmware(struct mt76_dev *dev, u32 addr, u32 option);
+void mt76_connac_mcu_build_rnr_scan_param(struct mt76_dev *mdev,
+ struct cfg80211_scan_request *sreq);
int mt76_connac_mcu_hw_scan(struct mt76_phy *phy, struct ieee80211_vif *vif,
struct ieee80211_scan_request *scan_req);
int mt76_connac_mcu_cancel_hw_scan(struct mt76_phy *phy,
diff --git a/drivers/net/wireless/mediatek/mt76/mt76x0/pci.c b/drivers/net/wireless/mediatek/mt76/mt76x0/pci.c
index b456ccd00d58..11c16d1fc70f 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x0/pci.c
+++ b/drivers/net/wireless/mediatek/mt76/mt76x0/pci.c
@@ -156,7 +156,8 @@ mt76x0e_probe(struct pci_dev *pdev, const struct pci_device_id *id)
static const struct mt76_driver_ops drv_ops = {
.txwi_size = sizeof(struct mt76x02_txwi),
.drv_flags = MT_DRV_TX_ALIGNED4_SKBS |
- MT_DRV_SW_RX_AIRTIME,
+ MT_DRV_SW_RX_AIRTIME |
+ MT_DRV_IGNORE_TXS_FAILED,
.survey_flags = SURVEY_INFO_TIME_TX,
.update_survey = mt76x02_update_channel,
.set_channel = mt76x0_set_channel,
diff --git a/drivers/net/wireless/mediatek/mt76/mt76x0/usb.c b/drivers/net/wireless/mediatek/mt76/mt76x0/usb.c
index b031c500b741..90e5666c0857 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x0/usb.c
+++ b/drivers/net/wireless/mediatek/mt76/mt76x0/usb.c
@@ -214,7 +214,8 @@ static int mt76x0u_probe(struct usb_interface *usb_intf,
const struct usb_device_id *id)
{
static const struct mt76_driver_ops drv_ops = {
- .drv_flags = MT_DRV_SW_RX_AIRTIME,
+ .drv_flags = MT_DRV_SW_RX_AIRTIME |
+ MT_DRV_IGNORE_TXS_FAILED,
.survey_flags = SURVEY_INFO_TIME_TX,
.update_survey = mt76x02_update_channel,
.set_channel = mt76x0_set_channel,
diff --git a/drivers/net/wireless/mediatek/mt76/mt76x02_mmio.c b/drivers/net/wireless/mediatek/mt76/mt76x02_mmio.c
index a82c75ba26e6..a683d53c7ceb 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x02_mmio.c
+++ b/drivers/net/wireless/mediatek/mt76/mt76x02_mmio.c
@@ -174,7 +174,6 @@ static int mt76x02_poll_tx(struct napi_struct *napi, int budget)
int mt76x02_dma_init(struct mt76x02_dev *dev)
{
- struct mt76_txwi_cache __maybe_unused *t;
int i, ret, fifo_size;
struct mt76_queue *q;
void *status_fifo;
diff --git a/drivers/net/wireless/mediatek/mt76/mt76x02_usb_core.c b/drivers/net/wireless/mediatek/mt76/mt76x02_usb_core.c
index 0e1ede9314d8..4840d0b500b3 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x02_usb_core.c
+++ b/drivers/net/wireless/mediatek/mt76/mt76x02_usb_core.c
@@ -264,8 +264,8 @@ void mt76x02u_init_beacon_config(struct mt76x02_dev *dev)
};
dev->beacon_ops = &beacon_ops;
- hrtimer_init(&dev->pre_tbtt_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
- dev->pre_tbtt_timer.function = mt76x02u_pre_tbtt_interrupt;
+ hrtimer_setup(&dev->pre_tbtt_timer, mt76x02u_pre_tbtt_interrupt, CLOCK_MONOTONIC,
+ HRTIMER_MODE_REL);
INIT_WORK(&dev->pre_tbtt_work, mt76x02u_pre_tbtt_work);
mt76x02_init_beacon_config(dev);
diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2/pci.c b/drivers/net/wireless/mediatek/mt76/mt76x2/pci.c
index 727bfdd00b40..2303019670e2 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x2/pci.c
+++ b/drivers/net/wireless/mediatek/mt76/mt76x2/pci.c
@@ -22,7 +22,8 @@ mt76x2e_probe(struct pci_dev *pdev, const struct pci_device_id *id)
static const struct mt76_driver_ops drv_ops = {
.txwi_size = sizeof(struct mt76x02_txwi),
.drv_flags = MT_DRV_TX_ALIGNED4_SKBS |
- MT_DRV_SW_RX_AIRTIME,
+ MT_DRV_SW_RX_AIRTIME |
+ MT_DRV_IGNORE_TXS_FAILED,
.survey_flags = SURVEY_INFO_TIME_TX,
.update_survey = mt76x02_update_channel,
.set_channel = mt76x2e_set_channel,
diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2/usb.c b/drivers/net/wireless/mediatek/mt76/mt76x2/usb.c
index e832ad53e239..96cecc576a98 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x2/usb.c
+++ b/drivers/net/wireless/mediatek/mt76/mt76x2/usb.c
@@ -17,11 +17,14 @@ static const struct usb_device_id mt76x2u_device_table[] = {
{ USB_DEVICE(0x057c, 0x8503) }, /* Avm FRITZ!WLAN AC860 */
{ USB_DEVICE(0x7392, 0xb711) }, /* Edimax EW 7722 UAC */
{ USB_DEVICE(0x0e8d, 0x7632) }, /* HC-M7662BU1 */
+ { USB_DEVICE(0x0471, 0x2126) }, /* LiteOn WN4516R module, nonstandard USB connector */
+ { USB_DEVICE(0x0471, 0x7600) }, /* LiteOn WN4519R module, nonstandard USB connector */
{ USB_DEVICE(0x2c4e, 0x0103) }, /* Mercury UD13 */
{ USB_DEVICE(0x0846, 0x9014) }, /* Netgear WNDA3100v3 */
{ USB_DEVICE(0x0846, 0x9053) }, /* Netgear A6210 */
{ USB_DEVICE(0x045e, 0x02e6) }, /* XBox One Wireless Adapter */
{ USB_DEVICE(0x045e, 0x02fe) }, /* XBox One Wireless Adapter */
+ { USB_DEVICE(0x2357, 0x0137) }, /* TP-Link TL-WDN6200 */
{ },
};
@@ -29,7 +32,8 @@ static int mt76x2u_probe(struct usb_interface *intf,
const struct usb_device_id *id)
{
static const struct mt76_driver_ops drv_ops = {
- .drv_flags = MT_DRV_SW_RX_AIRTIME,
+ .drv_flags = MT_DRV_SW_RX_AIRTIME |
+ MT_DRV_IGNORE_TXS_FAILED,
.survey_flags = SURVEY_INFO_TIME_TX,
.update_survey = mt76x02_update_channel,
.set_channel = mt76x2u_set_channel,
diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2/usb_init.c b/drivers/net/wireless/mediatek/mt76/mt76x2/usb_init.c
index 33a14365ec9b..3b5562811511 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x2/usb_init.c
+++ b/drivers/net/wireless/mediatek/mt76/mt76x2/usb_init.c
@@ -191,6 +191,7 @@ int mt76x2u_register_device(struct mt76x02_dev *dev)
{
struct ieee80211_hw *hw = mt76_hw(dev);
struct mt76_usb *usb = &dev->mt76.usb;
+ bool vht;
int err;
INIT_DELAYED_WORK(&dev->cal_work, mt76x2u_phy_calibrate);
@@ -217,7 +218,17 @@ int mt76x2u_register_device(struct mt76x02_dev *dev)
/* check hw sg support in order to enable AMSDU */
hw->max_tx_fragments = dev->mt76.usb.sg_en ? MT_TX_SG_MAX_SIZE : 1;
- err = mt76_register_device(&dev->mt76, true, mt76x02_rates,
+ switch (dev->mt76.rev) {
+ case 0x76320044:
+ /* these ASIC revisions do not support VHT */
+ vht = false;
+ break;
+ default:
+ vht = true;
+ break;
+ }
+
+ err = mt76_register_device(&dev->mt76, vht, mt76x02_rates,
ARRAY_SIZE(mt76x02_rates));
if (err)
goto fail;
diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/debugfs.c b/drivers/net/wireless/mediatek/mt76/mt7915/debugfs.c
index 578013884e43..b287b7d9394e 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7915/debugfs.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7915/debugfs.c
@@ -211,13 +211,28 @@ static const struct file_operations mt7915_sys_recovery_ops = {
static int
mt7915_radar_trigger(void *data, u64 val)
{
- struct mt7915_dev *dev = data;
+#define RADAR_MAIN_CHAIN 1
+#define RADAR_BACKGROUND 2
+ struct mt7915_phy *phy = data;
+ struct mt7915_dev *dev = phy->dev;
+ int rdd_idx;
+
+ if (!val || val > RADAR_BACKGROUND)
+ return -EINVAL;
- if (val > MT_RX_SEL2)
+ if (val == RADAR_BACKGROUND && !dev->rdd2_phy) {
+ dev_err(dev->mt76.dev, "Background radar is not enabled\n");
return -EINVAL;
+ }
+
+ rdd_idx = mt7915_get_rdd_idx(phy, val == RADAR_BACKGROUND);
+ if (rdd_idx < 0) {
+ dev_err(dev->mt76.dev, "No RDD found\n");
+ return -EINVAL;
+ }
return mt76_connac_mcu_rdd_cmd(&dev->mt76, RDD_RADAR_EMULATE,
- val, 0, 0);
+ rdd_idx, 0, 0);
}
DEFINE_DEBUGFS_ATTRIBUTE(fops_radar_trigger, NULL,
@@ -303,9 +318,9 @@ static int mt7915_muru_stats_show(struct seq_file *file, void *data)
phy->mib.dl_vht_3mu_cnt,
phy->mib.dl_vht_4mu_cnt);
- sub_total_cnt = phy->mib.dl_vht_2mu_cnt +
- phy->mib.dl_vht_3mu_cnt +
- phy->mib.dl_vht_4mu_cnt;
+ sub_total_cnt = (u64)phy->mib.dl_vht_2mu_cnt +
+ phy->mib.dl_vht_3mu_cnt +
+ phy->mib.dl_vht_4mu_cnt;
seq_printf(file, "\nTotal non-HE MU-MIMO DL PPDU count: %lld",
sub_total_cnt);
@@ -353,26 +368,27 @@ static int mt7915_muru_stats_show(struct seq_file *file, void *data)
phy->mib.dl_he_9to16ru_cnt,
phy->mib.dl_he_gtr16ru_cnt);
- sub_total_cnt = phy->mib.dl_he_2mu_cnt +
- phy->mib.dl_he_3mu_cnt +
- phy->mib.dl_he_4mu_cnt;
+ sub_total_cnt = (u64)phy->mib.dl_he_2mu_cnt +
+ phy->mib.dl_he_3mu_cnt +
+ phy->mib.dl_he_4mu_cnt;
total_ppdu_cnt = sub_total_cnt;
seq_printf(file, "\nTotal HE MU-MIMO DL PPDU count: %lld",
sub_total_cnt);
- sub_total_cnt = phy->mib.dl_he_2ru_cnt +
- phy->mib.dl_he_3ru_cnt +
- phy->mib.dl_he_4ru_cnt +
- phy->mib.dl_he_5to8ru_cnt +
- phy->mib.dl_he_9to16ru_cnt +
- phy->mib.dl_he_gtr16ru_cnt;
+ sub_total_cnt = (u64)phy->mib.dl_he_2ru_cnt +
+ phy->mib.dl_he_3ru_cnt +
+ phy->mib.dl_he_4ru_cnt +
+ phy->mib.dl_he_5to8ru_cnt +
+ phy->mib.dl_he_9to16ru_cnt +
+ phy->mib.dl_he_gtr16ru_cnt;
total_ppdu_cnt += sub_total_cnt;
seq_printf(file, "\nTotal HE OFDMA DL PPDU count: %lld",
sub_total_cnt);
- total_ppdu_cnt += phy->mib.dl_he_su_cnt + phy->mib.dl_he_ext_su_cnt;
+ total_ppdu_cnt += (u64)phy->mib.dl_he_su_cnt +
+ phy->mib.dl_he_ext_su_cnt;
seq_printf(file, "\nAll HE DL PPDU count: %lld", total_ppdu_cnt);
@@ -404,20 +420,20 @@ static int mt7915_muru_stats_show(struct seq_file *file, void *data)
phy->mib.ul_hetrig_9to16ru_cnt,
phy->mib.ul_hetrig_gtr16ru_cnt);
- sub_total_cnt = phy->mib.ul_hetrig_2mu_cnt +
- phy->mib.ul_hetrig_3mu_cnt +
- phy->mib.ul_hetrig_4mu_cnt;
+ sub_total_cnt = (u64)phy->mib.ul_hetrig_2mu_cnt +
+ phy->mib.ul_hetrig_3mu_cnt +
+ phy->mib.ul_hetrig_4mu_cnt;
total_ppdu_cnt = sub_total_cnt;
seq_printf(file, "\nTotal HE MU-MIMO UL TB PPDU count: %lld",
sub_total_cnt);
- sub_total_cnt = phy->mib.ul_hetrig_2ru_cnt +
- phy->mib.ul_hetrig_3ru_cnt +
- phy->mib.ul_hetrig_4ru_cnt +
- phy->mib.ul_hetrig_5to8ru_cnt +
- phy->mib.ul_hetrig_9to16ru_cnt +
- phy->mib.ul_hetrig_gtr16ru_cnt;
+ sub_total_cnt = (u64)phy->mib.ul_hetrig_2ru_cnt +
+ phy->mib.ul_hetrig_3ru_cnt +
+ phy->mib.ul_hetrig_4ru_cnt +
+ phy->mib.ul_hetrig_5to8ru_cnt +
+ phy->mib.ul_hetrig_9to16ru_cnt +
+ phy->mib.ul_hetrig_gtr16ru_cnt;
total_ppdu_cnt += sub_total_cnt;
seq_printf(file, "\nTotal HE OFDMA UL TB PPDU count: %lld",
@@ -444,6 +460,11 @@ mt7915_rdd_monitor(struct seq_file *s, void *data)
mutex_lock(&dev->mt76.mutex);
+ if (!mt7915_eeprom_has_background_radar(dev)) {
+ seq_puts(s, "no background radar capability\n");
+ goto out;
+ }
+
if (!cfg80211_chandef_valid(chandef)) {
ret = -EINVAL;
goto out;
@@ -1084,13 +1105,13 @@ mt7915_rate_txpower_set(struct file *file, const char __user *user_buf,
return -EINVAL;
if (pwr160)
- pwr160 = mt7915_get_power_bound(phy, pwr160);
+ pwr160 = mt76_get_power_bound(mphy, pwr160);
if (pwr80)
- pwr80 = mt7915_get_power_bound(phy, pwr80);
+ pwr80 = mt76_get_power_bound(mphy, pwr80);
if (pwr40)
- pwr40 = mt7915_get_power_bound(phy, pwr40);
+ pwr40 = mt76_get_power_bound(mphy, pwr40);
if (pwr20)
- pwr20 = mt7915_get_power_bound(phy, pwr20);
+ pwr20 = mt76_get_power_bound(mphy, pwr20);
if (pwr160 < 0 || pwr80 < 0 || pwr40 < 0 || pwr20 < 0)
return -EINVAL;
@@ -1241,7 +1262,7 @@ int mt7915_init_debugfs(struct mt7915_phy *phy)
if (!dev->dbdc_support || phy->mt76->band_idx) {
debugfs_create_u32("dfs_hw_pattern", 0400, dir,
&dev->hw_pattern);
- debugfs_create_file("radar_trigger", 0200, dir, dev,
+ debugfs_create_file("radar_trigger", 0200, dir, phy,
&fops_radar_trigger);
debugfs_create_devm_seqfile(dev->mt76.dev, "rdd_monitor", dir,
mt7915_rdd_monitor);
diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/eeprom.c b/drivers/net/wireless/mediatek/mt76/mt7915/eeprom.c
index 928e0b07a9bf..c0f3402d30bb 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7915/eeprom.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7915/eeprom.c
@@ -147,7 +147,7 @@ static int mt7915_eeprom_load(struct mt7915_dev *dev)
/* read eeprom data from efuse */
block_num = DIV_ROUND_UP(eeprom_size, eeprom_blk_size);
for (i = 0; i < block_num; i++) {
- ret = mt7915_mcu_get_eeprom(dev, i * eeprom_blk_size);
+ ret = mt7915_mcu_get_eeprom(dev, i * eeprom_blk_size, NULL);
if (ret < 0)
return ret;
}
@@ -361,6 +361,37 @@ s8 mt7915_eeprom_get_power_delta(struct mt7915_dev *dev, int band)
return val & MT_EE_RATE_DELTA_SIGN ? delta : -delta;
}
+bool
+mt7915_eeprom_has_background_radar(struct mt7915_dev *dev)
+{
+ u8 val, buf[MT7915_EEPROM_BLOCK_SIZE];
+ u8 band_sel, tx_path, rx_path;
+ int offs = MT_EE_WIFI_CONF + 1;
+
+ switch (mt76_chip(&dev->mt76)) {
+ case 0x7915:
+ return true;
+ case 0x7906:
+ /* read efuse to check background radar capability */
+ if (mt7915_mcu_get_eeprom(dev, offs, buf))
+ break;
+
+ val = buf[offs % MT7915_EEPROM_BLOCK_SIZE];
+ band_sel = u8_get_bits(val, MT_EE_WIFI_CONF0_BAND_SEL);
+ tx_path = u8_get_bits(val, MT_EE_WIFI_CONF0_TX_PATH);
+ rx_path = u8_get_bits(val, MT_EE_WIFI_CONF0_RX_PATH);
+
+ return (band_sel == MT_EE_V2_BAND_SEL_5GHZ &&
+ tx_path == rx_path && rx_path == 2);
+ case 0x7981:
+ case 0x7986:
+ default:
+ break;
+ }
+
+ return false;
+}
+
const u8 mt7915_sku_group_len[] = {
[SKU_CCK] = 4,
[SKU_OFDM] = 8,
diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/eeprom.h b/drivers/net/wireless/mediatek/mt76/mt7915/eeprom.h
index 509fb43d8a68..31aec0f40232 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7915/eeprom.h
+++ b/drivers/net/wireless/mediatek/mt76/mt7915/eeprom.h
@@ -55,6 +55,7 @@ enum mt7915_eeprom_field {
#define MT_EE_CAL_DPD_SIZE_V2_7981 (102 * MT_EE_CAL_UNIT) /* no 6g dpd data */
#define MT_EE_WIFI_CONF0_TX_PATH GENMASK(2, 0)
+#define MT_EE_WIFI_CONF0_RX_PATH GENMASK(5, 3)
#define MT_EE_WIFI_CONF0_BAND_SEL GENMASK(7, 6)
#define MT_EE_WIFI_CONF1_BAND_SEL GENMASK(7, 6)
#define MT_EE_WIFI_CONF_STREAM_NUM GENMASK(7, 5)
diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/init.c b/drivers/net/wireless/mediatek/mt76/mt7915/init.c
index bee4beabc4eb..3e30ca5155d2 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7915/init.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7915/init.c
@@ -285,7 +285,7 @@ static void __mt7915_init_txpower(struct mt7915_phy *phy,
{
struct mt7915_dev *dev = phy->dev;
int i, n_chains = hweight16(phy->mt76->chainmask);
- int nss_delta = mt76_tx_power_nss_delta(n_chains);
+ int path_delta = mt76_tx_power_path_delta(n_chains);
int pwr_delta = mt7915_eeprom_get_power_delta(dev, sband->band);
struct mt76_power_limits limits;
@@ -305,7 +305,7 @@ static void __mt7915_init_txpower(struct mt7915_phy *phy,
target_power = mt76_get_rate_power_limits(phy->mt76, chan,
&limits,
target_power);
- target_power += nss_delta;
+ target_power += path_delta;
target_power = DIV_ROUND_UP(target_power, 2);
chan->max_power = min_t(int, chan->max_reg_power,
target_power);
@@ -392,9 +392,10 @@ mt7915_init_wiphy(struct mt7915_phy *phy)
if (!is_mt7915(&dev->mt76))
wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_STA_TX_PWR);
- if (!mdev->dev->of_node ||
- !of_property_read_bool(mdev->dev->of_node,
- "mediatek,disable-radar-background"))
+ if (mt7915_eeprom_has_background_radar(phy->dev) &&
+ (!mdev->dev->of_node ||
+ !of_property_read_bool(mdev->dev->of_node,
+ "mediatek,disable-radar-background")))
wiphy_ext_feature_set(wiphy,
NL80211_EXT_FEATURE_RADAR_BACKGROUND);
@@ -924,8 +925,7 @@ mt7915_set_stream_he_txbf_caps(struct mt7915_phy *phy,
c = IEEE80211_HE_PHY_CAP2_NDP_4x_LTF_AND_3_2US;
if (!is_mt7915(&dev->mt76))
- c |= IEEE80211_HE_PHY_CAP2_UL_MU_FULL_MU_MIMO |
- IEEE80211_HE_PHY_CAP2_UL_MU_PARTIAL_MU_MIMO;
+ c |= IEEE80211_HE_PHY_CAP2_UL_MU_FULL_MU_MIMO;
elem->phy_cap_info[2] |= c;
c = IEEE80211_HE_PHY_CAP4_SU_BEAMFORMEE |
diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mac.c b/drivers/net/wireless/mediatek/mt76/mt7915/mac.c
index 2ba6eb3038ce..9400e4af2a04 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7915/mac.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7915/mac.c
@@ -2035,16 +2035,15 @@ void mt7915_mac_work(struct work_struct *work)
static void mt7915_dfs_stop_radar_detector(struct mt7915_phy *phy)
{
struct mt7915_dev *dev = phy->dev;
+ int rdd_idx = mt7915_get_rdd_idx(phy, false);
- if (phy->rdd_state & BIT(0))
- mt76_connac_mcu_rdd_cmd(&dev->mt76, RDD_STOP, 0,
- MT_RX_SEL0, 0);
- if (phy->rdd_state & BIT(1))
- mt76_connac_mcu_rdd_cmd(&dev->mt76, RDD_STOP, 1,
- MT_RX_SEL0, 0);
+ if (rdd_idx < 0)
+ return;
+
+ mt76_connac_mcu_rdd_cmd(&dev->mt76, RDD_STOP, rdd_idx, 0, 0);
}
-static int mt7915_dfs_start_rdd(struct mt7915_dev *dev, int chain)
+static int mt7915_dfs_start_rdd(struct mt7915_dev *dev, int rdd_idx)
{
int err, region;
@@ -2061,52 +2060,38 @@ static int mt7915_dfs_start_rdd(struct mt7915_dev *dev, int chain)
break;
}
- err = mt76_connac_mcu_rdd_cmd(&dev->mt76, RDD_START, chain,
- MT_RX_SEL0, region);
+ err = mt76_connac_mcu_rdd_cmd(&dev->mt76, RDD_START, rdd_idx, 0, region);
if (err < 0)
return err;
if (is_mt7915(&dev->mt76)) {
- err = mt76_connac_mcu_rdd_cmd(&dev->mt76, RDD_SET_WF_ANT, chain,
+ err = mt76_connac_mcu_rdd_cmd(&dev->mt76, RDD_SET_WF_ANT, rdd_idx,
0, dev->dbdc_support ? 2 : 0);
if (err < 0)
return err;
}
- return mt76_connac_mcu_rdd_cmd(&dev->mt76, RDD_DET_MODE, chain,
- MT_RX_SEL0, 1);
+ return mt76_connac_mcu_rdd_cmd(&dev->mt76, RDD_DET_MODE, rdd_idx, 0, 1);
}
static int mt7915_dfs_start_radar_detector(struct mt7915_phy *phy)
{
- struct cfg80211_chan_def *chandef = &phy->mt76->chandef;
struct mt7915_dev *dev = phy->dev;
- int err;
+ int err, rdd_idx;
+
+ rdd_idx = mt7915_get_rdd_idx(phy, false);
+ if (rdd_idx < 0)
+ return -EINVAL;
/* start CAC */
- err = mt76_connac_mcu_rdd_cmd(&dev->mt76, RDD_CAC_START,
- phy->mt76->band_idx, MT_RX_SEL0, 0);
+ err = mt76_connac_mcu_rdd_cmd(&dev->mt76, RDD_CAC_START, rdd_idx, 0, 0);
if (err < 0)
return err;
- err = mt7915_dfs_start_rdd(dev, phy->mt76->band_idx);
+ err = mt7915_dfs_start_rdd(dev, rdd_idx);
if (err < 0)
return err;
- phy->rdd_state |= BIT(phy->mt76->band_idx);
-
- if (!is_mt7915(&dev->mt76))
- return 0;
-
- if (chandef->width == NL80211_CHAN_WIDTH_160 ||
- chandef->width == NL80211_CHAN_WIDTH_80P80) {
- err = mt7915_dfs_start_rdd(dev, 1);
- if (err < 0)
- return err;
-
- phy->rdd_state |= BIT(1);
- }
-
return 0;
}
@@ -2148,12 +2133,12 @@ int mt7915_dfs_init_radar_detector(struct mt7915_phy *phy)
{
struct mt7915_dev *dev = phy->dev;
enum mt76_dfs_state dfs_state, prev_state;
- int err;
+ int err, rdd_idx = mt7915_get_rdd_idx(phy, false);
prev_state = phy->mt76->dfs_state;
dfs_state = mt76_phy_dfs_state(phy->mt76);
- if (prev_state == dfs_state)
+ if (prev_state == dfs_state || rdd_idx < 0)
return 0;
if (prev_state == MT_DFS_STATE_UNKNOWN)
@@ -2177,8 +2162,7 @@ int mt7915_dfs_init_radar_detector(struct mt7915_phy *phy)
if (dfs_state == MT_DFS_STATE_CAC)
return 0;
- err = mt76_connac_mcu_rdd_cmd(&dev->mt76, RDD_CAC_END,
- phy->mt76->band_idx, MT_RX_SEL0, 0);
+ err = mt76_connac_mcu_rdd_cmd(&dev->mt76, RDD_CAC_END, rdd_idx, 0, 0);
if (err < 0) {
phy->mt76->dfs_state = MT_DFS_STATE_UNKNOWN;
return err;
@@ -2188,15 +2172,13 @@ int mt7915_dfs_init_radar_detector(struct mt7915_phy *phy)
return 0;
stop:
- err = mt76_connac_mcu_rdd_cmd(&dev->mt76, RDD_NORMAL_START,
- phy->mt76->band_idx, MT_RX_SEL0, 0);
+ err = mt76_connac_mcu_rdd_cmd(&dev->mt76, RDD_NORMAL_START, rdd_idx, 0, 0);
if (err < 0)
return err;
if (is_mt7915(&dev->mt76)) {
err = mt76_connac_mcu_rdd_cmd(&dev->mt76, RDD_SET_WF_ANT,
- phy->mt76->band_idx, 0,
- dev->dbdc_support ? 2 : 0);
+ rdd_idx, 0, dev->dbdc_support ? 2 : 0);
if (err < 0)
return err;
}
diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c
index 9d790f234e82..427542777abc 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c
@@ -303,17 +303,35 @@ mt7915_mcu_rx_radar_detected(struct mt7915_dev *dev, struct sk_buff *skb)
{
struct mt76_phy *mphy = &dev->mt76.phy;
struct mt7915_mcu_rdd_report *r;
+ u32 sku;
r = (struct mt7915_mcu_rdd_report *)skb->data;
- if (r->band_idx > MT_RX_SEL2)
+ switch (r->rdd_idx) {
+ case MT_RDD_IDX_BAND0:
+ break;
+ case MT_RDD_IDX_BAND1:
+ sku = mt7915_check_adie(dev, true);
+ /* the main phy is bound to band 1 for this sku */
+ if (is_mt7986(&dev->mt76) &&
+ (sku == MT7975_ONE_ADIE || sku == MT7976_ONE_ADIE))
+ break;
+ mphy = dev->mt76.phys[MT_BAND1];
+ break;
+ case MT_RDD_IDX_BACKGROUND:
+ if (!dev->rdd2_phy)
+ return;
+ mphy = dev->rdd2_phy->mt76;
+ break;
+ default:
+ dev_err(dev->mt76.dev, "Unknown RDD idx %d\n", r->rdd_idx);
return;
+ }
- if ((r->band_idx && !dev->phy.mt76->band_idx) &&
- dev->mt76.phys[MT_BAND1])
- mphy = dev->mt76.phys[MT_BAND1];
+ if (!mphy)
+ return;
- if (r->band_idx == MT_RX_SEL2)
+ if (r->rdd_idx == MT_RDD_IDX_BACKGROUND)
cfg80211_background_radar_event(mphy->hw->wiphy,
&dev->rdd2_chandef,
GFP_ATOMIC);
@@ -2697,11 +2715,14 @@ int mt7915_mcu_rdd_background_enable(struct mt7915_phy *phy,
struct cfg80211_chan_def *chandef)
{
struct mt7915_dev *dev = phy->dev;
- int err, region;
+ int err, region, rdd_idx;
+
+ rdd_idx = mt7915_get_rdd_idx(phy, true);
+ if (rdd_idx < 0)
+ return -EINVAL;
if (!chandef) { /* disable offchain */
- err = mt76_connac_mcu_rdd_cmd(&dev->mt76, RDD_STOP, MT_RX_SEL2,
- 0, 0);
+ err = mt76_connac_mcu_rdd_cmd(&dev->mt76, RDD_STOP, rdd_idx, 0, 0);
if (err)
return err;
@@ -2727,8 +2748,7 @@ int mt7915_mcu_rdd_background_enable(struct mt7915_phy *phy,
break;
}
- return mt76_connac_mcu_rdd_cmd(&dev->mt76, RDD_START, MT_RX_SEL2,
- 0, region);
+ return mt76_connac_mcu_rdd_cmd(&dev->mt76, RDD_START, rdd_idx, 0, region);
}
int mt7915_mcu_set_chan_info(struct mt7915_phy *phy, int cmd)
@@ -2859,7 +2879,7 @@ int mt7915_mcu_set_eeprom(struct mt7915_dev *dev)
&req, sizeof(req), true);
}
-int mt7915_mcu_get_eeprom(struct mt7915_dev *dev, u32 offset)
+int mt7915_mcu_get_eeprom(struct mt7915_dev *dev, u32 offset, u8 *read_buf)
{
struct mt7915_mcu_eeprom_info req = {
.addr = cpu_to_le32(round_down(offset,
@@ -2867,8 +2887,8 @@ int mt7915_mcu_get_eeprom(struct mt7915_dev *dev, u32 offset)
};
struct mt7915_mcu_eeprom_info *res;
struct sk_buff *skb;
+ u8 *buf = read_buf;
int ret;
- u8 *buf;
ret = mt76_mcu_send_and_get_msg(&dev->mt76,
MCU_EXT_QUERY(EFUSE_ACCESS),
@@ -2877,8 +2897,10 @@ int mt7915_mcu_get_eeprom(struct mt7915_dev *dev, u32 offset)
return ret;
res = (struct mt7915_mcu_eeprom_info *)skb->data;
- buf = dev->mt76.eeprom.data + le32_to_cpu(res->addr);
+ if (!buf)
+ buf = dev->mt76.eeprom.data + le32_to_cpu(res->addr);
memcpy(buf, res->data, MT7915_EEPROM_BLOCK_SIZE);
+
dev_kfree_skb(skb);
return 0;
@@ -3323,7 +3345,7 @@ int mt7915_mcu_set_txpower_frame(struct mt7915_phy *phy,
if (ret)
return ret;
- txpower = mt7915_get_power_bound(phy, txpower);
+ txpower = mt76_get_power_bound(mphy, txpower);
if (txpower > mphy->txpower_cur || txpower < 0)
return -EINVAL;
@@ -3373,7 +3395,7 @@ int mt7915_mcu_set_txpower_sku(struct mt7915_phy *phy)
int i, idx;
int tx_power;
- tx_power = mt7915_get_power_bound(phy, hw->conf.power_level);
+ tx_power = mt76_get_power_bound(mphy, hw->conf.power_level);
tx_power = mt76_get_rate_power_limits(mphy, mphy->chandef.chan,
&limits_array, tx_power);
mphy->txpower_cur = tx_power;
diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.h b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.h
index 49476a4182fd..086ad89ecd91 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.h
+++ b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.h
@@ -57,7 +57,7 @@ struct mt7915_mcu_bcc_notify {
struct mt7915_mcu_rdd_report {
struct mt76_connac2_mcu_rxd_hdr rxd;
- u8 band_idx;
+ u8 rdd_idx;
u8 long_detected;
u8 constant_prf_detected;
u8 staggered_prf_detected;
@@ -515,16 +515,4 @@ enum {
sizeof(struct bss_info_bmc_rate) +\
sizeof(struct bss_info_ext_bss))
-static inline s8
-mt7915_get_power_bound(struct mt7915_phy *phy, s8 txpower)
-{
- struct mt76_phy *mphy = phy->mt76;
- int n_chains = hweight16(mphy->chainmask);
-
- txpower = mt76_get_sar_power(mphy, mphy->chandef.chan, txpower * 2);
- txpower -= mt76_tx_power_nss_delta(n_chains);
-
- return txpower;
-}
-
#endif
diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mmio.c b/drivers/net/wireless/mediatek/mt76/mt7915/mmio.c
index 876f0692850a..9c4d5cea0c42 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7915/mmio.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7915/mmio.c
@@ -651,6 +651,9 @@ int mt7915_mmio_wed_init(struct mt7915_dev *dev, void *pdev_ptr,
wed->wlan.base = devm_ioremap(dev->mt76.dev,
pci_resource_start(pci_dev, 0),
pci_resource_len(pci_dev, 0));
+ if (!wed->wlan.base)
+ return -ENOMEM;
+
wed->wlan.phy_base = pci_resource_start(pci_dev, 0);
wed->wlan.wpdma_int = pci_resource_start(pci_dev, 0) +
MT_INT_WED_SOURCE_CSR;
@@ -678,6 +681,9 @@ int mt7915_mmio_wed_init(struct mt7915_dev *dev, void *pdev_ptr,
wed->wlan.bus_type = MTK_WED_BUS_AXI;
wed->wlan.base = devm_ioremap(dev->mt76.dev, res->start,
resource_size(res));
+ if (!wed->wlan.base)
+ return -ENOMEM;
+
wed->wlan.phy_base = res->start;
wed->wlan.wpdma_int = res->start + MT_INT_SOURCE_CSR;
wed->wlan.wpdma_mask = res->start + MT_INT_MASK_CSR;
diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h b/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h
index 533939f2b7ed..2e94347c46d6 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h
+++ b/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h
@@ -215,8 +215,6 @@ struct mt7915_phy {
s16 coverage_class;
u8 slottime;
- u8 rdd_state;
-
u32 trb_ts;
u32 rx_ampdu_ts;
@@ -331,10 +329,10 @@ enum {
__MT_WFDMA_MAX,
};
-enum {
- MT_RX_SEL0,
- MT_RX_SEL1,
- MT_RX_SEL2, /* monitor chain */
+enum rdd_idx {
+ MT_RDD_IDX_BAND0, /* RDD idx for band idx 0 (single-band) */
+ MT_RDD_IDX_BAND1, /* RDD idx for band idx 1 */
+ MT_RDD_IDX_BACKGROUND, /* RDD idx for background chain */
};
enum mt7915_rdd_cmd {
@@ -354,6 +352,18 @@ enum mt7915_rdd_cmd {
RDD_IRQ_OFF,
};
+static inline int
+mt7915_get_rdd_idx(struct mt7915_phy *phy, bool is_background)
+{
+ if (!phy->mt76->cap.has_5ghz)
+ return -1;
+
+ if (is_background)
+ return MT_RDD_IDX_BACKGROUND;
+
+ return phy->mt76->band_idx;
+}
+
static inline struct mt7915_phy *
mt7915_hw_phy(struct ieee80211_hw *hw)
{
@@ -425,6 +435,7 @@ int mt7915_eeprom_get_target_power(struct mt7915_dev *dev,
struct ieee80211_channel *chan,
u8 chain_idx);
s8 mt7915_eeprom_get_power_delta(struct mt7915_dev *dev, int band);
+bool mt7915_eeprom_has_background_radar(struct mt7915_dev *dev);
int mt7915_dma_init(struct mt7915_dev *dev, struct mt7915_phy *phy2);
void mt7915_dma_prefetch(struct mt7915_dev *dev);
void mt7915_dma_cleanup(struct mt7915_dev *dev);
@@ -473,7 +484,7 @@ int mt7915_mcu_set_fixed_rate_ctrl(struct mt7915_dev *dev,
struct ieee80211_sta *sta,
void *data, u32 field);
int mt7915_mcu_set_eeprom(struct mt7915_dev *dev);
-int mt7915_mcu_get_eeprom(struct mt7915_dev *dev, u32 offset);
+int mt7915_mcu_get_eeprom(struct mt7915_dev *dev, u32 offset, u8 *read_buf);
int mt7915_mcu_get_eeprom_free_block(struct mt7915_dev *dev, u8 *block_num);
int mt7915_mcu_set_mac(struct mt7915_dev *dev, int band, bool enable,
bool hdr_trans);
diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/main.c b/drivers/net/wireless/mediatek/mt76/mt7921/main.c
index 13e58c328aff..1fffa43379b2 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7921/main.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7921/main.c
@@ -83,6 +83,11 @@ mt7921_init_he_caps(struct mt792x_phy *phy, enum nl80211_band band,
he_cap_elem->phy_cap_info[9] |=
IEEE80211_HE_PHY_CAP9_TX_1024_QAM_LESS_THAN_242_TONE_RU |
IEEE80211_HE_PHY_CAP9_RX_1024_QAM_LESS_THAN_242_TONE_RU;
+
+ if (is_mt7922(phy->mt76->dev)) {
+ he_cap_elem->phy_cap_info[0] |=
+ IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_160MHZ_IN_5G;
+ }
break;
case NL80211_IFTYPE_STATION:
he_cap_elem->mac_cap_info[1] |=
@@ -364,7 +369,7 @@ void mt7921_roc_abort_sync(struct mt792x_dev *dev)
{
struct mt792x_phy *phy = &dev->phy;
- del_timer_sync(&phy->roc_timer);
+ timer_delete_sync(&phy->roc_timer);
cancel_work_sync(&phy->roc_work);
if (test_and_clear_bit(MT76_STATE_ROC, &phy->mt76->state))
ieee80211_iterate_interfaces(mt76_hw(dev),
@@ -395,7 +400,7 @@ static int mt7921_abort_roc(struct mt792x_phy *phy, struct mt792x_vif *vif)
{
int err = 0;
- del_timer_sync(&phy->roc_timer);
+ timer_delete_sync(&phy->roc_timer);
cancel_work_sync(&phy->roc_work);
mt792x_mutex_acquire(phy->dev);
@@ -811,6 +816,7 @@ int mt7921_mac_sta_add(struct mt76_dev *mdev, struct ieee80211_vif *vif,
msta->deflink.wcid.phy_idx = mvif->bss_conf.mt76.band_idx;
msta->deflink.wcid.tx_info |= MT_WCID_TX_INFO_SET;
msta->deflink.last_txs = jiffies;
+ msta->deflink.sta = msta;
ret = mt76_connac_pm_wake(&dev->mphy, &dev->pm);
if (ret)
@@ -1475,7 +1481,7 @@ static void mt7921_abort_channel_switch(struct ieee80211_hw *hw,
{
struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv;
- del_timer_sync(&mvif->csa_timer);
+ timer_delete_sync(&mvif->csa_timer);
cancel_work_sync(&mvif->csa_work);
}
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/Makefile b/drivers/net/wireless/mediatek/mt76/mt7925/Makefile
index d321e4ed732f..ade5e647c941 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/Makefile
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/Makefile
@@ -5,5 +5,6 @@ obj-$(CONFIG_MT7925E) += mt7925e.o
obj-$(CONFIG_MT7925U) += mt7925u.o
mt7925-common-y := mac.o mcu.o main.o init.o debugfs.o
+mt7925-common-$(CONFIG_NL80211_TESTMODE) += testmode.o
mt7925e-y := pci.o pci_mac.o pci_mcu.o
mt7925u-y := usb.o
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/init.c b/drivers/net/wireless/mediatek/mt76/mt7925/init.c
index f41ca4248497..2a83ff59a968 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/init.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/init.c
@@ -58,15 +58,110 @@ static int mt7925_thermal_init(struct mt792x_phy *phy)
return PTR_ERR_OR_ZERO(hwmon);
}
+void mt7925_regd_be_ctrl(struct mt792x_dev *dev, u8 *alpha2)
+{
+ struct mt792x_phy *phy = &dev->phy;
+ struct mt7925_clc_rule_v2 *rule;
+ struct mt7925_clc *clc;
+ bool old = dev->has_eht, new = true;
+ u32 mtcl_conf = mt792x_acpi_get_mtcl_conf(&dev->phy, alpha2);
+ u8 *pos;
+
+ if (mtcl_conf != MT792X_ACPI_MTCL_INVALID &&
+ (((mtcl_conf >> 4) & 0x3) == 0)) {
+ new = false;
+ goto out;
+ }
+
+ if (!phy->clc[MT792x_CLC_BE_CTRL])
+ goto out;
+
+ clc = (struct mt7925_clc *)phy->clc[MT792x_CLC_BE_CTRL];
+ pos = clc->data;
+
+ while (1) {
+ rule = (struct mt7925_clc_rule_v2 *)pos;
+
+ if (rule->alpha2[0] == alpha2[0] &&
+ rule->alpha2[1] == alpha2[1]) {
+ new = false;
+ break;
+ }
+
+ /* Check the last one */
+ if (rule->flag & BIT(0))
+ break;
+
+ pos += sizeof(*rule);
+ }
+
+out:
+ if (old == new)
+ return;
+
+ dev->has_eht = new;
+ mt7925_set_stream_he_eht_caps(phy);
+}
+
+static void
+mt7925_regd_channel_update(struct wiphy *wiphy, struct mt792x_dev *dev)
+{
+#define IS_UNII_INVALID(idx, sfreq, efreq, cfreq) \
+ (!(dev->phy.clc_chan_conf & BIT(idx)) && (cfreq) >= (sfreq) && (cfreq) <= (efreq))
+#define MT7925_UNII_59G_IS_VALID 0x1
+#define MT7925_UNII_6G_IS_VALID 0x1e
+ struct ieee80211_supported_band *sband;
+ struct mt76_dev *mdev = &dev->mt76;
+ struct ieee80211_channel *ch;
+ u32 mtcl_conf = mt792x_acpi_get_mtcl_conf(&dev->phy, mdev->alpha2);
+ int i;
+
+ if (mtcl_conf != MT792X_ACPI_MTCL_INVALID) {
+ if ((mtcl_conf & 0x3) == 0)
+ dev->phy.clc_chan_conf &= ~MT7925_UNII_59G_IS_VALID;
+ if (((mtcl_conf >> 2) & 0x3) == 0)
+ dev->phy.clc_chan_conf &= ~MT7925_UNII_6G_IS_VALID;
+ }
+
+ sband = wiphy->bands[NL80211_BAND_5GHZ];
+ if (!sband)
+ return;
+
+ for (i = 0; i < sband->n_channels; i++) {
+ ch = &sband->channels[i];
+
+ /* UNII-4 */
+ if (IS_UNII_INVALID(0, 5845, 5925, ch->center_freq))
+ ch->flags |= IEEE80211_CHAN_DISABLED;
+ }
+
+ sband = wiphy->bands[NL80211_BAND_6GHZ];
+ if (!sband)
+ return;
+
+ for (i = 0; i < sband->n_channels; i++) {
+ ch = &sband->channels[i];
+
+ /* UNII-5/6/7/8 */
+ if (IS_UNII_INVALID(1, 5925, 6425, ch->center_freq) ||
+ IS_UNII_INVALID(2, 6425, 6525, ch->center_freq) ||
+ IS_UNII_INVALID(3, 6525, 6875, ch->center_freq) ||
+ IS_UNII_INVALID(4, 6875, 7125, ch->center_freq))
+ ch->flags |= IEEE80211_CHAN_DISABLED;
+ }
+}
+
void mt7925_regd_update(struct mt792x_dev *dev)
{
struct mt76_dev *mdev = &dev->mt76;
struct ieee80211_hw *hw = mdev->hw;
+ struct wiphy *wiphy = hw->wiphy;
if (!dev->regd_change)
return;
mt7925_mcu_set_clc(dev, mdev->alpha2, dev->country_ie_env);
+ mt7925_regd_channel_update(wiphy, dev);
mt7925_mcu_set_channel_domain(hw->priv);
mt7925_set_tx_sar_pwr(hw, NULL);
dev->regd_change = false;
@@ -227,6 +322,12 @@ static void mt7925_init_work(struct work_struct *work)
return;
}
+ ret = mt7925_mcu_set_thermal_protect(dev);
+ if (ret) {
+ dev_err(dev->mt76.dev, "thermal protection enable failed\n");
+ return;
+ }
+
/* we support chip reset now */
dev->hw_init_done = true;
@@ -244,6 +345,7 @@ int mt7925_register_device(struct mt792x_dev *dev)
dev->mt76.tx_worker.fn = mt792x_tx_worker;
INIT_DELAYED_WORK(&dev->pm.ps_work, mt792x_pm_power_save_work);
+ INIT_DELAYED_WORK(&dev->mlo_pm_work, mt7925_mlo_pm_work);
INIT_WORK(&dev->pm.wake_work, mt792x_pm_wake_work);
spin_lock_init(&dev->pm.wake.lock);
mutex_init(&dev->pm.mutex);
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/main.c b/drivers/net/wireless/mediatek/mt76/mt7925/main.c
index 98daf80ac131..94b0099dcd41 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/main.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/main.c
@@ -251,12 +251,12 @@ int mt7925_init_mlo_caps(struct mt792x_phy *phy)
},
};
- if (!(phy->chip_cap & MT792x_CHIP_CAP_MLO_EVT_EN))
+ if (!(phy->chip_cap & MT792x_CHIP_CAP_MLO_EN))
return 0;
ext_capab[0].eml_capabilities = phy->eml_cap;
ext_capab[0].mld_capa_and_ops =
- u16_encode_bits(1, IEEE80211_MLD_CAP_OP_MAX_SIMUL_LINKS);
+ u16_encode_bits(0, IEEE80211_MLD_CAP_OP_MAX_SIMUL_LINKS);
wiphy->flags |= WIPHY_FLAG_SUPPORTS_MLO;
wiphy->iftype_ext_capab = ext_capab;
@@ -334,6 +334,9 @@ int __mt7925_start(struct mt792x_phy *phy)
ieee80211_queue_delayed_work(mphy->hw, &mphy->mac_work,
MT792x_WATCHDOG_TIME);
+ if (phy->chip_cap & MT792x_CHIP_CAP_WF_RF_PIN_CTRL_EVT_EN)
+ wiphy_rfkill_start_polling(mphy->hw->wiphy);
+
return 0;
}
EXPORT_SYMBOL_GPL(__mt7925_start);
@@ -360,10 +363,15 @@ static int mt7925_mac_link_bss_add(struct mt792x_dev *dev,
struct mt76_txq *mtxq;
int idx, ret = 0;
- mconf->mt76.idx = __ffs64(~dev->mt76.vif_mask);
- if (mconf->mt76.idx >= MT792x_MAX_INTERFACES) {
- ret = -ENOSPC;
- goto out;
+ if (vif->type == NL80211_IFTYPE_P2P_DEVICE) {
+ mconf->mt76.idx = MT792x_MAX_INTERFACES;
+ } else {
+ mconf->mt76.idx = __ffs64(~dev->mt76.vif_mask);
+
+ if (mconf->mt76.idx >= MT792x_MAX_INTERFACES) {
+ ret = -ENOSPC;
+ goto out;
+ }
}
mconf->mt76.omac_idx = ieee80211_vif_is_mld(vif) ?
@@ -371,6 +379,7 @@ static int mt7925_mac_link_bss_add(struct mt792x_dev *dev,
mconf->mt76.band_idx = 0xff;
mconf->mt76.wmm_idx = ieee80211_vif_is_mld(vif) ?
0 : mconf->mt76.idx % MT76_CONNAC_MAX_WMM_SETS;
+ mconf->mt76.link_idx = hweight16(mvif->valid_links);
if (mvif->phy->mt76->chandef.chan->band != NL80211_BAND_2GHZ)
mconf->mt76.basic_rates_idx = MT792x_BASIC_RATES_TBL + 4;
@@ -421,6 +430,7 @@ mt7925_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
mvif->bss_conf.vif = mvif;
mvif->sta.vif = mvif;
mvif->deflink_id = IEEE80211_LINK_UNSPECIFIED;
+ mvif->mlo_pm_state = MT792x_MLO_LINK_DISASSOC;
ret = mt7925_mac_link_bss_add(dev, &vif->bss_conf, &mvif->sta.deflink);
if (ret < 0)
@@ -446,7 +456,7 @@ void mt7925_roc_abort_sync(struct mt792x_dev *dev)
{
struct mt792x_phy *phy = &dev->phy;
- del_timer_sync(&phy->roc_timer);
+ timer_delete_sync(&phy->roc_timer);
cancel_work_sync(&phy->roc_work);
if (test_and_clear_bit(MT76_STATE_ROC, &phy->mt76->state))
ieee80211_iterate_interfaces(mt76_hw(dev),
@@ -478,7 +488,7 @@ static int mt7925_abort_roc(struct mt792x_phy *phy,
{
int err = 0;
- del_timer_sync(&phy->roc_timer);
+ timer_delete_sync(&phy->roc_timer);
cancel_work_sync(&phy->roc_work);
mt792x_mutex_acquire(phy->dev);
@@ -1149,7 +1159,12 @@ static void mt7925_mac_link_sta_remove(struct mt76_dev *mdev,
struct mt792x_bss_conf *mconf;
mconf = mt792x_link_conf_to_mconf(link_conf);
- mt792x_mac_link_bss_remove(dev, mconf, mlink);
+
+ if (ieee80211_vif_is_mld(vif))
+ mt792x_mac_link_bss_remove(dev, mconf, mlink);
+ else
+ mt7925_mcu_add_bss_info(&dev->phy, mconf->mt76.ctx, link_conf,
+ link_sta, false);
}
spin_lock_bh(&mdev->sta_poll_lock);
@@ -1169,6 +1184,31 @@ mt7925_mac_sta_remove_links(struct mt792x_dev *dev, struct ieee80211_vif *vif,
struct mt76_wcid *wcid;
unsigned int link_id;
+ /* clean up bss before starec */
+ for_each_set_bit(link_id, &old_links, IEEE80211_MLD_MAX_NUM_LINKS) {
+ struct ieee80211_link_sta *link_sta;
+ struct ieee80211_bss_conf *link_conf;
+ struct mt792x_bss_conf *mconf;
+ struct mt792x_link_sta *mlink;
+
+ link_sta = mt792x_sta_to_link_sta(vif, sta, link_id);
+ if (!link_sta)
+ continue;
+
+ mlink = mt792x_sta_to_link(msta, link_id);
+ if (!mlink)
+ continue;
+
+ link_conf = mt792x_vif_to_bss_conf(vif, link_id);
+ if (!link_conf)
+ continue;
+
+ mconf = mt792x_link_conf_to_mconf(link_conf);
+
+ mt7925_mcu_add_bss_info(&dev->phy, mconf->mt76.ctx, link_conf,
+ link_sta, false);
+ }
+
for_each_set_bit(link_id, &old_links, IEEE80211_MLD_MAX_NUM_LINKS) {
struct ieee80211_link_sta *link_sta;
struct mt792x_link_sta *mlink;
@@ -1206,51 +1246,22 @@ void mt7925_mac_sta_remove(struct mt76_dev *mdev, struct ieee80211_vif *vif,
{
struct mt792x_dev *dev = container_of(mdev, struct mt792x_dev, mt76);
struct mt792x_sta *msta = (struct mt792x_sta *)sta->drv_priv;
- struct {
- struct {
- u8 omac_idx;
- u8 band_idx;
- __le16 pad;
- } __packed hdr;
- struct req_tlv {
- __le16 tag;
- __le16 len;
- u8 active;
- u8 link_idx; /* hw link idx */
- u8 omac_addr[ETH_ALEN];
- } __packed tlv;
- } dev_req = {
- .hdr = {
- .omac_idx = 0,
- .band_idx = 0,
- },
- .tlv = {
- .tag = cpu_to_le16(DEV_INFO_ACTIVE),
- .len = cpu_to_le16(sizeof(struct req_tlv)),
- .active = true,
- },
- };
+ struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv;
unsigned long rem;
rem = ieee80211_vif_is_mld(vif) ? msta->valid_links : BIT(0);
mt7925_mac_sta_remove_links(dev, vif, sta, rem);
- if (ieee80211_vif_is_mld(vif)) {
- mt7925_mcu_set_dbdc(&dev->mphy, false);
-
- /* recovery omac address for the legacy interface */
- memcpy(dev_req.tlv.omac_addr, vif->addr, ETH_ALEN);
- mt76_mcu_send_msg(mdev, MCU_UNI_CMD(DEV_INFO_UPDATE),
- &dev_req, sizeof(dev_req), true);
- }
+ if (ieee80211_vif_is_mld(vif))
+ mt7925_mcu_del_dev(mdev, vif);
if (vif->type == NL80211_IFTYPE_STATION) {
- struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv;
-
mvif->wep_sta = NULL;
ewma_rssi_init(&mvif->bss_conf.rssi);
}
+
+ mvif->mlo_pm_state = MT792x_MLO_LINK_DISASSOC;
}
EXPORT_SYMBOL_GPL(mt7925_mac_sta_remove);
@@ -1289,22 +1300,22 @@ mt7925_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
case IEEE80211_AMPDU_RX_START:
mt76_rx_aggr_start(&dev->mt76, &msta->deflink.wcid, tid, ssn,
params->buf_size);
- mt7925_mcu_uni_rx_ba(dev, vif, params, true);
+ mt7925_mcu_uni_rx_ba(dev, params, true);
break;
case IEEE80211_AMPDU_RX_STOP:
mt76_rx_aggr_stop(&dev->mt76, &msta->deflink.wcid, tid);
- mt7925_mcu_uni_rx_ba(dev, vif, params, false);
+ mt7925_mcu_uni_rx_ba(dev, params, false);
break;
case IEEE80211_AMPDU_TX_OPERATIONAL:
mtxq->aggr = true;
mtxq->send_bar = false;
- mt7925_mcu_uni_tx_ba(dev, vif, params, true);
+ mt7925_mcu_uni_tx_ba(dev, params, true);
break;
case IEEE80211_AMPDU_TX_STOP_FLUSH:
case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT:
mtxq->aggr = false;
clear_bit(tid, &msta->deflink.wcid.ampdu_state);
- mt7925_mcu_uni_tx_ba(dev, vif, params, false);
+ mt7925_mcu_uni_tx_ba(dev, params, false);
break;
case IEEE80211_AMPDU_TX_START:
set_bit(tid, &msta->deflink.wcid.ampdu_state);
@@ -1313,7 +1324,7 @@ mt7925_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
case IEEE80211_AMPDU_TX_STOP_CONT:
mtxq->aggr = false;
clear_bit(tid, &msta->deflink.wcid.ampdu_state);
- mt7925_mcu_uni_tx_ba(dev, vif, params, false);
+ mt7925_mcu_uni_tx_ba(dev, params, false);
ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid);
break;
}
@@ -1322,6 +1333,38 @@ mt7925_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
return ret;
}
+static void
+mt7925_mlo_pm_iter(void *priv, u8 *mac, struct ieee80211_vif *vif)
+{
+ struct mt792x_dev *dev = priv;
+ struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv;
+ unsigned long valid = ieee80211_vif_is_mld(vif) ?
+ mvif->valid_links : BIT(0);
+ struct ieee80211_bss_conf *bss_conf;
+ int i;
+
+ if (mvif->mlo_pm_state != MT792x_MLO_CHANGED_PS)
+ return;
+
+ mt792x_mutex_acquire(dev);
+ for_each_set_bit(i, &valid, IEEE80211_MLD_MAX_NUM_LINKS) {
+ bss_conf = mt792x_vif_to_bss_conf(vif, i);
+ mt7925_mcu_uni_bss_ps(dev, bss_conf);
+ }
+ mt792x_mutex_release(dev);
+}
+
+void mt7925_mlo_pm_work(struct work_struct *work)
+{
+ struct mt792x_dev *dev = container_of(work, struct mt792x_dev,
+ mlo_pm_work.work);
+ struct ieee80211_hw *hw = mt76_hw(dev);
+
+ ieee80211_iterate_active_interfaces(hw,
+ IEEE80211_IFACE_ITER_RESUME_ALL,
+ mt7925_mlo_pm_iter, dev);
+}
+
static bool is_valid_alpha2(const char *alpha2)
{
if (!alpha2)
@@ -1378,6 +1421,8 @@ void mt7925_scan_work(struct work_struct *work)
if (!is_valid_alpha2(evt->alpha2))
break;
+ mt7925_regd_be_ctrl(phy->dev, evt->alpha2);
+
if (mdev->alpha2[0] != '0' && mdev->alpha2[1] != '0')
break;
@@ -1823,6 +1868,10 @@ mt7925_change_chanctx(struct ieee80211_hw *hw,
link_conf = mt792x_vif_to_bss_conf(vif, mconf->link_id);
mt7925_mcu_set_chctx(mvif->phy->mt76, &mconf->mt76,
link_conf, ctx);
+
+ if (changed & IEEE80211_CHANCTX_CHANGE_PUNCTURING)
+ mt7925_mcu_set_eht_pp(mvif->phy->mt76, &mconf->mt76,
+ link_conf, ctx);
}
}
@@ -1871,6 +1920,9 @@ static void mt7925_vif_cfg_changed(struct ieee80211_hw *hw,
mt7925_mcu_sta_update(dev, NULL, vif, true,
MT76_STA_INFO_STATE_ASSOC);
mt7925_mcu_set_beacon_filter(dev, vif, vif->cfg.assoc);
+
+ if (ieee80211_vif_is_mld(vif))
+ mvif->mlo_pm_state = MT792x_MLO_LINK_ASSOC;
}
if (changed & BSS_CHANGED_ARP_FILTER) {
@@ -1881,9 +1933,19 @@ static void mt7925_vif_cfg_changed(struct ieee80211_hw *hw,
}
if (changed & BSS_CHANGED_PS) {
- for_each_set_bit(i, &valid, IEEE80211_MLD_MAX_NUM_LINKS) {
- bss_conf = mt792x_vif_to_bss_conf(vif, i);
+ if (hweight16(mvif->valid_links) < 2) {
+ /* legacy */
+ bss_conf = &vif->bss_conf;
mt7925_mcu_uni_bss_ps(dev, bss_conf);
+ } else {
+ if (mvif->mlo_pm_state == MT792x_MLO_LINK_ASSOC) {
+ mvif->mlo_pm_state = MT792x_MLO_CHANGED_PS_PENDING;
+ } else if (mvif->mlo_pm_state == MT792x_MLO_CHANGED_PS) {
+ for_each_set_bit(i, &valid, IEEE80211_MLD_MAX_NUM_LINKS) {
+ bss_conf = mt792x_vif_to_bss_conf(vif, i);
+ mt7925_mcu_uni_bss_ps(dev, bss_conf);
+ }
+ }
}
}
@@ -1899,8 +1961,10 @@ static void mt7925_link_info_changed(struct ieee80211_hw *hw,
struct mt792x_phy *phy = mt792x_hw_phy(hw);
struct mt792x_dev *dev = mt792x_hw_dev(hw);
struct mt792x_bss_conf *mconf;
+ struct ieee80211_bss_conf *link_conf;
mconf = mt792x_vif_to_link(mvif, info->link_id);
+ link_conf = mt792x_vif_to_bss_conf(vif, mconf->link_id);
mt792x_mutex_acquire(dev);
@@ -1934,13 +1998,18 @@ static void mt7925_link_info_changed(struct ieee80211_hw *hw,
if (changed & (BSS_CHANGED_QOS | BSS_CHANGED_BEACON_ENABLED))
mt7925_mcu_set_tx(dev, info);
- if (changed & BSS_CHANGED_BSSID) {
- if (ieee80211_vif_is_mld(vif) &&
- hweight16(mvif->valid_links) == 2)
- /* Indicate the secondary setup done */
- mt7925_mcu_uni_bss_bcnft(dev, info, true);
+ if (mvif->mlo_pm_state == MT792x_MLO_CHANGED_PS_PENDING) {
+ /* Indicate the secondary setup done */
+ mt7925_mcu_uni_bss_bcnft(dev, info, true);
+
+ ieee80211_queue_delayed_work(hw, &dev->mlo_pm_work, 5 * HZ);
+ mvif->mlo_pm_state = MT792x_MLO_CHANGED_PS;
}
+ if (changed & IEEE80211_CHANCTX_CHANGE_PUNCTURING)
+ mt7925_mcu_set_eht_pp(mvif->phy->mt76, &mconf->mt76,
+ link_conf, NULL);
+
mt792x_mutex_release(dev);
}
@@ -2022,8 +2091,6 @@ mt7925_change_vif_links(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
goto free;
if (mconf != &mvif->bss_conf) {
- mt7925_mcu_set_bss_pm(dev, link_conf, true);
-
err = mt7925_set_mlo_roc(phy, &mvif->bss_conf,
vif->active_links);
if (err < 0)
@@ -2141,6 +2208,18 @@ static void mt7925_unassign_vif_chanctx(struct ieee80211_hw *hw,
mutex_unlock(&dev->mt76.mutex);
}
+static void mt7925_rfkill_poll(struct ieee80211_hw *hw)
+{
+ struct mt792x_phy *phy = mt792x_hw_phy(hw);
+ int ret;
+
+ mt792x_mutex_acquire(phy->dev);
+ ret = mt7925_mcu_wf_rf_pin_ctrl(phy);
+ mt792x_mutex_release(phy->dev);
+
+ wiphy_rfkill_set_hw_state(hw->wiphy, ret == 0);
+}
+
const struct ieee80211_ops mt7925_ops = {
.tx = mt792x_tx,
.start = mt7925_start,
@@ -2180,6 +2259,8 @@ const struct ieee80211_ops mt7925_ops = {
.sta_statistics = mt792x_sta_statistics,
.sched_scan_start = mt7925_start_sched_scan,
.sched_scan_stop = mt7925_stop_sched_scan,
+ CFG80211_TESTMODE_CMD(mt7925_testmode_cmd)
+ CFG80211_TESTMODE_DUMP(mt7925_testmode_dump)
#ifdef CONFIG_PM
.suspend = mt7925_suspend,
.resume = mt7925_resume,
@@ -2201,6 +2282,7 @@ const struct ieee80211_ops mt7925_ops = {
.link_info_changed = mt7925_link_info_changed,
.change_vif_links = mt7925_change_vif_links,
.change_sta_links = mt7925_change_sta_links,
+ .rfkill_poll = mt7925_rfkill_poll,
};
EXPORT_SYMBOL_GPL(mt7925_ops);
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c
index 15815ad84713..b8542be0d945 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c
@@ -348,14 +348,10 @@ mt7925_mcu_handle_hif_ctrl_basic(struct mt792x_dev *dev, struct tlv *tlv)
basic = (struct mt7925_mcu_hif_ctrl_basic_tlv *)tlv;
if (basic->hifsuspend) {
- if (basic->hif_tx_traffic_status == HIF_TRAFFIC_IDLE &&
- basic->hif_rx_traffic_status == HIF_TRAFFIC_IDLE)
- /* success */
- dev->hif_idle = true;
- else
- /* busy */
- /* invalid */
- dev->hif_idle = false;
+ dev->hif_idle = true;
+ if (!(basic->hif_tx_traffic_status == HIF_TRAFFIC_IDLE &&
+ basic->hif_rx_traffic_status == HIF_TRAFFIC_IDLE))
+ dev_info(dev->mt76.dev, "Hif traffic not idle.\n");
} else {
dev->hif_resumed = true;
}
@@ -576,10 +572,10 @@ void mt7925_mcu_rx_event(struct mt792x_dev *dev, struct sk_buff *skb)
static int
mt7925_mcu_sta_ba(struct mt76_dev *dev, struct mt76_vif_link *mvif,
- struct mt76_wcid *wcid,
struct ieee80211_ampdu_params *params,
bool enable, bool tx)
{
+ struct mt76_wcid *wcid = (struct mt76_wcid *)params->sta->drv_priv;
struct sta_rec_ba_uni *ba;
struct sk_buff *skb;
struct tlv *tlv;
@@ -607,60 +603,76 @@ mt7925_mcu_sta_ba(struct mt76_dev *dev, struct mt76_vif_link *mvif,
/** starec & wtbl **/
int mt7925_mcu_uni_tx_ba(struct mt792x_dev *dev,
- struct ieee80211_vif *vif,
struct ieee80211_ampdu_params *params,
bool enable)
{
struct mt792x_sta *msta = (struct mt792x_sta *)params->sta->drv_priv;
- struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv;
- struct mt792x_link_sta *mlink;
- struct mt792x_bss_conf *mconf;
- unsigned long usable_links = ieee80211_vif_usable_links(vif);
- struct mt76_wcid *wcid;
- u8 link_id, ret;
-
- for_each_set_bit(link_id, &usable_links, IEEE80211_MLD_MAX_NUM_LINKS) {
- mconf = mt792x_vif_to_link(mvif, link_id);
- mlink = mt792x_sta_to_link(msta, link_id);
- wcid = &mlink->wcid;
+ struct mt792x_vif *mvif = msta->vif;
- if (enable && !params->amsdu)
- mlink->wcid.amsdu = false;
+ if (enable && !params->amsdu)
+ msta->deflink.wcid.amsdu = false;
- ret = mt7925_mcu_sta_ba(&dev->mt76, &mconf->mt76, wcid, params,
- enable, true);
- if (ret < 0)
- break;
- }
-
- return ret;
+ return mt7925_mcu_sta_ba(&dev->mt76, &mvif->bss_conf.mt76, params,
+ enable, true);
}
int mt7925_mcu_uni_rx_ba(struct mt792x_dev *dev,
- struct ieee80211_vif *vif,
struct ieee80211_ampdu_params *params,
bool enable)
{
struct mt792x_sta *msta = (struct mt792x_sta *)params->sta->drv_priv;
- struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv;
- struct mt792x_link_sta *mlink;
- struct mt792x_bss_conf *mconf;
- unsigned long usable_links = ieee80211_vif_usable_links(vif);
- struct mt76_wcid *wcid;
- u8 link_id, ret;
+ struct mt792x_vif *mvif = msta->vif;
- for_each_set_bit(link_id, &usable_links, IEEE80211_MLD_MAX_NUM_LINKS) {
- mconf = mt792x_vif_to_link(mvif, link_id);
- mlink = mt792x_sta_to_link(msta, link_id);
- wcid = &mlink->wcid;
+ return mt7925_mcu_sta_ba(&dev->mt76, &mvif->bss_conf.mt76, params,
+ enable, false);
+}
- ret = mt7925_mcu_sta_ba(&dev->mt76, &mconf->mt76, wcid, params,
- enable, false);
- if (ret < 0)
- break;
- }
+static int mt7925_mcu_read_eeprom(struct mt792x_dev *dev, u32 offset, u8 *val)
+{
+ struct {
+ u8 rsv[4];
- return ret;
+ __le16 tag;
+ __le16 len;
+
+ __le32 addr;
+ __le32 valid;
+ u8 data[MT7925_EEPROM_BLOCK_SIZE];
+ } __packed req = {
+ .tag = cpu_to_le16(1),
+ .len = cpu_to_le16(sizeof(req) - 4),
+ .addr = cpu_to_le32(round_down(offset,
+ MT7925_EEPROM_BLOCK_SIZE)),
+ };
+ struct evt {
+ u8 rsv[4];
+
+ __le16 tag;
+ __le16 len;
+
+ __le32 ver;
+ __le32 addr;
+ __le32 valid;
+ __le32 size;
+ __le32 magic_num;
+ __le32 type;
+ __le32 rsv1[4];
+ u8 data[32];
+ } __packed *res;
+ struct sk_buff *skb;
+ int ret;
+
+ ret = mt76_mcu_send_and_get_msg(&dev->mt76, MCU_WM_UNI_CMD_QUERY(EFUSE_CTRL),
+ &req, sizeof(req), true, &skb);
+ if (ret)
+ return ret;
+
+ res = (struct evt *)skb->data;
+ *val = res->data[offset % MT7925_EEPROM_BLOCK_SIZE];
+
+ dev_kfree_skb(skb);
+
+ return 0;
}
static int mt7925_load_clc(struct mt792x_dev *dev, const char *fw_name)
@@ -671,13 +683,21 @@ static int mt7925_load_clc(struct mt792x_dev *dev, const char *fw_name)
struct mt76_dev *mdev = &dev->mt76;
struct mt792x_phy *phy = &dev->phy;
const struct firmware *fw;
+ u8 *clc_base = NULL, hw_encap = 0;
int ret, i, len, offset = 0;
- u8 *clc_base = NULL;
+ dev->phy.clc_chan_conf = 0xff;
if (mt7925_disable_clc ||
mt76_is_usb(&dev->mt76))
return 0;
+ if (mt76_is_mmio(&dev->mt76)) {
+ ret = mt7925_mcu_read_eeprom(dev, MT_EE_HW_TYPE, &hw_encap);
+ if (ret)
+ return ret;
+ hw_encap = u8_get_bits(hw_encap, MT_EE_HW_TYPE_ENCAP);
+ }
+
ret = request_firmware(&fw, fw_name, mdev->dev);
if (ret)
return ret;
@@ -722,6 +742,12 @@ static int mt7925_load_clc(struct mt792x_dev *dev, const char *fw_name)
if (phy->clc[clc->idx])
continue;
+ /* header content sanity */
+ if ((clc->idx == MT792x_CLC_BE_CTRL &&
+ u8_get_bits(clc->t2.type, MT_EE_HW_TYPE_ENCAP) != hw_encap) ||
+ u8_get_bits(clc->t0.type, MT_EE_HW_TYPE_ENCAP) != hw_encap)
+ continue;
+
phy->clc[clc->idx] = devm_kmemdup(mdev->dev, clc,
le32_to_cpu(clc->len),
GFP_KERNEL);
@@ -757,7 +783,7 @@ int mt7925_mcu_fw_log_2_host(struct mt792x_dev *dev, u8 ctrl)
int ret;
ret = mt76_mcu_send_and_get_msg(&dev->mt76, MCU_UNI_CMD(WSYS_CONFIG),
- &req, sizeof(req), false, NULL);
+ &req, sizeof(req), true, NULL);
return ret;
}
@@ -828,7 +854,6 @@ mt7925_mcu_parse_phy_cap(struct mt792x_dev *dev, char *data)
mdev->phy.chainmask = mdev->phy.antenna_mask;
mdev->phy.cap.has_2ghz = cap->hw_path & BIT(WF0_24G);
mdev->phy.cap.has_5ghz = cap->hw_path & BIT(WF0_5G);
- dev->has_eht = cap->eht;
}
static void
@@ -949,6 +974,23 @@ int mt7925_mcu_set_deep_sleep(struct mt792x_dev *dev, bool enable)
}
EXPORT_SYMBOL_GPL(mt7925_mcu_set_deep_sleep);
+int mt7925_mcu_set_thermal_protect(struct mt792x_dev *dev)
+{
+ char cmd[64];
+ int ret = 0;
+
+ snprintf(cmd, sizeof(cmd), "ThermalProtGband %d %d %d %d %d %d %d %d %d %d",
+ 0, 100, 90, 80, 30, 1, 1, 115, 105, 5);
+ ret = mt7925_mcu_chip_config(dev, cmd);
+
+ snprintf(cmd, sizeof(cmd), "ThermalProtAband %d %d %d %d %d %d %d %d %d %d",
+ 1, 100, 90, 80, 30, 1, 1, 115, 105, 5);
+ ret |= mt7925_mcu_chip_config(dev, cmd);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(mt7925_mcu_set_thermal_protect);
+
int mt7925_run_firmware(struct mt792x_dev *dev)
{
int err;
@@ -1399,7 +1441,7 @@ int mt7925_mcu_set_eeprom(struct mt792x_dev *dev)
};
return mt76_mcu_send_and_get_msg(&dev->mt76, MCU_UNI_CMD(EFUSE_CTRL),
- &req, sizeof(req), false, NULL);
+ &req, sizeof(req), true, NULL);
}
EXPORT_SYMBOL_GPL(mt7925_mcu_set_eeprom);
@@ -1850,49 +1892,6 @@ mt7925_mcu_sta_mld_tlv(struct sk_buff *skb,
}
}
-static int
-mt7925_mcu_sta_cmd(struct mt76_phy *phy,
- struct mt76_sta_cmd_info *info)
-{
- struct mt76_vif_link *mvif = (struct mt76_vif_link *)info->vif->drv_priv;
- struct mt76_dev *dev = phy->dev;
- struct sk_buff *skb;
- int conn_state;
-
- skb = __mt76_connac_mcu_alloc_sta_req(dev, mvif, info->wcid,
- MT7925_STA_UPDATE_MAX_SIZE);
- if (IS_ERR(skb))
- return PTR_ERR(skb);
-
- conn_state = info->enable ? CONN_STATE_PORT_SECURE :
- CONN_STATE_DISCONNECT;
- if (info->link_sta)
- mt76_connac_mcu_sta_basic_tlv(dev, skb, info->link_conf,
- info->link_sta,
- conn_state, info->newly);
- if (info->link_sta && info->enable) {
- mt7925_mcu_sta_phy_tlv(skb, info->vif, info->link_sta);
- mt7925_mcu_sta_ht_tlv(skb, info->link_sta);
- mt7925_mcu_sta_vht_tlv(skb, info->link_sta);
- mt76_connac_mcu_sta_uapsd(skb, info->vif, info->link_sta->sta);
- mt7925_mcu_sta_amsdu_tlv(skb, info->vif, info->link_sta);
- mt7925_mcu_sta_he_tlv(skb, info->link_sta);
- mt7925_mcu_sta_he_6g_tlv(skb, info->link_sta);
- mt7925_mcu_sta_eht_tlv(skb, info->link_sta);
- mt7925_mcu_sta_rate_ctrl_tlv(skb, info->vif,
- info->link_sta);
- mt7925_mcu_sta_state_v2_tlv(phy, skb, info->link_sta,
- info->vif, info->rcpi,
- info->state);
- mt7925_mcu_sta_mld_tlv(skb, info->vif, info->link_sta->sta);
- }
-
- if (info->enable)
- mt7925_mcu_sta_hdr_trans_tlv(skb, info->vif, info->link_sta);
-
- return mt76_mcu_skb_send_msg(dev, skb, info->cmd, true);
-}
-
static void
mt7925_mcu_sta_remove_tlv(struct sk_buff *skb)
{
@@ -1905,8 +1904,8 @@ mt7925_mcu_sta_remove_tlv(struct sk_buff *skb)
}
static int
-mt7925_mcu_mlo_sta_cmd(struct mt76_phy *phy,
- struct mt76_sta_cmd_info *info)
+mt7925_mcu_sta_cmd(struct mt76_phy *phy,
+ struct mt76_sta_cmd_info *info)
{
struct mt792x_vif *mvif = (struct mt792x_vif *)info->vif->drv_priv;
struct mt76_dev *dev = phy->dev;
@@ -1920,12 +1919,10 @@ mt7925_mcu_mlo_sta_cmd(struct mt76_phy *phy,
if (IS_ERR(skb))
return PTR_ERR(skb);
- if (info->enable)
+ if (info->enable && info->link_sta) {
mt76_connac_mcu_sta_basic_tlv(dev, skb, info->link_conf,
info->link_sta,
info->enable, info->newly);
-
- if (info->enable && info->link_sta) {
mt7925_mcu_sta_phy_tlv(skb, info->vif, info->link_sta);
mt7925_mcu_sta_ht_tlv(skb, info->link_sta);
mt7925_mcu_sta_vht_tlv(skb, info->link_sta);
@@ -1944,14 +1941,14 @@ mt7925_mcu_mlo_sta_cmd(struct mt76_phy *phy,
mt7925_mcu_sta_mld_tlv(skb, info->vif, info->link_sta->sta);
mt7925_mcu_sta_eht_mld_tlv(skb, info->vif, info->link_sta->sta);
}
-
- mt7925_mcu_sta_hdr_trans_tlv(skb, info->vif, info->link_sta);
}
if (!info->enable) {
mt7925_mcu_sta_remove_tlv(skb);
mt76_connac_mcu_add_tlv(skb, STA_REC_MLD_OFF,
sizeof(struct tlv));
+ } else {
+ mt7925_mcu_sta_hdr_trans_tlv(skb, info->vif, info->link_sta);
}
return mt76_mcu_skb_send_msg(dev, skb, info->cmd, true);
@@ -1976,25 +1973,15 @@ int mt7925_mcu_sta_update(struct mt792x_dev *dev,
};
struct mt792x_sta *msta;
struct mt792x_link_sta *mlink;
- int err;
if (link_sta) {
msta = (struct mt792x_sta *)link_sta->sta->drv_priv;
mlink = mt792x_sta_to_link(msta, link_sta->link_id);
}
info.wcid = link_sta ? &mlink->wcid : &mvif->sta.deflink.wcid;
+ info.newly = state != MT76_STA_INFO_STATE_ASSOC;
- if (link_sta)
- info.newly = state != MT76_STA_INFO_STATE_ASSOC;
- else
- info.newly = state == MT76_STA_INFO_STATE_ASSOC ? false : true;
-
- if (ieee80211_vif_is_mld(vif))
- err = mt7925_mcu_mlo_sta_cmd(&dev->mphy, &info);
- else
- err = mt7925_mcu_sta_cmd(&dev->mphy, &info);
-
- return err;
+ return mt7925_mcu_sta_cmd(&dev->mphy, &info);
}
int mt7925_mcu_set_beacon_filter(struct mt792x_dev *dev,
@@ -2076,8 +2063,6 @@ int mt7925_mcu_set_sniffer(struct mt792x_dev *dev, struct ieee80211_vif *vif,
},
};
- mt76_mcu_send_msg(&dev->mt76, MCU_UNI_CMD(SNIFFER), &req, sizeof(req), true);
-
return mt76_mcu_send_msg(&dev->mt76, MCU_UNI_CMD(SNIFFER), &req, sizeof(req),
true);
}
@@ -2328,6 +2313,40 @@ __mt7925_mcu_alloc_bss_req(struct mt76_dev *dev, struct mt76_vif_link *mvif, int
return skb;
}
+static
+void mt7925_mcu_bss_eht_tlv(struct sk_buff *skb, struct mt76_phy *phy,
+ struct ieee80211_bss_conf *link_conf,
+ struct ieee80211_chanctx_conf *ctx)
+{
+ struct cfg80211_chan_def *chandef = ctx ? &ctx->def :
+ &link_conf->chanreq.oper;
+
+ struct bss_eht_tlv *req;
+ struct tlv *tlv;
+
+ tlv = mt76_connac_mcu_add_tlv(skb, UNI_BSS_INFO_EHT, sizeof(*req));
+ req = (struct bss_eht_tlv *)tlv;
+ req->is_eth_dscb_present = chandef->punctured ? 1 : 0;
+ req->eht_dis_sub_chan_bitmap = cpu_to_le16(chandef->punctured);
+}
+
+int mt7925_mcu_set_eht_pp(struct mt76_phy *phy, struct mt76_vif_link *mvif,
+ struct ieee80211_bss_conf *link_conf,
+ struct ieee80211_chanctx_conf *ctx)
+{
+ struct sk_buff *skb;
+
+ skb = __mt7925_mcu_alloc_bss_req(phy->dev, mvif,
+ MT7925_BSS_UPDATE_MAX_SIZE);
+ if (IS_ERR(skb))
+ return PTR_ERR(skb);
+
+ mt7925_mcu_bss_eht_tlv(skb, phy, link_conf, ctx);
+
+ return mt76_mcu_skb_send_msg(phy->dev, skb,
+ MCU_UNI_CMD(BSS_INFO_UPDATE), true);
+}
+
int mt7925_mcu_set_chctx(struct mt76_phy *phy, struct mt76_vif_link *mvif,
struct ieee80211_bss_conf *link_conf,
struct ieee80211_chanctx_conf *ctx)
@@ -2559,6 +2578,7 @@ mt7925_mcu_bss_mld_tlv(struct sk_buff *skb,
struct ieee80211_vif *vif = link_conf->vif;
struct mt792x_bss_conf *mconf = mt792x_link_conf_to_mconf(link_conf);
struct mt792x_vif *mvif = (struct mt792x_vif *)link_conf->vif->drv_priv;
+ struct mt792x_phy *phy = mvif->phy;
struct bss_mld_tlv *mld;
struct tlv *tlv;
bool is_mld;
@@ -2574,8 +2594,13 @@ mt7925_mcu_bss_mld_tlv(struct sk_buff *skb,
mld->group_mld_id = is_mld ? mvif->bss_conf.mt76.idx : 0xff;
mld->own_mld_id = mconf->mt76.idx + 32;
mld->remap_idx = 0xff;
- mld->eml_enable = !!(link_conf->vif->cfg.eml_cap &
- IEEE80211_EML_CAP_EMLSR_SUPP);
+
+ if (phy->chip_cap & MT792x_CHIP_CAP_MLO_EML_EN) {
+ mld->eml_enable = !!(link_conf->vif->cfg.eml_cap &
+ IEEE80211_EML_CAP_EMLSR_SUPP);
+ } else {
+ mld->eml_enable = 0;
+ }
memcpy(mld->mac_addr, vif->addr, ETH_ALEN);
}
@@ -2668,6 +2693,62 @@ int mt7925_mcu_set_timing(struct mt792x_phy *phy,
MCU_UNI_CMD(BSS_INFO_UPDATE), true);
}
+void mt7925_mcu_del_dev(struct mt76_dev *mdev,
+ struct ieee80211_vif *vif)
+{
+ struct mt76_vif_link *mvif = (struct mt76_vif_link *)vif->drv_priv;
+ struct {
+ struct {
+ u8 omac_idx;
+ u8 band_idx;
+ __le16 pad;
+ } __packed hdr;
+ struct req_tlv {
+ __le16 tag;
+ __le16 len;
+ u8 active;
+ u8 link_idx; /* hw link idx */
+ u8 omac_addr[ETH_ALEN];
+ } __packed tlv;
+ } dev_req = {
+ .tlv = {
+ .tag = cpu_to_le16(DEV_INFO_ACTIVE),
+ .len = cpu_to_le16(sizeof(struct req_tlv)),
+ .active = true,
+ },
+ };
+ struct {
+ struct {
+ u8 bss_idx;
+ u8 pad[3];
+ } __packed hdr;
+ struct mt76_connac_bss_basic_tlv basic;
+ } basic_req = {
+ .basic = {
+ .tag = cpu_to_le16(UNI_BSS_INFO_BASIC),
+ .len = cpu_to_le16(sizeof(struct mt76_connac_bss_basic_tlv)),
+ .active = true,
+ .conn_state = 1,
+ },
+ };
+
+ dev_req.hdr.omac_idx = mvif->omac_idx;
+ dev_req.hdr.band_idx = mvif->band_idx;
+
+ basic_req.hdr.bss_idx = mvif->idx;
+ basic_req.basic.omac_idx = mvif->omac_idx;
+ basic_req.basic.band_idx = mvif->band_idx;
+ basic_req.basic.link_idx = mvif->link_idx;
+
+ mt76_mcu_send_msg(mdev, MCU_UNI_CMD(BSS_INFO_UPDATE),
+ &basic_req, sizeof(basic_req), true);
+
+ /* recovery omac address for the legacy interface */
+ memcpy(dev_req.tlv.omac_addr, vif->addr, ETH_ALEN);
+ mt76_mcu_send_msg(mdev, MCU_UNI_CMD(DEV_INFO_UPDATE),
+ &dev_req, sizeof(dev_req), true);
+}
+
int mt7925_mcu_add_bss_info(struct mt792x_phy *phy,
struct ieee80211_chanctx_conf *ctx,
struct ieee80211_bss_conf *link_conf,
@@ -2732,7 +2813,7 @@ int mt7925_mcu_set_dbdc(struct mt76_phy *phy, bool enable)
conf->band = 0; /* unused */
err = mt76_mcu_skb_send_msg(mdev, skb, MCU_UNI_CMD(SET_DBDC_PARMS),
- false);
+ true);
return err;
}
@@ -2747,7 +2828,6 @@ int mt7925_mcu_hw_scan(struct mt76_phy *phy, struct ieee80211_vif *vif,
struct mt76_dev *mdev = phy->dev;
struct mt76_connac_mcu_scan_channel *chan;
struct sk_buff *skb;
-
struct scan_hdr_tlv *hdr;
struct scan_req_tlv *req;
struct scan_ssid_tlv *ssid;
@@ -2758,9 +2838,12 @@ int mt7925_mcu_hw_scan(struct mt76_phy *phy, struct ieee80211_vif *vif,
struct tlv *tlv;
int max_len;
+ if (test_bit(MT76_HW_SCANNING, &phy->state))
+ return -EBUSY;
+
max_len = sizeof(*hdr) + sizeof(*req) + sizeof(*ssid) +
- sizeof(*bssid) + sizeof(*chan_info) +
- sizeof(*misc) + sizeof(*ie);
+ sizeof(*bssid) * MT7925_RNR_SCAN_MAX_BSSIDS +
+ sizeof(*chan_info) + sizeof(*misc) + sizeof(*ie);
skb = mt76_mcu_msg_alloc(mdev, NULL, max_len);
if (!skb)
@@ -2783,6 +2866,8 @@ int mt7925_mcu_hw_scan(struct mt76_phy *phy, struct ieee80211_vif *vif,
for (i = 0; i < sreq->n_ssids; i++) {
if (!sreq->ssids[i].ssid_len)
continue;
+ if (i > MT7925_RNR_SCAN_MAX_BSSIDS)
+ break;
ssid->ssids[i].ssid_len = cpu_to_le32(sreq->ssids[i].ssid_len);
memcpy(ssid->ssids[i].ssid, sreq->ssids[i].ssid,
@@ -2792,10 +2877,31 @@ int mt7925_mcu_hw_scan(struct mt76_phy *phy, struct ieee80211_vif *vif,
ssid->ssid_type = n_ssids ? BIT(2) : BIT(0);
ssid->ssids_num = n_ssids;
- tlv = mt76_connac_mcu_add_tlv(skb, UNI_SCAN_BSSID, sizeof(*bssid));
- bssid = (struct scan_bssid_tlv *)tlv;
+ if (sreq->n_6ghz_params) {
+ u8 j;
+
+ mt76_connac_mcu_build_rnr_scan_param(mdev, sreq);
+
+ for (j = 0; j < mdev->rnr.bssid_num; j++) {
+ if (j > MT7925_RNR_SCAN_MAX_BSSIDS)
+ break;
+
+ tlv = mt76_connac_mcu_add_tlv(skb, UNI_SCAN_BSSID,
+ sizeof(*bssid));
+ bssid = (struct scan_bssid_tlv *)tlv;
- memcpy(bssid->bssid, sreq->bssid, ETH_ALEN);
+ ether_addr_copy(bssid->bssid, mdev->rnr.bssid[j]);
+ bssid->match_ch = mdev->rnr.channel[j];
+ bssid->match_ssid_ind = MT7925_RNR_SCAN_MAX_BSSIDS;
+ bssid->match_short_ssid_ind = MT7925_RNR_SCAN_MAX_BSSIDS;
+ }
+ req->scan_func |= SCAN_FUNC_RNR_SCAN;
+ } else {
+ tlv = mt76_connac_mcu_add_tlv(skb, UNI_SCAN_BSSID, sizeof(*bssid));
+ bssid = (struct scan_bssid_tlv *)tlv;
+
+ ether_addr_copy(bssid->bssid, sreq->bssid);
+ }
tlv = mt76_connac_mcu_add_tlv(skb, UNI_SCAN_CHANNEL, sizeof(*chan_info));
chan_info = (struct scan_chan_info_tlv *)tlv;
@@ -2837,7 +2943,7 @@ int mt7925_mcu_hw_scan(struct mt76_phy *phy, struct ieee80211_vif *vif,
}
err = mt76_mcu_skb_send_msg(mdev, skb, MCU_UNI_CMD(SCAN_REQ),
- false);
+ true);
if (err < 0)
clear_bit(MT76_HW_SCANNING, &phy->state);
@@ -2943,7 +3049,7 @@ int mt7925_mcu_sched_scan_req(struct mt76_phy *phy,
}
return mt76_mcu_skb_send_msg(mdev, skb, MCU_UNI_CMD(SCAN_REQ),
- false);
+ true);
}
EXPORT_SYMBOL_GPL(mt7925_mcu_sched_scan_req);
@@ -2979,7 +3085,7 @@ mt7925_mcu_sched_scan_enable(struct mt76_phy *phy,
clear_bit(MT76_HW_SCHED_SCANNING, &phy->state);
return mt76_mcu_skb_send_msg(mdev, skb, MCU_UNI_CMD(SCAN_REQ),
- false);
+ true);
}
int mt7925_mcu_cancel_hw_scan(struct mt76_phy *phy,
@@ -3018,7 +3124,7 @@ int mt7925_mcu_cancel_hw_scan(struct mt76_phy *phy,
}
return mt76_mcu_send_msg(phy->dev, MCU_UNI_CMD(SCAN_REQ),
- &req, sizeof(req), false);
+ &req, sizeof(req), true);
}
EXPORT_SYMBOL_GPL(mt7925_mcu_cancel_hw_scan);
@@ -3123,7 +3229,7 @@ int mt7925_mcu_set_channel_domain(struct mt76_phy *phy)
memcpy(__skb_push(skb, sizeof(req)), &req, sizeof(req));
return mt76_mcu_skb_send_msg(dev, skb, MCU_UNI_CMD(SET_DOMAIN_INFO),
- false);
+ true);
}
EXPORT_SYMBOL_GPL(mt7925_mcu_set_channel_domain);
@@ -3155,16 +3261,17 @@ __mt7925_mcu_set_clc(struct mt792x_dev *dev, u8 *alpha2,
.idx = idx,
.env = env_cap,
- .acpi_conf = mt792x_acpi_get_flags(&dev->phy),
};
int ret, valid_cnt = 0;
- u8 i, *pos;
+ u8 *pos, *last_pos;
if (!clc)
return 0;
- pos = clc->data + sizeof(*seg) * clc->nr_seg;
- for (i = 0; i < clc->nr_country; i++) {
+ req.ver = clc->ver;
+ pos = clc->data + sizeof(*seg) * clc->t0.nr_seg;
+ last_pos = clc->data + le32_to_cpu(*(__le32 *)(clc->data + 4));
+ while (pos < last_pos) {
struct mt7925_clc_rule *rule = (struct mt7925_clc_rule *)pos;
pos += sizeof(*rule);
@@ -3179,6 +3286,7 @@ __mt7925_mcu_set_clc(struct mt792x_dev *dev, u8 *alpha2,
memcpy(req.type, rule->type, 2);
req.size = cpu_to_le16(seg->len);
+ dev->phy.clc_chan_conf = clc->ver == 1 ? 0xff : rule->flag;
skb = __mt76_mcu_msg_alloc(&dev->mt76, &req,
le16_to_cpu(req.size) + sizeof(req),
sizeof(req), GFP_KERNEL);
@@ -3194,8 +3302,10 @@ __mt7925_mcu_set_clc(struct mt792x_dev *dev, u8 *alpha2,
valid_cnt++;
}
- if (!valid_cnt)
+ if (!valid_cnt) {
+ dev->phy.clc_chan_conf = 0xff;
return -ENOENT;
+ }
return 0;
}
@@ -3208,6 +3318,9 @@ int mt7925_mcu_set_clc(struct mt792x_dev *dev, u8 *alpha2,
/* submit all clc config */
for (i = 0; i < ARRAY_SIZE(phy->clc); i++) {
+ if (i == MT792x_CLC_BE_CTRL)
+ continue;
+
ret = __mt7925_mcu_set_clc(dev, alpha2, env_cap,
phy->clc[i], i);
@@ -3266,6 +3379,18 @@ int mt7925_mcu_fill_message(struct mt76_dev *mdev, struct sk_buff *skb,
else
uni_txd->option = MCU_CMD_UNI_EXT_ACK;
+ if (cmd == MCU_UNI_CMD(HIF_CTRL) ||
+ cmd == MCU_UNI_CMD(CHIP_CONFIG))
+ uni_txd->option &= ~MCU_CMD_ACK;
+
+ if (mcu_cmd == MCU_UNI_CMD_TESTMODE_CTRL ||
+ mcu_cmd == MCU_UNI_CMD_TESTMODE_RX_STAT) {
+ if (cmd & __MCU_CMD_FIELD_QUERY)
+ uni_txd->option = 0x2;
+ else
+ uni_txd->option = 0x6;
+ }
+
goto exit;
}
@@ -3557,6 +3682,43 @@ int mt7925_mcu_set_rate_txpower(struct mt76_phy *phy)
return 0;
}
+int mt7925_mcu_wf_rf_pin_ctrl(struct mt792x_phy *phy)
+{
+#define UNI_CMD_RADIO_STATUS_GET 0
+ struct mt792x_dev *dev = phy->dev;
+ struct sk_buff *skb;
+ int ret;
+ struct {
+ __le16 tag;
+ __le16 len;
+ u8 rsv[4];
+ } __packed req = {
+ .tag = UNI_CMD_RADIO_STATUS_GET,
+ .len = cpu_to_le16(sizeof(req)),
+ };
+ struct mt7925_radio_status_event {
+ __le16 tag;
+ __le16 len;
+
+ u8 data;
+ u8 rsv[3];
+ } __packed *status;
+
+ ret = mt76_mcu_send_and_get_msg(&dev->mt76,
+ MCU_UNI_CMD(RADIO_STATUS),
+ &req, sizeof(req), true, &skb);
+ if (ret)
+ return ret;
+
+ skb_pull(skb, sizeof(struct tlv));
+ status = (struct mt7925_radio_status_event *)skb->data;
+ ret = status->data;
+
+ dev_kfree_skb(skb);
+
+ return ret;
+}
+
int mt7925_mcu_set_rxfilter(struct mt792x_dev *dev, u32 fif,
u8 bit_op, u32 bit_map)
{
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/mcu.h b/drivers/net/wireless/mediatek/mt76/mt7925/mcu.h
index 1e47d2c61b54..ee6fb16e83c5 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/mcu.h
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/mcu.h
@@ -104,13 +104,6 @@ enum {
MT7925_TM_WIFISPECTRUM,
};
-struct mt7925_rftest_cmd {
- u8 action;
- u8 rsv[3];
- __le32 param0;
- __le32 param1;
-} __packed;
-
struct mt7925_rftest_evt {
__le32 param0;
__le32 param1;
@@ -196,6 +189,7 @@ enum {
UNI_SNIFFER_CONFIG,
};
+#define MT7925_RNR_SCAN_MAX_BSSIDS 10
struct scan_hdr_tlv {
/* fixed field */
u8 seq_num;
@@ -223,7 +217,7 @@ struct scan_req_tlv {
__le16 timeout_value;
__le16 probe_delay_time;
__le32 func_mask_ext;
-};
+} __packed;
struct scan_ssid_tlv {
__le16 tag;
@@ -235,9 +229,10 @@ struct scan_ssid_tlv {
* BIT(2) + ssid_type_ext BIT(0) specified SSID only
*/
u8 ssids_num;
- u8 pad[2];
- struct mt76_connac_mcu_scan_ssid ssids[4];
-};
+ u8 is_short_ssid;
+ u8 pad;
+ struct mt76_connac_mcu_scan_ssid ssids[MT7925_RNR_SCAN_MAX_BSSIDS];
+} __packed;
struct scan_bssid_tlv {
__le16 tag;
@@ -247,8 +242,9 @@ struct scan_bssid_tlv {
u8 match_ch;
u8 match_ssid_ind;
u8 rcpi;
- u8 pad[3];
-};
+ u8 match_short_ssid_ind;
+ u8 pad[2];
+} __packed;
struct scan_chan_info_tlv {
__le16 tag;
@@ -264,7 +260,7 @@ struct scan_chan_info_tlv {
u8 channels_num; /* valid when channel_type is 4 */
u8 pad[2];
struct mt76_connac_mcu_scan_channel channels[64];
-};
+} __packed;
struct scan_ie_tlv {
__le16 tag;
@@ -372,6 +368,19 @@ struct bss_mld_tlv {
u8 __rsv[3];
} __packed;
+struct bss_eht_tlv {
+ __le16 tag;
+ __le16 len;
+ u8 is_eht_op_present;
+ u8 is_eth_dscb_present;
+ u8 eht_ctrl;
+ u8 eht_ccfs0;
+ u8 eht_ccfs1;
+ u8 pad1;
+ __le16 eht_dis_sub_chan_bitmap;
+ u8 pad2[4];
+} __packed;
+
struct sta_rec_ba_uni {
__le16 tag;
__le16 len;
@@ -566,8 +575,8 @@ struct mt7925_wow_pattern_tlv {
u8 offset;
u8 mask[MT76_CONNAC_WOW_MASK_MAX_LEN];
u8 pattern[MT76_CONNAC_WOW_PATTEN_MAX_LEN];
- u8 rsv[7];
-} __packed;
+ u8 rsv[4];
+};
struct roc_acquire_tlv {
__le16 tag;
@@ -589,6 +598,47 @@ struct roc_acquire_tlv {
u8 rsv[3];
} __packed;
+enum ENUM_CMD_TEST_CTRL_ACT {
+ CMD_TEST_CTRL_ACT_SWITCH_MODE = 0,
+ CMD_TEST_CTRL_ACT_SET_AT = 1,
+ CMD_TEST_CTRL_ACT_GET_AT = 2,
+ CMD_TEST_CTRL_ACT_SET_AT_ENG = 3,
+ CMD_TEST_CTRL_ACT_GET_AT_ENG = 4,
+ CMD_TEST_CTRL_ACT_NUM
+};
+
+enum ENUM_CMD_TEST_CTRL_ACT_SWITCH_MODE_OP {
+ CMD_TEST_CTRL_ACT_SWITCH_MODE_NORMAL = 0,
+ CMD_TEST_CTRL_ACT_SWITCH_MODE_RF_TEST = 1,
+ CMD_TEST_CTRL_ACT_SWITCH_MODE_ICAP = 2,
+ CMD_TEST_CTRL_ACT_SWITCH_MODE_NUM
+};
+
+union testmode_data {
+ __le32 op_mode;
+ __le32 channel_freq;
+ u8 rf_at_info[84];
+};
+
+union testmode_evt {
+ __le32 op_mode;
+ __le32 channel_freq;
+ u8 rf_at_info[1024];
+};
+
+struct uni_cmd_testmode_ctrl {
+ u16 tag;
+ u16 length;
+ u8 action;
+ u8 reserved[3];
+ union testmode_data data;
+} __packed;
+
+struct mt7925_rftest_cmd {
+ u8 padding[4];
+ struct uni_cmd_testmode_ctrl ctrl;
+} __packed;
+
static inline enum connac3_mcu_cipher_type
mt7925_mcu_get_cipher(int cipher)
{
@@ -627,6 +677,8 @@ int mt7925_mcu_sched_scan_req(struct mt76_phy *phy,
int mt7925_mcu_sched_scan_enable(struct mt76_phy *phy,
struct ieee80211_vif *vif,
bool enable);
+void mt7925_mcu_del_dev(struct mt76_dev *mdev,
+ struct ieee80211_vif *vif);
int mt7925_mcu_add_bss_info(struct mt792x_phy *phy,
struct ieee80211_chanctx_conf *ctx,
struct ieee80211_bss_conf *link_conf,
@@ -635,11 +687,15 @@ int mt7925_mcu_add_bss_info(struct mt792x_phy *phy,
int mt7925_mcu_set_timing(struct mt792x_phy *phy,
struct ieee80211_bss_conf *link_conf);
int mt7925_mcu_set_deep_sleep(struct mt792x_dev *dev, bool enable);
+int mt7925_mcu_set_thermal_protect(struct mt792x_dev *dev);
int mt7925_mcu_set_channel_domain(struct mt76_phy *phy);
int mt7925_mcu_set_radio_en(struct mt792x_phy *phy, bool enable);
int mt7925_mcu_set_chctx(struct mt76_phy *phy, struct mt76_vif_link *mvif,
struct ieee80211_bss_conf *link_conf,
struct ieee80211_chanctx_conf *ctx);
+int mt7925_mcu_set_eht_pp(struct mt76_phy *phy, struct mt76_vif_link *mvif,
+ struct ieee80211_bss_conf *link_conf,
+ struct ieee80211_chanctx_conf *ctx);
int mt7925_mcu_set_rate_txpower(struct mt76_phy *phy);
int mt7925_mcu_update_arp_filter(struct mt76_dev *dev,
struct ieee80211_bss_conf *link_conf);
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/mt7925.h b/drivers/net/wireless/mediatek/mt76/mt7925/mt7925.h
index 8707b5d04743..1b165d0d8bd3 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/mt7925.h
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/mt7925.h
@@ -137,11 +137,18 @@ enum {
MT7925_CLC_MAX_NUM,
};
+struct mt7925_clc_rule_v2 {
+ u32 flag;
+ u8 alpha2[2];
+ u8 rsv[10];
+} __packed;
+
struct mt7925_clc_rule {
u8 alpha2[2];
u8 type[2];
u8 seg_idx;
- u8 rsv[3];
+ u8 flag; /* UNII4~8 ctrl flag */
+ u8 rsv[2];
} __packed;
struct mt7925_clc_segment {
@@ -152,14 +159,26 @@ struct mt7925_clc_segment {
u8 rsv2[4];
} __packed;
-struct mt7925_clc {
- __le32 len;
- u8 idx;
- u8 ver;
+struct mt7925_clc_type0 {
u8 nr_country;
u8 type;
u8 nr_seg;
u8 rsv[7];
+} __packed;
+
+struct mt7925_clc_type2 {
+ u8 type;
+ u8 rsv[9];
+} __packed;
+
+struct mt7925_clc {
+ __le32 len;
+ u8 idx;
+ u8 ver;
+ union {
+ struct mt7925_clc_type0 t0;
+ struct mt7925_clc_type2 t2;
+ };
u8 data[];
} __packed;
@@ -167,9 +186,12 @@ enum mt7925_eeprom_field {
MT_EE_CHIP_ID = 0x000,
MT_EE_VERSION = 0x002,
MT_EE_MAC_ADDR = 0x004,
+ MT_EE_HW_TYPE = 0xa71,
__MT_EE_MAX = 0x9ff
};
+#define MT_EE_HW_TYPE_ENCAP GENMASK(1, 0)
+
enum {
TXPWR_USER,
TXPWR_EEPROM,
@@ -235,6 +257,7 @@ int mt7925_mcu_chip_config(struct mt792x_dev *dev, const char *cmd);
int mt7925_mcu_set_rxfilter(struct mt792x_dev *dev, u32 fif,
u8 bit_op, u32 bit_map);
+void mt7925_regd_be_ctrl(struct mt792x_dev *dev, u8 *alpha2);
void mt7925_regd_update(struct mt792x_dev *dev);
int mt7925_mac_init(struct mt792x_dev *dev);
int mt7925_mac_sta_add(struct mt76_dev *mdev, struct ieee80211_vif *vif,
@@ -263,13 +286,12 @@ int mt7925_mcu_set_beacon_filter(struct mt792x_dev *dev,
struct ieee80211_vif *vif,
bool enable);
int mt7925_mcu_uni_tx_ba(struct mt792x_dev *dev,
- struct ieee80211_vif *vif,
struct ieee80211_ampdu_params *params,
bool enable);
int mt7925_mcu_uni_rx_ba(struct mt792x_dev *dev,
- struct ieee80211_vif *vif,
struct ieee80211_ampdu_params *params,
bool enable);
+void mt7925_mlo_pm_work(struct work_struct *work);
void mt7925_scan_work(struct work_struct *work);
void mt7925_roc_work(struct work_struct *work);
int mt7925_mcu_uni_bss_ps(struct mt792x_dev *dev,
@@ -343,5 +365,11 @@ int mt7925_mcu_wtbl_update_hdr_trans(struct mt792x_dev *dev,
struct ieee80211_vif *vif,
struct ieee80211_sta *sta,
int link_id);
+int mt7925_mcu_wf_rf_pin_ctrl(struct mt792x_phy *phy);
+
+int mt7925_testmode_cmd(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ void *data, int len);
+int mt7925_testmode_dump(struct ieee80211_hw *hw, struct sk_buff *msg,
+ struct netlink_callback *cb, void *data, int len);
#endif
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/pci.c b/drivers/net/wireless/mediatek/mt76/mt7925/pci.c
index c7b5dc1dbb34..89dc30f7c6b7 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/pci.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/pci.c
@@ -31,6 +31,10 @@ static void mt7925e_unregister_device(struct mt792x_dev *dev)
{
int i;
struct mt76_connac_pm *pm = &dev->pm;
+ struct ieee80211_hw *hw = mt76_hw(dev);
+
+ if (dev->phy.chip_cap & MT792x_CHIP_CAP_WF_RF_PIN_CTRL_EVT_EN)
+ wiphy_rfkill_stop_polling(hw->wiphy);
cancel_work_sync(&dev->init_work);
mt76_unregister_device(&dev->mt76);
@@ -490,9 +494,6 @@ static int mt7925_pci_suspend(struct device *device)
/* disable interrupt */
mt76_wr(dev, dev->irq_map->host_irq_enable, 0);
- mt76_wr(dev, MT_WFDMA0_HOST_INT_DIS,
- dev->irq_map->tx.all_complete_mask |
- MT_INT_RX_DONE_ALL | MT_INT_MCU_CMD);
mt76_wr(dev, MT_PCIE_MAC_INT_ENABLE, 0x0);
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/regs.h b/drivers/net/wireless/mediatek/mt76/mt7925/regs.h
index 985794a40c1a..547489092c29 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/regs.h
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/regs.h
@@ -28,7 +28,7 @@
#define MT_MDP_TO_HIF 0
#define MT_MDP_TO_WM 1
-#define MT_WFDMA0_HOST_INT_ENA MT_WFDMA0(0x228)
+#define MT_WFDMA0_HOST_INT_ENA MT_WFDMA0(0x204)
#define MT_WFDMA0_HOST_INT_DIS MT_WFDMA0(0x22c)
#define HOST_RX_DONE_INT_ENA4 BIT(12)
#define HOST_RX_DONE_INT_ENA5 BIT(13)
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/testmode.c b/drivers/net/wireless/mediatek/mt76/mt7925/testmode.c
new file mode 100644
index 000000000000..a3c97164ba21
--- /dev/null
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/testmode.c
@@ -0,0 +1,201 @@
+// SPDX-License-Identifier: ISC
+
+#include "mt7925.h"
+#include "mcu.h"
+
+#define MT7925_EVT_RSP_LEN 512
+
+enum mt7925_testmode_attr {
+ MT7925_TM_ATTR_UNSPEC,
+ MT7925_TM_ATTR_SET,
+ MT7925_TM_ATTR_QUERY,
+ MT7925_TM_ATTR_RSP,
+
+ /* keep last */
+ NUM_MT7925_TM_ATTRS,
+ MT7925_TM_ATTR_MAX = NUM_MT7925_TM_ATTRS - 1,
+};
+
+struct mt7925_tm_cmd {
+ u8 padding[4];
+ struct uni_cmd_testmode_ctrl c;
+} __packed;
+
+struct mt7925_tm_evt {
+ u32 param0;
+ u32 param1;
+} __packed;
+
+static const struct nla_policy mt7925_tm_policy[NUM_MT7925_TM_ATTRS] = {
+ [MT7925_TM_ATTR_SET] = NLA_POLICY_EXACT_LEN(sizeof(struct mt7925_tm_cmd)),
+ [MT7925_TM_ATTR_QUERY] = NLA_POLICY_EXACT_LEN(sizeof(struct mt7925_tm_cmd)),
+};
+
+static int
+mt7925_tm_set(struct mt792x_dev *dev, struct mt7925_tm_cmd *req)
+{
+ struct mt7925_rftest_cmd cmd;
+ struct mt7925_rftest_cmd *pcmd = &cmd;
+ bool testmode = false, normal = false;
+ struct mt76_connac_pm *pm = &dev->pm;
+ struct mt76_phy *phy = &dev->mphy;
+ int ret = -ENOTCONN;
+
+ memset(pcmd, 0, sizeof(*pcmd));
+ memcpy(pcmd, req, sizeof(struct mt7925_tm_cmd));
+
+ mutex_lock(&dev->mt76.mutex);
+
+ if (pcmd->ctrl.action == CMD_TEST_CTRL_ACT_SWITCH_MODE) {
+ if (pcmd->ctrl.data.op_mode == CMD_TEST_CTRL_ACT_SWITCH_MODE_NORMAL)
+ normal = true;
+ else
+ testmode = true;
+ }
+
+ if (testmode) {
+ /* Make sure testmode running on full power mode */
+ pm->enable = false;
+ cancel_delayed_work_sync(&pm->ps_work);
+ cancel_work_sync(&pm->wake_work);
+ __mt792x_mcu_drv_pmctrl(dev);
+
+ phy->test.state = MT76_TM_STATE_ON;
+ }
+
+ if (!mt76_testmode_enabled(phy))
+ goto out;
+
+ ret = mt76_mcu_send_msg(&dev->mt76, MCU_UNI_CMD(TESTMODE_CTRL), &cmd,
+ sizeof(cmd), false);
+
+ if (ret)
+ goto out;
+
+ if (normal) {
+ /* Switch back to the normal world */
+ phy->test.state = MT76_TM_STATE_OFF;
+ pm->enable = true;
+ }
+out:
+ mutex_unlock(&dev->mt76.mutex);
+
+ return ret;
+}
+
+static int
+mt7925_tm_query(struct mt792x_dev *dev, struct mt7925_tm_cmd *req,
+ char *evt_resp)
+{
+ struct mt7925_rftest_cmd cmd;
+ char *pcmd = (char *)&cmd;
+ struct sk_buff *skb = NULL;
+ int ret = 1;
+
+ memset(pcmd, 0, sizeof(*pcmd));
+ memcpy(pcmd + 4, (char *)&req->c, sizeof(struct uni_cmd_testmode_ctrl));
+
+ if (*((uint16_t *)req->padding) == MCU_UNI_CMD_TESTMODE_CTRL)
+ ret = mt76_mcu_send_and_get_msg(&dev->mt76, MCU_UNI_QUERY(TESTMODE_CTRL),
+ &cmd, sizeof(cmd), true, &skb);
+ else if (*((uint16_t *)req->padding) == MCU_UNI_CMD_TESTMODE_RX_STAT)
+ ret = mt76_mcu_send_and_get_msg(&dev->mt76, MCU_UNI_QUERY(TESTMODE_RX_STAT),
+ &cmd, sizeof(cmd), true, &skb);
+
+ if (ret)
+ goto out;
+
+ memcpy((char *)evt_resp, (char *)skb->data + 8, MT7925_EVT_RSP_LEN);
+
+out:
+ dev_kfree_skb(skb);
+
+ return ret;
+}
+
+int mt7925_testmode_cmd(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ void *data, int len)
+{
+ struct nlattr *tb[NUM_MT76_TM_ATTRS];
+ struct mt76_phy *mphy = hw->priv;
+ struct mt792x_phy *phy = mphy->priv;
+ int err;
+
+ if (!test_bit(MT76_STATE_RUNNING, &mphy->state) ||
+ !(hw->conf.flags & IEEE80211_CONF_MONITOR))
+ return -ENOTCONN;
+
+ err = nla_parse_deprecated(tb, MT76_TM_ATTR_MAX, data, len,
+ mt76_tm_policy, NULL);
+ if (err)
+ return err;
+
+ if (tb[MT76_TM_ATTR_DRV_DATA]) {
+ struct nlattr *drv_tb[NUM_MT7925_TM_ATTRS], *data;
+ int ret;
+
+ data = tb[MT76_TM_ATTR_DRV_DATA];
+ ret = nla_parse_nested_deprecated(drv_tb,
+ MT7925_TM_ATTR_MAX,
+ data, mt7925_tm_policy,
+ NULL);
+ if (ret)
+ return ret;
+
+ data = drv_tb[MT7925_TM_ATTR_SET];
+ if (data)
+ return mt7925_tm_set(phy->dev, nla_data(data));
+ }
+
+ return -EINVAL;
+}
+
+int mt7925_testmode_dump(struct ieee80211_hw *hw, struct sk_buff *msg,
+ struct netlink_callback *cb, void *data, int len)
+{
+ struct nlattr *tb[NUM_MT76_TM_ATTRS];
+ struct mt76_phy *mphy = hw->priv;
+ struct mt792x_phy *phy = mphy->priv;
+ int err;
+
+ if (!test_bit(MT76_STATE_RUNNING, &mphy->state) ||
+ !(hw->conf.flags & IEEE80211_CONF_MONITOR) ||
+ !mt76_testmode_enabled(mphy))
+ return -ENOTCONN;
+
+ if (cb->args[2]++ > 0)
+ return -ENOENT;
+
+ err = nla_parse_deprecated(tb, MT76_TM_ATTR_MAX, data, len,
+ mt76_tm_policy, NULL);
+ if (err)
+ return err;
+
+ if (tb[MT76_TM_ATTR_DRV_DATA]) {
+ struct nlattr *drv_tb[NUM_MT7925_TM_ATTRS], *data;
+ int ret;
+
+ data = tb[MT76_TM_ATTR_DRV_DATA];
+ ret = nla_parse_nested_deprecated(drv_tb,
+ MT7925_TM_ATTR_MAX,
+ data, mt7925_tm_policy,
+ NULL);
+ if (ret)
+ return ret;
+
+ data = drv_tb[MT7925_TM_ATTR_QUERY];
+ if (data) {
+ char evt_resp[MT7925_EVT_RSP_LEN];
+
+ err = mt7925_tm_query(phy->dev, nla_data(data),
+ evt_resp);
+ if (err)
+ return err;
+
+ return nla_put(msg, MT7925_TM_ATTR_RSP,
+ sizeof(evt_resp), evt_resp);
+ }
+ }
+
+ return -EINVAL;
+}
diff --git a/drivers/net/wireless/mediatek/mt76/mt792x.h b/drivers/net/wireless/mediatek/mt76/mt792x.h
index 32ed01a96bf7..e0359d431eca 100644
--- a/drivers/net/wireless/mediatek/mt76/mt792x.h
+++ b/drivers/net/wireless/mediatek/mt76/mt792x.h
@@ -27,8 +27,9 @@
#define MT792x_CHIP_CAP_CLC_EVT_EN BIT(0)
#define MT792x_CHIP_CAP_RSSI_NOTIFY_EVT_EN BIT(1)
-#define MT792x_CHIP_CAP_MLO_EVT_EN BIT(2)
#define MT792x_CHIP_CAP_WF_RF_PIN_CTRL_EVT_EN BIT(3)
+#define MT792x_CHIP_CAP_MLO_EN BIT(8)
+#define MT792x_CHIP_CAP_MLO_EML_EN BIT(9)
/* NOTE: used to map mt76_rates. idx may change if firmware expands table */
#define MT792x_BASIC_RATES_TBL 11
@@ -70,6 +71,7 @@ struct mt792x_fw_features {
enum {
MT792x_CLC_POWER,
MT792x_CLC_POWER_EXT,
+ MT792x_CLC_BE_CTRL,
MT792x_CLC_MAX_NUM,
};
@@ -81,6 +83,13 @@ enum mt792x_reg_power_type {
MT_AP_VLP,
};
+enum mt792x_mlo_pm_state {
+ MT792x_MLO_LINK_DISASSOC,
+ MT792x_MLO_LINK_ASSOC,
+ MT792x_MLO_CHANGED_PS_PENDING,
+ MT792x_MLO_CHANGED_PS,
+};
+
DECLARE_EWMA(avg_signal, 10, 8)
struct mt792x_link_sta {
@@ -134,6 +143,7 @@ struct mt792x_vif {
struct mt792x_phy *phy;
u16 valid_links;
u8 deflink_id;
+ enum mt792x_mlo_pm_state mlo_pm_state;
struct work_struct csa_work;
struct timer_list csa_timer;
@@ -239,6 +249,7 @@ struct mt792x_dev {
const struct mt792x_irq_map *irq_map;
struct work_struct ipv6_ns_work;
+ struct delayed_work mlo_pm_work;
/* IPv6 addresses for WoWLAN */
struct sk_buff_head ipv6_ns_list;
@@ -497,7 +508,7 @@ int mt792xe_mcu_fw_pmctrl(struct mt792x_dev *dev);
int mt792x_init_acpi_sar(struct mt792x_dev *dev);
int mt792x_init_acpi_sar_power(struct mt792x_phy *phy, bool set_default);
u8 mt792x_acpi_get_flags(struct mt792x_phy *phy);
-u8 mt792x_acpi_get_mtcl_conf(struct mt792x_phy *phy, char *alpha2);
+u32 mt792x_acpi_get_mtcl_conf(struct mt792x_phy *phy, char *alpha2);
#else
static inline int mt792x_init_acpi_sar(struct mt792x_dev *dev)
{
@@ -515,9 +526,9 @@ static inline u8 mt792x_acpi_get_flags(struct mt792x_phy *phy)
return 0;
}
-static inline u8 mt792x_acpi_get_mtcl_conf(struct mt792x_phy *phy, char *alpha2)
+static inline u32 mt792x_acpi_get_mtcl_conf(struct mt792x_phy *phy, char *alpha2)
{
- return 0xf;
+ return MT792X_ACPI_MTCL_INVALID;
}
#endif
diff --git a/drivers/net/wireless/mediatek/mt76/mt792x_acpi_sar.c b/drivers/net/wireless/mediatek/mt76/mt792x_acpi_sar.c
index 9317f8ff2070..d1aebadd50aa 100644
--- a/drivers/net/wireless/mediatek/mt76/mt792x_acpi_sar.c
+++ b/drivers/net/wireless/mediatek/mt76/mt792x_acpi_sar.c
@@ -4,6 +4,28 @@
#include <linux/acpi.h>
#include "mt792x.h"
+static const char * const cc_list_all[] = {
+ "00", "EU", "AR", "AU", "AZ", "BY", "BO", "BR",
+ "CA", "CL", "CN", "ID", "JP", "MY", "MX", "ME",
+ "MA", "NZ", "NG", "PH", "RU", "RS", "SG", "KR",
+ "TW", "TH", "UA", "GB", "US", "VN", "KH", "PY",
+};
+
+static const char * const cc_list_eu[] = {
+ "AD", "AT", "BE", "BG", "CY", "CZ", "HR", "DK",
+ "EE", "FI", "FR", "DE", "GR", "HU", "IS", "IE",
+ "IT", "LV", "LI", "LT", "LU", "MC", "MT", "NL",
+ "NO", "PL", "PT", "RO", "SK", "SI", "ES", "SE",
+ "CH",
+};
+
+static const char * const cc_list_be[] = {
+ "AR", "BR", "BY", "CL", "IQ", "MX", "OM", "RU",
+ "RW", "VN", "KR", "UA", "", "", "", "",
+ "EU", "AT", "CN", "CA", "TW", "NZ", "PH", "UK",
+ "US",
+};
+
static int
mt792x_acpi_read(struct mt792x_dev *dev, u8 *method, u8 **tbl, u32 *len)
{
@@ -66,13 +88,22 @@ free:
}
/* MTCL : Country List Table for 6G band */
+/* MTCL : Country List Table for 6G band and 11BE */
static int
mt792x_asar_acpi_read_mtcl(struct mt792x_dev *dev, u8 **table, u8 *version)
{
- int ret;
+ int len, ret;
- *version = ((ret = mt792x_acpi_read(dev, MT792x_ACPI_MTCL, table, NULL)) < 0)
- ? 1 : 2;
+ ret = mt792x_acpi_read(dev, MT792x_ACPI_MTCL, table, &len);
+ if (ret)
+ return ret;
+
+ if (len == sizeof(struct mt792x_asar_cl))
+ *version = ((struct mt792x_asar_cl *)*table)->version;
+ else if (len == sizeof(struct mt792x_asar_cl_v3))
+ *version = ((struct mt792x_asar_cl_v3 *)*table)->version;
+ else
+ return -EINVAL;
return ret;
}
@@ -351,10 +382,24 @@ u8 mt792x_acpi_get_flags(struct mt792x_phy *phy)
}
EXPORT_SYMBOL_GPL(mt792x_acpi_get_flags);
-static u8
+static u32
+mt792x_acpi_get_mtcl_map_v3(int row, int column, struct mt792x_asar_cl_v3 *cl)
+{
+ u32 config = 0;
+ u8 mode_be = 0;
+
+ mode_be = (cl->mode_be > 0x02) ? 0 : cl->mode_be;
+
+ if (cl->version > 2 && cl->clbe[row] & BIT(column))
+ config |= (mode_be & 0x3) << 4;
+
+ return config;
+}
+
+static u32
mt792x_acpi_get_mtcl_map(int row, int column, struct mt792x_asar_cl *cl)
{
- u8 config = 0;
+ u32 config = 0;
u8 mode_6g, mode_5g9;
mode_6g = (cl->mode_6g > 0x02) ? 0 : cl->mode_6g;
@@ -368,30 +413,44 @@ mt792x_acpi_get_mtcl_map(int row, int column, struct mt792x_asar_cl *cl)
return config;
}
-u8 mt792x_acpi_get_mtcl_conf(struct mt792x_phy *phy, char *alpha2)
+static u32
+mt792x_acpi_parse_mtcl_tbl_v3(struct mt792x_phy *phy, char *alpha2)
{
- static const char * const cc_list_all[] = {
- "00", "EU", "AR", "AU", "AZ", "BY", "BO", "BR",
- "CA", "CL", "CN", "ID", "JP", "MY", "MX", "ME",
- "MA", "NZ", "NG", "PH", "RU", "RS", "SG", "KR",
- "TW", "TH", "UA", "GB", "US", "VN", "KH", "PY",
- };
- static const char * const cc_list_eu[] = {
- "AT", "BE", "BG", "CY", "CZ", "HR", "DK", "EE",
- "FI", "FR", "DE", "GR", "HU", "IS", "IE", "IT",
- "LV", "LI", "LT", "LU", "MT", "NL", "NO", "PL",
- "PT", "RO", "SK", "SI", "ES", "SE", "CH",
- };
struct mt792x_acpi_sar *sar = phy->acpisar;
- struct mt792x_asar_cl *cl;
+ struct mt792x_asar_cl_v3 *cl = sar->countrylist_v3;
int col, row, i;
- if (!sar)
- return 0xf;
+ if (sar->ver != 3)
+ goto out;
- cl = sar->countrylist;
if (!cl)
- return 0xc;
+ return MT792X_ACPI_MTCL_INVALID;
+
+ for (i = 0; i < ARRAY_SIZE(cc_list_be); i++) {
+ col = 7 - i % 8;
+ row = i / 8;
+ if (!memcmp(cc_list_be[i], alpha2, 2))
+ return mt792x_acpi_get_mtcl_map_v3(row, col, cl);
+ }
+ for (i = 0; i < ARRAY_SIZE(cc_list_eu); i++) {
+ if (!memcmp(cc_list_eu[i], alpha2, 2))
+ return mt792x_acpi_get_mtcl_map_v3(3, 7, cl);
+ }
+
+out:
+ /* Depends on driver */
+ return 0x20;
+}
+
+static u32
+mt792x_acpi_parse_mtcl_tbl(struct mt792x_phy *phy, char *alpha2)
+{
+ struct mt792x_acpi_sar *sar = phy->acpisar;
+ struct mt792x_asar_cl *cl = sar->countrylist;
+ int col, row, i;
+
+ if (!cl)
+ return MT792X_ACPI_MTCL_INVALID;
for (i = 0; i < ARRAY_SIZE(cc_list_all); i++) {
col = 7 - i % 8;
@@ -406,4 +465,22 @@ u8 mt792x_acpi_get_mtcl_conf(struct mt792x_phy *phy, char *alpha2)
return mt792x_acpi_get_mtcl_map(0, 7, cl);
}
+
+u32 mt792x_acpi_get_mtcl_conf(struct mt792x_phy *phy, char *alpha2)
+{
+ struct mt792x_acpi_sar *sar = phy->acpisar;
+ u32 config = 0;
+
+ if (!sar)
+ return MT792X_ACPI_MTCL_INVALID;
+
+ config = mt792x_acpi_parse_mtcl_tbl_v3(phy, alpha2);
+
+ if (config == MT792X_ACPI_MTCL_INVALID)
+ return MT792X_ACPI_MTCL_INVALID;
+
+ config |= mt792x_acpi_parse_mtcl_tbl(phy, alpha2);
+
+ return config;
+}
EXPORT_SYMBOL_GPL(mt792x_acpi_get_mtcl_conf);
diff --git a/drivers/net/wireless/mediatek/mt76/mt792x_acpi_sar.h b/drivers/net/wireless/mediatek/mt76/mt792x_acpi_sar.h
index 2298983b6342..e45dcd7fbdb1 100644
--- a/drivers/net/wireless/mediatek/mt76/mt792x_acpi_sar.h
+++ b/drivers/net/wireless/mediatek/mt76/mt792x_acpi_sar.h
@@ -15,6 +15,8 @@
#define MT792x_ACPI_MTGS "MTGS"
#define MT792x_ACPI_MTFG "MTFG"
+#define MT792X_ACPI_MTCL_INVALID 0xffffffff
+
struct mt792x_asar_dyn_limit {
u8 idx;
u8 frp[5];
@@ -72,6 +74,17 @@ struct mt792x_asar_geo_v2 {
DECLARE_FLEX_ARRAY(struct mt792x_asar_geo_limit_v2, tbl);
} __packed;
+struct mt792x_asar_cl_v3 {
+ u8 names[4];
+ u8 version;
+ u8 mode_6g;
+ u8 cl6g[6];
+ u8 mode_5g9;
+ u8 cl5g9[6];
+ u8 mode_be;
+ u8 clbe[6];
+} __packed;
+
struct mt792x_asar_cl {
u8 names[4];
u8 version;
@@ -100,7 +113,10 @@ struct mt792x_acpi_sar {
struct mt792x_asar_geo *geo;
struct mt792x_asar_geo_v2 *geo_v2;
};
- struct mt792x_asar_cl *countrylist;
+ union {
+ struct mt792x_asar_cl *countrylist;
+ struct mt792x_asar_cl_v3 *countrylist_v3;
+ };
struct mt792x_asar_fg *fg;
};
diff --git a/drivers/net/wireless/mediatek/mt76/mt792x_core.c b/drivers/net/wireless/mediatek/mt76/mt792x_core.c
index 8799627f6292..38dd58f6e493 100644
--- a/drivers/net/wireless/mediatek/mt76/mt792x_core.c
+++ b/drivers/net/wireless/mediatek/mt76/mt792x_core.c
@@ -340,7 +340,7 @@ void mt792x_unassign_vif_chanctx(struct ieee80211_hw *hw,
mutex_unlock(&dev->mt76.mutex);
if (vif->bss_conf.csa_active) {
- del_timer_sync(&mvif->csa_timer);
+ timer_delete_sync(&mvif->csa_timer);
cancel_work_sync(&mvif->csa_work);
}
}
@@ -665,7 +665,8 @@ int mt792x_init_wiphy(struct ieee80211_hw *hw)
ieee80211_hw_set(hw, SUPPORTS_DYNAMIC_PS);
ieee80211_hw_set(hw, SUPPORTS_VHT_EXT_NSS_BW);
ieee80211_hw_set(hw, CONNECTION_MONITOR);
- ieee80211_hw_set(hw, CHANCTX_STA_CSA);
+ if (is_mt7921(&dev->mt76))
+ ieee80211_hw_set(hw, CHANCTX_STA_CSA);
if (dev->pm.enable)
ieee80211_hw_set(hw, CONNECTION_MONITOR);
diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/coredump.c b/drivers/net/wireless/mediatek/mt76/mt7996/coredump.c
index ccab0d7b9be4..303d6e80a666 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7996/coredump.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7996/coredump.c
@@ -48,8 +48,8 @@ const struct mt7996_mem_region*
mt7996_coredump_get_mem_layout(struct mt7996_dev *dev, u32 *num)
{
switch (mt76_chip(&dev->mt76)) {
- case 0x7990:
- case 0x7991:
+ case MT7996_DEVICE_ID:
+ case MT7996_DEVICE_ID_2:
*num = ARRAY_SIZE(mt7996_mem_regions);
return &mt7996_mem_regions[0];
default:
diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/debugfs.c b/drivers/net/wireless/mediatek/mt76/mt7996/debugfs.c
index 7b2bb72b407d..0ab827f52fd7 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7996/debugfs.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7996/debugfs.c
@@ -222,18 +222,27 @@ static const struct file_operations mt7996_sys_recovery_ops = {
static int
mt7996_radar_trigger(void *data, u64 val)
{
+#define RADAR_MAIN_CHAIN 1
+#define RADAR_BACKGROUND 2
struct mt7996_dev *dev = data;
+ struct mt7996_phy *phy = mt7996_band_phy(dev, NL80211_BAND_5GHZ);
+ int rdd_idx;
- if (val > MT_RX_SEL2)
+ if (!phy || !val || val > RADAR_BACKGROUND)
return -EINVAL;
- if (val == MT_RX_SEL2 && !dev->rdd2_phy) {
+ if (val == RADAR_BACKGROUND && !dev->rdd2_phy) {
dev_err(dev->mt76.dev, "Background radar is not enabled\n");
return -EINVAL;
}
- return mt7996_mcu_rdd_cmd(dev, RDD_RADAR_EMULATE,
- val, 0, 0);
+ rdd_idx = mt7996_get_rdd_idx(phy, val == RADAR_BACKGROUND);
+ if (rdd_idx < 0) {
+ dev_err(dev->mt76.dev, "No RDD found\n");
+ return -EINVAL;
+ }
+
+ return mt7996_mcu_rdd_cmd(dev, RDD_RADAR_EMULATE, rdd_idx, 0);
}
DEFINE_DEBUGFS_ATTRIBUTE(fops_radar_trigger, NULL,
@@ -616,28 +625,51 @@ static void
mt7996_sta_hw_queue_read(void *data, struct ieee80211_sta *sta)
{
struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
- struct mt7996_dev *dev = msta->vif->deflink.phy->dev;
+ struct mt7996_vif *mvif = msta->vif;
+ struct mt7996_dev *dev = mvif->deflink.phy->dev;
+ struct ieee80211_link_sta *link_sta;
struct seq_file *s = data;
- u8 ac;
+ struct ieee80211_vif *vif;
+ unsigned int link_id;
- for (ac = 0; ac < 4; ac++) {
- u32 qlen, ctrl, val;
- u32 idx = msta->wcid.idx >> 5;
- u8 offs = msta->wcid.idx & GENMASK(4, 0);
+ vif = container_of((void *)mvif, struct ieee80211_vif, drv_priv);
- ctrl = BIT(31) | BIT(11) | (ac << 24);
- val = mt76_rr(dev, MT_PLE_AC_QEMPTY(ac, idx));
+ rcu_read_lock();
+
+ for_each_sta_active_link(vif, sta, link_sta, link_id) {
+ struct mt7996_sta_link *msta_link;
+ struct mt76_vif_link *mlink;
+ u8 ac;
- if (val & BIT(offs))
+ mlink = rcu_dereference(mvif->mt76.link[link_id]);
+ if (!mlink)
continue;
- mt76_wr(dev, MT_FL_Q0_CTRL, ctrl | msta->wcid.idx);
- qlen = mt76_get_field(dev, MT_FL_Q3_CTRL,
- GENMASK(11, 0));
- seq_printf(s, "\tSTA %pM wcid %d: AC%d%d queued:%d\n",
- sta->addr, msta->wcid.idx,
- msta->vif->deflink.mt76.wmm_idx, ac, qlen);
+ msta_link = rcu_dereference(msta->link[link_id]);
+ if (!msta_link)
+ continue;
+
+ for (ac = 0; ac < 4; ac++) {
+ u32 idx = msta_link->wcid.idx >> 5, qlen, ctrl, val;
+ u8 offs = msta_link->wcid.idx & GENMASK(4, 0);
+
+ ctrl = BIT(31) | BIT(11) | (ac << 24);
+ val = mt76_rr(dev, MT_PLE_AC_QEMPTY(ac, idx));
+
+ if (val & BIT(offs))
+ continue;
+
+ mt76_wr(dev,
+ MT_FL_Q0_CTRL, ctrl | msta_link->wcid.idx);
+ qlen = mt76_get_field(dev, MT_FL_Q3_CTRL,
+ GENMASK(11, 0));
+ seq_printf(s, "\tSTA %pM wcid %d: AC%d%d queued:%d\n",
+ sta->addr, msta_link->wcid.idx,
+ mlink->wmm_idx, ac, qlen);
+ }
}
+
+ rcu_read_unlock();
}
static int
@@ -930,6 +962,7 @@ static ssize_t mt7996_sta_fixed_rate_set(struct file *file,
struct ieee80211_sta *sta = file->private_data;
struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
struct mt7996_dev *dev = msta->vif->deflink.phy->dev;
+ struct mt7996_sta_link *msta_link = &msta->deflink;
struct ra_rate phy = {};
char buf[100];
int ret;
@@ -964,7 +997,7 @@ static ssize_t mt7996_sta_fixed_rate_set(struct file *file,
goto out;
}
- phy.wlan_idx = cpu_to_le16(msta->wcid.idx);
+ phy.wlan_idx = cpu_to_le16(msta_link->wcid.idx);
phy.gi = cpu_to_le16(gi);
phy.ltf = cpu_to_le16(ltf);
phy.ldpc = phy.ldpc ? 7 : 0;
diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/dma.c b/drivers/net/wireless/mediatek/mt76/mt7996/dma.c
index 69a7d9b2e38b..c8bef0b2a144 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7996/dma.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7996/dma.c
@@ -55,20 +55,32 @@ static void mt7996_dma_config(struct mt7996_dev *dev)
/* rx queue */
RXQ_CONFIG(MT_RXQ_MCU, WFDMA0, MT_INT_RX_DONE_WM, MT7996_RXQ_MCU_WM);
+ /* for mt7990, RX ring 1 is for SDO instead */
RXQ_CONFIG(MT_RXQ_MCU_WA, WFDMA0, MT_INT_RX_DONE_WA, MT7996_RXQ_MCU_WA);
-
- /* mt7996: band0 and band1, mt7992: band0 */
RXQ_CONFIG(MT_RXQ_MAIN, WFDMA0, MT_INT_RX_DONE_BAND0, MT7996_RXQ_BAND0);
- RXQ_CONFIG(MT_RXQ_MAIN_WA, WFDMA0, MT_INT_RX_DONE_WA_MAIN, MT7996_RXQ_MCU_WA_MAIN);
+ if (mt7996_has_wa(dev))
+ RXQ_CONFIG(MT_RXQ_MAIN_WA, WFDMA0, MT_INT_RX_DONE_WA_MAIN,
+ MT7996_RXQ_MCU_WA_MAIN);
- if (is_mt7996(&dev->mt76)) {
+ switch (mt76_chip(&dev->mt76)) {
+ case MT7992_DEVICE_ID:
+ RXQ_CONFIG(MT_RXQ_BAND1_WA, WFDMA0, MT_INT_RX_DONE_WA_EXT, MT7996_RXQ_MCU_WA_EXT);
+ RXQ_CONFIG(MT_RXQ_BAND1, WFDMA0, MT_INT_RX_DONE_BAND1, MT7996_RXQ_BAND1);
+ break;
+ case MT7990_DEVICE_ID:
+ RXQ_CONFIG(MT_RXQ_BAND1, WFDMA0, MT_INT_RX_DONE_BAND1, MT7996_RXQ_BAND1);
+ RXQ_CONFIG(MT_RXQ_TXFREE_BAND0, WFDMA0,
+ MT_INT_RX_TXFREE_BAND0_MT7990, MT7990_RXQ_TXFREE0);
+ if (dev->hif2)
+ RXQ_CONFIG(MT_RXQ_TXFREE_BAND1, WFDMA0,
+ MT_INT_RX_TXFREE_BAND1_MT7990, MT7990_RXQ_TXFREE1);
+ break;
+ case MT7996_DEVICE_ID:
+ default:
/* mt7996 band2 */
- RXQ_CONFIG(MT_RXQ_BAND2, WFDMA0, MT_INT_RX_DONE_BAND2, MT7996_RXQ_BAND2);
RXQ_CONFIG(MT_RXQ_BAND2_WA, WFDMA0, MT_INT_RX_DONE_WA_TRI, MT7996_RXQ_MCU_WA_TRI);
- } else {
- /* mt7992 band1 */
- RXQ_CONFIG(MT_RXQ_BAND1, WFDMA0, MT_INT_RX_DONE_BAND1, MT7996_RXQ_BAND1);
- RXQ_CONFIG(MT_RXQ_BAND1_WA, WFDMA0, MT_INT_RX_DONE_WA_EXT, MT7996_RXQ_MCU_WA_EXT);
+ RXQ_CONFIG(MT_RXQ_BAND2, WFDMA0, MT_INT_RX_DONE_BAND2, MT7996_RXQ_BAND2);
+ break;
}
if (dev->has_rro) {
@@ -104,9 +116,11 @@ static void mt7996_dma_config(struct mt7996_dev *dev)
}
/* mcu tx queue */
- MCUQ_CONFIG(MT_MCUQ_WM, WFDMA0, MT_INT_TX_DONE_MCU_WM, MT7996_TXQ_MCU_WM);
- MCUQ_CONFIG(MT_MCUQ_WA, WFDMA0, MT_INT_TX_DONE_MCU_WA, MT7996_TXQ_MCU_WA);
MCUQ_CONFIG(MT_MCUQ_FWDL, WFDMA0, MT_INT_TX_DONE_FWDL, MT7996_TXQ_FWDL);
+ MCUQ_CONFIG(MT_MCUQ_WM, WFDMA0, MT_INT_TX_DONE_MCU_WM, MT7996_TXQ_MCU_WM);
+ if (mt7996_has_wa(dev))
+ MCUQ_CONFIG(MT_MCUQ_WA, WFDMA0, MT_INT_TX_DONE_MCU_WA,
+ MT7996_TXQ_MCU_WA);
}
static u32 __mt7996_dma_prefetch_base(u16 *base, u8 depth)
@@ -121,43 +135,62 @@ static u32 __mt7996_dma_prefetch_base(u16 *base, u8 depth)
static void __mt7996_dma_prefetch(struct mt7996_dev *dev, u32 ofs)
{
u16 base = 0;
- u8 queue;
+ u8 queue, val;
#define PREFETCH(_depth) (__mt7996_dma_prefetch_base(&base, (_depth)))
/* prefetch SRAM wrapping boundary for tx/rx ring. */
- mt76_wr(dev, MT_MCUQ_EXT_CTRL(MT_MCUQ_FWDL) + ofs, PREFETCH(0x2));
- mt76_wr(dev, MT_MCUQ_EXT_CTRL(MT_MCUQ_WM) + ofs, PREFETCH(0x2));
+ /* Tx Command Rings */
+ val = is_mt7996(&dev->mt76) ? 2 : 4;
+ mt76_wr(dev, MT_MCUQ_EXT_CTRL(MT_MCUQ_FWDL) + ofs, PREFETCH(val));
+ mt76_wr(dev, MT_MCUQ_EXT_CTRL(MT_MCUQ_WM) + ofs, PREFETCH(val));
+ if (mt7996_has_wa(dev))
+ mt76_wr(dev, MT_MCUQ_EXT_CTRL(MT_MCUQ_WA) + ofs, PREFETCH(val));
+
+ /* Tx Data Rings */
mt76_wr(dev, MT_TXQ_EXT_CTRL(0) + ofs, PREFETCH(0x8));
- mt76_wr(dev, MT_TXQ_EXT_CTRL(1) + ofs, PREFETCH(0x8));
- mt76_wr(dev, MT_MCUQ_EXT_CTRL(MT_MCUQ_WA) + ofs, PREFETCH(0x2));
- mt76_wr(dev, MT_TXQ_EXT_CTRL(2) + ofs, PREFETCH(0x8));
- mt76_wr(dev, MT_RXQ_BAND1_CTRL(MT_RXQ_MCU) + ofs, PREFETCH(0x2));
- mt76_wr(dev, MT_RXQ_BAND1_CTRL(MT_RXQ_MCU_WA) + ofs, PREFETCH(0x2));
- mt76_wr(dev, MT_RXQ_BAND1_CTRL(MT_RXQ_MAIN_WA) + ofs, PREFETCH(0x2));
-
- queue = is_mt7996(&dev->mt76) ? MT_RXQ_BAND2_WA : MT_RXQ_BAND1_WA;
- mt76_wr(dev, MT_RXQ_BAND1_CTRL(queue) + ofs, PREFETCH(0x2));
-
- mt76_wr(dev, MT_RXQ_BAND1_CTRL(MT_RXQ_MAIN) + ofs, PREFETCH(0x10));
+ if (!is_mt7996(&dev->mt76) || dev->hif2)
+ mt76_wr(dev, MT_TXQ_EXT_CTRL(1) + ofs, PREFETCH(0x8));
+ if (is_mt7996(&dev->mt76))
+ mt76_wr(dev, MT_TXQ_EXT_CTRL(2) + ofs, PREFETCH(0x8));
+
+ /* Rx Event Rings */
+ mt76_wr(dev, MT_RXQ_EXT_CTRL(MT_RXQ_MCU) + ofs, PREFETCH(val));
+ mt76_wr(dev, MT_RXQ_EXT_CTRL(MT_RXQ_MCU_WA) + ofs, PREFETCH(val));
+
+ /* Rx TxFreeDone From WA Rings */
+ if (mt7996_has_wa(dev)) {
+ mt76_wr(dev, MT_RXQ_EXT_CTRL(MT_RXQ_MAIN_WA) + ofs, PREFETCH(val));
+ queue = is_mt7996(&dev->mt76) ? MT_RXQ_BAND2_WA : MT_RXQ_BAND1_WA;
+ mt76_wr(dev, MT_RXQ_EXT_CTRL(queue) + ofs, PREFETCH(val));
+ }
+ /* Rx TxFreeDone From MAC Rings */
+ val = is_mt7996(&dev->mt76) ? 4 : 8;
+ if (is_mt7990(&dev->mt76) || (is_mt7996(&dev->mt76) && dev->has_rro))
+ mt76_wr(dev, MT_RXQ_EXT_CTRL(MT_RXQ_TXFREE_BAND0) + ofs, PREFETCH(val));
+ if (is_mt7990(&dev->mt76) && dev->hif2)
+ mt76_wr(dev, MT_RXQ_EXT_CTRL(MT_RXQ_TXFREE_BAND1) + ofs, PREFETCH(val));
+ else if (is_mt7996(&dev->mt76) && dev->has_rro)
+ mt76_wr(dev, MT_RXQ_EXT_CTRL(MT_RXQ_TXFREE_BAND2) + ofs, PREFETCH(val));
+
+ /* Rx Data Rings */
+ mt76_wr(dev, MT_RXQ_EXT_CTRL(MT_RXQ_MAIN) + ofs, PREFETCH(0x10));
queue = is_mt7996(&dev->mt76) ? MT_RXQ_BAND2 : MT_RXQ_BAND1;
- mt76_wr(dev, MT_RXQ_BAND1_CTRL(queue) + ofs, PREFETCH(0x10));
+ mt76_wr(dev, MT_RXQ_EXT_CTRL(queue) + ofs, PREFETCH(0x10));
+ /* Rx RRO Rings */
if (dev->has_rro) {
- mt76_wr(dev, MT_RXQ_BAND1_CTRL(MT_RXQ_RRO_BAND0) + ofs,
- PREFETCH(0x10));
- mt76_wr(dev, MT_RXQ_BAND1_CTRL(MT_RXQ_RRO_BAND2) + ofs,
- PREFETCH(0x10));
- mt76_wr(dev, MT_RXQ_BAND1_CTRL(MT_RXQ_MSDU_PAGE_BAND0) + ofs,
- PREFETCH(0x4));
- mt76_wr(dev, MT_RXQ_BAND1_CTRL(MT_RXQ_MSDU_PAGE_BAND1) + ofs,
- PREFETCH(0x4));
- mt76_wr(dev, MT_RXQ_BAND1_CTRL(MT_RXQ_MSDU_PAGE_BAND2) + ofs,
- PREFETCH(0x4));
- mt76_wr(dev, MT_RXQ_BAND1_CTRL(MT_RXQ_TXFREE_BAND0) + ofs,
- PREFETCH(0x4));
- mt76_wr(dev, MT_RXQ_BAND1_CTRL(MT_RXQ_TXFREE_BAND2) + ofs,
- PREFETCH(0x4));
+ mt76_wr(dev, MT_RXQ_EXT_CTRL(MT_RXQ_RRO_BAND0) + ofs, PREFETCH(0x10));
+ queue = is_mt7996(&dev->mt76) ? MT_RXQ_RRO_BAND2 : MT_RXQ_RRO_BAND1;
+ mt76_wr(dev, MT_RXQ_EXT_CTRL(queue) + ofs, PREFETCH(0x10));
+
+ mt76_wr(dev, MT_RXQ_EXT_CTRL(MT_RXQ_MSDU_PAGE_BAND0) + ofs, PREFETCH(val));
+ if (is_mt7996(&dev->mt76)) {
+ mt76_wr(dev, MT_RXQ_EXT_CTRL(MT_RXQ_MSDU_PAGE_BAND1) + ofs,
+ PREFETCH(val));
+ mt76_wr(dev, MT_RXQ_EXT_CTRL(MT_RXQ_MSDU_PAGE_BAND2) + ofs,
+ PREFETCH(val));
+ }
}
#undef PREFETCH
@@ -269,6 +302,9 @@ void mt7996_dma_start(struct mt7996_dev *dev, bool reset, bool wed_reset)
mtk_wed_device_start(wed, wed_irq_mask);
}
+ if (!mt7996_has_wa(dev))
+ irq_mask &= ~(MT_INT_RX(MT_RXQ_MAIN_WA) |
+ MT_INT_RX(MT_RXQ_BAND1_WA));
irq_mask = reset ? MT_INT_MCU_CMD : irq_mask;
mt7996_irq_enable(dev, irq_mask);
@@ -474,12 +510,14 @@ int mt7996_dma_init(struct mt7996_dev *dev)
return ret;
/* command to WA */
- ret = mt76_init_mcu_queue(&dev->mt76, MT_MCUQ_WA,
- MT_MCUQ_ID(MT_MCUQ_WA),
- MT7996_TX_MCU_RING_SIZE,
- MT_MCUQ_RING_BASE(MT_MCUQ_WA));
- if (ret)
- return ret;
+ if (mt7996_has_wa(dev)) {
+ ret = mt76_init_mcu_queue(&dev->mt76, MT_MCUQ_WA,
+ MT_MCUQ_ID(MT_MCUQ_WA),
+ MT7996_TX_MCU_RING_SIZE,
+ MT_MCUQ_RING_BASE(MT_MCUQ_WA));
+ if (ret)
+ return ret;
+ }
/* firmware download */
ret = mt76_init_mcu_queue(&dev->mt76, MT_MCUQ_FWDL,
@@ -493,16 +531,16 @@ int mt7996_dma_init(struct mt7996_dev *dev)
ret = mt76_queue_alloc(dev, &dev->mt76.q_rx[MT_RXQ_MCU],
MT_RXQ_ID(MT_RXQ_MCU),
MT7996_RX_MCU_RING_SIZE,
- MT_RX_BUF_SIZE,
+ MT7996_RX_MCU_BUF_SIZE,
MT_RXQ_RING_BASE(MT_RXQ_MCU));
if (ret)
return ret;
- /* event from WA */
+ /* event from WA, or SDO event for mt7990 */
ret = mt76_queue_alloc(dev, &dev->mt76.q_rx[MT_RXQ_MCU_WA],
MT_RXQ_ID(MT_RXQ_MCU_WA),
MT7996_RX_MCU_RING_SIZE_WA,
- MT_RX_BUF_SIZE,
+ MT7996_RX_MCU_BUF_SIZE,
MT_RXQ_RING_BASE(MT_RXQ_MCU_WA));
if (ret)
return ret;
@@ -527,13 +565,41 @@ int mt7996_dma_init(struct mt7996_dev *dev)
dev->mt76.q_rx[MT_RXQ_MAIN_WA].wed = wed;
}
- ret = mt76_queue_alloc(dev, &dev->mt76.q_rx[MT_RXQ_MAIN_WA],
- MT_RXQ_ID(MT_RXQ_MAIN_WA),
- MT7996_RX_MCU_RING_SIZE,
- MT_RX_BUF_SIZE,
- MT_RXQ_RING_BASE(MT_RXQ_MAIN_WA));
- if (ret)
- return ret;
+ if (mt7996_has_wa(dev)) {
+ ret = mt76_queue_alloc(dev, &dev->mt76.q_rx[MT_RXQ_MAIN_WA],
+ MT_RXQ_ID(MT_RXQ_MAIN_WA),
+ MT7996_RX_MCU_RING_SIZE,
+ MT_RX_BUF_SIZE,
+ MT_RXQ_RING_BASE(MT_RXQ_MAIN_WA));
+ if (ret)
+ return ret;
+ } else {
+ if (mtk_wed_device_active(wed)) {
+ dev->mt76.q_rx[MT_RXQ_TXFREE_BAND0].flags = MT_WED_Q_TXFREE;
+ dev->mt76.q_rx[MT_RXQ_TXFREE_BAND0].wed = wed;
+ }
+ ret = mt76_queue_alloc(dev, &dev->mt76.q_rx[MT_RXQ_TXFREE_BAND0],
+ MT_RXQ_ID(MT_RXQ_TXFREE_BAND0),
+ MT7996_RX_MCU_RING_SIZE,
+ MT7996_RX_BUF_SIZE,
+ MT_RXQ_RING_BASE(MT_RXQ_TXFREE_BAND0));
+ if (ret)
+ return ret;
+ }
+
+ if (!mt7996_has_wa(dev) && dev->hif2) {
+ if (mtk_wed_device_active(wed)) {
+ dev->mt76.q_rx[MT_RXQ_TXFREE_BAND1].flags = MT_WED_Q_TXFREE;
+ dev->mt76.q_rx[MT_RXQ_TXFREE_BAND1].wed = wed;
+ }
+ ret = mt76_queue_alloc(dev, &dev->mt76.q_rx[MT_RXQ_TXFREE_BAND1],
+ MT_RXQ_ID(MT_RXQ_TXFREE_BAND1),
+ MT7996_RX_MCU_RING_SIZE,
+ MT7996_RX_BUF_SIZE,
+ MT_RXQ_RING_BASE(MT_RXQ_TXFREE_BAND1));
+ if (ret)
+ return ret;
+ }
if (mt7996_band_valid(dev, MT_BAND2)) {
/* rx data queue for mt7996 band2 */
@@ -573,14 +639,16 @@ int mt7996_dma_init(struct mt7996_dev *dev)
return ret;
/* tx free notify event from WA for mt7992 band1 */
- rx_base = MT_RXQ_RING_BASE(MT_RXQ_BAND1_WA) + hif1_ofs;
- ret = mt76_queue_alloc(dev, &dev->mt76.q_rx[MT_RXQ_BAND1_WA],
- MT_RXQ_ID(MT_RXQ_BAND1_WA),
- MT7996_RX_MCU_RING_SIZE,
- MT_RX_BUF_SIZE,
- rx_base);
- if (ret)
- return ret;
+ if (mt7996_has_wa(dev)) {
+ rx_base = MT_RXQ_RING_BASE(MT_RXQ_BAND1_WA) + hif1_ofs;
+ ret = mt76_queue_alloc(dev, &dev->mt76.q_rx[MT_RXQ_BAND1_WA],
+ MT_RXQ_ID(MT_RXQ_BAND1_WA),
+ MT7996_RX_MCU_RING_SIZE,
+ MT_RX_BUF_SIZE,
+ rx_base);
+ if (ret)
+ return ret;
+ }
}
if (mtk_wed_device_active(wed) && mtk_wed_get_rx_capa(wed) &&
diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/eeprom.c b/drivers/net/wireless/mediatek/mt76/mt7996/eeprom.c
index 53dfac02f8af..87c6192b6384 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7996/eeprom.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7996/eeprom.c
@@ -13,10 +13,12 @@ static int mt7996_check_eeprom(struct mt7996_dev *dev)
u16 val = get_unaligned_le16(eeprom);
switch (val) {
- case 0x7990:
+ case MT7996_DEVICE_ID:
return is_mt7996(&dev->mt76) ? 0 : -EINVAL;
- case 0x7992:
+ case MT7992_DEVICE_ID:
return is_mt7992(&dev->mt76) ? 0 : -EINVAL;
+ case MT7990_DEVICE_ID:
+ return is_mt7990(&dev->mt76) ? 0 : -EINVAL;
default:
return -EINVAL;
}
@@ -25,7 +27,7 @@ static int mt7996_check_eeprom(struct mt7996_dev *dev)
static char *mt7996_eeprom_name(struct mt7996_dev *dev)
{
switch (mt76_chip(&dev->mt76)) {
- case 0x7992:
+ case MT7992_DEVICE_ID:
switch (dev->var.type) {
case MT7992_VAR_TYPE_23:
if (dev->var.fem == MT7996_FEM_INT)
@@ -39,7 +41,11 @@ static char *mt7996_eeprom_name(struct mt7996_dev *dev)
return MT7992_EEPROM_DEFAULT_MIX;
return MT7992_EEPROM_DEFAULT;
}
- case 0x7990:
+ case MT7990_DEVICE_ID:
+ if (dev->var.fem == MT7996_FEM_INT)
+ return MT7990_EEPROM_DEFAULT_INT;
+ return MT7990_EEPROM_DEFAULT;
+ case MT7996_DEVICE_ID:
default:
switch (dev->var.type) {
case MT7996_VAR_TYPE_233:
@@ -304,6 +310,7 @@ int mt7996_eeprom_parse_hw_cap(struct mt7996_dev *dev, struct mt7996_phy *phy)
phy->has_aux_rx = true;
mphy->antenna_mask = BIT(nss) - 1;
+ phy->orig_antenna_mask = mphy->antenna_mask;
mphy->chainmask = (BIT(path) - 1) << dev->chainshift[band_idx];
phy->orig_chainmask = mphy->chainmask;
dev->chainmask |= mphy->chainmask;
@@ -370,3 +377,30 @@ s8 mt7996_eeprom_get_power_delta(struct mt7996_dev *dev, int band)
return val & MT_EE_RATE_DELTA_SIGN ? delta : -delta;
}
+
+bool mt7996_eeprom_has_background_radar(struct mt7996_dev *dev)
+{
+ switch (mt76_chip(&dev->mt76)) {
+ case MT7996_DEVICE_ID:
+ if (dev->var.type == MT7996_VAR_TYPE_233)
+ return false;
+ break;
+ case MT7992_DEVICE_ID:
+ if (dev->var.type == MT7992_VAR_TYPE_23)
+ return false;
+ break;
+ case MT7990_DEVICE_ID: {
+ u8 path, rx_path, nss, *eeprom = dev->mt76.eeprom.data;
+
+ mt7996_eeprom_parse_stream(eeprom, MT_BAND1, &path, &rx_path, &nss);
+ /* Disable background radar capability in 3T3R */
+ if (path == 3 || rx_path == 3)
+ return false;
+ break;
+ }
+ default:
+ return false;
+ }
+
+ return true;
+}
diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/init.c b/drivers/net/wireless/mediatek/mt76/mt7996/init.c
index 6b660424aedc..a9599c286328 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7996/init.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7996/init.c
@@ -217,6 +217,9 @@ static int mt7996_thermal_init(struct mt7996_phy *phy)
name = devm_kasprintf(&wiphy->dev, GFP_KERNEL, "mt7996_%s.%d",
wiphy_name(wiphy), phy->mt76->band_idx);
+ if (!name)
+ return -ENOMEM;
+
snprintf(cname, sizeof(cname), "cooling_device%d", phy->mt76->band_idx);
cdev = thermal_cooling_device_register(name, phy, &mt7996_thermal_ops);
@@ -314,8 +317,8 @@ static void __mt7996_init_txpower(struct mt7996_phy *phy,
struct ieee80211_supported_band *sband)
{
struct mt7996_dev *dev = phy->dev;
- int i, nss = hweight16(phy->mt76->chainmask);
- int nss_delta = mt76_tx_power_nss_delta(nss);
+ int i, n_chains = hweight16(phy->mt76->chainmask);
+ int path_delta = mt76_tx_power_path_delta(n_chains);
int pwr_delta = mt7996_eeprom_get_power_delta(dev, sband->band);
struct mt76_power_limits limits;
@@ -327,7 +330,7 @@ static void __mt7996_init_txpower(struct mt7996_phy *phy,
target_power = mt76_get_rate_power_limits(phy->mt76, chan,
&limits,
target_power);
- target_power += nss_delta;
+ target_power += path_delta;
target_power = DIV_ROUND_UP(target_power, 2);
chan->max_power = min_t(int, chan->max_reg_power,
target_power);
@@ -440,6 +443,9 @@ mt7996_init_wiphy(struct ieee80211_hw *hw, struct mtk_wed_device *wed)
hw->queues = 4;
hw->max_rx_aggregation_subframes = max_subframes;
hw->max_tx_aggregation_subframes = max_subframes;
+ if (is_mt7990(mdev) && dev->has_eht)
+ hw->max_tx_aggregation_subframes = 512;
+
hw->netdev_features = NETIF_F_RXCSUM;
if (mtk_wed_device_active(wed))
hw->netdev_features |= NETIF_F_HW_TC;
@@ -472,7 +478,7 @@ mt7996_init_wiphy(struct ieee80211_hw *hw, struct mtk_wed_device *wed)
wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_CAN_REPLACE_PTK0);
wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_MU_MIMO_AIR_SNIFFER);
- if (mt7996_has_background_radar(dev) &&
+ if (mt7996_eeprom_has_background_radar(dev) &&
(!mdev->dev->of_node ||
!of_property_read_bool(mdev->dev->of_node,
"mediatek,disable-radar-background")))
@@ -929,13 +935,13 @@ static int mt7996_variant_type_init(struct mt7996_dev *dev)
u8 var_type;
switch (mt76_chip(&dev->mt76)) {
- case 0x7990:
+ case MT7996_DEVICE_ID:
if (val & MT_PAD_GPIO_2ADIE_TBTC)
var_type = MT7996_VAR_TYPE_233;
else
var_type = MT7996_VAR_TYPE_444;
break;
- case 0x7992:
+ case MT7992_DEVICE_ID:
if (val & MT_PAD_GPIO_ADIE_SINGLE)
var_type = MT7992_VAR_TYPE_23;
else if (u32_get_bits(val, MT_PAD_GPIO_ADIE_COMB_7992))
@@ -943,6 +949,9 @@ static int mt7996_variant_type_init(struct mt7996_dev *dev)
else
return -EINVAL;
break;
+ case MT7990_DEVICE_ID:
+ var_type = MT7990_VAR_TYPE_23;
+ break;
default:
return -EINVAL;
}
@@ -1061,10 +1070,10 @@ void mt7996_set_stream_vht_txbf_caps(struct mt7996_phy *phy)
*cap |= IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE |
IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE;
- if (is_mt7996(phy->mt76->dev))
- *cap |= FIELD_PREP(IEEE80211_VHT_CAP_BEAMFORMEE_STS_MASK, 3);
- else
+ if (is_mt7992(phy->mt76->dev))
*cap |= FIELD_PREP(IEEE80211_VHT_CAP_BEAMFORMEE_STS_MASK, 4);
+ else
+ *cap |= FIELD_PREP(IEEE80211_VHT_CAP_BEAMFORMEE_STS_MASK, 3);
*cap &= ~(IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_MASK |
IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE |
@@ -1107,18 +1116,17 @@ mt7996_set_stream_he_txbf_caps(struct mt7996_phy *phy,
elem->phy_cap_info[7] &= ~IEEE80211_HE_PHY_CAP7_MAX_NC_MASK;
c = IEEE80211_HE_PHY_CAP2_NDP_4x_LTF_AND_3_2US |
- IEEE80211_HE_PHY_CAP2_UL_MU_FULL_MU_MIMO |
- IEEE80211_HE_PHY_CAP2_UL_MU_PARTIAL_MU_MIMO;
+ IEEE80211_HE_PHY_CAP2_UL_MU_FULL_MU_MIMO;
elem->phy_cap_info[2] |= c;
c = IEEE80211_HE_PHY_CAP4_SU_BEAMFORMEE;
- if (is_mt7996(phy->mt76->dev))
- c |= IEEE80211_HE_PHY_CAP4_BEAMFORMEE_MAX_STS_UNDER_80MHZ_4 |
- (IEEE80211_HE_PHY_CAP4_BEAMFORMEE_MAX_STS_ABOVE_80MHZ_4 * non_2g);
- else
+ if (is_mt7992(phy->mt76->dev))
c |= IEEE80211_HE_PHY_CAP4_BEAMFORMEE_MAX_STS_UNDER_80MHZ_5 |
(IEEE80211_HE_PHY_CAP4_BEAMFORMEE_MAX_STS_ABOVE_80MHZ_5 * non_2g);
+ else
+ c |= IEEE80211_HE_PHY_CAP4_BEAMFORMEE_MAX_STS_UNDER_80MHZ_4 |
+ (IEEE80211_HE_PHY_CAP4_BEAMFORMEE_MAX_STS_ABOVE_80MHZ_4 * non_2g);
elem->phy_cap_info[4] |= c;
@@ -1318,6 +1326,9 @@ mt7996_init_eht_caps(struct mt7996_phy *phy, enum nl80211_band band,
u8_encode_bits(IEEE80211_EHT_MAC_CAP0_MAX_MPDU_LEN_11454,
IEEE80211_EHT_MAC_CAP0_MAX_MPDU_LEN_MASK);
+ eht_cap_elem->mac_cap_info[1] |=
+ IEEE80211_EHT_MAC_CAP1_MAX_AMPDU_LEN_MASK;
+
eht_cap_elem->phy_cap_info[0] =
IEEE80211_EHT_PHY_CAP0_NDP_4_EHT_LFT_32_GI |
IEEE80211_EHT_PHY_CAP0_SU_BEAMFORMER |
diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mac.c b/drivers/net/wireless/mediatek/mt76/mt7996/mac.c
index 019c925ae600..0dbd4662bc84 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7996/mac.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7996/mac.c
@@ -53,26 +53,48 @@ static const struct mt7996_dfs_radar_spec jp_radar_specs = {
};
static struct mt76_wcid *mt7996_rx_get_wcid(struct mt7996_dev *dev,
- u16 idx, bool unicast)
+ u16 idx, u8 band_idx)
{
- struct mt7996_sta *sta;
+ struct mt7996_sta_link *msta_link;
+ struct mt7996_sta *msta;
+ struct mt7996_vif *mvif;
struct mt76_wcid *wcid;
+ int i;
if (idx >= ARRAY_SIZE(dev->mt76.wcid))
return NULL;
wcid = rcu_dereference(dev->mt76.wcid[idx]);
- if (unicast || !wcid)
- return wcid;
+ if (!wcid)
+ return NULL;
- if (!wcid->sta)
+ if (!mt7996_band_valid(dev, band_idx))
return NULL;
- sta = container_of(wcid, struct mt7996_sta, wcid);
- if (!sta->vif)
+ if (wcid->phy_idx == band_idx)
+ return wcid;
+
+ msta_link = container_of(wcid, struct mt7996_sta_link, wcid);
+ msta = msta_link->sta;
+ if (!msta || !msta->vif)
return NULL;
- return &sta->vif->deflink.sta.wcid;
+ mvif = msta->vif;
+ for (i = 0; i < ARRAY_SIZE(mvif->mt76.link); i++) {
+ struct mt76_vif_link *mlink;
+
+ mlink = rcu_dereference(mvif->mt76.link[i]);
+ if (!mlink)
+ continue;
+
+ if (mlink->band_idx != band_idx)
+ continue;
+
+ msta_link = rcu_dereference(msta->link[i]);
+ break;
+ }
+
+ return &msta_link->wcid;
}
bool mt7996_mac_wtbl_update(struct mt7996_dev *dev, int idx, u32 mask)
@@ -100,10 +122,13 @@ static void mt7996_mac_sta_poll(struct mt7996_dev *dev)
[IEEE80211_AC_VI] = 4,
[IEEE80211_AC_VO] = 6
};
+ struct mt7996_sta_link *msta_link;
+ struct mt76_vif_link *mlink;
struct ieee80211_sta *sta;
struct mt7996_sta *msta;
u32 tx_time[IEEE80211_NUM_ACS], rx_time[IEEE80211_NUM_ACS];
LIST_HEAD(sta_poll_list);
+ struct mt76_wcid *wcid;
int i;
spin_lock_bh(&dev->mt76.sta_poll_lock);
@@ -123,25 +148,28 @@ static void mt7996_mac_sta_poll(struct mt7996_dev *dev)
spin_unlock_bh(&dev->mt76.sta_poll_lock);
break;
}
- msta = list_first_entry(&sta_poll_list,
- struct mt7996_sta, wcid.poll_list);
- list_del_init(&msta->wcid.poll_list);
+ msta_link = list_first_entry(&sta_poll_list,
+ struct mt7996_sta_link,
+ wcid.poll_list);
+ msta = msta_link->sta;
+ wcid = &msta_link->wcid;
+ list_del_init(&wcid->poll_list);
spin_unlock_bh(&dev->mt76.sta_poll_lock);
- idx = msta->wcid.idx;
+ idx = wcid->idx;
/* refresh peer's airtime reporting */
addr = mt7996_mac_wtbl_lmac_addr(dev, idx, 20);
for (i = 0; i < IEEE80211_NUM_ACS; i++) {
- u32 tx_last = msta->airtime_ac[i];
- u32 rx_last = msta->airtime_ac[i + 4];
+ u32 tx_last = msta_link->airtime_ac[i];
+ u32 rx_last = msta_link->airtime_ac[i + 4];
- msta->airtime_ac[i] = mt76_rr(dev, addr);
- msta->airtime_ac[i + 4] = mt76_rr(dev, addr + 4);
+ msta_link->airtime_ac[i] = mt76_rr(dev, addr);
+ msta_link->airtime_ac[i + 4] = mt76_rr(dev, addr + 4);
- tx_time[i] = msta->airtime_ac[i] - tx_last;
- rx_time[i] = msta->airtime_ac[i + 4] - rx_last;
+ tx_time[i] = msta_link->airtime_ac[i] - tx_last;
+ rx_time[i] = msta_link->airtime_ac[i + 4] - rx_last;
if ((tx_last | rx_last) & BIT(30))
clear = true;
@@ -152,10 +180,11 @@ static void mt7996_mac_sta_poll(struct mt7996_dev *dev)
if (clear) {
mt7996_mac_wtbl_update(dev, idx,
MT_WTBL_UPDATE_ADM_COUNT_CLEAR);
- memset(msta->airtime_ac, 0, sizeof(msta->airtime_ac));
+ memset(msta_link->airtime_ac, 0,
+ sizeof(msta_link->airtime_ac));
}
- if (!msta->wcid.sta)
+ if (!wcid->sta)
continue;
sta = container_of((void *)msta, struct ieee80211_sta,
@@ -181,28 +210,23 @@ static void mt7996_mac_sta_poll(struct mt7996_dev *dev)
rssi[2] = to_rssi(GENMASK(23, 16), val);
rssi[3] = to_rssi(GENMASK(31, 14), val);
- msta->ack_signal =
- mt76_rx_signal(msta->vif->deflink.phy->mt76->antenna_mask, rssi);
+ mlink = rcu_dereference(msta->vif->mt76.link[wcid->link_id]);
+ if (mlink) {
+ struct mt76_phy *mphy = mt76_vif_link_phy(mlink);
- ewma_avg_signal_add(&msta->avg_ack_signal, -msta->ack_signal);
+ if (mphy)
+ msta_link->ack_signal =
+ mt76_rx_signal(mphy->antenna_mask,
+ rssi);
+ }
+
+ ewma_avg_signal_add(&msta_link->avg_ack_signal,
+ -msta_link->ack_signal);
}
rcu_read_unlock();
}
-void mt7996_mac_enable_rtscts(struct mt7996_dev *dev,
- struct ieee80211_vif *vif, bool enable)
-{
- struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
- u32 addr;
-
- addr = mt7996_mac_wtbl_lmac_addr(dev, mvif->deflink.sta.wcid.idx, 5);
- if (enable)
- mt76_set(dev, addr, BIT(5));
- else
- mt76_clear(dev, addr, BIT(5));
-}
-
/* The HW does not translate the mac header to 802.3 for mesh point */
static int mt7996_reverse_frag0_hdr_trans(struct sk_buff *skb, u16 hdr_gap)
{
@@ -474,11 +498,15 @@ mt7996_mac_fill_rx(struct mt7996_dev *dev, enum mt76_rxq_id q,
unicast = FIELD_GET(MT_RXD3_NORMAL_ADDR_TYPE, rxd3) == MT_RXD3_NORMAL_U2M;
idx = FIELD_GET(MT_RXD1_NORMAL_WLAN_IDX, rxd1);
- status->wcid = mt7996_rx_get_wcid(dev, idx, unicast);
+ status->wcid = mt7996_rx_get_wcid(dev, idx, band_idx);
if (status->wcid) {
- msta = container_of(status->wcid, struct mt7996_sta, wcid);
- mt76_wcid_add_poll(&dev->mt76, &msta->wcid);
+ struct mt7996_sta_link *msta_link;
+
+ msta_link = container_of(status->wcid, struct mt7996_sta_link,
+ wcid);
+ msta = msta_link->sta;
+ mt76_wcid_add_poll(&dev->mt76, &msta_link->wcid);
}
status->freq = mphy->chandef.chan->center_freq;
@@ -619,6 +647,14 @@ mt7996_mac_fill_rx(struct mt7996_dev *dev, enum mt76_rxq_id q,
status->last_amsdu = amsdu_info == MT_RXD4_LAST_AMSDU_FRAME;
}
+ /* IEEE 802.11 fragmentation can only be applied to unicast frames.
+ * Hence, drop fragments with multicast/broadcast RA.
+ * This check fixes vulnerabilities, like CVE-2020-26145.
+ */
+ if ((ieee80211_has_morefrags(fc) || seq_ctrl & IEEE80211_SCTL_FRAG) &&
+ FIELD_GET(MT_RXD3_NORMAL_ADDR_TYPE, rxd3) != MT_RXD3_NORMAL_U2M)
+ return -EINVAL;
+
hdr_gap = (u8 *)rxd - skb->data + 2 * remove_pad;
if (hdr_trans && ieee80211_has_morefrags(fc)) {
if (mt7996_reverse_frag0_hdr_trans(skb, hdr_gap))
@@ -717,9 +753,8 @@ mt7996_mac_write_txwi_8023(struct mt7996_dev *dev, __le32 *txwi,
u32 val;
if (wcid->sta) {
- struct ieee80211_sta *sta;
+ struct ieee80211_sta *sta = wcid_to_sta(wcid);
- sta = container_of((void *)wcid, struct ieee80211_sta, drv_priv);
wmm = sta->wme;
}
@@ -746,7 +781,9 @@ mt7996_mac_write_txwi_8023(struct mt7996_dev *dev, __le32 *txwi,
static void
mt7996_mac_write_txwi_80211(struct mt7996_dev *dev, __le32 *txwi,
- struct sk_buff *skb, struct ieee80211_key_conf *key)
+ struct sk_buff *skb,
+ struct ieee80211_key_conf *key,
+ struct mt76_wcid *wcid)
{
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)skb->data;
@@ -754,15 +791,19 @@ mt7996_mac_write_txwi_80211(struct mt7996_dev *dev, __le32 *txwi,
bool multicast = is_multicast_ether_addr(hdr->addr1);
u8 tid = skb->priority & IEEE80211_QOS_CTL_TID_MASK;
__le16 fc = hdr->frame_control, sc = hdr->seq_ctrl;
+ u16 seqno = le16_to_cpu(sc);
u8 fc_type, fc_stype;
u32 val;
if (ieee80211_is_action(fc) &&
mgmt->u.action.category == WLAN_CATEGORY_BACK &&
- mgmt->u.action.u.addba_req.action_code == WLAN_ACTION_ADDBA_REQ)
+ mgmt->u.action.u.addba_req.action_code == WLAN_ACTION_ADDBA_REQ) {
+ if (is_mt7990(&dev->mt76))
+ txwi[6] |= cpu_to_le32(FIELD_PREP(MT_TXD6_TID_ADDBA, tid));
tid = MT_TX_ADDBA;
- else if (ieee80211_is_mgmt(hdr->frame_control))
+ } else if (ieee80211_is_mgmt(hdr->frame_control)) {
tid = MT_TX_NORMAL;
+ }
val = FIELD_PREP(MT_TXD1_HDR_FORMAT, MT_HDR_FORMAT_802_11) |
FIELD_PREP(MT_TXD1_HDR_INFO,
@@ -773,8 +814,7 @@ mt7996_mac_write_txwi_80211(struct mt7996_dev *dev, __le32 *txwi,
info->flags & IEEE80211_TX_CTL_USE_MINRATE)
val |= MT_TXD1_FIXED_RATE;
- if (key && multicast && ieee80211_is_robust_mgmt_frame(skb) &&
- key->cipher == WLAN_CIPHER_SUITE_AES_CMAC) {
+ if (key && multicast && ieee80211_is_robust_mgmt_frame(skb)) {
val |= MT_TXD1_BIP;
txwi[3] &= ~cpu_to_le32(MT_TXD3_PROTECT_FRAME);
}
@@ -804,9 +844,13 @@ mt7996_mac_write_txwi_80211(struct mt7996_dev *dev, __le32 *txwi,
txwi[3] |= cpu_to_le32(MT_TXD3_REM_TX_COUNT);
}
- if (info->flags & IEEE80211_TX_CTL_INJECTED) {
- u16 seqno = le16_to_cpu(sc);
+ if (multicast && ieee80211_vif_is_mld(info->control.vif)) {
+ val = MT_TXD3_SN_VALID |
+ FIELD_PREP(MT_TXD3_SEQ, IEEE80211_SEQ_TO_SN(seqno));
+ txwi[3] |= cpu_to_le32(val);
+ }
+ if (info->flags & IEEE80211_TX_CTL_INJECTED) {
if (ieee80211_is_back_req(hdr->frame_control)) {
struct ieee80211_bar *bar;
@@ -819,6 +863,19 @@ mt7996_mac_write_txwi_80211(struct mt7996_dev *dev, __le32 *txwi,
txwi[3] |= cpu_to_le32(val);
txwi[3] &= ~cpu_to_le32(MT_TXD3_HW_AMSDU);
}
+
+ if (ieee80211_vif_is_mld(info->control.vif) &&
+ (multicast || unlikely(skb->protocol == cpu_to_be16(ETH_P_PAE))))
+ txwi[5] |= cpu_to_le32(MT_TXD5_FL);
+
+ if (ieee80211_is_nullfunc(fc) && ieee80211_has_a4(fc) &&
+ ieee80211_vif_is_mld(info->control.vif)) {
+ txwi[5] |= cpu_to_le32(MT_TXD5_FL);
+ txwi[6] |= cpu_to_le32(MT_TXD6_DIS_MAT);
+ }
+
+ if (!wcid->sta && ieee80211_is_mgmt(fc))
+ txwi[6] |= cpu_to_le32(MT_TXD6_DIS_MAT);
}
void mt7996_mac_write_txwi(struct mt7996_dev *dev, __le32 *txwi,
@@ -832,7 +889,9 @@ void mt7996_mac_write_txwi(struct mt7996_dev *dev, __le32 *txwi,
u8 band_idx = (info->hw_queue & MT_TX_HW_QUEUE_PHY) >> 2;
u8 p_fmt, q_idx, omac_idx = 0, wmm_idx = 0;
bool is_8023 = info->flags & IEEE80211_TX_CTL_HW_80211_ENCAP;
- struct mt76_vif_link *mvif;
+ struct mt76_vif_link *mlink = NULL;
+ struct mt7996_vif *mvif;
+ unsigned int link_id;
u16 tx_count = 15;
u32 val;
bool inband_disc = !!(changed & (BSS_CHANGED_UNSOL_BCAST_PROBE_RESP |
@@ -840,11 +899,20 @@ void mt7996_mac_write_txwi(struct mt7996_dev *dev, __le32 *txwi,
bool beacon = !!(changed & (BSS_CHANGED_BEACON |
BSS_CHANGED_BEACON_ENABLED)) && (!inband_disc);
- mvif = vif ? (struct mt76_vif_link *)vif->drv_priv : NULL;
- if (mvif) {
- omac_idx = mvif->omac_idx;
- wmm_idx = mvif->wmm_idx;
- band_idx = mvif->band_idx;
+ if (wcid != &dev->mt76.global_wcid)
+ link_id = wcid->link_id;
+ else
+ link_id = u32_get_bits(info->control.flags,
+ IEEE80211_TX_CTRL_MLO_LINK);
+
+ mvif = vif ? (struct mt7996_vif *)vif->drv_priv : NULL;
+ if (mvif)
+ mlink = rcu_dereference(mvif->mt76.link[link_id]);
+
+ if (mlink) {
+ omac_idx = mlink->omac_idx;
+ wmm_idx = mlink->wmm_idx;
+ band_idx = mlink->band_idx;
}
if (inband_disc) {
@@ -891,7 +959,10 @@ void mt7996_mac_write_txwi(struct mt7996_dev *dev, __le32 *txwi,
val |= MT_TXD5_TX_STATUS_HOST;
txwi[5] = cpu_to_le32(val);
- val = MT_TXD6_DIS_MAT | MT_TXD6_DAS;
+ val = MT_TXD6_DAS;
+ if (q_idx >= MT_LMAC_ALTX0 && q_idx <= MT_LMAC_BCN0)
+ val |= MT_TXD6_DIS_MAT;
+
if (is_mt7996(&dev->mt76))
val |= FIELD_PREP(MT_TXD6_MSDU_CNT, 1);
else if (is_8023 || !ieee80211_is_mgmt(hdr->frame_control))
@@ -903,34 +974,56 @@ void mt7996_mac_write_txwi(struct mt7996_dev *dev, __le32 *txwi,
if (is_8023)
mt7996_mac_write_txwi_8023(dev, txwi, skb, wcid);
else
- mt7996_mac_write_txwi_80211(dev, txwi, skb, key);
+ mt7996_mac_write_txwi_80211(dev, txwi, skb, key, wcid);
if (txwi[1] & cpu_to_le32(MT_TXD1_FIXED_RATE)) {
bool mcast = ieee80211_is_data(hdr->frame_control) &&
is_multicast_ether_addr(hdr->addr1);
u8 idx = MT7996_BASIC_RATES_TBL;
- if (mvif) {
- if (mcast && mvif->mcast_rates_idx)
- idx = mvif->mcast_rates_idx;
- else if (beacon && mvif->beacon_rates_idx)
- idx = mvif->beacon_rates_idx;
+ if (mlink) {
+ if (mcast && mlink->mcast_rates_idx)
+ idx = mlink->mcast_rates_idx;
+ else if (beacon && mlink->beacon_rates_idx)
+ idx = mlink->beacon_rates_idx;
else
- idx = mvif->basic_rates_idx;
+ idx = mlink->basic_rates_idx;
}
val = FIELD_PREP(MT_TXD6_TX_RATE, idx) | MT_TXD6_FIXED_BW;
+ if (mcast)
+ val |= MT_TXD6_DIS_MAT;
txwi[6] |= cpu_to_le32(val);
txwi[3] |= cpu_to_le32(MT_TXD3_BA_DISABLE);
}
}
+static bool
+mt7996_tx_use_mgmt(struct mt7996_dev *dev, struct sk_buff *skb)
+{
+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
+
+ if (ieee80211_is_mgmt(hdr->frame_control))
+ return true;
+
+ /* for SDO to bypass specific data frame */
+ if (!mt7996_has_wa(dev)) {
+ if (unlikely(skb->protocol == cpu_to_be16(ETH_P_PAE)))
+ return true;
+
+ if (ieee80211_has_a4(hdr->frame_control) &&
+ !ieee80211_is_data_present(hdr->frame_control))
+ return true;
+ }
+
+ return false;
+}
+
int mt7996_tx_prepare_skb(struct mt76_dev *mdev, void *txwi_ptr,
enum mt76_txq_id qid, struct mt76_wcid *wcid,
struct ieee80211_sta *sta,
struct mt76_tx_info *tx_info)
{
- struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)tx_info->skb->data;
struct mt7996_dev *dev = container_of(mdev, struct mt7996_dev, mt76);
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(tx_info->skb);
struct ieee80211_key_conf *key = info->control.hw_key;
@@ -955,8 +1048,11 @@ int mt7996_tx_prepare_skb(struct mt76_dev *mdev, void *txwi_ptr,
return id;
pid = mt76_tx_status_skb_add(mdev, wcid, tx_info->skb);
- mt7996_mac_write_txwi(dev, txwi_ptr, tx_info->skb, wcid, key,
- pid, qid, 0);
+ memset(txwi_ptr, 0, MT_TXD_SIZE);
+ /* Transmit non qos data by 802.11 header and need to fill txd by host*/
+ if (!is_8023 || pid >= MT_PACKET_ID_FIRST)
+ mt7996_mac_write_txwi(dev, txwi_ptr, tx_info->skb, wcid, key,
+ pid, qid, 0);
txp = (struct mt76_connac_txp_common *)(txwi + MT_TXD_SIZE);
for (i = 0; i < nbuf; i++) {
@@ -973,19 +1069,27 @@ int mt7996_tx_prepare_skb(struct mt76_dev *mdev, void *txwi_ptr,
}
txp->fw.nbuf = nbuf;
- txp->fw.flags =
- cpu_to_le16(MT_CT_INFO_FROM_HOST | MT_CT_INFO_APPLY_TXD);
+ txp->fw.flags = cpu_to_le16(MT_CT_INFO_FROM_HOST);
+
+ if (!is_8023 || pid >= MT_PACKET_ID_FIRST)
+ txp->fw.flags |= cpu_to_le16(MT_CT_INFO_APPLY_TXD);
if (!key)
txp->fw.flags |= cpu_to_le16(MT_CT_INFO_NONE_CIPHER_FRAME);
- if (!is_8023 && ieee80211_is_mgmt(hdr->frame_control))
+ if (!is_8023 && mt7996_tx_use_mgmt(dev, tx_info->skb))
txp->fw.flags |= cpu_to_le16(MT_CT_INFO_MGMT_FRAME);
if (vif) {
struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+ struct mt76_vif_link *mlink = NULL;
- txp->fw.bss_idx = mvif->deflink.mt76.idx;
+ if (wcid->offchannel)
+ mlink = rcu_dereference(mvif->mt76.offchannel_link);
+ if (!mlink)
+ mlink = &mvif->deflink.mt76;
+
+ txp->fw.bss_idx = mlink->idx;
}
txp->fw.token = cpu_to_le16(id);
@@ -1027,9 +1131,10 @@ u32 mt7996_wed_init_buf(void *ptr, dma_addr_t phys, int token_id)
static void
mt7996_tx_check_aggr(struct ieee80211_sta *sta, struct sk_buff *skb)
{
- struct mt7996_sta *msta;
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
bool is_8023 = info->flags & IEEE80211_TX_CTL_HW_80211_ENCAP;
+ struct mt7996_sta_link *msta_link;
+ struct mt7996_sta *msta;
u16 fc, tid;
if (!sta || !(sta->deflink.ht_cap.ht_supported || sta->deflink.he_cap.has_he))
@@ -1058,7 +1163,9 @@ mt7996_tx_check_aggr(struct ieee80211_sta *sta, struct sk_buff *skb)
return;
msta = (struct mt7996_sta *)sta->drv_priv;
- if (!test_and_set_bit(tid, &msta->wcid.ampdu_state))
+ msta_link = &msta->deflink;
+
+ if (!test_and_set_bit(tid, &msta_link->wcid.ampdu_state))
ieee80211_start_tx_ba_session(sta, tid, 0);
}
@@ -1108,6 +1215,7 @@ mt7996_mac_tx_free(struct mt7996_dev *dev, void *data, int len)
void *end = data + len;
bool wake = false;
u16 total, count = 0;
+ u8 ver;
/* clean DMA queues and unmap buffers first */
mt76_queue_tx_cleanup(dev, dev->mphy.q_tx[MT_TXQ_PSD], false);
@@ -1121,7 +1229,8 @@ mt7996_mac_tx_free(struct mt7996_dev *dev, void *data, int len)
mt76_queue_tx_cleanup(dev, phy3->q_tx[MT_TXQ_BE], false);
}
- if (WARN_ON_ONCE(le32_get_bits(tx_free[1], MT_TXFREE1_VER) < 5))
+ ver = le32_get_bits(tx_free[1], MT_TXFREE1_VER);
+ if (WARN_ON_ONCE(ver < 5))
return;
total = le32_get_bits(tx_free[0], MT_TXFREE0_MSDU_CNT);
@@ -1136,17 +1245,23 @@ mt7996_mac_tx_free(struct mt7996_dev *dev, void *data, int len)
*/
info = le32_to_cpu(*cur_info);
if (info & MT_TXFREE_INFO_PAIR) {
- struct mt7996_sta *msta;
+ struct mt7996_sta_link *msta_link;
u16 idx;
idx = FIELD_GET(MT_TXFREE_INFO_WLAN_ID, info);
wcid = rcu_dereference(dev->mt76.wcid[idx]);
sta = wcid_to_sta(wcid);
if (!sta)
- continue;
-
- msta = container_of(wcid, struct mt7996_sta, wcid);
- mt76_wcid_add_poll(&dev->mt76, &msta->wcid);
+ goto next;
+
+ msta_link = container_of(wcid, struct mt7996_sta_link,
+ wcid);
+ mt76_wcid_add_poll(&dev->mt76, &msta_link->wcid);
+next:
+ /* ver 7 has a new DW with pair = 1, skip it */
+ if (ver == 7 && ((void *)(cur_info + 1) < end) &&
+ (le32_to_cpu(*(cur_info + 1)) & MT_TXFREE_INFO_PAIR))
+ cur_info++;
continue;
} else if (info & MT_TXFREE_INFO_HEADER) {
u32 tx_retries = 0, tx_failed = 0;
@@ -1230,7 +1345,7 @@ mt7996_mac_add_txs_skb(struct mt7996_dev *dev, struct mt76_wcid *wcid,
struct ieee80211_sta *sta;
u8 tid;
- sta = container_of((void *)wcid, struct ieee80211_sta, drv_priv);
+ sta = wcid_to_sta(wcid);
tid = FIELD_GET(MT_TXS0_TID, txs);
ieee80211_refresh_tx_agg_session_timer(sta, tid);
}
@@ -1344,7 +1459,7 @@ out:
static void mt7996_mac_add_txs(struct mt7996_dev *dev, void *data)
{
- struct mt7996_sta *msta = NULL;
+ struct mt7996_sta_link *msta_link;
struct mt76_wcid *wcid;
__le32 *txs_data = data;
u16 wcidx;
@@ -1365,14 +1480,13 @@ static void mt7996_mac_add_txs(struct mt7996_dev *dev, void *data)
if (!wcid)
goto out;
- msta = container_of(wcid, struct mt7996_sta, wcid);
-
mt7996_mac_add_txs_skb(dev, wcid, pid, txs_data);
if (!wcid->sta)
goto out;
- mt76_wcid_add_poll(&dev->mt76, &msta->wcid);
+ msta_link = container_of(wcid, struct mt7996_sta_link, wcid);
+ mt76_wcid_add_poll(&dev->mt76, &msta_link->wcid);
out:
rcu_read_unlock();
@@ -1399,7 +1513,7 @@ bool mt7996_rx_check(struct mt76_dev *mdev, void *data, int len)
mt7996_mac_tx_free(dev, data, len);
return false;
case PKT_TYPE_TXS:
- for (rxd += 4; rxd + 8 <= end; rxd += 8)
+ for (rxd += MT_TXS_HDR_SIZE; rxd + MT_TXS_SIZE <= end; rxd += MT_TXS_SIZE)
mt7996_mac_add_txs(dev, rxd);
return false;
case PKT_TYPE_RX_FW_MONITOR:
@@ -1442,7 +1556,7 @@ void mt7996_queue_rx_skb(struct mt76_dev *mdev, enum mt76_rxq_id q,
mt7996_mcu_rx_event(dev, skb);
break;
case PKT_TYPE_TXS:
- for (rxd += 4; rxd + 8 <= end; rxd += 8)
+ for (rxd += MT_TXS_HDR_SIZE; rxd + MT_TXS_SIZE <= end; rxd += MT_TXS_SIZE)
mt7996_mac_add_txs(dev, rxd);
dev_kfree_skb(skb);
break;
@@ -2239,38 +2353,70 @@ void mt7996_mac_update_stats(struct mt7996_phy *phy)
void mt7996_mac_sta_rc_work(struct work_struct *work)
{
struct mt7996_dev *dev = container_of(work, struct mt7996_dev, rc_work);
+ struct ieee80211_bss_conf *link_conf;
+ struct ieee80211_link_sta *link_sta;
+ struct mt7996_sta_link *msta_link;
+ struct mt7996_vif_link *link;
+ struct mt76_vif_link *mlink;
struct ieee80211_sta *sta;
struct ieee80211_vif *vif;
struct mt7996_sta *msta;
- u32 changed;
+ struct mt7996_vif *mvif;
LIST_HEAD(list);
+ u32 changed;
+ u8 link_id;
+ rcu_read_lock();
spin_lock_bh(&dev->mt76.sta_poll_lock);
list_splice_init(&dev->sta_rc_list, &list);
while (!list_empty(&list)) {
- msta = list_first_entry(&list, struct mt7996_sta, rc_list);
- list_del_init(&msta->rc_list);
- changed = msta->changed;
- msta->changed = 0;
+ msta_link = list_first_entry(&list, struct mt7996_sta_link,
+ rc_list);
+ list_del_init(&msta_link->rc_list);
+
+ changed = msta_link->changed;
+ msta_link->changed = 0;
+
+ sta = wcid_to_sta(&msta_link->wcid);
+ link_id = msta_link->wcid.link_id;
+ msta = msta_link->sta;
+ mvif = msta->vif;
+ vif = container_of((void *)mvif, struct ieee80211_vif, drv_priv);
+
+ mlink = rcu_dereference(mvif->mt76.link[link_id]);
+ if (!mlink)
+ continue;
+
+ link_sta = rcu_dereference(sta->link[link_id]);
+ if (!link_sta)
+ continue;
+
+ link_conf = rcu_dereference(vif->link_conf[link_id]);
+ if (!link_conf)
+ continue;
+
spin_unlock_bh(&dev->mt76.sta_poll_lock);
- sta = container_of((void *)msta, struct ieee80211_sta, drv_priv);
- vif = container_of((void *)msta->vif, struct ieee80211_vif, drv_priv);
+ link = (struct mt7996_vif_link *)mlink;
if (changed & (IEEE80211_RC_SUPP_RATES_CHANGED |
IEEE80211_RC_NSS_CHANGED |
IEEE80211_RC_BW_CHANGED))
- mt7996_mcu_add_rate_ctrl(dev, vif, sta, true);
+ mt7996_mcu_add_rate_ctrl(dev, vif, link_conf,
+ link_sta, link, msta_link,
+ true);
if (changed & IEEE80211_RC_SMPS_CHANGED)
- mt7996_mcu_set_fixed_field(dev, vif, sta, NULL,
+ mt7996_mcu_set_fixed_field(dev, link_sta, link,
+ msta_link, NULL,
RATE_PARAM_MMPS_UPDATE);
spin_lock_bh(&dev->mt76.sta_poll_lock);
}
spin_unlock_bh(&dev->mt76.sta_poll_lock);
+ rcu_read_unlock();
}
void mt7996_mac_work(struct work_struct *work)
@@ -2308,16 +2454,15 @@ void mt7996_mac_work(struct work_struct *work)
static void mt7996_dfs_stop_radar_detector(struct mt7996_phy *phy)
{
struct mt7996_dev *dev = phy->dev;
+ int rdd_idx = mt7996_get_rdd_idx(phy, false);
- if (phy->rdd_state & BIT(0))
- mt7996_mcu_rdd_cmd(dev, RDD_STOP, 0,
- MT_RX_SEL0, 0);
- if (phy->rdd_state & BIT(1))
- mt7996_mcu_rdd_cmd(dev, RDD_STOP, 1,
- MT_RX_SEL0, 0);
+ if (rdd_idx < 0)
+ return;
+
+ mt7996_mcu_rdd_cmd(dev, RDD_STOP, rdd_idx, 0);
}
-static int mt7996_dfs_start_rdd(struct mt7996_dev *dev, int chain)
+static int mt7996_dfs_start_rdd(struct mt7996_dev *dev, int rdd_idx)
{
int err, region;
@@ -2334,44 +2479,30 @@ static int mt7996_dfs_start_rdd(struct mt7996_dev *dev, int chain)
break;
}
- err = mt7996_mcu_rdd_cmd(dev, RDD_START, chain,
- MT_RX_SEL0, region);
+ err = mt7996_mcu_rdd_cmd(dev, RDD_START, rdd_idx, region);
if (err < 0)
return err;
- return mt7996_mcu_rdd_cmd(dev, RDD_DET_MODE, chain,
- MT_RX_SEL0, 1);
+ return mt7996_mcu_rdd_cmd(dev, RDD_DET_MODE, rdd_idx, 1);
}
static int mt7996_dfs_start_radar_detector(struct mt7996_phy *phy)
{
- struct cfg80211_chan_def *chandef = &phy->mt76->chandef;
struct mt7996_dev *dev = phy->dev;
- u8 band_idx = phy->mt76->band_idx;
- int err;
+ int err, rdd_idx;
- /* start CAC */
- err = mt7996_mcu_rdd_cmd(dev, RDD_CAC_START, band_idx,
- MT_RX_SEL0, 0);
- if (err < 0)
- return err;
+ rdd_idx = mt7996_get_rdd_idx(phy, false);
+ if (rdd_idx < 0)
+ return -EINVAL;
- err = mt7996_dfs_start_rdd(dev, band_idx);
+ /* start CAC */
+ err = mt7996_mcu_rdd_cmd(dev, RDD_CAC_START, rdd_idx, 0);
if (err < 0)
return err;
- phy->rdd_state |= BIT(band_idx);
+ err = mt7996_dfs_start_rdd(dev, rdd_idx);
- if (chandef->width == NL80211_CHAN_WIDTH_160 ||
- chandef->width == NL80211_CHAN_WIDTH_80P80) {
- err = mt7996_dfs_start_rdd(dev, 1);
- if (err < 0)
- return err;
-
- phy->rdd_state |= BIT(1);
- }
-
- return 0;
+ return err;
}
static int
@@ -2412,12 +2543,12 @@ int mt7996_dfs_init_radar_detector(struct mt7996_phy *phy)
{
struct mt7996_dev *dev = phy->dev;
enum mt76_dfs_state dfs_state, prev_state;
- int err;
+ int err, rdd_idx = mt7996_get_rdd_idx(phy, false);
prev_state = phy->mt76->dfs_state;
dfs_state = mt76_phy_dfs_state(phy->mt76);
- if (prev_state == dfs_state)
+ if (prev_state == dfs_state || rdd_idx < 0)
return 0;
if (prev_state == MT_DFS_STATE_UNKNOWN)
@@ -2441,8 +2572,7 @@ int mt7996_dfs_init_radar_detector(struct mt7996_phy *phy)
if (dfs_state == MT_DFS_STATE_CAC)
return 0;
- err = mt7996_mcu_rdd_cmd(dev, RDD_CAC_END,
- phy->mt76->band_idx, MT_RX_SEL0, 0);
+ err = mt7996_mcu_rdd_cmd(dev, RDD_CAC_END, rdd_idx, 0);
if (err < 0) {
phy->mt76->dfs_state = MT_DFS_STATE_UNKNOWN;
return err;
@@ -2452,8 +2582,7 @@ int mt7996_dfs_init_radar_detector(struct mt7996_phy *phy)
return 0;
stop:
- err = mt7996_mcu_rdd_cmd(dev, RDD_NORMAL_START,
- phy->mt76->band_idx, MT_RX_SEL0, 0);
+ err = mt7996_mcu_rdd_cmd(dev, RDD_NORMAL_START, rdd_idx, 0);
if (err < 0)
return err;
@@ -2538,7 +2667,7 @@ static int mt7996_mac_check_twt_req(struct ieee80211_twt_setup *twt)
}
static bool
-mt7996_mac_twt_param_equal(struct mt7996_sta *msta,
+mt7996_mac_twt_param_equal(struct mt7996_sta_link *msta_link,
struct ieee80211_twt_params *twt_agrt)
{
u16 type = le16_to_cpu(twt_agrt->req_type);
@@ -2549,10 +2678,10 @@ mt7996_mac_twt_param_equal(struct mt7996_sta *msta,
for (i = 0; i < MT7996_MAX_STA_TWT_AGRT; i++) {
struct mt7996_twt_flow *f;
- if (!(msta->twt.flowid_mask & BIT(i)))
+ if (!(msta_link->twt.flowid_mask & BIT(i)))
continue;
- f = &msta->twt.flow[i];
+ f = &msta_link->twt.flow[i];
if (f->duration == twt_agrt->min_twt_dur &&
f->mantissa == twt_agrt->mantissa &&
f->exp == exp &&
@@ -2572,6 +2701,7 @@ void mt7996_mac_add_twt_setup(struct ieee80211_hw *hw,
enum ieee80211_twt_setup_cmd setup_cmd = TWT_SETUP_CMD_REJECT;
struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
struct ieee80211_twt_params *twt_agrt = (void *)twt->params;
+ struct mt7996_sta_link *msta_link = &msta->deflink;
u16 req_type = le16_to_cpu(twt_agrt->req_type);
enum ieee80211_twt_setup_cmd sta_setup_cmd;
struct mt7996_dev *dev = mt7996_hw_dev(hw);
@@ -2586,7 +2716,8 @@ void mt7996_mac_add_twt_setup(struct ieee80211_hw *hw,
if (dev->twt.n_agrt == MT7996_MAX_TWT_AGRT)
goto unlock;
- if (hweight8(msta->twt.flowid_mask) == ARRAY_SIZE(msta->twt.flow))
+ if (hweight8(msta_link->twt.flowid_mask) ==
+ ARRAY_SIZE(msta_link->twt.flow))
goto unlock;
if (twt_agrt->min_twt_dur < MT7996_MIN_TWT_DUR) {
@@ -2595,10 +2726,10 @@ void mt7996_mac_add_twt_setup(struct ieee80211_hw *hw,
goto unlock;
}
- if (mt7996_mac_twt_param_equal(msta, twt_agrt))
+ if (mt7996_mac_twt_param_equal(msta_link, twt_agrt))
goto unlock;
- flowid = ffs(~msta->twt.flowid_mask) - 1;
+ flowid = ffs(~msta_link->twt.flowid_mask) - 1;
twt_agrt->req_type &= ~cpu_to_le16(IEEE80211_TWT_REQTYPE_FLOWID);
twt_agrt->req_type |= le16_encode_bits(flowid,
IEEE80211_TWT_REQTYPE_FLOWID);
@@ -2607,10 +2738,10 @@ void mt7996_mac_add_twt_setup(struct ieee80211_hw *hw,
exp = FIELD_GET(IEEE80211_TWT_REQTYPE_WAKE_INT_EXP, req_type);
sta_setup_cmd = FIELD_GET(IEEE80211_TWT_REQTYPE_SETUP_CMD, req_type);
- flow = &msta->twt.flow[flowid];
+ flow = &msta_link->twt.flow[flowid];
memset(flow, 0, sizeof(*flow));
INIT_LIST_HEAD(&flow->list);
- flow->wcid = msta->wcid.idx;
+ flow->wcid = msta_link->wcid.idx;
flow->table_id = table_id;
flow->id = flowid;
flow->duration = twt_agrt->min_twt_dur;
@@ -2628,7 +2759,7 @@ void mt7996_mac_add_twt_setup(struct ieee80211_hw *hw,
flow->sched = true;
flow->start_tsf = mt7996_mac_twt_sched_list_add(dev, flow);
- curr_tsf = __mt7996_get_tsf(hw, msta->vif);
+ curr_tsf = __mt7996_get_tsf(hw, &msta->vif->deflink);
div_u64_rem(curr_tsf - flow->start_tsf, interval, &rem);
flow_tsf = curr_tsf + interval - rem;
twt_agrt->twt = cpu_to_le64(flow_tsf);
@@ -2637,12 +2768,13 @@ void mt7996_mac_add_twt_setup(struct ieee80211_hw *hw,
}
flow->tsf = le64_to_cpu(twt_agrt->twt);
- if (mt7996_mcu_twt_agrt_update(dev, msta->vif, flow, MCU_TWT_AGRT_ADD))
+ if (mt7996_mcu_twt_agrt_update(dev, &msta->vif->deflink, flow,
+ MCU_TWT_AGRT_ADD))
goto unlock;
setup_cmd = TWT_SETUP_CMD_ACCEPT;
dev->twt.table_mask |= BIT(table_id);
- msta->twt.flowid_mask |= BIT(flowid);
+ msta_link->twt.flowid_mask |= BIT(flowid);
dev->twt.n_agrt++;
unlock:
@@ -2655,26 +2787,26 @@ out:
}
void mt7996_mac_twt_teardown_flow(struct mt7996_dev *dev,
- struct mt7996_sta *msta,
+ struct mt7996_vif_link *link,
+ struct mt7996_sta_link *msta_link,
u8 flowid)
{
struct mt7996_twt_flow *flow;
lockdep_assert_held(&dev->mt76.mutex);
- if (flowid >= ARRAY_SIZE(msta->twt.flow))
+ if (flowid >= ARRAY_SIZE(msta_link->twt.flow))
return;
- if (!(msta->twt.flowid_mask & BIT(flowid)))
+ if (!(msta_link->twt.flowid_mask & BIT(flowid)))
return;
- flow = &msta->twt.flow[flowid];
- if (mt7996_mcu_twt_agrt_update(dev, msta->vif, flow,
- MCU_TWT_AGRT_DELETE))
+ flow = &msta_link->twt.flow[flowid];
+ if (mt7996_mcu_twt_agrt_update(dev, link, flow, MCU_TWT_AGRT_DELETE))
return;
list_del_init(&flow->list);
- msta->twt.flowid_mask &= ~BIT(flowid);
+ msta_link->twt.flowid_mask &= ~BIT(flowid);
dev->twt.table_mask &= ~BIT(flow->table_id);
dev->twt.n_agrt--;
}
diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/main.c b/drivers/net/wireless/mediatek/mt76/mt7996/main.c
index 69dd565d8319..78ae9f5cb176 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7996/main.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7996/main.c
@@ -68,11 +68,13 @@ static int mt7996_start(struct ieee80211_hw *hw)
static void mt7996_stop_phy(struct mt7996_phy *phy)
{
- struct mt7996_dev *dev = phy->dev;
+ struct mt7996_dev *dev;
if (!phy || !test_bit(MT76_STATE_RUNNING, &phy->mt76->state))
return;
+ dev = phy->dev;
+
cancel_delayed_work_sync(&phy->mt76->mac_work);
mutex_lock(&dev->mt76.mutex);
@@ -158,58 +160,101 @@ mt7996_init_bitrate_mask(struct ieee80211_vif *vif, struct mt7996_vif_link *mlin
static int
mt7996_set_hw_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
struct ieee80211_vif *vif, struct ieee80211_sta *sta,
- struct mt7996_vif_link *mlink, struct ieee80211_key_conf *key)
+ struct ieee80211_key_conf *key)
{
struct mt7996_dev *dev = mt7996_hw_dev(hw);
- struct mt7996_sta *msta = sta ? (struct mt7996_sta *)sta->drv_priv :
- &mlink->sta;
- struct mt76_wcid *wcid = &msta->wcid;
- u8 *wcid_keyidx = &wcid->hw_key_idx;
- struct mt7996_phy *phy;
int idx = key->keyidx;
+ unsigned int link_id;
+ unsigned long links;
+
+ if (key->link_id >= 0)
+ links = BIT(key->link_id);
+ else if (sta && sta->valid_links)
+ links = sta->valid_links;
+ else if (vif->valid_links)
+ links = vif->valid_links;
+ else
+ links = BIT(0);
- phy = mt7996_vif_link_phy(mlink);
- if (!phy)
- return -EINVAL;
+ for_each_set_bit(link_id, &links, IEEE80211_MLD_MAX_NUM_LINKS) {
+ struct mt7996_sta_link *msta_link;
+ struct mt7996_vif_link *link;
+ u8 *wcid_keyidx;
+ int err;
- if (sta && !wcid->sta)
- return -EOPNOTSUPP;
+ link = mt7996_vif_link(dev, vif, link_id);
+ if (!link)
+ continue;
- switch (key->cipher) {
- case WLAN_CIPHER_SUITE_AES_CMAC:
- case WLAN_CIPHER_SUITE_BIP_CMAC_256:
- case WLAN_CIPHER_SUITE_BIP_GMAC_128:
- case WLAN_CIPHER_SUITE_BIP_GMAC_256:
- if (key->keyidx == 6 || key->keyidx == 7) {
- wcid_keyidx = &wcid->hw_key_idx2;
- key->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIE;
+ if (sta) {
+ struct mt7996_sta *msta;
+
+ msta = (struct mt7996_sta *)sta->drv_priv;
+ msta_link = mt76_dereference(msta->link[link_id],
+ &dev->mt76);
+ if (!msta_link)
+ continue;
+
+ if (!msta_link->wcid.sta)
+ return -EOPNOTSUPP;
+ } else {
+ msta_link = &link->msta_link;
+ }
+ wcid_keyidx = &msta_link->wcid.hw_key_idx;
+
+ switch (key->cipher) {
+ case WLAN_CIPHER_SUITE_AES_CMAC:
+ case WLAN_CIPHER_SUITE_BIP_CMAC_256:
+ case WLAN_CIPHER_SUITE_BIP_GMAC_128:
+ case WLAN_CIPHER_SUITE_BIP_GMAC_256:
+ if (key->keyidx == 6 || key->keyidx == 7) {
+ wcid_keyidx = &msta_link->wcid.hw_key_idx2;
+ key->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIE;
+ }
+ break;
+ default:
+ break;
}
- break;
- default:
- break;
- }
- if (cmd == SET_KEY && !sta && !mlink->mt76.cipher) {
- mlink->mt76.cipher = mt76_connac_mcu_get_cipher(key->cipher);
- mt7996_mcu_add_bss_info(phy, vif, &vif->bss_conf, &mlink->mt76, true);
- }
+ if (cmd == SET_KEY && !sta && !link->mt76.cipher) {
+ struct ieee80211_bss_conf *link_conf;
- if (cmd == SET_KEY) {
- *wcid_keyidx = idx;
- } else {
- if (idx == *wcid_keyidx)
- *wcid_keyidx = -1;
- return 0;
- }
+ link_conf = link_conf_dereference_protected(vif,
+ link_id);
+ if (!link_conf)
+ link_conf = &vif->bss_conf;
- mt76_wcid_key_setup(&dev->mt76, wcid, key);
+ link->mt76.cipher =
+ mt76_connac_mcu_get_cipher(key->cipher);
+ mt7996_mcu_add_bss_info(link->phy, vif, link_conf,
+ &link->mt76, msta_link, true);
+ }
- if (key->keyidx == 6 || key->keyidx == 7)
- return mt7996_mcu_bcn_prot_enable(dev, vif, key);
+ if (cmd == SET_KEY) {
+ *wcid_keyidx = idx;
+ } else {
+ if (idx == *wcid_keyidx)
+ *wcid_keyidx = -1;
+ continue;
+ }
- return mt7996_mcu_add_key(&dev->mt76, vif, key,
- MCU_WMWA_UNI_CMD(STA_REC_UPDATE),
- &msta->wcid, cmd);
+ mt76_wcid_key_setup(&dev->mt76, &msta_link->wcid, key);
+
+ if (key->keyidx == 6 || key->keyidx == 7) {
+ err = mt7996_mcu_bcn_prot_enable(dev, link,
+ msta_link, key);
+ if (err)
+ return err;
+ }
+
+ err = mt7996_mcu_add_key(&dev->mt76, vif, key,
+ MCU_WMWA_UNI_CMD(STA_REC_UPDATE),
+ &msta_link->wcid, cmd);
+ if (err)
+ return err;
+ }
+
+ return 0;
}
static void
@@ -217,12 +262,10 @@ mt7996_key_iter(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
struct ieee80211_sta *sta, struct ieee80211_key_conf *key,
void *data)
{
- struct mt7996_vif_link *mlink = data;
-
if (sta)
return;
- WARN_ON(mt7996_set_hw_key(hw, SET_KEY, vif, NULL, mlink, key));
+ WARN_ON(mt7996_set_hw_key(hw, SET_KEY, vif, NULL, key));
}
int mt7996_vif_link_add(struct mt76_phy *mphy, struct ieee80211_vif *vif,
@@ -230,6 +273,8 @@ int mt7996_vif_link_add(struct mt76_phy *mphy, struct ieee80211_vif *vif,
struct mt76_vif_link *mlink)
{
struct mt7996_vif_link *link = container_of(mlink, struct mt7996_vif_link, mt76);
+ struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+ struct mt7996_sta_link *msta_link = &link->msta_link;
struct mt7996_phy *phy = mphy->priv;
struct mt7996_dev *dev = phy->dev;
u8 band_idx = phy->mt76->band_idx;
@@ -248,7 +293,8 @@ int mt7996_vif_link_add(struct mt76_phy *mphy, struct ieee80211_vif *vif,
mlink->omac_idx = idx;
mlink->band_idx = band_idx;
mlink->wmm_idx = vif->type == NL80211_IFTYPE_AP ? 0 : 3;
- mlink->wcid = &link->sta.wcid;
+ mlink->wcid = &msta_link->wcid;
+ mlink->wcid->offchannel = mlink->offchannel;
ret = mt7996_mcu_add_dev_info(phy, vif, link_conf, mlink, true);
if (ret)
@@ -259,10 +305,11 @@ int mt7996_vif_link_add(struct mt76_phy *mphy, struct ieee80211_vif *vif,
idx = MT7996_WTBL_RESERVED - mlink->idx;
- INIT_LIST_HEAD(&link->sta.rc_list);
- link->sta.wcid.idx = idx;
- link->sta.wcid.tx_info |= MT_WCID_TX_INFO_SET;
- mt76_wcid_init(&link->sta.wcid, band_idx);
+ INIT_LIST_HEAD(&msta_link->rc_list);
+ msta_link->wcid.idx = idx;
+ msta_link->wcid.link_id = link_conf->link_id;
+ msta_link->wcid.tx_info |= MT_WCID_TX_INFO_SET;
+ mt76_wcid_init(&msta_link->wcid, band_idx);
mt7996_mac_wtbl_update(dev, idx,
MT_WTBL_UPDATE_ADM_COUNT_CLEAR);
@@ -283,15 +330,19 @@ int mt7996_vif_link_add(struct mt76_phy *mphy, struct ieee80211_vif *vif,
mt7996_init_bitrate_mask(vif, link);
- mt7996_mcu_add_bss_info(phy, vif, link_conf, mlink, true);
+ mt7996_mcu_add_bss_info(phy, vif, link_conf, mlink, msta_link, true);
/* defer the first STA_REC of BMC entry to BSS_CHANGED_BSSID for STA
* interface, since firmware only records BSSID when the entry is new
*/
if (vif->type != NL80211_IFTYPE_STATION)
- mt7996_mcu_add_sta(dev, vif, mlink, NULL, CONN_STATE_PORT_SECURE, true);
- rcu_assign_pointer(dev->mt76.wcid[idx], &link->sta.wcid);
+ mt7996_mcu_add_sta(dev, link_conf, NULL, link, NULL,
+ CONN_STATE_PORT_SECURE, true);
+ rcu_assign_pointer(dev->mt76.wcid[idx], &msta_link->wcid);
+
+ ieee80211_iter_keys(mphy->hw, vif, mt7996_key_iter, NULL);
- ieee80211_iter_keys(mphy->hw, vif, mt7996_key_iter, link);
+ if (mvif->mt76.deflink_id == IEEE80211_LINK_UNSPECIFIED)
+ mvif->mt76.deflink_id = link_conf->link_id;
return 0;
}
@@ -301,29 +352,42 @@ void mt7996_vif_link_remove(struct mt76_phy *mphy, struct ieee80211_vif *vif,
struct mt76_vif_link *mlink)
{
struct mt7996_vif_link *link = container_of(mlink, struct mt7996_vif_link, mt76);
+ struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+ struct mt7996_sta_link *msta_link = &link->msta_link;
struct mt7996_phy *phy = mphy->priv;
struct mt7996_dev *dev = phy->dev;
- struct mt7996_sta *msta;
- int idx;
+ int idx = msta_link->wcid.idx;
- msta = &link->sta;
- idx = msta->wcid.idx;
- mt7996_mcu_add_sta(dev, vif, mlink, NULL, CONN_STATE_DISCONNECT, false);
- mt7996_mcu_add_bss_info(phy, vif, link_conf, mlink, false);
+ mt7996_mcu_add_sta(dev, link_conf, NULL, link, NULL,
+ CONN_STATE_DISCONNECT, false);
+ mt7996_mcu_add_bss_info(phy, vif, link_conf, mlink, msta_link, false);
mt7996_mcu_add_dev_info(phy, vif, link_conf, mlink, false);
rcu_assign_pointer(dev->mt76.wcid[idx], NULL);
+ if (mvif->mt76.deflink_id == link_conf->link_id) {
+ struct ieee80211_bss_conf *iter;
+ unsigned int link_id;
+
+ mvif->mt76.deflink_id = IEEE80211_LINK_UNSPECIFIED;
+ for_each_vif_active_link(vif, iter, link_id) {
+ if (link_id != IEEE80211_LINK_UNSPECIFIED) {
+ mvif->mt76.deflink_id = link_id;
+ break;
+ }
+ }
+ }
+
dev->mt76.vif_mask &= ~BIT_ULL(mlink->idx);
phy->omac_mask &= ~BIT_ULL(mlink->omac_idx);
spin_lock_bh(&dev->mt76.sta_poll_lock);
- if (!list_empty(&msta->wcid.poll_list))
- list_del_init(&msta->wcid.poll_list);
+ if (!list_empty(&msta_link->wcid.poll_list))
+ list_del_init(&msta_link->wcid.poll_list);
spin_unlock_bh(&dev->mt76.sta_poll_lock);
- mt76_wcid_cleanup(&dev->mt76, &msta->wcid);
+ mt76_wcid_cleanup(&dev->mt76, &msta_link->wcid);
}
static void mt7996_phy_set_rxfilter(struct mt7996_phy *phy)
@@ -352,11 +416,13 @@ static void mt7996_phy_set_rxfilter(struct mt7996_phy *phy)
static void mt7996_set_monitor(struct mt7996_phy *phy, bool enabled)
{
- struct mt7996_dev *dev = phy->dev;
+ struct mt7996_dev *dev;
if (!phy)
return;
+ dev = phy->dev;
+
if (enabled == !(phy->rxfilter & MT_WF_RFCR_DROP_OTHER_UC))
return;
@@ -399,6 +465,7 @@ static int mt7996_add_interface(struct ieee80211_hw *hw,
mt76_vif_init(vif, &mvif->mt76);
vif->offload_flags |= IEEE80211_OFFLOAD_ENCAP_4ADDR;
+ mvif->mt76.deflink_id = IEEE80211_LINK_UNSPECIFIED;
out:
mutex_unlock(&dev->mt76.mutex);
@@ -480,7 +547,6 @@ static int mt7996_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
{
struct mt7996_dev *dev = mt7996_hw_dev(hw);
struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
- struct mt7996_vif_link *mlink = &mvif->deflink;
int err;
/* The hardware does not support per-STA RX GTK, fallback
@@ -515,11 +581,11 @@ static int mt7996_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
return -EOPNOTSUPP;
}
- if (!mt7996_vif_link_phy(mlink))
- return 0; /* defer until after link add */
+ if (!mt7996_vif_link_phy(&mvif->deflink))
+ return 0; /* defer until after link add */
mutex_lock(&dev->mt76.mutex);
- err = mt7996_set_hw_key(hw, cmd, vif, sta, mlink, key);
+ err = mt7996_set_hw_key(hw, cmd, vif, sta, key);
mutex_unlock(&dev->mt76.mutex);
return err;
@@ -601,6 +667,33 @@ static void mt7996_configure_filter(struct ieee80211_hw *hw,
mutex_unlock(&dev->mt76.mutex);
}
+static int
+mt7996_get_txpower(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ unsigned int link_id, int *dbm)
+{
+ struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+ struct mt7996_phy *phy = mt7996_vif_link_phy(&mvif->deflink);
+ struct mt7996_dev *dev = mt7996_hw_dev(hw);
+ struct wireless_dev *wdev;
+ int n_chains, delta, i;
+
+ if (!phy) {
+ wdev = ieee80211_vif_to_wdev(vif);
+ for (i = 0; i < hw->wiphy->n_radio; i++)
+ if (wdev->radio_mask & BIT(i))
+ phy = dev->radio_phy[i];
+
+ if (!phy)
+ return -EINVAL;
+ }
+
+ n_chains = hweight16(phy->mt76->chainmask);
+ delta = mt76_tx_power_path_delta(n_chains);
+ *dbm = DIV_ROUND_UP(phy->mt76->txpower_cur + delta, 2);
+
+ return 0;
+}
+
static u8
mt7996_get_rates_table(struct mt7996_phy *phy, struct ieee80211_bss_conf *conf,
bool beacon, bool mcast)
@@ -630,12 +723,11 @@ mt7996_get_rates_table(struct mt7996_phy *phy, struct ieee80211_bss_conf *conf,
}
static void
-mt7996_update_mu_group(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+mt7996_update_mu_group(struct ieee80211_hw *hw, struct mt7996_vif_link *link,
struct ieee80211_bss_conf *info)
{
- struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
struct mt7996_dev *dev = mt7996_hw_dev(hw);
- u8 band = mvif->deflink.mt76.band_idx;
+ u8 band = link->mt76.band_idx;
u32 *mu;
mu = (u32 *)info->mu_group.membership;
@@ -649,23 +741,56 @@ mt7996_update_mu_group(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
mt76_wr(dev, MT_WF_PHYRX_BAND_GID_TAB_POS3(band), mu[3]);
}
-static void mt7996_bss_info_changed(struct ieee80211_hw *hw,
- struct ieee80211_vif *vif,
- struct ieee80211_bss_conf *info,
- u64 changed)
+static void
+mt7996_vif_cfg_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ u64 changed)
+{
+ struct mt7996_dev *dev = mt7996_hw_dev(hw);
+
+ mutex_lock(&dev->mt76.mutex);
+
+ if ((changed & BSS_CHANGED_ASSOC) && vif->cfg.assoc) {
+ struct ieee80211_bss_conf *link_conf;
+ unsigned long link_id;
+
+ for_each_vif_active_link(vif, link_conf, link_id) {
+ struct mt7996_vif_link *link;
+
+ link = mt7996_vif_link(dev, vif, link_id);
+ if (!link)
+ continue;
+
+ if (!link->phy)
+ continue;
+
+ mt7996_mcu_add_bss_info(link->phy, vif, link_conf,
+ &link->mt76, &link->msta_link,
+ true);
+ mt7996_mcu_add_sta(dev, link_conf, NULL, link, NULL,
+ CONN_STATE_PORT_SECURE,
+ !!(changed & BSS_CHANGED_BSSID));
+ }
+ }
+
+ mutex_unlock(&dev->mt76.mutex);
+}
+
+static void
+mt7996_link_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ struct ieee80211_bss_conf *info, u64 changed)
{
struct mt7996_dev *dev = mt7996_hw_dev(hw);
- struct mt76_vif_link *mvif;
+ struct mt7996_vif_link *link;
struct mt7996_phy *phy;
struct mt76_phy *mphy;
mutex_lock(&dev->mt76.mutex);
- mvif = mt76_vif_conf_link(&dev->mt76, vif, info);
- if (!mvif)
+ link = mt7996_vif_conf_link(dev, vif, info);
+ if (!link)
goto out;
- mphy = mt76_vif_link_phy(mvif);
+ mphy = mt76_vif_link_phy(&link->mt76);
if (!mphy)
goto out;
@@ -675,16 +800,14 @@ static void mt7996_bss_info_changed(struct ieee80211_hw *hw,
* and then peer references bss_info_rfch to set bandwidth cap.
*/
if ((changed & BSS_CHANGED_BSSID && !is_zero_ether_addr(info->bssid)) ||
- (changed & BSS_CHANGED_ASSOC && vif->cfg.assoc) ||
(changed & BSS_CHANGED_BEACON_ENABLED && info->enable_beacon)) {
- mt7996_mcu_add_bss_info(phy, vif, info, mvif, true);
- mt7996_mcu_add_sta(dev, vif, mvif, NULL, CONN_STATE_PORT_SECURE,
+ mt7996_mcu_add_bss_info(phy, vif, info, &link->mt76,
+ &link->msta_link, true);
+ mt7996_mcu_add_sta(dev, info, NULL, link, NULL,
+ CONN_STATE_PORT_SECURE,
!!(changed & BSS_CHANGED_BSSID));
}
- if (changed & BSS_CHANGED_ERP_CTS_PROT)
- mt7996_mac_enable_rtscts(dev, vif, info->use_cts_prot);
-
if (changed & BSS_CHANGED_ERP_SLOT) {
int slottime = info->use_short_slot ? 9 : 20;
@@ -695,11 +818,11 @@ static void mt7996_bss_info_changed(struct ieee80211_hw *hw,
}
if (changed & BSS_CHANGED_MCAST_RATE)
- mvif->mcast_rates_idx =
+ link->mt76.mcast_rates_idx =
mt7996_get_rates_table(phy, info, false, true);
if (changed & BSS_CHANGED_BASIC_RATES)
- mvif->basic_rates_idx =
+ link->mt76.basic_rates_idx =
mt7996_get_rates_table(phy, info, false, false);
/* ensure that enable txcmd_mode after bss_info */
@@ -707,19 +830,19 @@ static void mt7996_bss_info_changed(struct ieee80211_hw *hw,
mt7996_mcu_set_tx(dev, vif, info);
if (changed & BSS_CHANGED_HE_OBSS_PD)
- mt7996_mcu_add_obss_spr(phy, vif, &info->he_obss_pd);
+ mt7996_mcu_add_obss_spr(phy, link, &info->he_obss_pd);
if (changed & BSS_CHANGED_HE_BSS_COLOR) {
if ((vif->type == NL80211_IFTYPE_AP &&
- mvif->omac_idx <= HW_BSSID_MAX) ||
+ link->mt76.omac_idx <= HW_BSSID_MAX) ||
vif->type == NL80211_IFTYPE_STATION)
- mt7996_mcu_update_bss_color(dev, mvif,
+ mt7996_mcu_update_bss_color(dev, &link->mt76,
&info->he_bss_color);
}
if (changed & (BSS_CHANGED_BEACON |
BSS_CHANGED_BEACON_ENABLED)) {
- mvif->beacon_rates_idx =
+ link->mt76.beacon_rates_idx =
mt7996_get_rates_table(phy, info, true, false);
mt7996_mcu_add_beacon(hw, vif, info);
@@ -727,10 +850,10 @@ static void mt7996_bss_info_changed(struct ieee80211_hw *hw,
if (changed & (BSS_CHANGED_UNSOL_BCAST_PROBE_RESP |
BSS_CHANGED_FILS_DISCOVERY))
- mt7996_mcu_beacon_inband_discov(dev, vif, changed);
+ mt7996_mcu_beacon_inband_discov(dev, info, link, changed);
if (changed & BSS_CHANGED_MU_GROUPS)
- mt7996_update_mu_group(hw, vif, info);
+ mt7996_update_mu_group(hw, link, info);
if (changed & BSS_CHANGED_TXPOWER &&
info->txpower != phy->txpower) {
@@ -754,96 +877,328 @@ mt7996_channel_switch_beacon(struct ieee80211_hw *hw,
mutex_unlock(&dev->mt76.mutex);
}
-int mt7996_mac_sta_add(struct mt76_dev *mdev, struct ieee80211_vif *vif,
- struct ieee80211_sta *sta)
+static int
+mt7996_mac_sta_init_link(struct mt7996_dev *dev,
+ struct ieee80211_bss_conf *link_conf,
+ struct ieee80211_link_sta *link_sta,
+ struct mt7996_vif_link *link, unsigned int link_id)
{
- struct mt7996_dev *dev = container_of(mdev, struct mt7996_dev, mt76);
+ struct ieee80211_sta *sta = link_sta->sta;
struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
- struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
- struct mt7996_vif_link *link = &mvif->deflink;
- u8 band_idx = link->phy->mt76->band_idx;
+ struct mt7996_phy *phy = link->phy;
+ struct mt7996_sta_link *msta_link;
int idx;
idx = mt76_wcid_alloc(dev->mt76.wcid_mask, MT7996_WTBL_STA);
if (idx < 0)
return -ENOSPC;
- INIT_LIST_HEAD(&msta->rc_list);
- INIT_LIST_HEAD(&msta->wcid.poll_list);
- msta->vif = mvif;
- msta->wcid.sta = 1;
- msta->wcid.idx = idx;
- msta->wcid.phy_idx = band_idx;
+ if (msta->deflink_id == IEEE80211_LINK_UNSPECIFIED) {
+ int i;
- ewma_avg_signal_init(&msta->avg_ack_signal);
+ msta_link = &msta->deflink;
+ msta->deflink_id = link_id;
- mt7996_mac_wtbl_update(dev, idx,
+ for (i = 0; i < ARRAY_SIZE(sta->txq); i++) {
+ struct mt76_txq *mtxq;
+
+ if (!sta->txq[i])
+ continue;
+
+ mtxq = (struct mt76_txq *)sta->txq[i]->drv_priv;
+ mtxq->wcid = idx;
+ }
+ } else {
+ msta_link = kzalloc(sizeof(*msta_link), GFP_KERNEL);
+ if (!msta_link)
+ return -ENOMEM;
+ }
+
+ INIT_LIST_HEAD(&msta_link->rc_list);
+ INIT_LIST_HEAD(&msta_link->wcid.poll_list);
+ msta_link->sta = msta;
+ msta_link->wcid.sta = 1;
+ msta_link->wcid.idx = idx;
+ msta_link->wcid.link_id = link_id;
+
+ ewma_avg_signal_init(&msta_link->avg_ack_signal);
+ ewma_signal_init(&msta_link->wcid.rssi);
+
+ rcu_assign_pointer(msta->link[link_id], msta_link);
+
+ mt7996_mac_wtbl_update(dev, idx, MT_WTBL_UPDATE_ADM_COUNT_CLEAR);
+ mt7996_mcu_add_sta(dev, link_conf, link_sta, link, msta_link,
+ CONN_STATE_DISCONNECT, true);
+
+ rcu_assign_pointer(dev->mt76.wcid[idx], &msta_link->wcid);
+ mt76_wcid_init(&msta_link->wcid, phy->mt76->band_idx);
+
+ return 0;
+}
+
+static void
+mt7996_mac_sta_deinit_link(struct mt7996_dev *dev,
+ struct mt7996_sta_link *msta_link)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(msta_link->wcid.aggr); i++)
+ mt76_rx_aggr_stop(&dev->mt76, &msta_link->wcid, i);
+
+ mt7996_mac_wtbl_update(dev, msta_link->wcid.idx,
MT_WTBL_UPDATE_ADM_COUNT_CLEAR);
- mt7996_mcu_add_sta(dev, vif, &link->mt76, sta, CONN_STATE_DISCONNECT,
- true);
+
+ spin_lock_bh(&dev->mt76.sta_poll_lock);
+ if (!list_empty(&msta_link->wcid.poll_list))
+ list_del_init(&msta_link->wcid.poll_list);
+ if (!list_empty(&msta_link->rc_list))
+ list_del_init(&msta_link->rc_list);
+ spin_unlock_bh(&dev->mt76.sta_poll_lock);
+
+ mt76_wcid_cleanup(&dev->mt76, &msta_link->wcid);
+ mt76_wcid_mask_clear(dev->mt76.wcid_mask, msta_link->wcid.idx);
+}
+
+static void
+mt7996_mac_sta_remove_links(struct mt7996_dev *dev, struct ieee80211_sta *sta,
+ unsigned long links)
+{
+ struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
+ struct mt76_dev *mdev = &dev->mt76;
+ unsigned int link_id;
+
+ for_each_set_bit(link_id, &links, IEEE80211_MLD_MAX_NUM_LINKS) {
+ struct mt7996_sta_link *msta_link = NULL;
+
+ msta_link = rcu_replace_pointer(msta->link[link_id], msta_link,
+ lockdep_is_held(&mdev->mutex));
+ if (!msta_link)
+ continue;
+
+ mt7996_mac_sta_deinit_link(dev, msta_link);
+ if (msta->deflink_id == link_id) {
+ msta->deflink_id = IEEE80211_LINK_UNSPECIFIED;
+ continue;
+ }
+
+ kfree_rcu(msta_link, rcu_head);
+ }
+}
+
+static int
+mt7996_mac_sta_add_links(struct mt7996_dev *dev, struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta, unsigned long new_links)
+{
+ struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
+ unsigned int link_id;
+ int err = 0;
+
+ for_each_set_bit(link_id, &new_links, IEEE80211_MLD_MAX_NUM_LINKS) {
+ struct ieee80211_bss_conf *link_conf;
+ struct ieee80211_link_sta *link_sta;
+ struct mt7996_vif_link *link;
+
+ if (rcu_access_pointer(msta->link[link_id]))
+ continue;
+
+ link_conf = link_conf_dereference_protected(vif, link_id);
+ if (!link_conf) {
+ err = -EINVAL;
+ goto error_unlink;
+ }
+
+ link = mt7996_vif_link(dev, vif, link_id);
+ if (!link) {
+ err = -EINVAL;
+ goto error_unlink;
+ }
+
+ link_sta = link_sta_dereference_protected(sta, link_id);
+ if (!link_sta) {
+ err = -EINVAL;
+ goto error_unlink;
+ }
+
+ err = mt7996_mac_sta_init_link(dev, link_conf, link_sta, link,
+ link_id);
+ if (err)
+ goto error_unlink;
+ }
return 0;
+
+error_unlink:
+ mt7996_mac_sta_remove_links(dev, sta, new_links);
+
+ return err;
}
-int mt7996_mac_sta_event(struct mt76_dev *mdev, struct ieee80211_vif *vif,
- struct ieee80211_sta *sta, enum mt76_sta_event ev)
+static int
+mt7996_mac_sta_change_links(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta, u16 old_links,
+ u16 new_links)
{
+ struct mt7996_dev *dev = mt7996_hw_dev(hw);
+ unsigned long add = new_links & ~old_links;
+ unsigned long rem = old_links & ~new_links;
+ int ret;
+
+ mutex_lock(&dev->mt76.mutex);
+
+ mt7996_mac_sta_remove_links(dev, sta, rem);
+ ret = mt7996_mac_sta_add_links(dev, vif, sta, add);
+
+ mutex_unlock(&dev->mt76.mutex);
+
+ return ret;
+}
+
+static int
+mt7996_mac_sta_add(struct mt76_phy *mphy, struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta)
+{
+ struct mt76_dev *mdev = mphy->dev;
struct mt7996_dev *dev = container_of(mdev, struct mt7996_dev, mt76);
struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
- struct mt7996_vif_link *link = &mvif->deflink;
- int i, ret;
+ unsigned long links = sta->mlo ? sta->valid_links : BIT(0);
+ int err;
- switch (ev) {
- case MT76_STA_EVENT_ASSOC:
- ret = mt7996_mcu_add_sta(dev, vif, &link->mt76, sta,
- CONN_STATE_CONNECT, true);
- if (ret)
- return ret;
+ mutex_lock(&mdev->mutex);
- ret = mt7996_mcu_add_rate_ctrl(dev, vif, sta, false);
- if (ret)
- return ret;
+ msta->deflink_id = IEEE80211_LINK_UNSPECIFIED;
+ msta->vif = mvif;
+ err = mt7996_mac_sta_add_links(dev, vif, sta, links);
+ if (!err)
+ mphy->num_sta++;
- msta->wcid.tx_info |= MT_WCID_TX_INFO_SET;
- msta->wcid.sta = 1;
+ mutex_unlock(&mdev->mutex);
- return 0;
+ return err;
+}
- case MT76_STA_EVENT_AUTHORIZE:
- return mt7996_mcu_add_sta(dev, vif, &link->mt76, sta,
- CONN_STATE_PORT_SECURE, false);
+static int
+mt7996_mac_sta_event(struct mt7996_dev *dev, struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta, enum mt76_sta_event ev)
+{
+ struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
+ unsigned long links = sta->valid_links;
+ struct ieee80211_link_sta *link_sta;
+ unsigned int link_id;
+
+ for_each_sta_active_link(vif, sta, link_sta, link_id) {
+ struct ieee80211_bss_conf *link_conf;
+ struct mt7996_sta_link *msta_link;
+ struct mt7996_vif_link *link;
+ int i, err;
+
+ link_conf = link_conf_dereference_protected(vif, link_id);
+ if (!link_conf)
+ continue;
- case MT76_STA_EVENT_DISASSOC:
- for (i = 0; i < ARRAY_SIZE(msta->twt.flow); i++)
- mt7996_mac_twt_teardown_flow(dev, msta, i);
+ link = mt7996_vif_link(dev, vif, link_id);
+ if (!link)
+ continue;
- mt7996_mcu_add_sta(dev, vif, &link->mt76, sta,
- CONN_STATE_DISCONNECT, false);
- msta->wcid.sta_disabled = 1;
- msta->wcid.sta = 0;
+ msta_link = mt76_dereference(msta->link[link_id], &dev->mt76);
+ if (!msta_link)
+ continue;
- return 0;
+ switch (ev) {
+ case MT76_STA_EVENT_ASSOC:
+ err = mt7996_mcu_add_sta(dev, link_conf, link_sta,
+ link, msta_link,
+ CONN_STATE_CONNECT, true);
+ if (err)
+ return err;
+
+ err = mt7996_mcu_add_rate_ctrl(dev, vif, link_conf,
+ link_sta, link,
+ msta_link, false);
+ if (err)
+ return err;
+
+ msta_link->wcid.tx_info |= MT_WCID_TX_INFO_SET;
+ msta_link->wcid.sta = 1;
+ break;
+ case MT76_STA_EVENT_AUTHORIZE:
+ err = mt7996_mcu_add_sta(dev, link_conf, link_sta,
+ link, msta_link,
+ CONN_STATE_PORT_SECURE, false);
+ if (err)
+ return err;
+ break;
+ case MT76_STA_EVENT_DISASSOC:
+ for (i = 0; i < ARRAY_SIZE(msta_link->twt.flow); i++)
+ mt7996_mac_twt_teardown_flow(dev, link,
+ msta_link, i);
+
+ if (sta->mlo && links == BIT(link_id)) /* last link */
+ mt7996_mcu_teardown_mld_sta(dev, link,
+ msta_link);
+ else
+ mt7996_mcu_add_sta(dev, link_conf, link_sta,
+ link, msta_link,
+ CONN_STATE_DISCONNECT, false);
+ msta_link->wcid.sta_disabled = 1;
+ msta_link->wcid.sta = 0;
+ links = links & ~BIT(link_id);
+ break;
+ }
}
return 0;
}
-void mt7996_mac_sta_remove(struct mt76_dev *mdev, struct ieee80211_vif *vif,
- struct ieee80211_sta *sta)
+static void
+mt7996_mac_sta_remove(struct mt76_phy *mphy, struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta)
{
+ struct mt76_dev *mdev = mphy->dev;
struct mt7996_dev *dev = container_of(mdev, struct mt7996_dev, mt76);
- struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
+ unsigned long links = sta->mlo ? sta->valid_links : BIT(0);
- mt7996_mac_wtbl_update(dev, msta->wcid.idx,
- MT_WTBL_UPDATE_ADM_COUNT_CLEAR);
+ mutex_lock(&mdev->mutex);
- spin_lock_bh(&mdev->sta_poll_lock);
- if (!list_empty(&msta->wcid.poll_list))
- list_del_init(&msta->wcid.poll_list);
- if (!list_empty(&msta->rc_list))
- list_del_init(&msta->rc_list);
- spin_unlock_bh(&mdev->sta_poll_lock);
+ mt7996_mac_sta_remove_links(dev, sta, links);
+ mphy->num_sta--;
+
+ mutex_unlock(&mdev->mutex);
+}
+
+static int
+mt7996_sta_state(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta, enum ieee80211_sta_state old_state,
+ enum ieee80211_sta_state new_state)
+{
+ struct mt76_phy *mphy = mt76_vif_phy(hw, vif);
+ struct mt7996_dev *dev = mt7996_hw_dev(hw);
+ enum mt76_sta_event ev;
+
+ if (!mphy)
+ return -EINVAL;
+
+ if (old_state == IEEE80211_STA_NOTEXIST &&
+ new_state == IEEE80211_STA_NONE)
+ return mt7996_mac_sta_add(mphy, vif, sta);
+
+ if (old_state == IEEE80211_STA_NONE &&
+ new_state == IEEE80211_STA_NOTEXIST)
+ mt7996_mac_sta_remove(mphy, vif, sta);
+
+ if (old_state == IEEE80211_STA_AUTH &&
+ new_state == IEEE80211_STA_ASSOC)
+ ev = MT76_STA_EVENT_ASSOC;
+ else if (old_state == IEEE80211_STA_ASSOC &&
+ new_state == IEEE80211_STA_AUTHORIZED)
+ ev = MT76_STA_EVENT_AUTHORIZE;
+ else if (old_state == IEEE80211_STA_ASSOC &&
+ new_state == IEEE80211_STA_AUTH)
+ ev = MT76_STA_EVENT_DISASSOC;
+ else
+ return 0;
+
+ return mt7996_mac_sta_event(dev, vif, sta, ev);
}
static void mt7996_tx(struct ieee80211_hw *hw,
@@ -855,12 +1210,18 @@ static void mt7996_tx(struct ieee80211_hw *hw,
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
struct ieee80211_vif *vif = info->control.vif;
struct mt76_wcid *wcid = &dev->mt76.global_wcid;
+ u8 link_id = u32_get_bits(info->control.flags,
+ IEEE80211_TX_CTRL_MLO_LINK);
+
+ rcu_read_lock();
if (vif) {
- struct mt7996_vif *mvif;
+ struct mt7996_vif *mvif = (void *)vif->drv_priv;
+ struct mt76_vif_link *mlink;
- mvif = (struct mt7996_vif *)vif->drv_priv;
- wcid = &mvif->deflink.sta.wcid;
+ mlink = rcu_dereference(mvif->mt76.link[link_id]);
+ if (mlink && mlink->wcid)
+ wcid = mlink->wcid;
if (mvif->mt76.roc_phy &&
(info->flags & IEEE80211_TX_CTL_TX_OFFCHAN)) {
@@ -872,25 +1233,28 @@ static void mt7996_tx(struct ieee80211_hw *hw,
}
}
- if (control->sta) {
- struct mt7996_sta *sta;
-
- sta = (struct mt7996_sta *)control->sta->drv_priv;
- wcid = &sta->wcid;
- }
-
if (!mphy) {
ieee80211_free_txskb(hw, skb);
- return;
+ goto unlock;
}
+ if (control->sta) {
+ struct mt7996_sta *msta = (void *)control->sta->drv_priv;
+ struct mt7996_sta_link *msta_link;
+
+ msta_link = rcu_dereference(msta->link[link_id]);
+ if (msta_link)
+ wcid = &msta_link->wcid;
+ }
mt76_tx(mphy, control->sta, wcid, skb);
+unlock:
+ rcu_read_unlock();
}
static int mt7996_set_rts_threshold(struct ieee80211_hw *hw, u32 val)
{
struct mt7996_dev *dev = mt7996_hw_dev(hw);
- int i, ret;
+ int i, ret = 0;
mutex_lock(&dev->mt76.mutex);
@@ -914,11 +1278,13 @@ mt7996_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
enum ieee80211_ampdu_mlme_action action = params->action;
struct mt7996_dev *dev = mt7996_hw_dev(hw);
struct ieee80211_sta *sta = params->sta;
- struct ieee80211_txq *txq = sta->txq[params->tid];
struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
+ struct ieee80211_txq *txq = sta->txq[params->tid];
+ struct ieee80211_link_sta *link_sta;
u16 tid = params->tid;
u16 ssn = params->ssn;
struct mt76_txq *mtxq;
+ unsigned int link_id;
int ret = 0;
if (!txq)
@@ -927,38 +1293,61 @@ mt7996_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
mtxq = (struct mt76_txq *)txq->drv_priv;
mutex_lock(&dev->mt76.mutex);
- switch (action) {
- case IEEE80211_AMPDU_RX_START:
- mt76_rx_aggr_start(&dev->mt76, &msta->wcid, tid, ssn,
- params->buf_size);
- ret = mt7996_mcu_add_rx_ba(dev, params, true);
- break;
- case IEEE80211_AMPDU_RX_STOP:
- mt76_rx_aggr_stop(&dev->mt76, &msta->wcid, tid);
- ret = mt7996_mcu_add_rx_ba(dev, params, false);
- break;
- case IEEE80211_AMPDU_TX_OPERATIONAL:
- mtxq->aggr = true;
- mtxq->send_bar = false;
- ret = mt7996_mcu_add_tx_ba(dev, params, true);
- break;
- case IEEE80211_AMPDU_TX_STOP_FLUSH:
- case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT:
- mtxq->aggr = false;
- clear_bit(tid, &msta->wcid.ampdu_state);
- ret = mt7996_mcu_add_tx_ba(dev, params, false);
- break;
- case IEEE80211_AMPDU_TX_START:
- set_bit(tid, &msta->wcid.ampdu_state);
- ret = IEEE80211_AMPDU_TX_START_IMMEDIATE;
- break;
- case IEEE80211_AMPDU_TX_STOP_CONT:
- mtxq->aggr = false;
- clear_bit(tid, &msta->wcid.ampdu_state);
- ret = mt7996_mcu_add_tx_ba(dev, params, false);
- ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid);
- break;
+
+ for_each_sta_active_link(vif, sta, link_sta, link_id) {
+ struct mt7996_sta_link *msta_link;
+ struct mt7996_vif_link *link;
+
+ msta_link = mt76_dereference(msta->link[link_id], &dev->mt76);
+ if (!msta_link)
+ continue;
+
+ link = mt7996_vif_link(dev, vif, link_id);
+ if (!link)
+ continue;
+
+ switch (action) {
+ case IEEE80211_AMPDU_RX_START:
+ mt76_rx_aggr_start(&dev->mt76, &msta_link->wcid, tid,
+ ssn, params->buf_size);
+ ret = mt7996_mcu_add_rx_ba(dev, params, link, true);
+ break;
+ case IEEE80211_AMPDU_RX_STOP:
+ mt76_rx_aggr_stop(&dev->mt76, &msta_link->wcid, tid);
+ ret = mt7996_mcu_add_rx_ba(dev, params, link, false);
+ break;
+ case IEEE80211_AMPDU_TX_OPERATIONAL:
+ mtxq->aggr = true;
+ mtxq->send_bar = false;
+ ret = mt7996_mcu_add_tx_ba(dev, params, link,
+ msta_link, true);
+ break;
+ case IEEE80211_AMPDU_TX_STOP_FLUSH:
+ case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT:
+ mtxq->aggr = false;
+ clear_bit(tid, &msta_link->wcid.ampdu_state);
+ ret = mt7996_mcu_add_tx_ba(dev, params, link,
+ msta_link, false);
+ break;
+ case IEEE80211_AMPDU_TX_START:
+ set_bit(tid, &msta_link->wcid.ampdu_state);
+ ret = IEEE80211_AMPDU_TX_START_IMMEDIATE;
+ break;
+ case IEEE80211_AMPDU_TX_STOP_CONT:
+ mtxq->aggr = false;
+ clear_bit(tid, &msta_link->wcid.ampdu_state);
+ ret = mt7996_mcu_add_tx_ba(dev, params, link,
+ msta_link, false);
+ break;
+ }
+
+ if (ret)
+ break;
}
+
+ if (action == IEEE80211_AMPDU_TX_STOP_CONT)
+ ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid);
+
mutex_unlock(&dev->mt76.mutex);
return ret;
@@ -989,10 +1378,10 @@ mt7996_get_stats(struct ieee80211_hw *hw,
return 0;
}
-u64 __mt7996_get_tsf(struct ieee80211_hw *hw, struct mt7996_vif *mvif)
+u64 __mt7996_get_tsf(struct ieee80211_hw *hw, struct mt7996_vif_link *link)
{
struct mt7996_dev *dev = mt7996_hw_dev(hw);
- struct mt7996_phy *phy = mt7996_vif_link_phy(&mvif->deflink);
+ struct mt7996_phy *phy = link->phy;
union {
u64 t64;
u32 t32[2];
@@ -1004,8 +1393,8 @@ u64 __mt7996_get_tsf(struct ieee80211_hw *hw, struct mt7996_vif *mvif)
lockdep_assert_held(&dev->mt76.mutex);
- n = mvif->deflink.mt76.omac_idx > HW_BSSID_MAX ? HW_BSSID_0
- : mvif->deflink.mt76.omac_idx;
+ n = link->mt76.omac_idx > HW_BSSID_MAX ? HW_BSSID_0
+ : link->mt76.omac_idx;
/* TSF software read */
mt76_rmw(dev, MT_LPON_TCR(phy->mt76->band_idx, n), MT_LPON_TCR_SW_MODE,
MT_LPON_TCR_SW_READ);
@@ -1023,7 +1412,7 @@ mt7996_get_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
u64 ret;
mutex_lock(&dev->mt76.mutex);
- ret = __mt7996_get_tsf(hw, mvif);
+ ret = __mt7996_get_tsf(hw, &mvif->deflink);
mutex_unlock(&dev->mt76.mutex);
return ret;
@@ -1035,26 +1424,33 @@ mt7996_set_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
{
struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
struct mt7996_dev *dev = mt7996_hw_dev(hw);
- struct mt7996_phy *phy = mt7996_vif_link_phy(&mvif->deflink);
+ struct mt7996_vif_link *link;
+ struct mt7996_phy *phy;
union {
u64 t64;
u32 t32[2];
} tsf = { .t64 = timestamp, };
u16 n;
- if (!phy)
- return;
-
mutex_lock(&dev->mt76.mutex);
- n = mvif->deflink.mt76.omac_idx > HW_BSSID_MAX ? HW_BSSID_0
- : mvif->deflink.mt76.omac_idx;
+ link = mt7996_vif_link(dev, vif, mvif->mt76.deflink_id);
+ if (!link)
+ goto unlock;
+
+ n = link->mt76.omac_idx > HW_BSSID_MAX ? HW_BSSID_0
+ : link->mt76.omac_idx;
+ phy = link->phy;
+ if (!phy)
+ goto unlock;
+
mt76_wr(dev, MT_LPON_UTTR0(phy->mt76->band_idx), tsf.t32[0]);
mt76_wr(dev, MT_LPON_UTTR1(phy->mt76->band_idx), tsf.t32[1]);
/* TSF software overwrite */
mt76_rmw(dev, MT_LPON_TCR(phy->mt76->band_idx, n), MT_LPON_TCR_SW_MODE,
MT_LPON_TCR_SW_WRITE);
+unlock:
mutex_unlock(&dev->mt76.mutex);
}
@@ -1064,26 +1460,33 @@ mt7996_offset_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
{
struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
struct mt7996_dev *dev = mt7996_hw_dev(hw);
- struct mt7996_phy *phy = mt7996_vif_link_phy(&mvif->deflink);
+ struct mt7996_vif_link *link;
+ struct mt7996_phy *phy;
union {
u64 t64;
u32 t32[2];
} tsf = { .t64 = timestamp, };
u16 n;
- if (!phy)
- return;
-
mutex_lock(&dev->mt76.mutex);
- n = mvif->deflink.mt76.omac_idx > HW_BSSID_MAX ? HW_BSSID_0
- : mvif->deflink.mt76.omac_idx;
+ link = mt7996_vif_link(dev, vif, mvif->mt76.deflink_id);
+ if (!link)
+ goto unlock;
+
+ phy = link->phy;
+ if (!phy)
+ goto unlock;
+
+ n = link->mt76.omac_idx > HW_BSSID_MAX ? HW_BSSID_0
+ : link->mt76.omac_idx;
mt76_wr(dev, MT_LPON_UTTR0(phy->mt76->band_idx), tsf.t32[0]);
mt76_wr(dev, MT_LPON_UTTR1(phy->mt76->band_idx), tsf.t32[1]);
/* TSF software adjust*/
mt76_rmw(dev, MT_LPON_TCR(phy->mt76->band_idx, n), MT_LPON_TCR_SW_MODE,
MT_LPON_TCR_SW_ADJUST);
+unlock:
mutex_unlock(&dev->mt76.mutex);
}
@@ -1125,7 +1528,8 @@ mt7996_set_antenna(struct ieee80211_hw *hw, u32 tx_ant, u32 rx_ant)
u8 shift = dev->chainshift[band_idx];
phy->mt76->chainmask = tx_ant & phy->orig_chainmask;
- phy->mt76->antenna_mask = phy->mt76->chainmask >> shift;
+ phy->mt76->antenna_mask = (phy->mt76->chainmask >> shift) &
+ phy->orig_antenna_mask;
mt76_set_stream_caps(phy->mt76, true);
mt7996_set_stream_vht_txbf_caps(phy);
@@ -1145,7 +1549,8 @@ static void mt7996_sta_statistics(struct ieee80211_hw *hw,
{
struct mt7996_dev *dev = mt7996_hw_dev(hw);
struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
- struct rate_info *txrate = &msta->wcid.rate;
+ struct mt7996_sta_link *msta_link = &msta->deflink;
+ struct rate_info *txrate = &msta_link->wcid.rate;
if (txrate->legacy || txrate->flags) {
if (txrate->legacy) {
@@ -1165,55 +1570,67 @@ static void mt7996_sta_statistics(struct ieee80211_hw *hw,
sinfo->txrate.flags = txrate->flags;
sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_BITRATE);
- sinfo->tx_failed = msta->wcid.stats.tx_failed;
+ sinfo->tx_failed = msta_link->wcid.stats.tx_failed;
sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_FAILED);
- sinfo->tx_retries = msta->wcid.stats.tx_retries;
+ sinfo->tx_retries = msta_link->wcid.stats.tx_retries;
sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_RETRIES);
- sinfo->ack_signal = (s8)msta->ack_signal;
+ sinfo->ack_signal = (s8)msta_link->ack_signal;
sinfo->filled |= BIT_ULL(NL80211_STA_INFO_ACK_SIGNAL);
- sinfo->avg_ack_signal = -(s8)ewma_avg_signal_read(&msta->avg_ack_signal);
+ sinfo->avg_ack_signal =
+ -(s8)ewma_avg_signal_read(&msta_link->avg_ack_signal);
sinfo->filled |= BIT_ULL(NL80211_STA_INFO_ACK_SIGNAL_AVG);
if (mtk_wed_device_active(&dev->mt76.mmio.wed)) {
- sinfo->tx_bytes = msta->wcid.stats.tx_bytes;
+ sinfo->tx_bytes = msta_link->wcid.stats.tx_bytes;
sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_BYTES64);
- sinfo->rx_bytes = msta->wcid.stats.rx_bytes;
+ sinfo->rx_bytes = msta_link->wcid.stats.rx_bytes;
sinfo->filled |= BIT_ULL(NL80211_STA_INFO_RX_BYTES64);
- sinfo->tx_packets = msta->wcid.stats.tx_packets;
+ sinfo->tx_packets = msta_link->wcid.stats.tx_packets;
sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_PACKETS);
- sinfo->rx_packets = msta->wcid.stats.rx_packets;
+ sinfo->rx_packets = msta_link->wcid.stats.rx_packets;
sinfo->filled |= BIT_ULL(NL80211_STA_INFO_RX_PACKETS);
}
}
-static void mt7996_sta_rc_work(void *data, struct ieee80211_sta *sta)
+static void mt7996_link_rate_ctrl_update(void *data, struct ieee80211_sta *sta)
{
struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
struct mt7996_dev *dev = msta->vif->deflink.phy->dev;
+ struct mt7996_sta_link *msta_link;
u32 *changed = data;
+ rcu_read_lock();
+
+ msta_link = rcu_dereference(msta->link[msta->deflink_id]);
+ if (!msta_link)
+ goto out;
+
spin_lock_bh(&dev->mt76.sta_poll_lock);
- msta->changed |= *changed;
- if (list_empty(&msta->rc_list))
- list_add_tail(&msta->rc_list, &dev->sta_rc_list);
+
+ msta_link->changed |= *changed;
+ if (list_empty(&msta_link->rc_list))
+ list_add_tail(&msta_link->rc_list, &dev->sta_rc_list);
+
spin_unlock_bh(&dev->mt76.sta_poll_lock);
+out:
+ rcu_read_unlock();
}
-static void mt7996_sta_rc_update(struct ieee80211_hw *hw,
- struct ieee80211_vif *vif,
- struct ieee80211_link_sta *link_sta,
- u32 changed)
+static void mt7996_link_sta_rc_update(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_link_sta *link_sta,
+ u32 changed)
{
struct mt7996_dev *dev = mt7996_hw_dev(hw);
struct ieee80211_sta *sta = link_sta->sta;
- mt7996_sta_rc_work(&changed, sta);
+ mt7996_link_rate_ctrl_update(&changed, sta);
ieee80211_queue_work(hw, &dev->rc_work);
}
@@ -1235,7 +1652,8 @@ mt7996_set_bitrate_mask(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
* - multiple rates: if it's not in range format i.e 0-{7,8,9} for VHT
* then multiple MCS setting (MCS 4,5,6) is not supported.
*/
- ieee80211_iterate_stations_atomic(hw, mt7996_sta_rc_work, &changed);
+ ieee80211_iterate_stations_atomic(hw, mt7996_link_rate_ctrl_update,
+ &changed);
ieee80211_queue_work(hw, &dev->rc_work);
return 0;
@@ -1246,18 +1664,37 @@ static void mt7996_sta_set_4addr(struct ieee80211_hw *hw,
struct ieee80211_sta *sta,
bool enabled)
{
- struct mt7996_dev *dev = mt7996_hw_dev(hw);
struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
+ struct mt7996_dev *dev = mt7996_hw_dev(hw);
+ struct ieee80211_link_sta *link_sta;
+ unsigned int link_id;
- if (enabled)
- set_bit(MT_WCID_FLAG_4ADDR, &msta->wcid.flags);
- else
- clear_bit(MT_WCID_FLAG_4ADDR, &msta->wcid.flags);
+ mutex_lock(&dev->mt76.mutex);
- if (!msta->wcid.sta)
- return;
+ for_each_sta_active_link(vif, sta, link_sta, link_id) {
+ struct mt7996_sta_link *msta_link;
+ struct mt7996_vif_link *link;
+
+ link = mt7996_vif_link(dev, vif, link_id);
+ if (!link)
+ continue;
+
+ msta_link = mt76_dereference(msta->link[link_id], &dev->mt76);
+ if (!msta_link)
+ continue;
+
+ if (enabled)
+ set_bit(MT_WCID_FLAG_4ADDR, &msta_link->wcid.flags);
+ else
+ clear_bit(MT_WCID_FLAG_4ADDR, &msta_link->wcid.flags);
+
+ if (!msta_link->wcid.sta)
+ continue;
+
+ mt7996_mcu_wtbl_update_hdr_trans(dev, vif, link, msta_link);
+ }
- mt7996_mcu_wtbl_update_hdr_trans(dev, vif, sta);
+ mutex_unlock(&dev->mt76.mutex);
}
static void mt7996_sta_set_decap_offload(struct ieee80211_hw *hw,
@@ -1265,18 +1702,39 @@ static void mt7996_sta_set_decap_offload(struct ieee80211_hw *hw,
struct ieee80211_sta *sta,
bool enabled)
{
- struct mt7996_dev *dev = mt7996_hw_dev(hw);
struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
+ struct mt7996_dev *dev = mt7996_hw_dev(hw);
+ struct ieee80211_link_sta *link_sta;
+ unsigned int link_id;
- if (enabled)
- set_bit(MT_WCID_FLAG_HDR_TRANS, &msta->wcid.flags);
- else
- clear_bit(MT_WCID_FLAG_HDR_TRANS, &msta->wcid.flags);
+ mutex_lock(&dev->mt76.mutex);
- if (!msta->wcid.sta)
- return;
+ for_each_sta_active_link(vif, sta, link_sta, link_id) {
+ struct mt7996_sta_link *msta_link;
+ struct mt7996_vif_link *link;
+
+ link = mt7996_vif_link(dev, vif, link_id);
+ if (!link)
+ continue;
+
+ msta_link = mt76_dereference(msta->link[link_id], &dev->mt76);
+ if (!msta_link)
+ continue;
+
+ if (enabled)
+ set_bit(MT_WCID_FLAG_HDR_TRANS,
+ &msta_link->wcid.flags);
+ else
+ clear_bit(MT_WCID_FLAG_HDR_TRANS,
+ &msta_link->wcid.flags);
- mt7996_mcu_wtbl_update_hdr_trans(dev, vif, sta);
+ if (!msta_link->wcid.sta)
+ continue;
+
+ mt7996_mcu_wtbl_update_hdr_trans(dev, vif, link, msta_link);
+ }
+
+ mutex_unlock(&dev->mt76.mutex);
}
static const char mt7996_gstrings_stats[][ETH_GSTRING_LEN] = {
@@ -1408,11 +1866,12 @@ static void mt7996_ethtool_worker(void *wi_data, struct ieee80211_sta *sta)
{
struct mt76_ethtool_worker_info *wi = wi_data;
struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
+ struct mt7996_sta_link *msta_link = &msta->deflink;
if (msta->vif->deflink.mt76.idx != wi->idx)
return;
- mt76_ethtool_worker(wi, &msta->wcid.stats, true);
+ mt76_ethtool_worker(wi, &msta_link->wcid.stats, true);
}
static
@@ -1516,10 +1975,12 @@ mt7996_twt_teardown_request(struct ieee80211_hw *hw,
u8 flowid)
{
struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
+ struct mt7996_sta_link *msta_link = &msta->deflink;
+ struct mt7996_vif_link *link = &msta->vif->deflink;
struct mt7996_dev *dev = mt7996_hw_dev(hw);
mutex_lock(&dev->mt76.mutex);
- mt7996_mac_twt_teardown_flow(dev, msta, flowid);
+ mt7996_mac_twt_teardown_flow(dev, link, msta_link, flowid);
mutex_unlock(&dev->mt76.mutex);
}
@@ -1589,12 +2050,26 @@ mt7996_net_fill_forward_path(struct ieee80211_hw *hw,
{
struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
- struct mt7996_vif_link *mlink = &mvif->deflink;
struct mt7996_dev *dev = mt7996_hw_dev(hw);
struct mtk_wed_device *wed = &dev->mt76.mmio.wed;
+ struct mt7996_sta_link *msta_link;
+ struct mt7996_vif_link *link;
+ struct mt76_vif_link *mlink;
struct mt7996_phy *phy;
- phy = mt7996_vif_link_phy(mlink);
+ mlink = rcu_dereference(mvif->mt76.link[msta->deflink_id]);
+ if (!mlink)
+ return -EIO;
+
+ msta_link = rcu_dereference(msta->link[msta->deflink_id]);
+ if (!msta_link)
+ return -EIO;
+
+ if (!msta_link->wcid.sta || msta_link->wcid.idx > MT7996_WTBL_STA)
+ return -EIO;
+
+ link = (struct mt7996_vif_link *)mlink;
+ phy = mt7996_vif_link_phy(link);
if (!phy)
return -ENODEV;
@@ -1604,15 +2079,12 @@ mt7996_net_fill_forward_path(struct ieee80211_hw *hw,
if (!mtk_wed_device_active(wed))
return -ENODEV;
- if (!msta->wcid.sta || msta->wcid.idx > MT7996_WTBL_STA)
- return -EIO;
-
path->type = DEV_PATH_MTK_WDMA;
path->dev = ctx->dev;
path->mtk_wdma.wdma_idx = wed->wdma_idx;
- path->mtk_wdma.bss = mvif->deflink.mt76.idx;
+ path->mtk_wdma.bss = mlink->idx;
path->mtk_wdma.queue = 0;
- path->mtk_wdma.wcid = msta->wcid.idx;
+ path->mtk_wdma.wcid = msta_link->wcid.idx;
path->mtk_wdma.amsdu = mtk_wed_is_amsdu_supported(wed);
ctx->dev = NULL;
@@ -1622,6 +2094,14 @@ mt7996_net_fill_forward_path(struct ieee80211_hw *hw,
#endif
+static int
+mt7996_change_vif_links(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ u16 old_links, u16 new_links,
+ struct ieee80211_bss_conf *old[IEEE80211_MLD_MAX_NUM_LINKS])
+{
+ return 0;
+}
+
const struct ieee80211_ops mt7996_ops = {
.add_chanctx = mt76_add_chanctx,
.remove_chanctx = mt76_remove_chanctx,
@@ -1637,10 +2117,11 @@ const struct ieee80211_ops mt7996_ops = {
.config = mt7996_config,
.conf_tx = mt7996_conf_tx,
.configure_filter = mt7996_configure_filter,
- .bss_info_changed = mt7996_bss_info_changed,
- .sta_state = mt76_sta_state,
+ .vif_cfg_changed = mt7996_vif_cfg_changed,
+ .link_info_changed = mt7996_link_info_changed,
+ .sta_state = mt7996_sta_state,
.sta_pre_rcu_remove = mt76_sta_pre_rcu_remove,
- .link_sta_rc_update = mt7996_sta_rc_update,
+ .link_sta_rc_update = mt7996_link_sta_rc_update,
.set_key = mt7996_set_key,
.ampdu_action = mt7996_ampdu_action,
.set_rts_threshold = mt7996_set_rts_threshold,
@@ -1650,7 +2131,7 @@ const struct ieee80211_ops mt7996_ops = {
.remain_on_channel = mt76_remain_on_channel,
.cancel_remain_on_channel = mt76_cancel_remain_on_channel,
.release_buffered_frames = mt76_release_buffered_frames,
- .get_txpower = mt76_get_txpower,
+ .get_txpower = mt7996_get_txpower,
.channel_switch_beacon = mt7996_channel_switch_beacon,
.get_stats = mt7996_get_stats,
.get_et_sset_count = mt7996_get_et_sset_count,
@@ -1677,4 +2158,6 @@ const struct ieee80211_ops mt7996_ops = {
.net_fill_forward_path = mt7996_net_fill_forward_path,
.net_setup_tc = mt76_wed_net_setup_tc,
#endif
+ .change_vif_links = mt7996_change_vif_links,
+ .change_sta_links = mt7996_mac_sta_change_links,
};
diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c
index e4569d032221..f0adc0b4b8b6 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c
@@ -13,7 +13,7 @@
#define fw_name(_dev, name, ...) ({ \
char *_fw; \
switch (mt76_chip(&(_dev)->mt76)) { \
- case 0x7992: \
+ case MT7992_DEVICE_ID: \
switch ((_dev)->var.type) { \
case MT7992_VAR_TYPE_23: \
_fw = MT7992_##name##_23; \
@@ -22,7 +22,10 @@
_fw = MT7992_##name; \
} \
break; \
- case 0x7990: \
+ case MT7990_DEVICE_ID: \
+ _fw = MT7990_##name; \
+ break; \
+ case MT7996_DEVICE_ID: \
default: \
switch ((_dev)->var.type) { \
case MT7996_VAR_TYPE_233: \
@@ -118,13 +121,13 @@ mt7996_mcu_get_sta_nss(u16 mcs_map)
}
static void
-mt7996_mcu_set_sta_he_mcs(struct ieee80211_sta *sta, __le16 *he_mcs,
- u16 mcs_map)
+mt7996_mcu_set_sta_he_mcs(struct ieee80211_link_sta *link_sta,
+ struct mt7996_vif_link *link,
+ __le16 *he_mcs, u16 mcs_map)
{
- struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
- enum nl80211_band band = msta->vif->deflink.phy->mt76->chandef.chan->band;
- const u16 *mask = msta->vif->deflink.bitrate_mask.control[band].he_mcs;
- int nss, max_nss = sta->deflink.rx_nss > 3 ? 4 : sta->deflink.rx_nss;
+ int nss, max_nss = link_sta->rx_nss > 3 ? 4 : link_sta->rx_nss;
+ enum nl80211_band band = link->phy->mt76->chandef.chan->band;
+ const u16 *mask = link->bitrate_mask.control[band].he_mcs;
for (nss = 0; nss < max_nss; nss++) {
int mcs;
@@ -167,11 +170,11 @@ mt7996_mcu_set_sta_he_mcs(struct ieee80211_sta *sta, __le16 *he_mcs,
}
static void
-mt7996_mcu_set_sta_vht_mcs(struct ieee80211_sta *sta, __le16 *vht_mcs,
- const u16 *mask)
+mt7996_mcu_set_sta_vht_mcs(struct ieee80211_link_sta *link_sta,
+ __le16 *vht_mcs, const u16 *mask)
{
- u16 mcs, mcs_map = le16_to_cpu(sta->deflink.vht_cap.vht_mcs.rx_mcs_map);
- int nss, max_nss = sta->deflink.rx_nss > 3 ? 4 : sta->deflink.rx_nss;
+ u16 mcs, mcs_map = le16_to_cpu(link_sta->vht_cap.vht_mcs.rx_mcs_map);
+ int nss, max_nss = link_sta->rx_nss > 3 ? 4 : link_sta->rx_nss;
for (nss = 0; nss < max_nss; nss++, mcs_map >>= 2) {
switch (mcs_map & 0x3) {
@@ -193,13 +196,13 @@ mt7996_mcu_set_sta_vht_mcs(struct ieee80211_sta *sta, __le16 *vht_mcs,
}
static void
-mt7996_mcu_set_sta_ht_mcs(struct ieee80211_sta *sta, u8 *ht_mcs,
- const u8 *mask)
+mt7996_mcu_set_sta_ht_mcs(struct ieee80211_link_sta *link_sta,
+ u8 *ht_mcs, const u8 *mask)
{
- int nss, max_nss = sta->deflink.rx_nss > 3 ? 4 : sta->deflink.rx_nss;
+ int nss, max_nss = link_sta->rx_nss > 3 ? 4 : link_sta->rx_nss;
for (nss = 0; nss < max_nss; nss++)
- ht_mcs[nss] = sta->deflink.ht_cap.mcs.rx_mask[nss] & mask[nss];
+ ht_mcs[nss] = link_sta->ht_cap.mcs.rx_mask[nss] & mask[nss];
}
static int
@@ -265,7 +268,7 @@ mt7996_mcu_send_message(struct mt76_dev *mdev, struct sk_buff *skb,
txd_len = cmd & __MCU_CMD_FIELD_UNI ? sizeof(*uni_txd) : sizeof(*mcu_txd);
txd = (__le32 *)skb_push(skb, txd_len);
- if (test_bit(MT76_STATE_MCU_RUNNING, &dev->mphy.state))
+ if (test_bit(MT76_STATE_MCU_RUNNING, &dev->mphy.state) && mt7996_has_wa(dev))
qid = MT_MCUQ_WA;
else
qid = MT_MCUQ_WM;
@@ -335,8 +338,12 @@ exit:
int mt7996_mcu_wa_cmd(struct mt7996_dev *dev, int cmd, u32 a1, u32 a2, u32 a3)
{
struct {
+ u8 _rsv[4];
+
+ __le16 tag;
+ __le16 len;
__le32 args[3];
- } req = {
+ } __packed req = {
.args = {
cpu_to_le32(a1),
cpu_to_le32(a2),
@@ -344,7 +351,16 @@ int mt7996_mcu_wa_cmd(struct mt7996_dev *dev, int cmd, u32 a1, u32 a2, u32 a3)
},
};
- return mt76_mcu_send_msg(&dev->mt76, cmd, &req, sizeof(req), false);
+ if (mt7996_has_wa(dev))
+ return mt76_mcu_send_msg(&dev->mt76, cmd, &req.args,
+ sizeof(req.args), false);
+
+ req.tag = cpu_to_le16(cmd == MCU_WA_PARAM_CMD(QUERY) ? UNI_CMD_SDO_QUERY :
+ UNI_CMD_SDO_SET);
+ req.len = cpu_to_le16(sizeof(req) - 4);
+
+ return mt76_mcu_send_msg(&dev->mt76, MCU_WA_UNI_CMD(SDO), &req,
+ sizeof(req), false);
}
static void
@@ -364,21 +380,27 @@ mt7996_mcu_rx_radar_detected(struct mt7996_dev *dev, struct sk_buff *skb)
r = (struct mt7996_mcu_rdd_report *)skb->data;
- if (r->band_idx >= ARRAY_SIZE(dev->mt76.phys))
- return;
-
- if (r->band_idx == MT_RX_SEL2 && !dev->rdd2_phy)
- return;
-
- if (r->band_idx == MT_RX_SEL2)
+ switch (r->rdd_idx) {
+ case MT_RDD_IDX_BAND2:
+ mphy = dev->mt76.phys[MT_BAND2];
+ break;
+ case MT_RDD_IDX_BAND1:
+ mphy = dev->mt76.phys[MT_BAND1];
+ break;
+ case MT_RDD_IDX_BACKGROUND:
+ if (!dev->rdd2_phy)
+ return;
mphy = dev->rdd2_phy->mt76;
- else
- mphy = dev->mt76.phys[r->band_idx];
+ break;
+ default:
+ dev_err(dev->mt76.dev, "Unknown RDD idx %d\n", r->rdd_idx);
+ return;
+ }
if (!mphy)
return;
- if (r->band_idx == MT_RX_SEL2)
+ if (r->rdd_idx == MT_RDD_IDX_BACKGROUND)
cfg80211_background_radar_event(mphy->hw->wiphy,
&dev->rdd2_chandef,
GFP_ATOMIC);
@@ -1064,7 +1086,8 @@ __mt7996_mcu_alloc_bss_req(struct mt76_dev *dev, struct mt76_vif_link *mvif, int
int mt7996_mcu_add_bss_info(struct mt7996_phy *phy, struct ieee80211_vif *vif,
struct ieee80211_bss_conf *link_conf,
- struct mt76_vif_link *mlink, int enable)
+ struct mt76_vif_link *mlink,
+ struct mt7996_sta_link *msta_link, int enable)
{
struct mt7996_dev *dev = phy->dev;
struct sk_buff *skb;
@@ -1081,7 +1104,7 @@ int mt7996_mcu_add_bss_info(struct mt7996_phy *phy, struct ieee80211_vif *vif,
/* bss_basic must be first */
mt7996_mcu_bss_basic_tlv(skb, vif, link_conf, mlink, phy->mt76,
- mlink->wcid->idx, enable);
+ msta_link->wcid.idx, enable);
mt7996_mcu_bss_sec_tlv(skb, mlink);
if (vif->type == NL80211_IFTYPE_MONITOR)
@@ -1159,37 +1182,34 @@ mt7996_mcu_sta_ba(struct mt7996_dev *dev, struct mt76_vif_link *mvif,
/** starec & wtbl **/
int mt7996_mcu_add_tx_ba(struct mt7996_dev *dev,
struct ieee80211_ampdu_params *params,
- bool enable)
+ struct mt7996_vif_link *link,
+ struct mt7996_sta_link *msta_link, bool enable)
{
- struct mt7996_sta *msta = (struct mt7996_sta *)params->sta->drv_priv;
- struct mt7996_vif *mvif = msta->vif;
-
if (enable && !params->amsdu)
- msta->wcid.amsdu = false;
+ msta_link->wcid.amsdu = false;
- return mt7996_mcu_sta_ba(dev, &mvif->deflink.mt76, params, enable, true);
+ return mt7996_mcu_sta_ba(dev, &link->mt76, params, enable, true);
}
int mt7996_mcu_add_rx_ba(struct mt7996_dev *dev,
struct ieee80211_ampdu_params *params,
- bool enable)
+ struct mt7996_vif_link *link, bool enable)
{
- struct mt7996_sta *msta = (struct mt7996_sta *)params->sta->drv_priv;
- struct mt7996_vif *mvif = msta->vif;
-
- return mt7996_mcu_sta_ba(dev, &mvif->deflink.mt76, params, enable, false);
+ return mt7996_mcu_sta_ba(dev, &link->mt76, params, enable, false);
}
static void
-mt7996_mcu_sta_he_tlv(struct sk_buff *skb, struct ieee80211_sta *sta)
+mt7996_mcu_sta_he_tlv(struct sk_buff *skb,
+ struct ieee80211_link_sta *link_sta,
+ struct mt7996_vif_link *link)
{
- struct ieee80211_he_cap_elem *elem = &sta->deflink.he_cap.he_cap_elem;
+ struct ieee80211_he_cap_elem *elem = &link_sta->he_cap.he_cap_elem;
struct ieee80211_he_mcs_nss_supp mcs_map;
struct sta_rec_he_v2 *he;
struct tlv *tlv;
int i = 0;
- if (!sta->deflink.he_cap.has_he)
+ if (!link_sta->he_cap.has_he)
return;
tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_HE_V2, sizeof(*he));
@@ -1201,21 +1221,21 @@ mt7996_mcu_sta_he_tlv(struct sk_buff *skb, struct ieee80211_sta *sta)
he->he_phy_cap[i] = elem->phy_cap_info[i];
}
- mcs_map = sta->deflink.he_cap.he_mcs_nss_supp;
- switch (sta->deflink.bandwidth) {
+ mcs_map = link_sta->he_cap.he_mcs_nss_supp;
+ switch (link_sta->bandwidth) {
case IEEE80211_STA_RX_BW_160:
if (elem->phy_cap_info[0] &
IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_80PLUS80_MHZ_IN_5G)
- mt7996_mcu_set_sta_he_mcs(sta,
+ mt7996_mcu_set_sta_he_mcs(link_sta, link,
&he->max_nss_mcs[CMD_HE_MCS_BW8080],
le16_to_cpu(mcs_map.rx_mcs_80p80));
- mt7996_mcu_set_sta_he_mcs(sta,
+ mt7996_mcu_set_sta_he_mcs(link_sta, link,
&he->max_nss_mcs[CMD_HE_MCS_BW160],
le16_to_cpu(mcs_map.rx_mcs_160));
fallthrough;
default:
- mt7996_mcu_set_sta_he_mcs(sta,
+ mt7996_mcu_set_sta_he_mcs(link_sta, link,
&he->max_nss_mcs[CMD_HE_MCS_BW80],
le16_to_cpu(mcs_map.rx_mcs_80));
break;
@@ -1225,24 +1245,26 @@ mt7996_mcu_sta_he_tlv(struct sk_buff *skb, struct ieee80211_sta *sta)
}
static void
-mt7996_mcu_sta_he_6g_tlv(struct sk_buff *skb, struct ieee80211_sta *sta)
+mt7996_mcu_sta_he_6g_tlv(struct sk_buff *skb,
+ struct ieee80211_link_sta *link_sta)
{
struct sta_rec_he_6g_capa *he_6g;
struct tlv *tlv;
- if (!sta->deflink.he_6ghz_capa.capa)
+ if (!link_sta->he_6ghz_capa.capa)
return;
tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_HE_6G, sizeof(*he_6g));
he_6g = (struct sta_rec_he_6g_capa *)tlv;
- he_6g->capa = sta->deflink.he_6ghz_capa.capa;
+ he_6g->capa = link_sta->he_6ghz_capa.capa;
}
static void
-mt7996_mcu_sta_eht_tlv(struct sk_buff *skb, struct ieee80211_sta *sta)
+mt7996_mcu_sta_eht_tlv(struct sk_buff *skb,
+ struct ieee80211_link_sta *link_sta)
{
- struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
+ struct mt7996_sta *msta = (struct mt7996_sta *)link_sta->sta->drv_priv;
struct ieee80211_vif *vif = container_of((void *)msta->vif,
struct ieee80211_vif, drv_priv);
struct ieee80211_eht_mcs_nss_supp *mcs_map;
@@ -1250,11 +1272,11 @@ mt7996_mcu_sta_eht_tlv(struct sk_buff *skb, struct ieee80211_sta *sta)
struct sta_rec_eht *eht;
struct tlv *tlv;
- if (!sta->deflink.eht_cap.has_eht)
+ if (!link_sta->eht_cap.has_eht)
return;
- mcs_map = &sta->deflink.eht_cap.eht_mcs_nss_supp;
- elem = &sta->deflink.eht_cap.eht_cap_elem;
+ mcs_map = &link_sta->eht_cap.eht_mcs_nss_supp;
+ elem = &link_sta->eht_cap.eht_cap_elem;
tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_EHT, sizeof(*eht));
@@ -1265,7 +1287,7 @@ mt7996_mcu_sta_eht_tlv(struct sk_buff *skb, struct ieee80211_sta *sta)
eht->phy_cap_ext = cpu_to_le64(elem->phy_cap_info[8]);
if (vif->type != NL80211_IFTYPE_STATION &&
- (sta->deflink.he_cap.he_cap_elem.phy_cap_info[0] &
+ (link_sta->he_cap.he_cap_elem.phy_cap_info[0] &
(IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_IN_2G |
IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G |
IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_160MHZ_IN_5G |
@@ -1281,47 +1303,48 @@ mt7996_mcu_sta_eht_tlv(struct sk_buff *skb, struct ieee80211_sta *sta)
}
static void
-mt7996_mcu_sta_ht_tlv(struct sk_buff *skb, struct ieee80211_sta *sta)
+mt7996_mcu_sta_ht_tlv(struct sk_buff *skb, struct ieee80211_link_sta *link_sta)
{
struct sta_rec_ht_uni *ht;
struct tlv *tlv;
- if (!sta->deflink.ht_cap.ht_supported)
+ if (!link_sta->ht_cap.ht_supported)
return;
tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_HT, sizeof(*ht));
ht = (struct sta_rec_ht_uni *)tlv;
- ht->ht_cap = cpu_to_le16(sta->deflink.ht_cap.cap);
- ht->ampdu_param = u8_encode_bits(sta->deflink.ht_cap.ampdu_factor,
+ ht->ht_cap = cpu_to_le16(link_sta->ht_cap.cap);
+ ht->ampdu_param = u8_encode_bits(link_sta->ht_cap.ampdu_factor,
IEEE80211_HT_AMPDU_PARM_FACTOR) |
- u8_encode_bits(sta->deflink.ht_cap.ampdu_density,
+ u8_encode_bits(link_sta->ht_cap.ampdu_density,
IEEE80211_HT_AMPDU_PARM_DENSITY);
}
static void
-mt7996_mcu_sta_vht_tlv(struct sk_buff *skb, struct ieee80211_sta *sta)
+mt7996_mcu_sta_vht_tlv(struct sk_buff *skb, struct ieee80211_link_sta *link_sta)
{
struct sta_rec_vht *vht;
struct tlv *tlv;
/* For 6G band, this tlv is necessary to let hw work normally */
- if (!sta->deflink.he_6ghz_capa.capa && !sta->deflink.vht_cap.vht_supported)
+ if (!link_sta->he_6ghz_capa.capa && !link_sta->vht_cap.vht_supported)
return;
tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_VHT, sizeof(*vht));
vht = (struct sta_rec_vht *)tlv;
- vht->vht_cap = cpu_to_le32(sta->deflink.vht_cap.cap);
- vht->vht_rx_mcs_map = sta->deflink.vht_cap.vht_mcs.rx_mcs_map;
- vht->vht_tx_mcs_map = sta->deflink.vht_cap.vht_mcs.tx_mcs_map;
+ vht->vht_cap = cpu_to_le32(link_sta->vht_cap.cap);
+ vht->vht_rx_mcs_map = link_sta->vht_cap.vht_mcs.rx_mcs_map;
+ vht->vht_tx_mcs_map = link_sta->vht_cap.vht_mcs.tx_mcs_map;
}
static void
mt7996_mcu_sta_amsdu_tlv(struct mt7996_dev *dev, struct sk_buff *skb,
- struct ieee80211_vif *vif, struct ieee80211_sta *sta)
+ struct ieee80211_vif *vif,
+ struct ieee80211_link_sta *link_sta,
+ struct mt7996_sta_link *msta_link)
{
- struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
struct sta_rec_amsdu *amsdu;
struct tlv *tlv;
@@ -1330,16 +1353,16 @@ mt7996_mcu_sta_amsdu_tlv(struct mt7996_dev *dev, struct sk_buff *skb,
vif->type != NL80211_IFTYPE_AP)
return;
- if (!sta->deflink.agg.max_amsdu_len)
+ if (!link_sta->agg.max_amsdu_len)
return;
tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_HW_AMSDU, sizeof(*amsdu));
amsdu = (struct sta_rec_amsdu *)tlv;
amsdu->max_amsdu_num = 8;
amsdu->amsdu_en = true;
- msta->wcid.amsdu = true;
+ msta_link->wcid.amsdu = true;
- switch (sta->deflink.agg.max_amsdu_len) {
+ switch (link_sta->agg.max_amsdu_len) {
case IEEE80211_MAX_MPDU_LEN_VHT_11454:
amsdu->max_mpdu_size =
IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_11454;
@@ -1356,30 +1379,31 @@ mt7996_mcu_sta_amsdu_tlv(struct mt7996_dev *dev, struct sk_buff *skb,
static void
mt7996_mcu_sta_muru_tlv(struct mt7996_dev *dev, struct sk_buff *skb,
- struct ieee80211_vif *vif, struct ieee80211_sta *sta)
+ struct ieee80211_bss_conf *link_conf,
+ struct ieee80211_link_sta *link_sta)
{
- struct ieee80211_he_cap_elem *elem = &sta->deflink.he_cap.he_cap_elem;
+ struct ieee80211_he_cap_elem *elem = &link_sta->he_cap.he_cap_elem;
struct sta_rec_muru *muru;
struct tlv *tlv;
- if (vif->type != NL80211_IFTYPE_STATION &&
- vif->type != NL80211_IFTYPE_AP)
+ if (link_conf->vif->type != NL80211_IFTYPE_STATION &&
+ link_conf->vif->type != NL80211_IFTYPE_AP)
return;
tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_MURU, sizeof(*muru));
muru = (struct sta_rec_muru *)tlv;
- muru->cfg.mimo_dl_en = vif->bss_conf.eht_mu_beamformer ||
- vif->bss_conf.he_mu_beamformer ||
- vif->bss_conf.vht_mu_beamformer ||
- vif->bss_conf.vht_mu_beamformee;
+ muru->cfg.mimo_dl_en = link_conf->eht_mu_beamformer ||
+ link_conf->he_mu_beamformer ||
+ link_conf->vht_mu_beamformer ||
+ link_conf->vht_mu_beamformee;
muru->cfg.ofdma_dl_en = true;
- if (sta->deflink.vht_cap.vht_supported)
+ if (link_sta->vht_cap.vht_supported)
muru->mimo_dl.vht_mu_bfee =
- !!(sta->deflink.vht_cap.cap & IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE);
+ !!(link_sta->vht_cap.cap & IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE);
- if (!sta->deflink.he_cap.has_he)
+ if (!link_sta->he_cap.has_he)
return;
muru->mimo_dl.partial_bw_dl_mimo =
@@ -1410,49 +1434,50 @@ mt7996_mcu_sta_muru_tlv(struct mt7996_dev *dev, struct sk_buff *skb,
}
static inline bool
-mt7996_is_ebf_supported(struct mt7996_phy *phy, struct ieee80211_vif *vif,
- struct ieee80211_sta *sta, bool bfee)
+mt7996_is_ebf_supported(struct mt7996_phy *phy,
+ struct ieee80211_bss_conf *link_conf,
+ struct ieee80211_link_sta *link_sta, bool bfee)
{
int sts = hweight16(phy->mt76->chainmask);
- if (vif->type != NL80211_IFTYPE_STATION &&
- vif->type != NL80211_IFTYPE_AP)
+ if (link_conf->vif->type != NL80211_IFTYPE_STATION &&
+ link_conf->vif->type != NL80211_IFTYPE_AP)
return false;
if (!bfee && sts < 2)
return false;
- if (sta->deflink.eht_cap.has_eht) {
- struct ieee80211_sta_eht_cap *pc = &sta->deflink.eht_cap;
+ if (link_sta->eht_cap.has_eht) {
+ struct ieee80211_sta_eht_cap *pc = &link_sta->eht_cap;
struct ieee80211_eht_cap_elem_fixed *pe = &pc->eht_cap_elem;
if (bfee)
- return vif->bss_conf.eht_su_beamformee &&
+ return link_conf->eht_su_beamformee &&
EHT_PHY(CAP0_SU_BEAMFORMER, pe->phy_cap_info[0]);
else
- return vif->bss_conf.eht_su_beamformer &&
+ return link_conf->eht_su_beamformer &&
EHT_PHY(CAP0_SU_BEAMFORMEE, pe->phy_cap_info[0]);
}
- if (sta->deflink.he_cap.has_he) {
- struct ieee80211_he_cap_elem *pe = &sta->deflink.he_cap.he_cap_elem;
+ if (link_sta->he_cap.has_he) {
+ struct ieee80211_he_cap_elem *pe = &link_sta->he_cap.he_cap_elem;
if (bfee)
- return vif->bss_conf.he_su_beamformee &&
+ return link_conf->he_su_beamformee &&
HE_PHY(CAP3_SU_BEAMFORMER, pe->phy_cap_info[3]);
else
- return vif->bss_conf.he_su_beamformer &&
+ return link_conf->he_su_beamformer &&
HE_PHY(CAP4_SU_BEAMFORMEE, pe->phy_cap_info[4]);
}
- if (sta->deflink.vht_cap.vht_supported) {
- u32 cap = sta->deflink.vht_cap.cap;
+ if (link_sta->vht_cap.vht_supported) {
+ u32 cap = link_sta->vht_cap.cap;
if (bfee)
- return vif->bss_conf.vht_su_beamformee &&
+ return link_conf->vht_su_beamformee &&
(cap & IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE);
else
- return vif->bss_conf.vht_su_beamformer &&
+ return link_conf->vht_su_beamformer &&
(cap & IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE);
}
@@ -1473,10 +1498,11 @@ mt7996_mcu_sta_sounding_rate(struct sta_rec_bf *bf, struct mt7996_phy *phy)
}
static void
-mt7996_mcu_sta_bfer_ht(struct ieee80211_sta *sta, struct mt7996_phy *phy,
- struct sta_rec_bf *bf, bool explicit)
+mt7996_mcu_sta_bfer_ht(struct ieee80211_link_sta *link_sta,
+ struct mt7996_phy *phy, struct sta_rec_bf *bf,
+ bool explicit)
{
- struct ieee80211_mcs_info *mcs = &sta->deflink.ht_cap.mcs;
+ struct ieee80211_mcs_info *mcs = &link_sta->ht_cap.mcs;
u8 n = 0;
bf->tx_mode = MT_PHY_TYPE_HT;
@@ -1499,10 +1525,11 @@ mt7996_mcu_sta_bfer_ht(struct ieee80211_sta *sta, struct mt7996_phy *phy,
}
static void
-mt7996_mcu_sta_bfer_vht(struct ieee80211_sta *sta, struct mt7996_phy *phy,
- struct sta_rec_bf *bf, bool explicit)
+mt7996_mcu_sta_bfer_vht(struct ieee80211_link_sta *link_sta,
+ struct mt7996_phy *phy, struct sta_rec_bf *bf,
+ bool explicit)
{
- struct ieee80211_sta_vht_cap *pc = &sta->deflink.vht_cap;
+ struct ieee80211_sta_vht_cap *pc = &link_sta->vht_cap;
struct ieee80211_sta_vht_cap *vc = &phy->mt76->sband_5g.sband.vht_cap;
u16 mcs_map = le16_to_cpu(pc->vht_mcs.rx_mcs_map);
u8 nss_mcs = mt7996_mcu_get_sta_nss(mcs_map);
@@ -1523,24 +1550,24 @@ mt7996_mcu_sta_bfer_vht(struct ieee80211_sta *sta, struct mt7996_phy *phy,
bf->ncol = min_t(u8, nss_mcs, bf->nrow);
bf->ibf_ncol = min_t(u8, MT7996_IBF_MAX_NC, bf->ncol);
- if (sta->deflink.bandwidth == IEEE80211_STA_RX_BW_160)
+ if (link_sta->bandwidth == IEEE80211_STA_RX_BW_160)
bf->nrow = 1;
} else {
bf->nrow = tx_ant;
bf->ncol = min_t(u8, nss_mcs, bf->nrow);
bf->ibf_ncol = min_t(u8, MT7996_IBF_MAX_NC, nss_mcs);
- if (sta->deflink.bandwidth == IEEE80211_STA_RX_BW_160)
+ if (link_sta->bandwidth == IEEE80211_STA_RX_BW_160)
bf->ibf_nrow = 1;
}
}
static void
-mt7996_mcu_sta_bfer_he(struct ieee80211_sta *sta, struct ieee80211_vif *vif,
- struct mt7996_phy *phy, struct sta_rec_bf *bf,
- bool explicit)
+mt7996_mcu_sta_bfer_he(struct ieee80211_link_sta *link_sta,
+ struct ieee80211_vif *vif, struct mt7996_phy *phy,
+ struct sta_rec_bf *bf, bool explicit)
{
- struct ieee80211_sta_he_cap *pc = &sta->deflink.he_cap;
+ struct ieee80211_sta_he_cap *pc = &link_sta->he_cap;
struct ieee80211_he_cap_elem *pe = &pc->he_cap_elem;
const struct ieee80211_sta_he_cap *vc =
mt76_connac_get_he_phy_cap(phy->mt76, vif);
@@ -1569,7 +1596,7 @@ mt7996_mcu_sta_bfer_he(struct ieee80211_sta *sta, struct ieee80211_vif *vif,
bf->ibf_ncol = explicit ? min_t(u8, MT7996_IBF_MAX_NC, bf->ncol) :
min_t(u8, MT7996_IBF_MAX_NC, nss_mcs);
- if (sta->deflink.bandwidth != IEEE80211_STA_RX_BW_160)
+ if (link_sta->bandwidth != IEEE80211_STA_RX_BW_160)
return;
/* go over for 160MHz and 80p80 */
@@ -1601,11 +1628,11 @@ mt7996_mcu_sta_bfer_he(struct ieee80211_sta *sta, struct ieee80211_vif *vif,
}
static void
-mt7996_mcu_sta_bfer_eht(struct ieee80211_sta *sta, struct ieee80211_vif *vif,
- struct mt7996_phy *phy, struct sta_rec_bf *bf,
- bool explicit)
+mt7996_mcu_sta_bfer_eht(struct ieee80211_link_sta *link_sta,
+ struct ieee80211_vif *vif, struct mt7996_phy *phy,
+ struct sta_rec_bf *bf, bool explicit)
{
- struct ieee80211_sta_eht_cap *pc = &sta->deflink.eht_cap;
+ struct ieee80211_sta_eht_cap *pc = &link_sta->eht_cap;
struct ieee80211_eht_cap_elem_fixed *pe = &pc->eht_cap_elem;
struct ieee80211_eht_mcs_nss_supp *eht_nss = &pc->eht_mcs_nss_supp;
const struct ieee80211_sta_eht_cap *vc =
@@ -1629,10 +1656,10 @@ mt7996_mcu_sta_bfer_eht(struct ieee80211_sta *sta, struct ieee80211_vif *vif,
bf->ibf_ncol = explicit ? min_t(u8, MT7996_IBF_MAX_NC, bf->ncol) :
min_t(u8, MT7996_IBF_MAX_NC, nss_mcs);
- if (sta->deflink.bandwidth < IEEE80211_STA_RX_BW_160)
+ if (link_sta->bandwidth < IEEE80211_STA_RX_BW_160)
return;
- switch (sta->deflink.bandwidth) {
+ switch (link_sta->bandwidth) {
case IEEE80211_STA_RX_BW_160:
snd_dim = EHT_PHY(CAP2_SOUNDING_DIM_160MHZ_MASK, ve->phy_cap_info[2]);
sts = EHT_PHY(CAP1_BEAMFORMEE_SS_160MHZ_MASK, pe->phy_cap_info[1]);
@@ -1660,13 +1687,15 @@ mt7996_mcu_sta_bfer_eht(struct ieee80211_sta *sta, struct ieee80211_vif *vif,
static void
mt7996_mcu_sta_bfer_tlv(struct mt7996_dev *dev, struct sk_buff *skb,
- struct ieee80211_vif *vif, struct ieee80211_sta *sta)
+ struct ieee80211_bss_conf *link_conf,
+ struct ieee80211_link_sta *link_sta,
+ struct mt7996_vif_link *link)
{
#define EBF_MODE BIT(0)
#define IBF_MODE BIT(1)
#define BF_MAT_ORDER 4
- struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
- struct mt7996_phy *phy = mvif->deflink.phy;
+ struct ieee80211_vif *vif = link_conf->vif;
+ struct mt7996_phy *phy = link->phy;
int tx_ant = hweight16(phy->mt76->chainmask) - 1;
struct sta_rec_bf *bf;
struct tlv *tlv;
@@ -1678,10 +1707,10 @@ mt7996_mcu_sta_bfer_tlv(struct mt7996_dev *dev, struct sk_buff *skb,
};
bool ebf;
- if (!(sta->deflink.ht_cap.ht_supported || sta->deflink.he_cap.has_he))
+ if (!(link_sta->ht_cap.ht_supported || link_sta->he_cap.has_he))
return;
- ebf = mt7996_is_ebf_supported(phy, vif, sta, false);
+ ebf = mt7996_is_ebf_supported(phy, link_conf, link_sta, false);
if (!ebf && !dev->ibf)
return;
@@ -1692,28 +1721,29 @@ mt7996_mcu_sta_bfer_tlv(struct mt7996_dev *dev, struct sk_buff *skb,
* vht: support eBF and iBF
* ht: iBF only, since mac80211 lacks of eBF support
*/
- if (sta->deflink.eht_cap.has_eht)
- mt7996_mcu_sta_bfer_eht(sta, vif, phy, bf, ebf);
- else if (sta->deflink.he_cap.has_he)
- mt7996_mcu_sta_bfer_he(sta, vif, phy, bf, ebf);
- else if (sta->deflink.vht_cap.vht_supported)
- mt7996_mcu_sta_bfer_vht(sta, phy, bf, ebf);
- else if (sta->deflink.ht_cap.ht_supported)
- mt7996_mcu_sta_bfer_ht(sta, phy, bf, ebf);
+ if (link_sta->eht_cap.has_eht)
+ mt7996_mcu_sta_bfer_eht(link_sta, vif, link->phy, bf, ebf);
+ else if (link_sta->he_cap.has_he)
+ mt7996_mcu_sta_bfer_he(link_sta, vif, link->phy, bf, ebf);
+ else if (link_sta->vht_cap.vht_supported)
+ mt7996_mcu_sta_bfer_vht(link_sta, link->phy, bf, ebf);
+ else if (link_sta->ht_cap.ht_supported)
+ mt7996_mcu_sta_bfer_ht(link_sta, link->phy, bf, ebf);
else
return;
bf->bf_cap = ebf ? EBF_MODE : (dev->ibf ? IBF_MODE : 0);
if (is_mt7992(&dev->mt76) && tx_ant == 4)
bf->bf_cap |= IBF_MODE;
- bf->bw = sta->deflink.bandwidth;
- bf->ibf_dbw = sta->deflink.bandwidth;
+
+ bf->bw = link_sta->bandwidth;
+ bf->ibf_dbw = link_sta->bandwidth;
bf->ibf_nrow = tx_ant;
- if (sta->deflink.eht_cap.has_eht || sta->deflink.he_cap.has_he)
+ if (link_sta->eht_cap.has_eht || link_sta->he_cap.has_he)
bf->ibf_timeout = is_mt7996(&dev->mt76) ? MT7996_IBF_TIMEOUT :
MT7992_IBF_TIMEOUT;
- else if (!ebf && sta->deflink.bandwidth <= IEEE80211_STA_RX_BW_40 && !bf->ncol)
+ else if (!ebf && link_sta->bandwidth <= IEEE80211_STA_RX_BW_40 && !bf->ncol)
bf->ibf_timeout = MT7996_IBF_TIMEOUT_LEGACY;
else
bf->ibf_timeout = MT7996_IBF_TIMEOUT;
@@ -1727,7 +1757,7 @@ mt7996_mcu_sta_bfer_tlv(struct mt7996_dev *dev, struct sk_buff *skb,
matrix[bf->nrow][bf->ncol] : 0;
}
- switch (sta->deflink.bandwidth) {
+ switch (link_sta->bandwidth) {
case IEEE80211_STA_RX_BW_160:
case IEEE80211_STA_RX_BW_80:
bf->mem_total = bf->mem_20m * 2;
@@ -1743,31 +1773,32 @@ mt7996_mcu_sta_bfer_tlv(struct mt7996_dev *dev, struct sk_buff *skb,
static void
mt7996_mcu_sta_bfee_tlv(struct mt7996_dev *dev, struct sk_buff *skb,
- struct ieee80211_vif *vif, struct ieee80211_sta *sta)
+ struct ieee80211_bss_conf *link_conf,
+ struct ieee80211_link_sta *link_sta,
+ struct mt7996_vif_link *link)
{
- struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
- struct mt7996_phy *phy = mvif->deflink.phy;
+ struct mt7996_phy *phy = link->phy;
int tx_ant = hweight8(phy->mt76->antenna_mask) - 1;
struct sta_rec_bfee *bfee;
struct tlv *tlv;
u8 nrow = 0;
- if (!(sta->deflink.vht_cap.vht_supported || sta->deflink.he_cap.has_he))
+ if (!(link_sta->vht_cap.vht_supported || link_sta->he_cap.has_he))
return;
- if (!mt7996_is_ebf_supported(phy, vif, sta, true))
+ if (!mt7996_is_ebf_supported(phy, link_conf, link_sta, true))
return;
tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_BFEE, sizeof(*bfee));
bfee = (struct sta_rec_bfee *)tlv;
- if (sta->deflink.he_cap.has_he) {
- struct ieee80211_he_cap_elem *pe = &sta->deflink.he_cap.he_cap_elem;
+ if (link_sta->he_cap.has_he) {
+ struct ieee80211_he_cap_elem *pe = &link_sta->he_cap.he_cap_elem;
nrow = HE_PHY(CAP5_BEAMFORMEE_NUM_SND_DIM_UNDER_80MHZ_MASK,
pe->phy_cap_info[5]);
- } else if (sta->deflink.vht_cap.vht_supported) {
- struct ieee80211_sta_vht_cap *pc = &sta->deflink.vht_cap;
+ } else if (link_sta->vht_cap.vht_supported) {
+ struct ieee80211_sta_vht_cap *pc = &link_sta->vht_cap;
nrow = FIELD_GET(IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_MASK,
pc->cap);
@@ -1874,18 +1905,19 @@ int mt7996_mcu_set_fixed_rate_ctrl(struct mt7996_dev *dev,
MCU_WM_UNI_CMD(RA), true);
}
-int mt7996_mcu_set_fixed_field(struct mt7996_dev *dev, struct ieee80211_vif *vif,
- struct ieee80211_sta *sta, void *data, u32 field)
+int mt7996_mcu_set_fixed_field(struct mt7996_dev *dev,
+ struct ieee80211_link_sta *link_sta,
+ struct mt7996_vif_link *link,
+ struct mt7996_sta_link *msta_link,
+ void *data, u32 field)
{
- struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
- struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
struct sta_phy_uni *phy = data;
struct sta_rec_ra_fixed_uni *ra;
struct sk_buff *skb;
struct tlv *tlv;
- skb = __mt76_connac_mcu_alloc_sta_req(&dev->mt76, &mvif->deflink.mt76,
- &msta->wcid,
+ skb = __mt76_connac_mcu_alloc_sta_req(&dev->mt76, &link->mt76,
+ &msta_link->wcid,
MT7996_STA_UPDATE_MAX_SIZE);
if (IS_ERR(skb))
return PTR_ERR(skb);
@@ -1904,7 +1936,7 @@ int mt7996_mcu_set_fixed_field(struct mt7996_dev *dev, struct ieee80211_vif *vif
ra->phy = *phy;
break;
case RATE_PARAM_MMPS_UPDATE:
- ra->mmps_mode = mt7996_mcu_get_mmps_mode(sta->deflink.smps_mode);
+ ra->mmps_mode = mt7996_mcu_get_mmps_mode(link_sta->smps_mode);
break;
default:
break;
@@ -1916,12 +1948,13 @@ int mt7996_mcu_set_fixed_field(struct mt7996_dev *dev, struct ieee80211_vif *vif
}
static int
-mt7996_mcu_add_rate_ctrl_fixed(struct mt7996_dev *dev, struct ieee80211_vif *vif,
- struct ieee80211_sta *sta)
+mt7996_mcu_add_rate_ctrl_fixed(struct mt7996_dev *dev,
+ struct ieee80211_link_sta *link_sta,
+ struct mt7996_vif_link *link,
+ struct mt7996_sta_link *msta_link)
{
- struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
- struct cfg80211_chan_def *chandef = &mvif->deflink.phy->mt76->chandef;
- struct cfg80211_bitrate_mask *mask = &mvif->deflink.bitrate_mask;
+ struct cfg80211_chan_def *chandef = &link->phy->mt76->chandef;
+ struct cfg80211_bitrate_mask *mask = &link->bitrate_mask;
enum nl80211_band band = chandef->chan->band;
struct sta_phy_uni phy = {};
int ret, nrates = 0;
@@ -1942,11 +1975,11 @@ mt7996_mcu_add_rate_ctrl_fixed(struct mt7996_dev *dev, struct ieee80211_vif *vif
} \
} while (0)
- if (sta->deflink.he_cap.has_he) {
+ if (link_sta->he_cap.has_he) {
__sta_phy_bitrate_mask_check(he_mcs, he_gi, 0, 1);
- } else if (sta->deflink.vht_cap.vht_supported) {
+ } else if (link_sta->vht_cap.vht_supported) {
__sta_phy_bitrate_mask_check(vht_mcs, gi, 0, 0);
- } else if (sta->deflink.ht_cap.ht_supported) {
+ } else if (link_sta->ht_cap.ht_supported) {
__sta_phy_bitrate_mask_check(ht_mcs, gi, 1, 0);
} else {
nrates = hweight32(mask->control[band].legacy);
@@ -1963,7 +1996,8 @@ mt7996_mcu_add_rate_ctrl_fixed(struct mt7996_dev *dev, struct ieee80211_vif *vif
/* fixed single rate */
if (nrates == 1) {
- ret = mt7996_mcu_set_fixed_field(dev, vif, sta, &phy,
+ ret = mt7996_mcu_set_fixed_field(dev, link_sta, link,
+ msta_link, &phy,
RATE_PARAM_FIXED_MCS);
if (ret)
return ret;
@@ -1972,20 +2006,20 @@ mt7996_mcu_add_rate_ctrl_fixed(struct mt7996_dev *dev, struct ieee80211_vif *vif
/* fixed GI */
if (mask->control[band].gi != NL80211_TXRATE_DEFAULT_GI ||
mask->control[band].he_gi != GENMASK(7, 0)) {
- struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
u32 addr;
/* firmware updates only TXCMD but doesn't take WTBL into
* account, so driver should update here to reflect the
* actual txrate hardware sends out.
*/
- addr = mt7996_mac_wtbl_lmac_addr(dev, msta->wcid.idx, 7);
- if (sta->deflink.he_cap.has_he)
+ addr = mt7996_mac_wtbl_lmac_addr(dev, msta_link->wcid.idx, 7);
+ if (link_sta->he_cap.has_he)
mt76_rmw_field(dev, addr, GENMASK(31, 24), phy.sgi);
else
mt76_rmw_field(dev, addr, GENMASK(15, 12), phy.sgi);
- ret = mt7996_mcu_set_fixed_field(dev, vif, sta, &phy,
+ ret = mt7996_mcu_set_fixed_field(dev, link_sta, link,
+ msta_link, &phy,
RATE_PARAM_FIXED_GI);
if (ret)
return ret;
@@ -1993,7 +2027,8 @@ mt7996_mcu_add_rate_ctrl_fixed(struct mt7996_dev *dev, struct ieee80211_vif *vif
/* fixed HE_LTF */
if (mask->control[band].he_ltf != GENMASK(7, 0)) {
- ret = mt7996_mcu_set_fixed_field(dev, vif, sta, &phy,
+ ret = mt7996_mcu_set_fixed_field(dev, link_sta, link,
+ msta_link, &phy,
RATE_PARAM_FIXED_HE_LTF);
if (ret)
return ret;
@@ -2004,30 +2039,32 @@ mt7996_mcu_add_rate_ctrl_fixed(struct mt7996_dev *dev, struct ieee80211_vif *vif
static void
mt7996_mcu_sta_rate_ctrl_tlv(struct sk_buff *skb, struct mt7996_dev *dev,
- struct ieee80211_vif *vif, struct ieee80211_sta *sta)
+ struct ieee80211_vif *vif,
+ struct ieee80211_bss_conf *link_conf,
+ struct ieee80211_link_sta *link_sta,
+ struct mt7996_vif_link *link)
{
#define INIT_RCPI 180
- struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
- struct mt76_phy *mphy = mvif->deflink.phy->mt76;
+ struct mt76_phy *mphy = link->phy->mt76;
struct cfg80211_chan_def *chandef = &mphy->chandef;
- struct cfg80211_bitrate_mask *mask = &mvif->deflink.bitrate_mask;
+ struct cfg80211_bitrate_mask *mask = &link->bitrate_mask;
+ u32 cap = link_sta->sta->wme ? STA_CAP_WMM : 0;
enum nl80211_band band = chandef->chan->band;
struct sta_rec_ra_uni *ra;
struct tlv *tlv;
- u32 supp_rate = sta->deflink.supp_rates[band];
- u32 cap = sta->wme ? STA_CAP_WMM : 0;
+ u32 supp_rate = link_sta->supp_rates[band];
tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_RA, sizeof(*ra));
ra = (struct sta_rec_ra_uni *)tlv;
ra->valid = true;
ra->auto_rate = true;
- ra->phy_mode = mt76_connac_get_phy_mode(mphy, vif, band, &sta->deflink);
+ ra->phy_mode = mt76_connac_get_phy_mode(mphy, vif, band, link_sta);
ra->channel = chandef->chan->hw_value;
- ra->bw = (sta->deflink.bandwidth == IEEE80211_STA_RX_BW_320) ?
- CMD_CBW_320MHZ : sta->deflink.bandwidth;
+ ra->bw = (link_sta->bandwidth == IEEE80211_STA_RX_BW_320) ?
+ CMD_CBW_320MHZ : link_sta->bandwidth;
ra->phy.bw = ra->bw;
- ra->mmps_mode = mt7996_mcu_get_mmps_mode(sta->deflink.smps_mode);
+ ra->mmps_mode = mt7996_mcu_get_mmps_mode(link_sta->smps_mode);
if (supp_rate) {
supp_rate &= mask->control[band].legacy;
@@ -2047,60 +2084,60 @@ mt7996_mcu_sta_rate_ctrl_tlv(struct sk_buff *skb, struct mt7996_dev *dev,
}
}
- if (sta->deflink.ht_cap.ht_supported) {
+ if (link_sta->ht_cap.ht_supported) {
ra->supp_mode |= MODE_HT;
- ra->af = sta->deflink.ht_cap.ampdu_factor;
- ra->ht_gf = !!(sta->deflink.ht_cap.cap & IEEE80211_HT_CAP_GRN_FLD);
+ ra->af = link_sta->ht_cap.ampdu_factor;
+ ra->ht_gf = !!(link_sta->ht_cap.cap & IEEE80211_HT_CAP_GRN_FLD);
cap |= STA_CAP_HT;
- if (sta->deflink.ht_cap.cap & IEEE80211_HT_CAP_SGI_20)
+ if (link_sta->ht_cap.cap & IEEE80211_HT_CAP_SGI_20)
cap |= STA_CAP_SGI_20;
- if (sta->deflink.ht_cap.cap & IEEE80211_HT_CAP_SGI_40)
+ if (link_sta->ht_cap.cap & IEEE80211_HT_CAP_SGI_40)
cap |= STA_CAP_SGI_40;
- if (sta->deflink.ht_cap.cap & IEEE80211_HT_CAP_TX_STBC)
+ if (link_sta->ht_cap.cap & IEEE80211_HT_CAP_TX_STBC)
cap |= STA_CAP_TX_STBC;
- if (sta->deflink.ht_cap.cap & IEEE80211_HT_CAP_RX_STBC)
+ if (link_sta->ht_cap.cap & IEEE80211_HT_CAP_RX_STBC)
cap |= STA_CAP_RX_STBC;
- if (vif->bss_conf.ht_ldpc &&
- (sta->deflink.ht_cap.cap & IEEE80211_HT_CAP_LDPC_CODING))
+ if (link_conf->ht_ldpc &&
+ (link_sta->ht_cap.cap & IEEE80211_HT_CAP_LDPC_CODING))
cap |= STA_CAP_LDPC;
- mt7996_mcu_set_sta_ht_mcs(sta, ra->ht_mcs,
+ mt7996_mcu_set_sta_ht_mcs(link_sta, ra->ht_mcs,
mask->control[band].ht_mcs);
ra->supp_ht_mcs = *(__le32 *)ra->ht_mcs;
}
- if (sta->deflink.vht_cap.vht_supported) {
+ if (link_sta->vht_cap.vht_supported) {
u8 af;
ra->supp_mode |= MODE_VHT;
af = FIELD_GET(IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK,
- sta->deflink.vht_cap.cap);
+ link_sta->vht_cap.cap);
ra->af = max_t(u8, ra->af, af);
cap |= STA_CAP_VHT;
- if (sta->deflink.vht_cap.cap & IEEE80211_VHT_CAP_SHORT_GI_80)
+ if (link_sta->vht_cap.cap & IEEE80211_VHT_CAP_SHORT_GI_80)
cap |= STA_CAP_VHT_SGI_80;
- if (sta->deflink.vht_cap.cap & IEEE80211_VHT_CAP_SHORT_GI_160)
+ if (link_sta->vht_cap.cap & IEEE80211_VHT_CAP_SHORT_GI_160)
cap |= STA_CAP_VHT_SGI_160;
- if (sta->deflink.vht_cap.cap & IEEE80211_VHT_CAP_TXSTBC)
+ if (link_sta->vht_cap.cap & IEEE80211_VHT_CAP_TXSTBC)
cap |= STA_CAP_VHT_TX_STBC;
- if (sta->deflink.vht_cap.cap & IEEE80211_VHT_CAP_RXSTBC_1)
+ if (link_sta->vht_cap.cap & IEEE80211_VHT_CAP_RXSTBC_1)
cap |= STA_CAP_VHT_RX_STBC;
- if ((vif->type != NL80211_IFTYPE_AP || vif->bss_conf.vht_ldpc) &&
- (sta->deflink.vht_cap.cap & IEEE80211_VHT_CAP_RXLDPC))
+ if ((vif->type != NL80211_IFTYPE_AP || link_conf->vht_ldpc) &&
+ (link_sta->vht_cap.cap & IEEE80211_VHT_CAP_RXLDPC))
cap |= STA_CAP_VHT_LDPC;
- mt7996_mcu_set_sta_vht_mcs(sta, ra->supp_vht_mcs,
+ mt7996_mcu_set_sta_vht_mcs(link_sta, ra->supp_vht_mcs,
mask->control[band].vht_mcs);
}
- if (sta->deflink.he_cap.has_he) {
+ if (link_sta->he_cap.has_he) {
ra->supp_mode |= MODE_HE;
cap |= STA_CAP_HE;
- if (sta->deflink.he_6ghz_capa.capa)
- ra->af = le16_get_bits(sta->deflink.he_6ghz_capa.capa,
+ if (link_sta->he_6ghz_capa.capa)
+ ra->af = le16_get_bits(link_sta->he_6ghz_capa.capa,
IEEE80211_HE_6GHZ_CAP_MAX_AMPDU_LEN_EXP);
}
ra->sta_cap = cpu_to_le32(cap);
@@ -2108,16 +2145,18 @@ mt7996_mcu_sta_rate_ctrl_tlv(struct sk_buff *skb, struct mt7996_dev *dev,
memset(ra->rx_rcpi, INIT_RCPI, sizeof(ra->rx_rcpi));
}
-int mt7996_mcu_add_rate_ctrl(struct mt7996_dev *dev, struct ieee80211_vif *vif,
- struct ieee80211_sta *sta, bool changed)
+int mt7996_mcu_add_rate_ctrl(struct mt7996_dev *dev,
+ struct ieee80211_vif *vif,
+ struct ieee80211_bss_conf *link_conf,
+ struct ieee80211_link_sta *link_sta,
+ struct mt7996_vif_link *link,
+ struct mt7996_sta_link *msta_link, bool changed)
{
- struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
- struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
struct sk_buff *skb;
int ret;
- skb = __mt76_connac_mcu_alloc_sta_req(&dev->mt76, &mvif->deflink.mt76,
- &msta->wcid,
+ skb = __mt76_connac_mcu_alloc_sta_req(&dev->mt76, &link->mt76,
+ &msta_link->wcid,
MT7996_STA_UPDATE_MAX_SIZE);
if (IS_ERR(skb))
return PTR_ERR(skb);
@@ -2127,19 +2166,19 @@ int mt7996_mcu_add_rate_ctrl(struct mt7996_dev *dev, struct ieee80211_vif *vif,
* update sta_rec_he here.
*/
if (changed)
- mt7996_mcu_sta_he_tlv(skb, sta);
+ mt7996_mcu_sta_he_tlv(skb, link_sta, link);
/* sta_rec_ra accommodates BW, NSS and only MCS range format
* i.e 0-{7,8,9} for VHT.
*/
- mt7996_mcu_sta_rate_ctrl_tlv(skb, dev, vif, sta);
+ mt7996_mcu_sta_rate_ctrl_tlv(skb, dev, vif, link_conf, link_sta, link);
ret = mt76_mcu_skb_send_msg(&dev->mt76, skb,
MCU_WMWA_UNI_CMD(STA_REC_UPDATE), true);
if (ret)
return ret;
- return mt7996_mcu_add_rate_ctrl_fixed(dev, vif, sta);
+ return mt7996_mcu_add_rate_ctrl_fixed(dev, link_sta, link, msta_link);
}
static int
@@ -2148,6 +2187,7 @@ mt7996_mcu_add_group(struct mt7996_dev *dev, struct ieee80211_vif *vif,
{
#define MT_STA_BSS_GROUP 1
struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+ struct mt7996_sta_link *msta_link;
struct mt7996_sta *msta;
struct {
u8 __rsv1[4];
@@ -2166,73 +2206,151 @@ mt7996_mcu_add_group(struct mt7996_dev *dev, struct ieee80211_vif *vif,
.val = cpu_to_le32(mvif->deflink.mt76.idx % 16),
};
- msta = sta ? (struct mt7996_sta *)sta->drv_priv : &mvif->deflink.sta;
- req.wlan_idx = cpu_to_le16(msta->wcid.idx);
+ msta = sta ? (struct mt7996_sta *)sta->drv_priv : NULL;
+ msta_link = msta ? &msta->deflink : &mvif->deflink.msta_link;
+ req.wlan_idx = cpu_to_le16(msta_link->wcid.idx);
return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(VOW), &req,
sizeof(req), true);
}
-int mt7996_mcu_add_sta(struct mt7996_dev *dev, struct ieee80211_vif *vif,
- struct mt76_vif_link *mlink,
- struct ieee80211_sta *sta, int conn_state, bool newly)
+static void
+mt7996_mcu_sta_mld_setup_tlv(struct mt7996_dev *dev, struct sk_buff *skb,
+ struct ieee80211_sta *sta)
{
- struct ieee80211_link_sta *link_sta = NULL;
- struct mt76_wcid *wcid = mlink->wcid;
- struct sk_buff *skb;
- int ret;
+ struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
+ unsigned long links = sta->valid_links;
+ unsigned int nlinks = hweight16(links);
+ struct mld_setup_link *mld_setup_link;
+ struct sta_rec_mld_setup *mld_setup;
+ struct mt7996_sta_link *msta_link;
+ struct ieee80211_vif *vif;
+ unsigned int link_id;
+ struct tlv *tlv;
- if (sta) {
- struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
+ msta_link = mt76_dereference(msta->link[msta->deflink_id], &dev->mt76);
+ if (!msta_link)
+ return;
+
+ tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_MLD,
+ sizeof(struct sta_rec_mld_setup) +
+ sizeof(struct mld_setup_link) * nlinks);
- wcid = &msta->wcid;
- link_sta = &sta->deflink;
+ mld_setup = (struct sta_rec_mld_setup *)tlv;
+ memcpy(mld_setup->mld_addr, sta->addr, ETH_ALEN);
+ mld_setup->setup_wcid = cpu_to_le16(msta_link->wcid.idx);
+ mld_setup->primary_id = cpu_to_le16(msta_link->wcid.idx);
+
+ if (nlinks > 1) {
+ link_id = __ffs(links & ~BIT(msta->deflink_id));
+ msta_link = mt76_dereference(msta->link[msta->deflink_id],
+ &dev->mt76);
+ if (!msta_link)
+ return;
}
+ mld_setup->seconed_id = cpu_to_le16(msta_link->wcid.idx);
+ mld_setup->link_num = nlinks;
- skb = __mt76_connac_mcu_alloc_sta_req(&dev->mt76, mlink, wcid,
+ vif = container_of((void *)msta->vif, struct ieee80211_vif, drv_priv);
+ mld_setup_link = (struct mld_setup_link *)mld_setup->link_info;
+ for_each_set_bit(link_id, &links, IEEE80211_MLD_MAX_NUM_LINKS) {
+ struct mt7996_vif_link *link;
+
+ msta_link = mt76_dereference(msta->link[link_id], &dev->mt76);
+ if (!msta_link)
+ continue;
+
+ link = mt7996_vif_link(dev, vif, link_id);
+ if (!link)
+ continue;
+
+ mld_setup_link->wcid = cpu_to_le16(msta_link->wcid.idx);
+ mld_setup_link->bss_idx = link->mt76.idx;
+ mld_setup_link++;
+ }
+}
+
+static void
+mt7996_mcu_sta_eht_mld_tlv(struct mt7996_dev *dev, struct sk_buff *skb,
+ struct ieee80211_sta *sta)
+{
+ struct sta_rec_eht_mld *eht_mld;
+ struct tlv *tlv;
+ int i;
+
+ tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_EHT_MLD, sizeof(*eht_mld));
+ eht_mld = (struct sta_rec_eht_mld *)tlv;
+
+ for (i = 0; i < ARRAY_SIZE(eht_mld->str_cap); i++)
+ eht_mld->str_cap[i] = 0x7;
+}
+
+int mt7996_mcu_add_sta(struct mt7996_dev *dev,
+ struct ieee80211_bss_conf *link_conf,
+ struct ieee80211_link_sta *link_sta,
+ struct mt7996_vif_link *link,
+ struct mt7996_sta_link *msta_link,
+ int conn_state, bool newly)
+{
+ struct mt76_wcid *wcid = msta_link ? &msta_link->wcid : link->mt76.wcid;
+ struct ieee80211_sta *sta = link_sta ? link_sta->sta : NULL;
+ struct sk_buff *skb;
+ int ret;
+
+ skb = __mt76_connac_mcu_alloc_sta_req(&dev->mt76, &link->mt76, wcid,
MT7996_STA_UPDATE_MAX_SIZE);
if (IS_ERR(skb))
return PTR_ERR(skb);
/* starec basic */
- mt76_connac_mcu_sta_basic_tlv(&dev->mt76, skb, &vif->bss_conf, link_sta,
+ mt76_connac_mcu_sta_basic_tlv(&dev->mt76, skb, link_conf, link_sta,
conn_state, newly);
if (conn_state == CONN_STATE_DISCONNECT)
goto out;
/* starec hdr trans */
- mt7996_mcu_sta_hdr_trans_tlv(dev, skb, vif, wcid);
+ mt7996_mcu_sta_hdr_trans_tlv(dev, skb, link_conf->vif, wcid);
/* starec tx proc */
mt7996_mcu_sta_tx_proc_tlv(skb);
/* tag order is in accordance with firmware dependency. */
- if (sta) {
+ if (link_sta) {
/* starec hdrt mode */
mt7996_mcu_sta_hdrt_tlv(dev, skb);
- /* starec bfer */
- mt7996_mcu_sta_bfer_tlv(dev, skb, vif, sta);
+ if (conn_state == CONN_STATE_CONNECT) {
+ /* starec bfer */
+ mt7996_mcu_sta_bfer_tlv(dev, skb, link_conf, link_sta,
+ link);
+ /* starec bfee */
+ mt7996_mcu_sta_bfee_tlv(dev, skb, link_conf, link_sta,
+ link);
+ }
/* starec ht */
- mt7996_mcu_sta_ht_tlv(skb, sta);
+ mt7996_mcu_sta_ht_tlv(skb, link_sta);
/* starec vht */
- mt7996_mcu_sta_vht_tlv(skb, sta);
+ mt7996_mcu_sta_vht_tlv(skb, link_sta);
/* starec uapsd */
- mt76_connac_mcu_sta_uapsd(skb, vif, sta);
+ mt76_connac_mcu_sta_uapsd(skb, link_conf->vif, sta);
/* starec amsdu */
- mt7996_mcu_sta_amsdu_tlv(dev, skb, vif, sta);
+ mt7996_mcu_sta_amsdu_tlv(dev, skb, link_conf->vif, link_sta,
+ msta_link);
/* starec he */
- mt7996_mcu_sta_he_tlv(skb, sta);
+ mt7996_mcu_sta_he_tlv(skb, link_sta, link);
/* starec he 6g*/
- mt7996_mcu_sta_he_6g_tlv(skb, sta);
+ mt7996_mcu_sta_he_6g_tlv(skb, link_sta);
/* starec eht */
- mt7996_mcu_sta_eht_tlv(skb, sta);
+ mt7996_mcu_sta_eht_tlv(skb, link_sta);
/* starec muru */
- mt7996_mcu_sta_muru_tlv(dev, skb, vif, sta);
- /* starec bfee */
- mt7996_mcu_sta_bfee_tlv(dev, skb, vif, sta);
+ mt7996_mcu_sta_muru_tlv(dev, skb, link_conf, link_sta);
+
+ if (sta->mlo) {
+ mt7996_mcu_sta_mld_setup_tlv(dev, skb, sta);
+ mt7996_mcu_sta_eht_mld_tlv(dev, skb, sta);
+ }
}
- ret = mt7996_mcu_add_group(dev, vif, sta);
+ ret = mt7996_mcu_add_group(dev, link_conf->vif, sta);
if (ret) {
dev_kfree_skb(skb);
return ret;
@@ -2242,6 +2360,24 @@ out:
MCU_WMWA_UNI_CMD(STA_REC_UPDATE), true);
}
+int mt7996_mcu_teardown_mld_sta(struct mt7996_dev *dev,
+ struct mt7996_vif_link *link,
+ struct mt7996_sta_link *msta_link)
+{
+ struct sk_buff *skb;
+
+ skb = __mt76_connac_mcu_alloc_sta_req(&dev->mt76, &link->mt76,
+ &msta_link->wcid,
+ MT7996_STA_UPDATE_MAX_SIZE);
+ if (IS_ERR(skb))
+ return PTR_ERR(skb);
+
+ mt76_connac_mcu_add_tlv(skb, STA_REC_MLD_OFF, sizeof(struct tlv));
+
+ return mt76_mcu_skb_send_msg(&dev->mt76, skb,
+ MCU_WMWA_UNI_CMD(STA_REC_UPDATE), true);
+}
+
static int
mt7996_mcu_sta_key_tlv(struct mt76_wcid *wcid,
struct sk_buff *skb,
@@ -2307,17 +2443,18 @@ int mt7996_mcu_add_key(struct mt76_dev *dev, struct ieee80211_vif *vif,
return mt76_mcu_skb_send_msg(dev, skb, mcu_cmd, true);
}
-static int mt7996_mcu_get_pn(struct mt7996_dev *dev, struct ieee80211_vif *vif,
- u8 *pn)
+static int mt7996_mcu_get_pn(struct mt7996_dev *dev,
+ struct mt7996_vif_link *link,
+ struct mt7996_sta_link *msta_link, u8 *pn)
{
#define TSC_TYPE_BIGTK_PN 2
- struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
struct sta_rec_pn_info *pn_info;
struct sk_buff *skb, *rskb;
struct tlv *tlv;
int ret;
- skb = mt76_connac_mcu_alloc_sta_req(&dev->mt76, &mvif->deflink.mt76, &mvif->deflink.sta.wcid);
+ skb = mt76_connac_mcu_alloc_sta_req(&dev->mt76, &link->mt76,
+ &msta_link->wcid);
if (IS_ERR(skb))
return PTR_ERR(skb);
@@ -2341,10 +2478,11 @@ static int mt7996_mcu_get_pn(struct mt7996_dev *dev, struct ieee80211_vif *vif,
return 0;
}
-int mt7996_mcu_bcn_prot_enable(struct mt7996_dev *dev, struct ieee80211_vif *vif,
+int mt7996_mcu_bcn_prot_enable(struct mt7996_dev *dev,
+ struct mt7996_vif_link *link,
+ struct mt7996_sta_link *msta_link,
struct ieee80211_key_conf *key)
{
- struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
struct mt7996_mcu_bcn_prot_tlv *bcn_prot;
struct sk_buff *skb;
struct tlv *tlv;
@@ -2353,7 +2491,7 @@ int mt7996_mcu_bcn_prot_enable(struct mt7996_dev *dev, struct ieee80211_vif *vif
sizeof(struct mt7996_mcu_bcn_prot_tlv);
int ret;
- skb = __mt7996_mcu_alloc_bss_req(&dev->mt76, &mvif->deflink.mt76, len);
+ skb = __mt7996_mcu_alloc_bss_req(&dev->mt76, &link->mt76, len);
if (IS_ERR(skb))
return PTR_ERR(skb);
@@ -2361,7 +2499,7 @@ int mt7996_mcu_bcn_prot_enable(struct mt7996_dev *dev, struct ieee80211_vif *vif
bcn_prot = (struct mt7996_mcu_bcn_prot_tlv *)tlv;
- ret = mt7996_mcu_get_pn(dev, vif, pn);
+ ret = mt7996_mcu_get_pn(dev, link, msta_link, pn);
if (ret) {
dev_kfree_skb(skb);
return ret;
@@ -2592,13 +2730,14 @@ out:
}
int mt7996_mcu_beacon_inband_discov(struct mt7996_dev *dev,
- struct ieee80211_vif *vif, u32 changed)
+ struct ieee80211_bss_conf *link_conf,
+ struct mt7996_vif_link *link, u32 changed)
{
#define OFFLOAD_TX_MODE_SU BIT(0)
#define OFFLOAD_TX_MODE_MU BIT(1)
+ struct ieee80211_vif *vif = link_conf->vif;
struct ieee80211_hw *hw = mt76_hw(dev);
- struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
- struct mt7996_phy *phy = mt7996_vif_link_phy(&mvif->deflink);
+ struct mt7996_phy *phy = link->phy;
struct mt76_wcid *wcid = &dev->mt76.global_wcid;
struct bss_inband_discovery_tlv *discov;
struct ieee80211_tx_info *info;
@@ -2615,21 +2754,21 @@ int mt7996_mcu_beacon_inband_discov(struct mt7996_dev *dev,
chandef = &phy->mt76->chandef;
band = chandef->chan->band;
- if (vif->bss_conf.nontransmitted)
+ if (link_conf->nontransmitted)
return 0;
- rskb = __mt7996_mcu_alloc_bss_req(&dev->mt76, &mvif->deflink.mt76,
+ rskb = __mt7996_mcu_alloc_bss_req(&dev->mt76, &link->mt76,
MT7996_MAX_BSS_OFFLOAD_SIZE);
if (IS_ERR(rskb))
return PTR_ERR(rskb);
if (changed & BSS_CHANGED_FILS_DISCOVERY &&
- vif->bss_conf.fils_discovery.max_interval) {
- interval = vif->bss_conf.fils_discovery.max_interval;
+ link_conf->fils_discovery.max_interval) {
+ interval = link_conf->fils_discovery.max_interval;
skb = ieee80211_get_fils_discovery_tmpl(hw, vif);
} else if (changed & BSS_CHANGED_UNSOL_BCAST_PROBE_RESP &&
- vif->bss_conf.unsol_bcast_probe_resp_interval) {
- interval = vif->bss_conf.unsol_bcast_probe_resp_interval;
+ link_conf->unsol_bcast_probe_resp_interval) {
+ interval = link_conf->unsol_bcast_probe_resp_interval;
skb = ieee80211_get_unsol_bcast_probe_resp_tmpl(hw, vif);
}
@@ -2891,6 +3030,9 @@ static int mt7996_load_ram(struct mt7996_dev *dev)
if (ret)
return ret;
+ if (!mt7996_has_wa(dev))
+ return 0;
+
ret = __mt7996_load_ram(dev, "DSP", fw_name(dev, FIRMWARE_DSP),
MT7996_RAM_TYPE_DSP);
if (ret)
@@ -2901,10 +3043,9 @@ static int mt7996_load_ram(struct mt7996_dev *dev)
}
static int
-mt7996_firmware_state(struct mt7996_dev *dev, bool wa)
+mt7996_firmware_state(struct mt7996_dev *dev, u8 fw_state)
{
- u32 state = FIELD_PREP(MT_TOP_MISC_FW_STATE,
- wa ? FW_STATE_RDY : FW_STATE_FW_DOWNLOAD);
+ u32 state = FIELD_PREP(MT_TOP_MISC_FW_STATE, fw_state);
if (!mt76_poll_msec(dev, MT_TOP_MISC, MT_TOP_MISC_FW_STATE,
state, 1000)) {
@@ -2936,13 +3077,14 @@ mt7996_mcu_restart(struct mt76_dev *dev)
static int mt7996_load_firmware(struct mt7996_dev *dev)
{
+ u8 fw_state;
int ret;
/* make sure fw is download state */
- if (mt7996_firmware_state(dev, false)) {
+ if (mt7996_firmware_state(dev, FW_STATE_FW_DOWNLOAD)) {
/* restart firmware once */
mt7996_mcu_restart(&dev->mt76);
- ret = mt7996_firmware_state(dev, false);
+ ret = mt7996_firmware_state(dev, FW_STATE_FW_DOWNLOAD);
if (ret) {
dev_err(dev->mt76.dev,
"Firmware is not ready for download\n");
@@ -2958,7 +3100,8 @@ static int mt7996_load_firmware(struct mt7996_dev *dev)
if (ret)
return ret;
- ret = mt7996_firmware_state(dev, true);
+ fw_state = mt7996_has_wa(dev) ? FW_STATE_RDY : FW_STATE_NORMAL_TRX;
+ ret = mt7996_firmware_state(dev, fw_state);
if (ret)
return ret;
@@ -3096,13 +3239,15 @@ int mt7996_mcu_init_firmware(struct mt7996_dev *dev)
if (ret)
return ret;
- ret = mt7996_mcu_fw_log_2_host(dev, MCU_FW_LOG_WA, 0);
- if (ret)
- return ret;
+ if (mt7996_has_wa(dev)) {
+ ret = mt7996_mcu_fw_log_2_host(dev, MCU_FW_LOG_WA, 0);
+ if (ret)
+ return ret;
- ret = mt7996_mcu_set_mwds(dev, 1);
- if (ret)
- return ret;
+ ret = mt7996_mcu_set_mwds(dev, 1);
+ if (ret)
+ return ret;
+ }
ret = mt7996_mcu_init_rx_airtime(dev);
if (ret)
@@ -3128,7 +3273,7 @@ int mt7996_mcu_init(struct mt7996_dev *dev)
void mt7996_mcu_exit(struct mt7996_dev *dev)
{
mt7996_mcu_restart(&dev->mt76);
- if (mt7996_firmware_state(dev, false)) {
+ if (mt7996_firmware_state(dev, FW_STATE_FW_DOWNLOAD)) {
dev_err(dev->mt76.dev, "Failed to exit mcu\n");
goto out;
}
@@ -3415,11 +3560,10 @@ int mt7996_mcu_rdd_background_enable(struct mt7996_phy *phy,
struct cfg80211_chan_def *chandef)
{
struct mt7996_dev *dev = phy->dev;
- int err, region;
+ int err, region, rdd_idx = mt7996_get_rdd_idx(phy, true);
if (!chandef) { /* disable offchain */
- err = mt7996_mcu_rdd_cmd(dev, RDD_STOP, MT_RX_SEL2,
- 0, 0);
+ err = mt7996_mcu_rdd_cmd(dev, RDD_STOP, rdd_idx, 0);
if (err)
return err;
@@ -3445,8 +3589,7 @@ int mt7996_mcu_rdd_background_enable(struct mt7996_phy *phy,
break;
}
- return mt7996_mcu_rdd_cmd(dev, RDD_START, MT_RX_SEL2,
- 0, region);
+ return mt7996_mcu_rdd_cmd(dev, RDD_START, rdd_idx, region);
}
int mt7996_mcu_set_chan_info(struct mt7996_phy *phy, u16 tag)
@@ -4068,12 +4211,12 @@ mt7996_mcu_set_obss_spr_pd(struct mt7996_phy *phy,
}
static int
-mt7996_mcu_set_obss_spr_siga(struct mt7996_phy *phy, struct ieee80211_vif *vif,
+mt7996_mcu_set_obss_spr_siga(struct mt7996_phy *phy,
+ struct mt7996_vif_link *link,
struct ieee80211_he_obss_pd *he_obss_pd)
{
- struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
struct mt7996_dev *dev = phy->dev;
- u8 omac = mvif->deflink.mt76.omac_idx;
+ u8 omac = link->mt76.omac_idx;
struct {
u8 band_idx;
u8 __rsv[3];
@@ -4145,7 +4288,8 @@ mt7996_mcu_set_obss_spr_bitmap(struct mt7996_phy *phy,
sizeof(req), true);
}
-int mt7996_mcu_add_obss_spr(struct mt7996_phy *phy, struct ieee80211_vif *vif,
+int mt7996_mcu_add_obss_spr(struct mt7996_phy *phy,
+ struct mt7996_vif_link *link,
struct ieee80211_he_obss_pd *he_obss_pd)
{
int ret;
@@ -4179,7 +4323,7 @@ int mt7996_mcu_add_obss_spr(struct mt7996_phy *phy, struct ieee80211_vif *vif,
return ret;
/* Set SR prohibit */
- ret = mt7996_mcu_set_obss_spr_siga(phy, vif, he_obss_pd);
+ ret = mt7996_mcu_set_obss_spr_siga(phy, link, he_obss_pd);
if (ret)
return ret;
@@ -4215,7 +4359,7 @@ int mt7996_mcu_update_bss_color(struct mt7996_dev *dev,
#define TWT_AGRT_PROTECT BIT(2)
int mt7996_mcu_twt_agrt_update(struct mt7996_dev *dev,
- struct mt7996_vif *mvif,
+ struct mt7996_vif_link *link,
struct mt7996_twt_flow *flow,
int cmd)
{
@@ -4246,12 +4390,12 @@ int mt7996_mcu_twt_agrt_update(struct mt7996_dev *dev,
.len = cpu_to_le16(sizeof(req) - 4),
.tbl_idx = flow->table_id,
.cmd = cmd,
- .own_mac_idx = mvif->deflink.mt76.omac_idx,
+ .own_mac_idx = link->mt76.omac_idx,
.flowid = flow->id,
.peer_id = cpu_to_le16(flow->wcid),
.duration = flow->duration,
- .bss = mvif->deflink.mt76.idx,
- .bss_idx = mvif->deflink.mt76.idx,
+ .bss = link->mt76.idx,
+ .bss_idx = link->mt76.idx,
.start_tsf = cpu_to_le64(flow->tsf),
.mantissa = flow->mantissa,
.exponent = flow->exp,
@@ -4312,8 +4456,7 @@ int mt7996_mcu_set_radio_en(struct mt7996_phy *phy, bool enable)
&req, sizeof(req), true);
}
-int mt7996_mcu_rdd_cmd(struct mt7996_dev *dev, int cmd, u8 index,
- u8 rx_sel, u8 val)
+int mt7996_mcu_rdd_cmd(struct mt7996_dev *dev, int cmd, u8 rdd_idx, u8 val)
{
struct {
u8 _rsv[4];
@@ -4330,8 +4473,7 @@ int mt7996_mcu_rdd_cmd(struct mt7996_dev *dev, int cmd, u8 index,
.tag = cpu_to_le16(UNI_RDD_CTRL_PARM),
.len = cpu_to_le16(sizeof(req) - 4),
.ctrl = cmd,
- .rdd_idx = index,
- .rdd_rx_sel = rx_sel,
+ .rdd_idx = rdd_idx,
.val = val,
};
@@ -4341,22 +4483,19 @@ int mt7996_mcu_rdd_cmd(struct mt7996_dev *dev, int cmd, u8 index,
int mt7996_mcu_wtbl_update_hdr_trans(struct mt7996_dev *dev,
struct ieee80211_vif *vif,
- struct ieee80211_sta *sta)
+ struct mt7996_vif_link *link,
+ struct mt7996_sta_link *msta_link)
{
- struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
- struct mt7996_sta *msta;
struct sk_buff *skb;
- msta = sta ? (struct mt7996_sta *)sta->drv_priv : &mvif->deflink.sta;
-
- skb = __mt76_connac_mcu_alloc_sta_req(&dev->mt76, &mvif->deflink.mt76,
- &msta->wcid,
+ skb = __mt76_connac_mcu_alloc_sta_req(&dev->mt76, &link->mt76,
+ &msta_link->wcid,
MT7996_STA_UPDATE_MAX_SIZE);
if (IS_ERR(skb))
return PTR_ERR(skb);
/* starec hdr trans */
- mt7996_mcu_sta_hdr_trans_tlv(dev, skb, vif, &msta->wcid);
+ mt7996_mcu_sta_hdr_trans_tlv(dev, skb, vif, &msta_link->wcid);
return mt76_mcu_skb_send_msg(&dev->mt76, skb,
MCU_WMWA_UNI_CMD(STA_REC_UPDATE), true);
}
@@ -4579,7 +4718,7 @@ int mt7996_mcu_set_txpower_sku(struct mt7996_phy *phy)
struct sk_buff *skb;
int i, tx_power;
- tx_power = mt7996_get_power_bound(phy, phy->txpower);
+ tx_power = mt76_get_power_bound(mphy, phy->txpower);
tx_power = mt76_get_rate_power_limits(mphy, mphy->chandef.chan,
&la, tx_power);
mphy->txpower_cur = tx_power;
@@ -4624,7 +4763,26 @@ int mt7996_mcu_cp_support(struct mt7996_dev *dev, u8 mode)
mode > mt76_connac_lmac_mapping(IEEE80211_AC_VO))
return -EINVAL;
+ if (!mt7996_has_wa(dev)) {
+ struct {
+ u8 _rsv[4];
+
+ __le16 tag;
+ __le16 len;
+ u8 cp_mode;
+ u8 rsv[3];
+ } __packed req = {
+ .tag = cpu_to_le16(UNI_CMD_SDO_CP_MODE),
+ .len = cpu_to_le16(sizeof(req) - 4),
+ .cp_mode = mode,
+ };
+
+ return mt76_mcu_send_msg(&dev->mt76, MCU_WA_UNI_CMD(SDO),
+ &req, sizeof(req), false);
+ }
+
cp_mode = cpu_to_le32(mode);
+
return mt76_mcu_send_msg(&dev->mt76, MCU_WA_EXT_CMD(CP_SUPPORT),
&cp_mode, sizeof(cp_mode), true);
}
diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mcu.h b/drivers/net/wireless/mediatek/mt76/mt7996/mcu.h
index 43468bcaffc6..130ea95626d5 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7996/mcu.h
+++ b/drivers/net/wireless/mediatek/mt76/mt7996/mcu.h
@@ -69,7 +69,7 @@ struct mt7996_mcu_rdd_report {
__le16 tag;
__le16 len;
- u8 band_idx;
+ u8 rdd_idx;
u8 long_detected;
u8 constant_prf_detected;
u8 staggered_prf_detected;
@@ -625,6 +625,35 @@ struct sta_rec_hdr_trans {
u8 mesh;
} __packed;
+struct sta_rec_mld_setup {
+ __le16 tag;
+ __le16 len;
+ u8 mld_addr[ETH_ALEN];
+ __le16 primary_id;
+ __le16 seconed_id;
+ __le16 setup_wcid;
+ u8 link_num;
+ u8 info;
+ u8 __rsv[2];
+ u8 link_info[];
+} __packed;
+
+struct sta_rec_eht_mld {
+ __le16 tag;
+ __le16 len;
+ u8 nsep;
+ u8 __rsv1[2];
+ u8 str_cap[__MT_MAX_BAND];
+ __le16 eml_cap;
+ u8 __rsv2[4];
+} __packed;
+
+struct mld_setup_link {
+ __le16 wcid;
+ u8 bss_idx;
+ u8 __rsv;
+} __packed;
+
struct hdr_trans_en {
__le16 tag;
__le16 len;
@@ -798,29 +827,20 @@ enum {
sizeof(struct sta_rec_eht) + \
sizeof(struct sta_rec_hdrt) + \
sizeof(struct sta_rec_hdr_trans) + \
+ sizeof(struct sta_rec_mld_setup) + \
+ sizeof(struct mld_setup_link) * 3 + \
+ sizeof(struct sta_rec_eht_mld) + \
sizeof(struct tlv))
-#define MT7996_MAX_BEACON_SIZE 1338
#define MT7996_BEACON_UPDATE_SIZE (sizeof(struct bss_req_hdr) + \
sizeof(struct bss_bcn_content_tlv) + \
4 + MT_TXD_SIZE + \
sizeof(struct bss_bcn_cntdwn_tlv) + \
sizeof(struct bss_bcn_mbss_tlv))
-#define MT7996_MAX_BSS_OFFLOAD_SIZE (MT7996_MAX_BEACON_SIZE + \
+#define MT7996_MAX_BSS_OFFLOAD_SIZE 2048
+#define MT7996_MAX_BEACON_SIZE (MT7996_MAX_BSS_OFFLOAD_SIZE - \
MT7996_BEACON_UPDATE_SIZE)
-static inline s8
-mt7996_get_power_bound(struct mt7996_phy *phy, s8 txpower)
-{
- struct mt76_phy *mphy = phy->mt76;
- int n_chains = hweight16(mphy->chainmask);
-
- txpower = mt76_get_sar_power(mphy, mphy->chandef.chan, txpower * 2);
- txpower -= mt76_tx_power_nss_delta(n_chains);
-
- return txpower;
-}
-
enum {
UNI_BAND_CONFIG_RADIO_ENABLE,
UNI_BAND_CONFIG_RTS_THRESHOLD = 0x08,
@@ -908,7 +928,8 @@ enum {
UNI_CMD_SER_SET_RECOVER_L3_TX_DISABLE,
UNI_CMD_SER_SET_RECOVER_L3_BF,
UNI_CMD_SER_SET_RECOVER_L4_MDP,
- UNI_CMD_SER_SET_RECOVER_FULL,
+ UNI_CMD_SER_SET_RECOVER_FROM_ETH,
+ UNI_CMD_SER_SET_RECOVER_FULL = 8,
UNI_CMD_SER_SET_SYSTEM_ASSERT,
/* action */
UNI_CMD_SER_ENABLE = 1,
@@ -917,6 +938,12 @@ enum {
};
enum {
+ UNI_CMD_SDO_SET = 1,
+ UNI_CMD_SDO_QUERY,
+ UNI_CMD_SDO_CP_MODE = 6,
+};
+
+enum {
MT7996_SEC_MODE_PLAIN,
MT7996_SEC_MODE_AES,
MT7996_SEC_MODE_SCRAMBLE,
diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mmio.c b/drivers/net/wireless/mediatek/mt76/mt7996/mmio.c
index 7a8ee6c75cf2..30b40f4a91be 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7996/mmio.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7996/mmio.c
@@ -54,6 +54,17 @@ static const u32 mt7996_offs[] = {
[MIB_BSCR7] = 0x9e8,
[MIB_BSCR17] = 0xa10,
[MIB_TRDR1] = 0xa28,
+ [HIF_REMAP_L1] = 0x24,
+ [HIF_REMAP_BASE_L1] = 0x130000,
+ [HIF_REMAP_L2] = 0x1b4,
+ [HIF_REMAP_BASE_L2] = 0x1000,
+ [CBTOP1_PHY_END] = 0x77ffffff,
+ [INFRA_MCU_END] = 0x7c3fffff,
+ [WTBLON_WDUCR] = 0x370,
+ [WTBL_UPDATE] = 0x380,
+ [WTBL_ITCR] = 0x3b0,
+ [WTBL_ITCR0] = 0x3b8,
+ [WTBL_ITCR1] = 0x3bc,
};
static const u32 mt7992_offs[] = {
@@ -80,6 +91,54 @@ static const u32 mt7992_offs[] = {
[MIB_BSCR7] = 0xae4,
[MIB_BSCR17] = 0xb0c,
[MIB_TRDR1] = 0xb24,
+ [HIF_REMAP_L1] = 0x8,
+ [HIF_REMAP_BASE_L1] = 0x40000,
+ [HIF_REMAP_L2] = 0x1b4,
+ [HIF_REMAP_BASE_L2] = 0x1000,
+ [CBTOP1_PHY_END] = 0x77ffffff,
+ [INFRA_MCU_END] = 0x7c3fffff,
+ [WTBLON_WDUCR] = 0x370,
+ [WTBL_UPDATE] = 0x380,
+ [WTBL_ITCR] = 0x3b0,
+ [WTBL_ITCR0] = 0x3b8,
+ [WTBL_ITCR1] = 0x3bc,
+};
+
+static const u32 mt7990_offs[] = {
+ [MIB_RVSR0] = 0x800,
+ [MIB_RVSR1] = 0x804,
+ [MIB_BTSCR5] = 0x868,
+ [MIB_BTSCR6] = 0x878,
+ [MIB_RSCR1] = 0x890,
+ [MIB_RSCR27] = 0xa38,
+ [MIB_RSCR28] = 0xa3c,
+ [MIB_RSCR29] = 0xa40,
+ [MIB_RSCR30] = 0xa44,
+ [MIB_RSCR31] = 0xa48,
+ [MIB_RSCR33] = 0xa50,
+ [MIB_RSCR35] = 0xa58,
+ [MIB_RSCR36] = 0xa5c,
+ [MIB_BSCR0] = 0xbb8,
+ [MIB_BSCR1] = 0xbbc,
+ [MIB_BSCR2] = 0xbc0,
+ [MIB_BSCR3] = 0xbc4,
+ [MIB_BSCR4] = 0xbc8,
+ [MIB_BSCR5] = 0xbcc,
+ [MIB_BSCR6] = 0xbd0,
+ [MIB_BSCR7] = 0xbd4,
+ [MIB_BSCR17] = 0xbfc,
+ [MIB_TRDR1] = 0xc14,
+ [HIF_REMAP_L1] = 0x8,
+ [HIF_REMAP_BASE_L1] = 0x40000,
+ [HIF_REMAP_L2] = 0x1b8,
+ [HIF_REMAP_BASE_L2] = 0x110000,
+ [CBTOP1_PHY_END] = 0x7fffffff,
+ [INFRA_MCU_END] = 0x7cffffff,
+ [WTBLON_WDUCR] = 0x400,
+ [WTBL_UPDATE] = 0x410,
+ [WTBL_ITCR] = 0x440,
+ [WTBL_ITCR0] = 0x448,
+ [WTBL_ITCR1] = 0x44c,
};
static const struct __map mt7996_reg_map[] = {
@@ -135,14 +194,83 @@ static const struct __map mt7996_reg_map[] = {
{ 0x0, 0x0, 0x0 }, /* imply end of search */
};
+static const struct __map mt7990_reg_map[] = {
+ {0x54000000, 0x02000, 0x1000}, /* WFDMA_0 (PCIE0 MCU DMA0) */
+ {0x55000000, 0x03000, 0x1000}, /* WFDMA_1 (PCIE0 MCU DMA1) */
+ {0x56000000, 0x04000, 0x1000}, /* WFDMA_2 (Reserved) */
+ {0x57000000, 0x05000, 0x1000}, /* WFDMA_3 (MCU wrap CR) */
+ {0x58000000, 0x06000, 0x1000}, /* WFDMA_4 (PCIE1 MCU DMA0 (MEM_DMA)) */
+ {0x59000000, 0x07000, 0x1000}, /* WFDMA_5 (PCIE1 MCU DMA1) */
+ {0x820c0000, 0x08000, 0x4000}, /* WF_UMAC_TOP (PLE) */
+ {0x820c8000, 0x0c000, 0x2000}, /* WF_UMAC_TOP (PSE) */
+ {0x820cc000, 0x0e000, 0x2000}, /* WF_UMAC_TOP (PP) */
+ {0x820e0000, 0x20000, 0x0400}, /* WF_LMAC_TOP BN0 (WF_CFG) */
+ {0x820e1000, 0x20400, 0x0200}, /* WF_LMAC_TOP BN0 (WF_TRB) */
+ {0x820e2000, 0x20800, 0x0400}, /* WF_LMAC_TOP BN0 (WF_AGG) */
+ {0x820e3000, 0x20c00, 0x0400}, /* WF_LMAC_TOP BN0 (WF_ARB) */
+ {0x820e4000, 0x21000, 0x0400}, /* WF_LMAC_TOP BN0 (WF_TMAC) */
+ {0x820e5000, 0x21400, 0x0800}, /* WF_LMAC_TOP BN0 (WF_RMAC) */
+ {0x820ce000, 0x21c00, 0x0200}, /* WF_LMAC_TOP (WF_SEC) */
+ {0x820e7000, 0x21e00, 0x0200}, /* WF_LMAC_TOP BN0 (WF_DMA) */
+ {0x820cf000, 0x22000, 0x1000}, /* WF_LMAC_TOP (WF_PF) */
+ {0x820e9000, 0x23400, 0x0200}, /* WF_LMAC_TOP BN0 (WF_WTBLOFF) */
+ {0x820ea000, 0x24000, 0x0200}, /* WF_LMAC_TOP BN0 (WF_ETBF) */
+ {0x820eb000, 0x24200, 0x0400}, /* WF_LMAC_TOP BN0 (WF_LPON) */
+ {0x820ec000, 0x24600, 0x0200}, /* WF_LMAC_TOP BN0 (WF_INT) */
+ {0x820ed000, 0x24800, 0x0800}, /* WF_LMAC_TOP BN0 (WF_MIB) */
+ {0x820ca000, 0x26000, 0x2000}, /* WF_LMAC_TOP BN0 (WF_MUCOP) */
+ {0x820d0000, 0x30000, 0x10000}, /* WF_LMAC_TOP (WF_WTBLON) */
+ {0x00400000, 0x80000, 0x10000}, /* WF_MCU_SYSRAM */
+ {0x820f0000, 0xa0000, 0x0400}, /* WF_LMAC_TOP BN1 (WF_CFG) */
+ {0x820f1000, 0xa0600, 0x0200}, /* WF_LMAC_TOP BN1 (WF_TRB) */
+ {0x820f2000, 0xa0800, 0x0400}, /* WF_LMAC_TOP BN1 (WF_AGG) */
+ {0x820f3000, 0xa0c00, 0x0400}, /* WF_LMAC_TOP BN1 (WF_ARB) */
+ {0x820f4000, 0xa1000, 0x0400}, /* WF_LMAC_TOP BN1 (WF_TMAC) */
+ {0x820f5000, 0xa1400, 0x0800}, /* WF_LMAC_TOP BN1 (WF_RMAC) */
+ {0x820f7000, 0xa1e00, 0x0200}, /* WF_LMAC_TOP BN1 (WF_DMA) */
+ {0x820f9000, 0xa3400, 0x0200}, /* WF_LMAC_TOP BN1 (WF_WTBLOFF) */
+ {0x820fa000, 0xa4000, 0x0200}, /* WF_LMAC_TOP BN1 (WF_ETBF) */
+ {0x820fb000, 0xa4200, 0x0400}, /* WF_LMAC_TOP BN1 (WF_LPON) */
+ {0x820fc000, 0xa4600, 0x0200}, /* WF_LMAC_TOP BN1 (WF_INT) */
+ {0x820fd000, 0xa4800, 0x0800}, /* WF_LMAC_TOP BN1 (WF_MIB) */
+ {0x820cc000, 0xa5000, 0x2000}, /* WF_LMAC_TOP BN1 (WF_MUCOP) */
+ {0x820c4000, 0xa8000, 0x4000}, /* WF_LMAC_TOP (WF_UWTBL) */
+ {0x81030000, 0xae000, 0x100}, /* WFSYS_AON part 1 */
+ {0x81031000, 0xae100, 0x100}, /* WFSYS_AON part 2 */
+ {0x81032000, 0xae200, 0x100}, /* WFSYS_AON part 3 */
+ {0x81033000, 0xae300, 0x100}, /* WFSYS_AON part 4 */
+ {0x81034000, 0xae400, 0x100}, /* WFSYS_AON part 5 */
+ {0x80020000, 0xb0000, 0x10000}, /* WF_TOP_MISC_OFF */
+ {0x81020000, 0xc0000, 0x10000}, /* WF_TOP_MISC_ON */
+ {0x81040000, 0x120000, 0x1000}, /* WF_MCU_CFG_ON */
+ {0x81050000, 0x121000, 0x1000}, /* WF_MCU_EINT */
+ {0x81060000, 0x122000, 0x1000}, /* WF_MCU_GPT */
+ {0x81070000, 0x123000, 0x1000}, /* WF_MCU_WDT */
+ {0x80010000, 0x124000, 0x1000}, /* WF_AXIDMA */
+ {0x7c020000, 0xd0000, 0x10000}, /* CONN_INFRA, wfdma for from CODA flow use */
+ {0x7c060000, 0xe0000, 0x10000}, /* CONN_INFRA, conn_host_csr_top for from CODA flow use */
+ {0x20020000, 0xd0000, 0x10000}, /* CONN_INFRA, wfdma */
+ {0x20060000, 0xe0000, 0x10000}, /* CONN_INFRA, conn_host_csr_top */
+ {0x7c000000, 0xf0000, 0x10000}, /* CONN_INFRA */
+ {0x70020000, 0x1f0000, 0x9000}, /* PCIE remapping (AP2CONN) */
+ {0x0, 0x0, 0x0}, /* imply end of search */
+};
+
static u32 mt7996_reg_map_l1(struct mt7996_dev *dev, u32 addr)
{
u32 offset = FIELD_GET(MT_HIF_REMAP_L1_OFFSET, addr);
u32 base = FIELD_GET(MT_HIF_REMAP_L1_BASE, addr);
+ u32 l1_mask, val;
- dev->bus_ops->rmw(&dev->mt76, MT_HIF_REMAP_L1,
- MT_HIF_REMAP_L1_MASK,
- FIELD_PREP(MT_HIF_REMAP_L1_MASK, base));
+ if (is_mt7996(&dev->mt76)) {
+ l1_mask = MT_HIF_REMAP_L1_MASK_7996;
+ val = FIELD_PREP(MT_HIF_REMAP_L1_MASK_7996, base);
+ } else {
+ l1_mask = MT_HIF_REMAP_L1_MASK;
+ val = FIELD_PREP(MT_HIF_REMAP_L1_MASK, base);
+ }
+
+ dev->bus_ops->rmw(&dev->mt76, MT_HIF_REMAP_L1, l1_mask, val);
/* use read to push write */
dev->bus_ops->rr(&dev->mt76, MT_HIF_REMAP_L1);
@@ -151,18 +279,41 @@ static u32 mt7996_reg_map_l1(struct mt7996_dev *dev, u32 addr)
static u32 mt7996_reg_map_l2(struct mt7996_dev *dev, u32 addr)
{
- u32 offset = FIELD_GET(MT_HIF_REMAP_L2_OFFSET, addr);
- u32 base = FIELD_GET(MT_HIF_REMAP_L2_BASE, addr);
+ u32 offset, base, l2_mask, val;
- dev->bus_ops->rmw(&dev->mt76, MT_HIF_REMAP_L2,
- MT_HIF_REMAP_L2_MASK,
- FIELD_PREP(MT_HIF_REMAP_L2_MASK, base));
+ if (is_mt7990(&dev->mt76)) {
+ offset = FIELD_GET(MT_HIF_REMAP_L2_OFFSET_7990, addr);
+ base = FIELD_GET(MT_HIF_REMAP_L2_BASE_7990, addr);
+ l2_mask = MT_HIF_REMAP_L2_MASK_7990;
+ val = FIELD_PREP(MT_HIF_REMAP_L2_MASK_7990, base);
+ } else {
+ offset = FIELD_GET(MT_HIF_REMAP_L2_OFFSET, addr);
+ base = FIELD_GET(MT_HIF_REMAP_L2_BASE, addr);
+ l2_mask = MT_HIF_REMAP_L2_MASK;
+ val = FIELD_PREP(MT_HIF_REMAP_L2_MASK, base);
+ }
+
+ dev->bus_ops->rmw(&dev->mt76, MT_HIF_REMAP_L2, l2_mask, val);
/* use read to push write */
dev->bus_ops->rr(&dev->mt76, MT_HIF_REMAP_L2);
return MT_HIF_REMAP_BASE_L2 + offset;
}
+static u32 mt7996_reg_map_cbtop(struct mt7996_dev *dev, u32 addr)
+{
+ u32 offset = FIELD_GET(MT_HIF_REMAP_CBTOP_OFFSET, addr);
+ u32 base = FIELD_GET(MT_HIF_REMAP_CBTOP_BASE, addr);
+
+ dev->bus_ops->rmw(&dev->mt76, MT_HIF_REMAP_CBTOP,
+ MT_HIF_REMAP_CBTOP_MASK,
+ FIELD_PREP(MT_HIF_REMAP_CBTOP_MASK, base));
+ /* use read to push write */
+ dev->bus_ops->rr(&dev->mt76, MT_HIF_REMAP_CBTOP);
+
+ return MT_HIF_REMAP_BASE_CBTOP + offset;
+}
+
static u32 __mt7996_reg_addr(struct mt7996_dev *dev, u32 addr)
{
int i;
@@ -193,17 +344,20 @@ static u32 __mt7996_reg_remap_addr(struct mt7996_dev *dev, u32 addr)
(addr >= MT_WFSYS1_PHY_START && addr <= MT_WFSYS1_PHY_END))
return mt7996_reg_map_l1(dev, addr);
- if (dev_is_pci(dev->mt76.dev) &&
- ((addr >= MT_CBTOP1_PHY_START && addr <= MT_CBTOP1_PHY_END) ||
- addr >= MT_CBTOP2_PHY_START))
- return mt7996_reg_map_l1(dev, addr);
-
/* CONN_INFRA: covert to phyiscal addr and use layer 1 remap */
if (addr >= MT_INFRA_MCU_START && addr <= MT_INFRA_MCU_END) {
addr = addr - MT_INFRA_MCU_START + MT_INFRA_BASE;
return mt7996_reg_map_l1(dev, addr);
}
+ if (dev_is_pci(dev->mt76.dev) &&
+ ((addr >= MT_CBTOP1_PHY_START && addr <= MT_CBTOP1_PHY_END) ||
+ addr >= MT_CBTOP2_PHY_START)) {
+ if (is_mt7990(&dev->mt76))
+ return mt7996_reg_map_cbtop(dev, addr);
+ return mt7996_reg_map_l1(dev, addr);
+ }
+
return mt7996_reg_map_l2(dev, addr);
}
@@ -281,7 +435,7 @@ static int mt7996_mmio_wed_reset(struct mtk_wed_device *wed)
if (test_and_set_bit(MT76_STATE_WED_RESET, &mphy->state))
return -EBUSY;
- ret = mt7996_mcu_set_ser(dev, UNI_CMD_SER_TRIGGER, UNI_CMD_SER_SET_RECOVER_L1,
+ ret = mt7996_mcu_set_ser(dev, UNI_CMD_SER_TRIGGER, UNI_CMD_SER_SET_RECOVER_FROM_ETH,
mphy->band_idx);
if (ret)
goto out;
@@ -323,6 +477,9 @@ int mt7996_mmio_wed_init(struct mt7996_dev *dev, void *pdev_ptr,
wed->wlan.base = devm_ioremap(dev->mt76.dev,
pci_resource_start(pci_dev, 0),
pci_resource_len(pci_dev, 0));
+ if (!wed->wlan.base)
+ return -ENOMEM;
+
wed->wlan.phy_base = pci_resource_start(pci_dev, 0);
if (hif2) {
@@ -350,7 +507,7 @@ int mt7996_mmio_wed_init(struct mt7996_dev *dev, void *pdev_ptr,
MT_RXQ_RING_BASE(MT7996_RXQ_BAND0) +
MT7996_RXQ_BAND0 * MT_RING_SIZE;
- wed->wlan.id = 0x7991;
+ wed->wlan.id = MT7996_DEVICE_ID_2;
wed->wlan.tx_tbit[0] = ffs(MT_INT_TX_DONE_BAND2) - 1;
} else {
wed->wlan.hw_rro = dev->has_rro; /* default on */
@@ -443,18 +600,24 @@ static int mt7996_mmio_init(struct mt76_dev *mdev,
spin_lock_init(&dev->reg_lock);
switch (device_id) {
- case 0x7990:
+ case MT7996_DEVICE_ID:
dev->reg.base = mt7996_reg_base;
dev->reg.offs_rev = mt7996_offs;
dev->reg.map = mt7996_reg_map;
dev->reg.map_size = ARRAY_SIZE(mt7996_reg_map);
break;
- case 0x7992:
+ case MT7992_DEVICE_ID:
dev->reg.base = mt7996_reg_base;
dev->reg.offs_rev = mt7992_offs;
dev->reg.map = mt7996_reg_map;
dev->reg.map_size = ARRAY_SIZE(mt7996_reg_map);
break;
+ case MT7990_DEVICE_ID:
+ dev->reg.base = mt7996_reg_base;
+ dev->reg.offs_rev = mt7990_offs;
+ dev->reg.map = mt7990_reg_map;
+ dev->reg.map_size = ARRAY_SIZE(mt7990_reg_map);
+ break;
default:
return -EINVAL;
}
@@ -618,9 +781,6 @@ struct mt7996_dev *mt7996_mmio_probe(struct device *pdev,
.rx_skb = mt7996_queue_rx_skb,
.rx_check = mt7996_rx_check,
.rx_poll_complete = mt7996_rx_poll_complete,
- .sta_add = mt7996_mac_sta_add,
- .sta_event = mt7996_mac_sta_event,
- .sta_remove = mt7996_mac_sta_remove,
.update_survey = mt7996_update_channel,
.set_channel = mt7996_set_channel,
.vif_link_add = mt7996_vif_link_add,
diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h b/drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h
index 29fabb9b04ae..1ad6bc046f7c 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h
+++ b/drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h
@@ -14,7 +14,7 @@
#define MT7996_MAX_RADIOS 3
#define MT7996_MAX_INTERFACES 19 /* per-band */
#define MT7996_MAX_WMM_SETS 4
-#define MT7996_WTBL_BMC_SIZE (is_mt7992(&dev->mt76) ? 32 : 64)
+#define MT7996_WTBL_BMC_SIZE (is_mt7996(&dev->mt76) ? 64 : 32)
#define MT7996_WTBL_RESERVED (mt7996_wtbl_size(dev) - 1)
#define MT7996_WTBL_STA (MT7996_WTBL_RESERVED - \
mt7996_max_interface_num(dev))
@@ -29,6 +29,16 @@
#define MT7996_RX_RING_SIZE 1536
#define MT7996_RX_MCU_RING_SIZE 512
#define MT7996_RX_MCU_RING_SIZE_WA 1024
+/* scatter-gather of mcu event is not supported in connac3 */
+#define MT7996_RX_MCU_BUF_SIZE (2048 + \
+ SKB_DATA_ALIGN(sizeof(struct skb_shared_info)))
+
+#define MT7996_DEVICE_ID 0x7990
+#define MT7996_DEVICE_ID_2 0x7991
+#define MT7992_DEVICE_ID 0x7992
+#define MT7992_DEVICE_ID_2 0x799a
+#define MT7990_DEVICE_ID 0x7993
+#define MT7990_DEVICE_ID_2 0x799b
#define MT7996_FIRMWARE_WA "mediatek/mt7996/mt7996_wa.bin"
#define MT7996_FIRMWARE_WM "mediatek/mt7996/mt7996_wm.bin"
@@ -50,6 +60,11 @@
#define MT7992_FIRMWARE_DSP_23 "mediatek/mt7996/mt7992_dsp_23.bin"
#define MT7992_ROM_PATCH_23 "mediatek/mt7996/mt7992_rom_patch_23.bin"
+#define MT7990_FIRMWARE_WA ""
+#define MT7990_FIRMWARE_WM "mediatek/mt7996/mt7990_wm.bin"
+#define MT7990_FIRMWARE_DSP ""
+#define MT7990_ROM_PATCH "mediatek/mt7996/mt7990_rom_patch.bin"
+
#define MT7996_EEPROM_DEFAULT "mediatek/mt7996/mt7996_eeprom.bin"
#define MT7996_EEPROM_DEFAULT_INT "mediatek/mt7996/mt7996_eeprom_2i5i6i.bin"
#define MT7996_EEPROM_DEFAULT_233 "mediatek/mt7996/mt7996_eeprom_233.bin"
@@ -61,6 +76,9 @@
#define MT7992_EEPROM_DEFAULT_23 "mediatek/mt7996/mt7992_eeprom_23.bin"
#define MT7992_EEPROM_DEFAULT_23_INT "mediatek/mt7996/mt7992_eeprom_23_2i5i.bin"
+#define MT7990_EEPROM_DEFAULT "mediatek/mt7996/mt7990_eeprom.bin"
+#define MT7990_EEPROM_DEFAULT_INT "mediatek/mt7996/mt7990_eeprom_2i5i.bin"
+
#define MT7996_EEPROM_SIZE 7680
#define MT7996_EEPROM_BLOCK_SIZE 16
#define MT7996_TOKEN_SIZE 16384
@@ -131,6 +149,10 @@ enum mt7992_var_type {
MT7992_VAR_TYPE_23,
};
+enum mt7990_var_type {
+ MT7990_VAR_TYPE_23,
+};
+
enum mt7996_fem_type {
MT7996_FEM_EXT,
MT7996_FEM_INT,
@@ -165,6 +187,8 @@ enum mt7996_rxq_id {
MT7996_RXQ_TXFREE1 = 9,
MT7996_RXQ_TXFREE2 = 7,
MT7996_RXQ_RRO_IND = 0,
+ MT7990_RXQ_TXFREE0 = 6,
+ MT7990_RXQ_TXFREE1 = 7,
};
struct mt7996_twt_flow {
@@ -185,10 +209,10 @@ struct mt7996_twt_flow {
DECLARE_EWMA(avg_signal, 10, 8)
-struct mt7996_sta {
+struct mt7996_sta_link {
struct mt76_wcid wcid; /* must be first */
- struct mt7996_vif *vif;
+ struct mt7996_sta *sta;
struct list_head rc_list;
u32 airtime_ac[8];
@@ -204,12 +228,22 @@ struct mt7996_sta {
u8 flowid_mask;
struct mt7996_twt_flow flow[MT7996_MAX_STA_TWT_AGRT];
} twt;
+
+ struct rcu_head rcu_head;
+};
+
+struct mt7996_sta {
+ struct mt7996_sta_link deflink; /* must be first */
+ struct mt7996_sta_link __rcu *link[IEEE80211_MLD_MAX_NUM_LINKS];
+ u8 deflink_id;
+
+ struct mt7996_vif *vif;
};
struct mt7996_vif_link {
struct mt76_vif_link mt76; /* must be first */
- struct mt7996_sta sta;
+ struct mt7996_sta_link msta_link;
struct mt7996_phy *phy;
struct ieee80211_tx_queue_params queue_params[IEEE80211_NUM_ACS];
@@ -271,8 +305,6 @@ struct mt7996_phy {
s16 coverage_class;
u8 slottime;
- u8 rdd_state;
-
u16 beacon_rate;
u32 rx_ampdu_ts;
@@ -283,6 +315,7 @@ struct mt7996_phy {
struct mt76_channel_state state_ts;
u16 orig_chainmask;
+ u16 orig_antenna_mask;
bool has_aux_rx;
bool counter_reset;
@@ -395,10 +428,10 @@ enum {
__MT_WFDMA_MAX,
};
-enum {
- MT_RX_SEL0,
- MT_RX_SEL1,
- MT_RX_SEL2, /* monitor chain */
+enum rdd_idx {
+ MT_RDD_IDX_BAND2, /* RDD idx for band idx 2 */
+ MT_RDD_IDX_BAND1, /* RDD idx for band idx 1 */
+ MT_RDD_IDX_BACKGROUND, /* RDD idx for background chain */
};
enum mt7996_rdd_cmd {
@@ -417,6 +450,21 @@ enum mt7996_rdd_cmd {
RDD_IRQ_OFF,
};
+static inline int
+mt7996_get_rdd_idx(struct mt7996_phy *phy, bool is_background)
+{
+ if (!phy->mt76->cap.has_5ghz)
+ return -1;
+
+ if (is_background)
+ return MT_RDD_IDX_BACKGROUND;
+
+ if (phy->mt76->band_idx == MT_BAND2)
+ return MT_RDD_IDX_BAND2;
+
+ return MT_RDD_IDX_BAND1;
+}
+
static inline struct mt7996_dev *
mt7996_hw_dev(struct ieee80211_hw *hw)
{
@@ -451,31 +499,12 @@ mt7996_phy3(struct mt7996_dev *dev)
static inline bool
mt7996_band_valid(struct mt7996_dev *dev, u8 band)
{
- if (is_mt7992(&dev->mt76))
+ if (!is_mt7996(&dev->mt76))
return band <= MT_BAND1;
return band <= MT_BAND2;
}
-static inline bool
-mt7996_has_background_radar(struct mt7996_dev *dev)
-{
- switch (mt76_chip(&dev->mt76)) {
- case 0x7990:
- if (dev->var.type == MT7996_VAR_TYPE_233)
- return false;
- break;
- case 0x7992:
- if (dev->var.type == MT7992_VAR_TYPE_23)
- return false;
- break;
- default:
- return false;
- }
-
- return true;
-}
-
static inline struct mt7996_phy *
mt7996_band_phy(struct mt7996_dev *dev, enum nl80211_band band)
{
@@ -525,7 +554,7 @@ struct mt7996_dev *mt7996_mmio_probe(struct device *pdev,
void __iomem *mem_base, u32 device_id);
void mt7996_wfsys_reset(struct mt7996_dev *dev);
irqreturn_t mt7996_irq_handler(int irq, void *dev_instance);
-u64 __mt7996_get_tsf(struct ieee80211_hw *hw, struct mt7996_vif *mvif);
+u64 __mt7996_get_tsf(struct ieee80211_hw *hw, struct mt7996_vif_link *link);
int mt7996_register_device(struct mt7996_dev *dev);
void mt7996_unregister_device(struct mt7996_dev *dev);
int mt7996_vif_link_add(struct mt76_phy *mphy, struct ieee80211_vif *vif,
@@ -539,6 +568,7 @@ int mt7996_eeprom_parse_hw_cap(struct mt7996_dev *dev, struct mt7996_phy *phy);
int mt7996_eeprom_get_target_power(struct mt7996_dev *dev,
struct ieee80211_channel *chan);
s8 mt7996_eeprom_get_power_delta(struct mt7996_dev *dev, int band);
+bool mt7996_eeprom_has_background_radar(struct mt7996_dev *dev);
int mt7996_dma_init(struct mt7996_dev *dev);
void mt7996_dma_reset(struct mt7996_dev *dev, bool force);
void mt7996_dma_prefetch(struct mt7996_dev *dev);
@@ -553,7 +583,7 @@ int mt7996_run(struct mt7996_phy *phy);
int mt7996_mcu_init(struct mt7996_dev *dev);
int mt7996_mcu_init_firmware(struct mt7996_dev *dev);
int mt7996_mcu_twt_agrt_update(struct mt7996_dev *dev,
- struct mt7996_vif *mvif,
+ struct mt7996_vif_link *link,
struct mt7996_twt_flow *flow,
int cmd);
int mt7996_mcu_add_dev_info(struct mt7996_phy *phy, struct ieee80211_vif *vif,
@@ -561,35 +591,52 @@ int mt7996_mcu_add_dev_info(struct mt7996_phy *phy, struct ieee80211_vif *vif,
struct mt76_vif_link *mlink, bool enable);
int mt7996_mcu_add_bss_info(struct mt7996_phy *phy, struct ieee80211_vif *vif,
struct ieee80211_bss_conf *link_conf,
- struct mt76_vif_link *mlink, int enable);
-int mt7996_mcu_add_sta(struct mt7996_dev *dev, struct ieee80211_vif *vif,
- struct mt76_vif_link *mlink,
- struct ieee80211_sta *sta, int conn_state, bool newly);
+ struct mt76_vif_link *mlink,
+ struct mt7996_sta_link *msta_link, int enable);
+int mt7996_mcu_add_sta(struct mt7996_dev *dev,
+ struct ieee80211_bss_conf *link_conf,
+ struct ieee80211_link_sta *link_sta,
+ struct mt7996_vif_link *link,
+ struct mt7996_sta_link *msta_link,
+ int conn_state, bool newly);
+int mt7996_mcu_teardown_mld_sta(struct mt7996_dev *dev,
+ struct mt7996_vif_link *link,
+ struct mt7996_sta_link *msta_link);
int mt7996_mcu_add_tx_ba(struct mt7996_dev *dev,
struct ieee80211_ampdu_params *params,
- bool add);
+ struct mt7996_vif_link *link,
+ struct mt7996_sta_link *msta_link, bool enable);
int mt7996_mcu_add_rx_ba(struct mt7996_dev *dev,
struct ieee80211_ampdu_params *params,
- bool add);
+ struct mt7996_vif_link *link, bool enable);
int mt7996_mcu_update_bss_color(struct mt7996_dev *dev,
struct mt76_vif_link *mlink,
struct cfg80211_he_bss_color *he_bss_color);
int mt7996_mcu_add_beacon(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
struct ieee80211_bss_conf *link_conf);
int mt7996_mcu_beacon_inband_discov(struct mt7996_dev *dev,
- struct ieee80211_vif *vif, u32 changed);
-int mt7996_mcu_add_obss_spr(struct mt7996_phy *phy, struct ieee80211_vif *vif,
+ struct ieee80211_bss_conf *link_conf,
+ struct mt7996_vif_link *link, u32 changed);
+int mt7996_mcu_add_obss_spr(struct mt7996_phy *phy,
+ struct mt7996_vif_link *link,
struct ieee80211_he_obss_pd *he_obss_pd);
-int mt7996_mcu_add_rate_ctrl(struct mt7996_dev *dev, struct ieee80211_vif *vif,
- struct ieee80211_sta *sta, bool changed);
+int mt7996_mcu_add_rate_ctrl(struct mt7996_dev *dev,
+ struct ieee80211_vif *vif,
+ struct ieee80211_bss_conf *link_conf,
+ struct ieee80211_link_sta *link_sta,
+ struct mt7996_vif_link *link,
+ struct mt7996_sta_link *msta_link, bool changed);
int mt7996_set_channel(struct mt76_phy *mphy);
int mt7996_mcu_set_chan_info(struct mt7996_phy *phy, u16 tag);
int mt7996_mcu_set_tx(struct mt7996_dev *dev, struct ieee80211_vif *vif,
struct ieee80211_bss_conf *link_conf);
int mt7996_mcu_set_fixed_rate_ctrl(struct mt7996_dev *dev,
void *data, u16 version);
-int mt7996_mcu_set_fixed_field(struct mt7996_dev *dev, struct ieee80211_vif *vif,
- struct ieee80211_sta *sta, void *data, u32 field);
+int mt7996_mcu_set_fixed_field(struct mt7996_dev *dev,
+ struct ieee80211_link_sta *link_sta,
+ struct mt7996_vif_link *link,
+ struct mt7996_sta_link *msta_link,
+ void *data, u32 field);
int mt7996_mcu_set_eeprom(struct mt7996_dev *dev);
int mt7996_mcu_get_eeprom(struct mt7996_dev *dev, u32 offset, u8 *buf, u32 buf_len);
int mt7996_mcu_get_eeprom_free_block(struct mt7996_dev *dev, u8 *block_num);
@@ -610,8 +657,7 @@ int mt7996_mcu_get_temperature(struct mt7996_phy *phy);
int mt7996_mcu_set_thermal_throttling(struct mt7996_phy *phy, u8 state);
int mt7996_mcu_set_thermal_protect(struct mt7996_phy *phy, bool enable);
int mt7996_mcu_set_txpower_sku(struct mt7996_phy *phy);
-int mt7996_mcu_rdd_cmd(struct mt7996_dev *dev, int cmd, u8 index,
- u8 rx_sel, u8 val);
+int mt7996_mcu_rdd_cmd(struct mt7996_dev *dev, int cmd, u8 rdd_idx, u8 val);
int mt7996_mcu_rdd_background_enable(struct mt7996_phy *phy,
struct cfg80211_chan_def *chandef);
int mt7996_mcu_set_fixed_rate_table(struct mt7996_phy *phy, u8 table_idx,
@@ -677,32 +723,30 @@ static inline u16 mt7996_rx_chainmask(struct mt7996_phy *phy)
return tx_chainmask | (BIT(fls(tx_chainmask)) * phy->has_aux_rx);
}
+static inline bool mt7996_has_wa(struct mt7996_dev *dev)
+{
+ return !is_mt7990(&dev->mt76);
+}
+
void mt7996_mac_init(struct mt7996_dev *dev);
u32 mt7996_mac_wtbl_lmac_addr(struct mt7996_dev *dev, u16 wcid, u8 dw);
bool mt7996_mac_wtbl_update(struct mt7996_dev *dev, int idx, u32 mask);
void mt7996_mac_reset_counters(struct mt7996_phy *phy);
void mt7996_mac_cca_stats_reset(struct mt7996_phy *phy);
void mt7996_mac_enable_nf(struct mt7996_dev *dev, u8 band);
-void mt7996_mac_enable_rtscts(struct mt7996_dev *dev,
- struct ieee80211_vif *vif, bool enable);
void mt7996_mac_write_txwi(struct mt7996_dev *dev, __le32 *txwi,
struct sk_buff *skb, struct mt76_wcid *wcid,
struct ieee80211_key_conf *key, int pid,
enum mt76_txq_id qid, u32 changed);
void mt7996_mac_set_coverage_class(struct mt7996_phy *phy);
-int mt7996_mac_sta_add(struct mt76_dev *mdev, struct ieee80211_vif *vif,
- struct ieee80211_sta *sta);
-int mt7996_mac_sta_event(struct mt76_dev *mdev, struct ieee80211_vif *vif,
- struct ieee80211_sta *sta, enum mt76_sta_event ev);
-void mt7996_mac_sta_remove(struct mt76_dev *mdev, struct ieee80211_vif *vif,
- struct ieee80211_sta *sta);
void mt7996_mac_work(struct work_struct *work);
void mt7996_mac_reset_work(struct work_struct *work);
void mt7996_mac_dump_work(struct work_struct *work);
void mt7996_mac_sta_rc_work(struct work_struct *work);
void mt7996_mac_update_stats(struct mt7996_phy *phy);
void mt7996_mac_twt_teardown_flow(struct mt7996_dev *dev,
- struct mt7996_sta *msta,
+ struct mt7996_vif_link *link,
+ struct mt7996_sta_link *msta_link,
u8 flowid);
void mt7996_mac_add_twt_setup(struct ieee80211_hw *hw,
struct ieee80211_sta *sta,
@@ -727,11 +771,14 @@ bool mt7996_debugfs_rx_log(struct mt7996_dev *dev, const void *data, int len);
int mt7996_mcu_add_key(struct mt76_dev *dev, struct ieee80211_vif *vif,
struct ieee80211_key_conf *key, int mcu_cmd,
struct mt76_wcid *wcid, enum set_key_cmd cmd);
-int mt7996_mcu_bcn_prot_enable(struct mt7996_dev *dev, struct ieee80211_vif *vif,
+int mt7996_mcu_bcn_prot_enable(struct mt7996_dev *dev,
+ struct mt7996_vif_link *link,
+ struct mt7996_sta_link *msta_link,
struct ieee80211_key_conf *key);
int mt7996_mcu_wtbl_update_hdr_trans(struct mt7996_dev *dev,
struct ieee80211_vif *vif,
- struct ieee80211_sta *sta);
+ struct mt7996_vif_link *link,
+ struct mt7996_sta_link *msta_link);
int mt7996_mcu_cp_support(struct mt7996_dev *dev, u8 mode);
#ifdef CONFIG_MAC80211_DEBUGFS
void mt7996_sta_add_debugfs(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/pci.c b/drivers/net/wireless/mediatek/mt76/mt7996/pci.c
index 04056181368a..19e99bc1c6c4 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7996/pci.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7996/pci.c
@@ -16,14 +16,16 @@ static DEFINE_SPINLOCK(hif_lock);
static u32 hif_idx;
static const struct pci_device_id mt7996_pci_device_table[] = {
- { PCI_DEVICE(PCI_VENDOR_ID_MEDIATEK, 0x7990) },
- { PCI_DEVICE(PCI_VENDOR_ID_MEDIATEK, 0x7992) },
+ { PCI_DEVICE(PCI_VENDOR_ID_MEDIATEK, MT7996_DEVICE_ID) },
+ { PCI_DEVICE(PCI_VENDOR_ID_MEDIATEK, MT7992_DEVICE_ID) },
+ { PCI_DEVICE(PCI_VENDOR_ID_MEDIATEK, MT7990_DEVICE_ID) },
{ },
};
static const struct pci_device_id mt7996_hif_device_table[] = {
- { PCI_DEVICE(PCI_VENDOR_ID_MEDIATEK, 0x7991) },
- { PCI_DEVICE(PCI_VENDOR_ID_MEDIATEK, 0x799a) },
+ { PCI_DEVICE(PCI_VENDOR_ID_MEDIATEK, MT7996_DEVICE_ID_2) },
+ { PCI_DEVICE(PCI_VENDOR_ID_MEDIATEK, MT7992_DEVICE_ID_2) },
+ { PCI_DEVICE(PCI_VENDOR_ID_MEDIATEK, MT7990_DEVICE_ID_2) },
{ },
};
@@ -63,8 +65,9 @@ static struct mt7996_hif *mt7996_pci_init_hif2(struct pci_dev *pdev)
{
hif_idx++;
- if (!pci_get_device(PCI_VENDOR_ID_MEDIATEK, 0x7991, NULL) &&
- !pci_get_device(PCI_VENDOR_ID_MEDIATEK, 0x799a, NULL))
+ if (!pci_get_device(PCI_VENDOR_ID_MEDIATEK, MT7996_DEVICE_ID_2, NULL) &&
+ !pci_get_device(PCI_VENDOR_ID_MEDIATEK, MT7992_DEVICE_ID_2, NULL) &&
+ !pci_get_device(PCI_VENDOR_ID_MEDIATEK, MT7990_DEVICE_ID_2, NULL))
return NULL;
writel(hif_idx | MT_PCIE_RECOG_ID_SEM,
@@ -121,7 +124,9 @@ static int mt7996_pci_probe(struct pci_dev *pdev,
mt76_pci_disable_aspm(pdev);
- if (id->device == 0x7991 || id->device == 0x799a)
+ if (id->device == MT7996_DEVICE_ID_2 ||
+ id->device == MT7992_DEVICE_ID_2 ||
+ id->device == MT7990_DEVICE_ID_2)
return mt7996_pci_hif2_probe(pdev);
dev = mt7996_mmio_probe(&pdev->dev, pcim_iomap_table(pdev)[0],
@@ -256,3 +261,5 @@ MODULE_FIRMWARE(MT7992_FIRMWARE_WA);
MODULE_FIRMWARE(MT7992_FIRMWARE_WM);
MODULE_FIRMWARE(MT7992_FIRMWARE_DSP);
MODULE_FIRMWARE(MT7992_ROM_PATCH);
+MODULE_FIRMWARE(MT7990_FIRMWARE_WM);
+MODULE_FIRMWARE(MT7990_ROM_PATCH);
diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/regs.h b/drivers/net/wireless/mediatek/mt76/mt7996/regs.h
index 1876a968c92d..e942c0058731 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7996/regs.h
+++ b/drivers/net/wireless/mediatek/mt76/mt7996/regs.h
@@ -64,6 +64,17 @@ enum offs_rev {
MIB_BSCR7,
MIB_BSCR17,
MIB_TRDR1,
+ HIF_REMAP_L1,
+ HIF_REMAP_BASE_L1,
+ HIF_REMAP_L2,
+ HIF_REMAP_BASE_L2,
+ CBTOP1_PHY_END,
+ INFRA_MCU_END,
+ WTBLON_WDUCR,
+ WTBL_UPDATE,
+ WTBL_ITCR,
+ WTBL_ITCR0,
+ WTBL_ITCR1,
__MT_OFFS_MAX,
};
@@ -291,19 +302,19 @@ enum offs_rev {
/* WTBLON TOP */
#define MT_WTBLON_TOP_BASE 0x820d4000
#define MT_WTBLON_TOP(ofs) (MT_WTBLON_TOP_BASE + (ofs))
-#define MT_WTBLON_TOP_WDUCR MT_WTBLON_TOP(0x370)
+#define MT_WTBLON_TOP_WDUCR MT_WTBLON_TOP(__OFFS(WTBLON_WDUCR))
#define MT_WTBLON_TOP_WDUCR_GROUP GENMASK(4, 0)
-#define MT_WTBL_UPDATE MT_WTBLON_TOP(0x380)
+#define MT_WTBL_UPDATE MT_WTBLON_TOP(__OFFS(WTBL_UPDATE))
#define MT_WTBL_UPDATE_WLAN_IDX GENMASK(11, 0)
#define MT_WTBL_UPDATE_ADM_COUNT_CLEAR BIT(14)
#define MT_WTBL_UPDATE_BUSY BIT(31)
-#define MT_WTBL_ITCR MT_WTBLON_TOP(0x3b0)
+#define MT_WTBL_ITCR MT_WTBLON_TOP(__OFFS(WTBL_ITCR))
#define MT_WTBL_ITCR_WR BIT(16)
#define MT_WTBL_ITCR_EXEC BIT(31)
-#define MT_WTBL_ITDR0 MT_WTBLON_TOP(0x3b8)
-#define MT_WTBL_ITDR1 MT_WTBLON_TOP(0x3bc)
+#define MT_WTBL_ITDR0 MT_WTBLON_TOP(__OFFS(WTBL_ITCR0))
+#define MT_WTBL_ITDR1 MT_WTBLON_TOP(__OFFS(WTBL_ITCR1))
#define MT_WTBL_SPE_IDX_SEL BIT(6)
/* WTBL */
@@ -483,7 +494,7 @@ enum offs_rev {
#define MT_MCUQ_EXT_CTRL(q) (MT_Q_BASE(q) + 0x600 + \
MT_MCUQ_ID(q) * 0x4)
-#define MT_RXQ_BAND1_CTRL(q) (MT_Q_BASE(__RXQ(q)) + 0x680 + \
+#define MT_RXQ_EXT_CTRL(q) (MT_Q_BASE(__RXQ(q)) + 0x680 + \
MT_RXQ_ID(q) * 0x4)
#define MT_TXQ_EXT_CTRL(q) (MT_Q_BASE(__TXQ(q)) + 0x600 + \
MT_TXQ_ID(q) * 0x4)
@@ -504,6 +515,8 @@ enum offs_rev {
#define MT_INT_RX_DONE_WA_TRI BIT(3)
#define MT_INT_RX_TXFREE_MAIN BIT(17)
#define MT_INT_RX_TXFREE_TRI BIT(15)
+#define MT_INT_RX_TXFREE_BAND0_MT7990 BIT(14)
+#define MT_INT_RX_TXFREE_BAND1_MT7990 BIT(15)
#define MT_INT_RX_DONE_BAND2_EXT BIT(23)
#define MT_INT_RX_TXFREE_EXT BIT(26)
#define MT_INT_MCU_CMD BIT(29)
@@ -576,27 +589,39 @@ enum offs_rev {
#define MT_MCU_CMD_WDT_MASK GENMASK(31, 30)
/* l1/l2 remap */
-#define MT_HIF_REMAP_L1 0x155024
-#define MT_HIF_REMAP_L1_MASK GENMASK(31, 16)
+#define CONN_BUS_CR_VON_BASE 0x155000
+#define MT_HIF_REMAP_L1 (CONN_BUS_CR_VON_BASE + __OFFS(HIF_REMAP_L1))
+#define MT_HIF_REMAP_L1_MASK_7996 GENMASK(31, 16)
+#define MT_HIF_REMAP_L1_MASK GENMASK(15, 0)
#define MT_HIF_REMAP_L1_OFFSET GENMASK(15, 0)
#define MT_HIF_REMAP_L1_BASE GENMASK(31, 16)
-#define MT_HIF_REMAP_BASE_L1 0x130000
+#define MT_HIF_REMAP_BASE_L1 __OFFS(HIF_REMAP_BASE_L1)
-#define MT_HIF_REMAP_L2 0x1b4
+#define MT_HIF_REMAP_L2 __OFFS(HIF_REMAP_L2)
#define MT_HIF_REMAP_L2_MASK GENMASK(19, 0)
#define MT_HIF_REMAP_L2_OFFSET GENMASK(11, 0)
#define MT_HIF_REMAP_L2_BASE GENMASK(31, 12)
-#define MT_HIF_REMAP_BASE_L2 0x1000
+#define MT_HIF_REMAP_L2_MASK_7990 GENMASK(15, 0)
+#define MT_HIF_REMAP_L2_OFFSET_7990 GENMASK(15, 0)
+#define MT_HIF_REMAP_L2_BASE_7990 GENMASK(31, 16)
+#define MT_HIF_REMAP_BASE_L2 __OFFS(HIF_REMAP_BASE_L2)
+
+/* for mt7990 only */
+#define MT_HIF_REMAP_CBTOP 0x1f6554
+#define MT_HIF_REMAP_CBTOP_MASK GENMASK(15, 0)
+#define MT_HIF_REMAP_CBTOP_OFFSET GENMASK(15, 0)
+#define MT_HIF_REMAP_CBTOP_BASE GENMASK(31, 16)
+#define MT_HIF_REMAP_BASE_CBTOP 0x1c0000
#define MT_INFRA_BASE 0x18000000
#define MT_WFSYS0_PHY_START 0x18400000
#define MT_WFSYS1_PHY_START 0x18800000
#define MT_WFSYS1_PHY_END 0x18bfffff
#define MT_CBTOP1_PHY_START 0x70000000
-#define MT_CBTOP1_PHY_END 0x77ffffff
+#define MT_CBTOP1_PHY_END __OFFS(CBTOP1_PHY_END)
#define MT_CBTOP2_PHY_START 0xf0000000
#define MT_INFRA_MCU_START 0x7c000000
-#define MT_INFRA_MCU_END 0x7c3fffff
+#define MT_INFRA_MCU_END __OFFS(INFRA_MCU_END)
/* FW MODE SYNC */
#define MT_FW_ASSERT_CNT 0x02208274
diff --git a/drivers/net/wireless/mediatek/mt76/scan.c b/drivers/net/wireless/mediatek/mt76/scan.c
index 1c4f9deaaada..9b20ccbeb8cf 100644
--- a/drivers/net/wireless/mediatek/mt76/scan.c
+++ b/drivers/net/wireless/mediatek/mt76/scan.c
@@ -52,11 +52,6 @@ mt76_scan_send_probe(struct mt76_dev *dev, struct cfg80211_ssid *ssid)
ether_addr_copy(hdr->addr3, req->bssid);
}
- info = IEEE80211_SKB_CB(skb);
- if (req->no_cck)
- info->flags |= IEEE80211_TX_CTL_NO_CCK_RATE;
- info->control.flags |= IEEE80211_TX_CTRL_DONT_USE_RATE_MASK;
-
if (req->ie_len)
skb_put_data(skb, req->ie, req->ie_len);
@@ -64,10 +59,20 @@ mt76_scan_send_probe(struct mt76_dev *dev, struct cfg80211_ssid *ssid)
skb_set_queue_mapping(skb, IEEE80211_AC_VO);
rcu_read_lock();
- if (ieee80211_tx_prepare_skb(phy->hw, vif, skb, band, NULL))
- mt76_tx(phy, NULL, mvif->wcid, skb);
- else
+
+ if (!ieee80211_tx_prepare_skb(phy->hw, vif, skb, band, NULL)) {
ieee80211_free_txskb(phy->hw, skb);
+ goto out;
+ }
+
+ info = IEEE80211_SKB_CB(skb);
+ if (req->no_cck)
+ info->flags |= IEEE80211_TX_CTL_NO_CCK_RATE;
+ info->control.flags |= IEEE80211_TX_CTRL_DONT_USE_RATE_MASK;
+
+ mt76_tx(phy, NULL, mvif->wcid, skb);
+
+out:
rcu_read_unlock();
}
diff --git a/drivers/net/wireless/mediatek/mt76/tx.c b/drivers/net/wireless/mediatek/mt76/tx.c
index af0c50c983ec..513916469ca2 100644
--- a/drivers/net/wireless/mediatek/mt76/tx.c
+++ b/drivers/net/wireless/mediatek/mt76/tx.c
@@ -100,7 +100,8 @@ __mt76_tx_status_skb_done(struct mt76_dev *dev, struct sk_buff *skb, u8 flags,
return;
/* Tx status can be unreliable. if it fails, mark the frame as ACKed */
- if (flags & MT_TX_CB_TXS_FAILED) {
+ if (flags & MT_TX_CB_TXS_FAILED &&
+ (dev->drv->drv_flags & MT_DRV_IGNORE_TXS_FAILED)) {
info->status.rates[0].count = 0;
info->status.rates[0].idx = -1;
info->flags |= IEEE80211_TX_STAT_ACK;
diff --git a/drivers/net/wireless/microchip/wilc1000/hif.c b/drivers/net/wireless/microchip/wilc1000/hif.c
index bba53307b960..cb46a39ef757 100644
--- a/drivers/net/wireless/microchip/wilc1000/hif.c
+++ b/drivers/net/wireless/microchip/wilc1000/hif.c
@@ -643,7 +643,7 @@ static inline void host_int_parse_assoc_resp_info(struct wilc_vif *vif,
}
}
- del_timer(&hif_drv->connect_timer);
+ timer_delete(&hif_drv->connect_timer);
conn_info->conn_result(CONN_DISCONN_EVENT_CONN_RESP, mac_status,
hif_drv->conn_info.priv);
@@ -669,7 +669,7 @@ void wilc_handle_disconnect(struct wilc_vif *vif)
struct host_if_drv *hif_drv = vif->hif_drv;
if (hif_drv->usr_scan_req.scan_result) {
- del_timer(&hif_drv->scan_timer);
+ timer_delete(&hif_drv->scan_timer);
handle_scan_done(vif, SCAN_EVENT_ABORTED);
}
@@ -713,7 +713,7 @@ static void handle_rcvd_gnrl_async_info(struct work_struct *work)
if (hif_drv->hif_state == HOST_IF_CONNECTED) {
wilc_handle_disconnect(vif);
} else if (hif_drv->usr_scan_req.scan_result) {
- del_timer(&hif_drv->scan_timer);
+ timer_delete(&hif_drv->scan_timer);
handle_scan_done(vif, SCAN_EVENT_ABORTED);
}
}
@@ -746,7 +746,7 @@ int wilc_disconnect(struct wilc_vif *vif)
conn_info = &hif_drv->conn_info;
if (scan_req->scan_result) {
- del_timer(&hif_drv->scan_timer);
+ timer_delete(&hif_drv->scan_timer);
scan_req->scan_result(SCAN_EVENT_ABORTED, NULL, scan_req->priv);
scan_req->scan_result = NULL;
}
@@ -754,7 +754,7 @@ int wilc_disconnect(struct wilc_vif *vif)
if (conn_info->conn_result) {
if (hif_drv->hif_state == HOST_IF_WAITING_CONN_RESP ||
hif_drv->hif_state == HOST_IF_EXTERNAL_AUTH)
- del_timer(&hif_drv->connect_timer);
+ timer_delete(&hif_drv->connect_timer);
conn_info->conn_result(CONN_DISCONN_EVENT_DISCONN_NOTIF, 0,
conn_info->priv);
@@ -959,7 +959,7 @@ static void listen_timer_cb(struct timer_list *t)
int result;
struct host_if_msg *msg;
- del_timer(&vif->hif_drv->remain_on_ch_timer);
+ timer_delete(&vif->hif_drv->remain_on_ch_timer);
msg = wilc_alloc_work(vif, wilc_handle_listen_state_expired, false);
if (IS_ERR(msg))
@@ -1066,7 +1066,7 @@ static void handle_scan_complete(struct work_struct *work)
{
struct host_if_msg *msg = container_of(work, struct host_if_msg, work);
- del_timer(&msg->vif->hif_drv->scan_timer);
+ timer_delete(&msg->vif->hif_drv->scan_timer);
handle_scan_done(msg->vif, SCAN_EVENT_DONE);
@@ -1551,7 +1551,7 @@ int wilc_deinit(struct wilc_vif *vif)
timer_shutdown_sync(&hif_drv->scan_timer);
timer_shutdown_sync(&hif_drv->connect_timer);
- del_timer_sync(&vif->periodic_rssi);
+ timer_delete_sync(&vif->periodic_rssi);
timer_shutdown_sync(&hif_drv->remain_on_ch_timer);
if (hif_drv->usr_scan_req.scan_result) {
@@ -1718,7 +1718,7 @@ int wilc_listen_state_expired(struct wilc_vif *vif, u64 cookie)
return -EFAULT;
}
- del_timer(&vif->hif_drv->remain_on_ch_timer);
+ timer_delete(&vif->hif_drv->remain_on_ch_timer);
return wilc_handle_roc_expired(vif, cookie);
}
diff --git a/drivers/net/wireless/purelifi/plfxlc/mac.c b/drivers/net/wireless/purelifi/plfxlc/mac.c
index eae93efa6150..82d1bf7edba2 100644
--- a/drivers/net/wireless/purelifi/plfxlc/mac.c
+++ b/drivers/net/wireless/purelifi/plfxlc/mac.c
@@ -102,7 +102,6 @@ int plfxlc_mac_init_hw(struct ieee80211_hw *hw)
void plfxlc_mac_release(struct plfxlc_mac *mac)
{
plfxlc_chip_release(&mac->chip);
- lockdep_assert_held(&mac->lock);
}
int plfxlc_op_start(struct ieee80211_hw *hw)
diff --git a/drivers/net/wireless/purelifi/plfxlc/usb.c b/drivers/net/wireless/purelifi/plfxlc/usb.c
index 56d1139ba8bc..c2a1234b59db 100644
--- a/drivers/net/wireless/purelifi/plfxlc/usb.c
+++ b/drivers/net/wireless/purelifi/plfxlc/usb.c
@@ -503,8 +503,10 @@ int plfxlc_usb_wreq_async(struct plfxlc_usb *usb, const u8 *buffer,
(void *)buffer, buffer_len, complete_fn, context);
r = usb_submit_urb(urb, GFP_ATOMIC);
- if (r)
+ if (r) {
+ usb_free_urb(urb);
dev_err(&udev->dev, "Async write submit failed (%d)\n", r);
+ }
return r;
}
@@ -714,8 +716,8 @@ static void disconnect(struct usb_interface *intf)
mac = plfxlc_hw_mac(hw);
usb = &mac->chip.usb;
- del_timer_sync(&usb->tx.tx_retry_timer);
- del_timer_sync(&usb->sta_queue_cleanup);
+ timer_delete_sync(&usb->tx.tx_retry_timer);
+ timer_delete_sync(&usb->sta_queue_cleanup);
ieee80211_unregister_hw(hw);
diff --git a/drivers/net/wireless/quantenna/qtnfmac/qlink.h b/drivers/net/wireless/quantenna/qtnfmac/qlink.h
index 674461fa7fb3..eae35b678952 100644
--- a/drivers/net/wireless/quantenna/qtnfmac/qlink.h
+++ b/drivers/net/wireless/quantenna/qtnfmac/qlink.h
@@ -1510,10 +1510,15 @@ enum qlink_tlv_id {
};
struct qlink_tlv_hdr {
- __le16 type;
- __le16 len;
+ /* New members MUST be added within the struct_group() macro below. */
+ __struct_group(qlink_tlv_hdr_fixed, __hdr, __packed,
+ __le16 type;
+ __le16 len;
+ );
u8 val[];
} __packed;
+static_assert(offsetof(struct qlink_tlv_hdr, val) == sizeof(struct qlink_tlv_hdr_fixed),
+ "struct member likely outside of __struct_group()");
struct qlink_iface_limit {
__le16 max_num;
@@ -1567,7 +1572,7 @@ enum qlink_reg_rule_flags {
* @dfs_cac_ms: DFS CAC period.
*/
struct qlink_tlv_reg_rule {
- struct qlink_tlv_hdr hdr;
+ struct qlink_tlv_hdr_fixed hdr;
__le32 start_freq_khz;
__le32 end_freq_khz;
__le32 max_bandwidth_khz;
@@ -1606,7 +1611,7 @@ enum qlink_dfs_state {
* @channel: ieee80211 channel settings.
*/
struct qlink_tlv_channel {
- struct qlink_tlv_hdr hdr;
+ struct qlink_tlv_hdr_fixed hdr;
struct qlink_channel chan;
} __packed;
@@ -1618,7 +1623,7 @@ struct qlink_tlv_channel {
* @chan: channel definition data.
*/
struct qlink_tlv_chandef {
- struct qlink_tlv_hdr hdr;
+ struct qlink_tlv_hdr_fixed hdr;
struct qlink_chandef chdef;
} __packed;
@@ -1643,7 +1648,7 @@ enum qlink_ie_set_type {
* @ie_data: IEs data.
*/
struct qlink_tlv_ie_set {
- struct qlink_tlv_hdr hdr;
+ struct qlink_tlv_hdr_fixed hdr;
u8 type;
u8 flags;
u8 rsvd[2];
@@ -1657,7 +1662,7 @@ struct qlink_tlv_ie_set {
* @ie_data: IEs data.
*/
struct qlink_tlv_ext_ie {
- struct qlink_tlv_hdr hdr;
+ struct qlink_tlv_hdr_fixed hdr;
u8 eid_ext;
u8 rsvd[3];
u8 ie_data[];
@@ -1678,7 +1683,7 @@ struct qlink_sband_iftype_data {
* @iftype_data: interface type data entries.
*/
struct qlink_tlv_iftype_data {
- struct qlink_tlv_hdr hdr;
+ struct qlink_tlv_hdr_fixed hdr;
u8 n_iftype_data;
u8 rsvd[3];
struct qlink_sband_iftype_data iftype_data[];
diff --git a/drivers/net/wireless/ralink/rt2x00/rt2800lib.c b/drivers/net/wireless/ralink/rt2x00/rt2800lib.c
index e5f553a1ea24..b7ea606bda08 100644
--- a/drivers/net/wireless/ralink/rt2x00/rt2800lib.c
+++ b/drivers/net/wireless/ralink/rt2x00/rt2800lib.c
@@ -9393,7 +9393,7 @@ static void rt2800_loft_search(struct rt2x00_dev *rt2x00dev, u8 ch_idx,
p0, p1, pf, idx0, idx1, ibit);
if (bidx != 5 && pf <= p0 && pf < p1) {
- idxf[iorq] = idxf[iorq];
+ /* no need to adjust idxf[] */;
} else if (p0 < p1) {
pf = p0;
idxf[iorq] = idx0 & 0x3F;
diff --git a/drivers/net/wireless/ralink/rt2x00/rt2800mmio.c b/drivers/net/wireless/ralink/rt2x00/rt2800mmio.c
index 5323acff962a..45775ecdf221 100644
--- a/drivers/net/wireless/ralink/rt2x00/rt2800mmio.c
+++ b/drivers/net/wireless/ralink/rt2x00/rt2800mmio.c
@@ -842,7 +842,7 @@ int rt2800mmio_probe_hw(struct rt2x00_dev *rt2x00dev)
/*
* Set txstatus timer function.
*/
- rt2x00dev->txstatus_timer.function = rt2800mmio_tx_sta_fifo_timeout;
+ hrtimer_update_function(&rt2x00dev->txstatus_timer, rt2800mmio_tx_sta_fifo_timeout);
/*
* Overwrite TX done handler
diff --git a/drivers/net/wireless/ralink/rt2x00/rt2800usb.c b/drivers/net/wireless/ralink/rt2x00/rt2800usb.c
index 160bef79acdb..b51a23300ba2 100644
--- a/drivers/net/wireless/ralink/rt2x00/rt2800usb.c
+++ b/drivers/net/wireless/ralink/rt2x00/rt2800usb.c
@@ -618,7 +618,7 @@ static int rt2800usb_probe_hw(struct rt2x00_dev *rt2x00dev)
/*
* Set txstatus timer function.
*/
- rt2x00dev->txstatus_timer.function = rt2800usb_tx_sta_fifo_timeout;
+ hrtimer_update_function(&rt2x00dev->txstatus_timer, rt2800usb_tx_sta_fifo_timeout);
/*
* Overwrite TX done handler
diff --git a/drivers/net/wireless/ralink/rt2x00/rt2x00dev.c b/drivers/net/wireless/ralink/rt2x00/rt2x00dev.c
index 9e7d9dbe954c..432ddfac2c33 100644
--- a/drivers/net/wireless/ralink/rt2x00/rt2x00dev.c
+++ b/drivers/net/wireless/ralink/rt2x00/rt2x00dev.c
@@ -1391,8 +1391,8 @@ int rt2x00lib_probe_dev(struct rt2x00_dev *rt2x00dev)
mutex_init(&rt2x00dev->conf_mutex);
INIT_LIST_HEAD(&rt2x00dev->bar_list);
spin_lock_init(&rt2x00dev->bar_list_lock);
- hrtimer_init(&rt2x00dev->txstatus_timer, CLOCK_MONOTONIC,
- HRTIMER_MODE_REL);
+ hrtimer_setup(&rt2x00dev->txstatus_timer, hrtimer_dummy_timeout, CLOCK_MONOTONIC,
+ HRTIMER_MODE_REL);
set_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags);
diff --git a/drivers/net/wireless/realtek/rtl8xxxu/8192c.c b/drivers/net/wireless/realtek/rtl8xxxu/8192c.c
index 0abb1b092bc2..73034e7e41d1 100644
--- a/drivers/net/wireless/realtek/rtl8xxxu/8192c.c
+++ b/drivers/net/wireless/realtek/rtl8xxxu/8192c.c
@@ -644,6 +644,8 @@ struct rtl8xxxu_fileops rtl8192cu_fops = {
.rx_agg_buf_size = 16000,
.tx_desc_size = sizeof(struct rtl8xxxu_txdesc32),
.rx_desc_size = sizeof(struct rtl8xxxu_rxdesc16),
+ .supports_ap = 1,
+ .max_macid_num = 32,
.max_sec_cam_num = 32,
.adda_1t_init = 0x0b1b25a0,
.adda_1t_path_on = 0x0bdb25a0,
diff --git a/drivers/net/wireless/realtek/rtl8xxxu/core.c b/drivers/net/wireless/realtek/rtl8xxxu/core.c
index 4ce0c05c5129..569856ca677f 100644
--- a/drivers/net/wireless/realtek/rtl8xxxu/core.c
+++ b/drivers/net/wireless/realtek/rtl8xxxu/core.c
@@ -860,9 +860,10 @@ rtl8xxxu_writeN(struct rtl8xxxu_priv *priv, u16 addr, u8 *buf, u16 len)
return len;
write_error:
- dev_info(&udev->dev,
- "%s: Failed to write block at addr: %04x size: %04x\n",
- __func__, addr, blocksize);
+ if (rtl8xxxu_debug & RTL8XXXU_DEBUG_REG_WRITE)
+ dev_info(&udev->dev,
+ "%s: Failed to write block at addr: %04x size: %04x\n",
+ __func__, addr, blocksize);
return -EAGAIN;
}
@@ -4064,8 +4065,14 @@ static int rtl8xxxu_init_device(struct ieee80211_hw *hw)
*/
rtl8xxxu_write16(priv, REG_TRXFF_BNDY + 2, fops->trxff_boundary);
- ret = rtl8xxxu_download_firmware(priv);
- dev_dbg(dev, "%s: download_firmware %i\n", __func__, ret);
+ for (int retry = 5; retry >= 0 ; retry--) {
+ ret = rtl8xxxu_download_firmware(priv);
+ dev_dbg(dev, "%s: download_firmware %i\n", __func__, ret);
+ if (ret != -EAGAIN)
+ break;
+ if (retry)
+ dev_dbg(dev, "%s: retry firmware download\n", __func__);
+ }
if (ret)
goto exit;
ret = rtl8xxxu_start_firmware(priv);
diff --git a/drivers/net/wireless/realtek/rtlwifi/base.c b/drivers/net/wireless/realtek/rtlwifi/base.c
index ff61867d142f..6189edc1d8d7 100644
--- a/drivers/net/wireless/realtek/rtlwifi/base.c
+++ b/drivers/net/wireless/realtek/rtlwifi/base.c
@@ -473,7 +473,7 @@ void rtl_deinit_deferred_work(struct ieee80211_hw *hw, bool ips_wq)
{
struct rtl_priv *rtlpriv = rtl_priv(hw);
- del_timer_sync(&rtlpriv->works.watchdog_timer);
+ timer_delete_sync(&rtlpriv->works.watchdog_timer);
cancel_delayed_work_sync(&rtlpriv->works.watchdog_wq);
if (ips_wq)
diff --git a/drivers/net/wireless/realtek/rtlwifi/core.c b/drivers/net/wireless/realtek/rtlwifi/core.c
index 7537f04b1930..819cf519e66e 100644
--- a/drivers/net/wireless/realtek/rtlwifi/core.c
+++ b/drivers/net/wireless/realtek/rtlwifi/core.c
@@ -58,17 +58,6 @@ void rtl_rfreg_delay(struct ieee80211_hw *hw, enum radio_path rfpath, u32 addr,
}
EXPORT_SYMBOL(rtl_rfreg_delay);
-void rtl_bb_delay(struct ieee80211_hw *hw, u32 addr, u32 data)
-{
- if (addr >= 0xf9 && addr <= 0xfe) {
- rtl_addr_delay(addr);
- } else {
- rtl_set_bbreg(hw, addr, MASKDWORD, data);
- udelay(1);
- }
-}
-EXPORT_SYMBOL(rtl_bb_delay);
-
static void rtl_fw_do_work(const struct firmware *firmware, void *context,
bool is_wow)
{
diff --git a/drivers/net/wireless/realtek/rtlwifi/core.h b/drivers/net/wireless/realtek/rtlwifi/core.h
index 42c2d9e13bb8..45225d89ac5e 100644
--- a/drivers/net/wireless/realtek/rtlwifi/core.h
+++ b/drivers/net/wireless/realtek/rtlwifi/core.h
@@ -58,7 +58,6 @@ void rtl_wowlan_fw_cb(const struct firmware *firmware, void *context);
void rtl_addr_delay(u32 addr);
void rtl_rfreg_delay(struct ieee80211_hw *hw, enum radio_path rfpath, u32 addr,
u32 mask, u32 data);
-void rtl_bb_delay(struct ieee80211_hw *hw, u32 addr, u32 data);
bool rtl_cmd_send_packet(struct ieee80211_hw *hw, struct sk_buff *skb);
bool rtl_btc_status_false(void);
void rtl_dm_diginit(struct ieee80211_hw *hw, u32 cur_igval);
diff --git a/drivers/net/wireless/realtek/rtlwifi/pci.c b/drivers/net/wireless/realtek/rtlwifi/pci.c
index 0eafc4d125f9..898f597f70a9 100644
--- a/drivers/net/wireless/realtek/rtlwifi/pci.c
+++ b/drivers/net/wireless/realtek/rtlwifi/pci.c
@@ -155,6 +155,16 @@ static void _rtl_pci_update_default_setting(struct ieee80211_hw *hw)
((u8)init_aspm) == (PCI_EXP_LNKCTL_ASPM_L0S |
PCI_EXP_LNKCTL_ASPM_L1 | PCI_EXP_LNKCTL_CCC))
ppsc->support_aspm = false;
+
+ /* RTL8723BE found on some ASUSTek laptops, such as F441U and
+ * X555UQ with subsystem ID 11ad:1723 are known to output large
+ * amounts of PCIe AER errors during and after boot up, causing
+ * heavy lags, poor network throughput, and occasional lock-ups.
+ */
+ if (rtlpriv->rtlhal.hw_type == HARDWARE_TYPE_RTL8723BE &&
+ (rtlpci->pdev->subsystem_vendor == 0x11ad &&
+ rtlpci->pdev->subsystem_device == 0x1723))
+ ppsc->support_aspm = false;
}
static bool _rtl_pci_platform_switch_device_pci_aspm(
diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8188ee/sw.c b/drivers/net/wireless/realtek/rtlwifi/rtl8188ee/sw.c
index 35875cda30fc..2ad4523d1bef 100644
--- a/drivers/net/wireless/realtek/rtlwifi/rtl8188ee/sw.c
+++ b/drivers/net/wireless/realtek/rtlwifi/rtl8188ee/sw.c
@@ -179,9 +179,9 @@ static void rtl88e_deinit_sw_vars(struct ieee80211_hw *hw)
}
if (rtlpriv->psc.low_power_enable)
- del_timer_sync(&rtlpriv->works.fw_clockoff_timer);
+ timer_delete_sync(&rtlpriv->works.fw_clockoff_timer);
- del_timer_sync(&rtlpriv->works.fast_antenna_training_timer);
+ timer_delete_sync(&rtlpriv->works.fast_antenna_training_timer);
}
/* get bt coexist status */
diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192de/phy.c b/drivers/net/wireless/realtek/rtlwifi/rtl8192de/phy.c
index d429560009bb..68f890050afb 100644
--- a/drivers/net/wireless/realtek/rtlwifi/rtl8192de/phy.c
+++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192de/phy.c
@@ -484,7 +484,7 @@ bool rtl92d_phy_config_rf_with_headerfile(struct ieee80211_hw *hw,
* pathA or mac1 has to set phy0&phy1 pathA */
if ((content == radiob_txt) && (rfpath == RF90_PATH_A)) {
rtl_dbg(rtlpriv, COMP_INIT, DBG_LOUD,
- " ===> althougth Path A, we load radiob.txt\n");
+ " ===> although Path A, we load radiob.txt\n");
radioa_arraylen = radiob_arraylen;
radioa_array_table = radiob_array_table;
}
@@ -750,7 +750,7 @@ static void _rtl92d_phy_switch_rf_setting(struct ieee80211_hw *hw, u8 channel)
&& rtlhal->interfaceindex == 1) {
need_pwr_down = rtl92d_phy_enable_anotherphy(hw, false);
rtlhal->during_mac1init_radioa = true;
- /* asume no this case */
+ /* assume no this case */
if (need_pwr_down)
rtl92d_phy_enable_rf_env(hw, path,
&u4regvalue);
@@ -1885,7 +1885,7 @@ static void _rtl92d_phy_reload_lck_setting(struct ieee80211_hw *hw,
bneed_powerdown_radio =
rtl92d_phy_enable_anotherphy(hw, false);
rtlpriv->rtlhal.during_mac1init_radioa = true;
- /* asume no this case */
+ /* assume no this case */
if (bneed_powerdown_radio)
rtl92d_phy_enable_rf_env(hw, erfpath,
&u4regvalue);
@@ -2055,11 +2055,6 @@ void rtl92d_phy_lc_calibrate(struct ieee80211_hw *hw, bool is2t)
RTPRINT(rtlpriv, FINIT, INIT_IQK, "LCK:Finish!!!\n");
}
-void rtl92d_phy_ap_calibrate(struct ieee80211_hw *hw, s8 delta)
-{
- return;
-}
-
static bool _rtl92d_phy_set_sw_chnl_cmdarray(struct swchnlcmd *cmdtable,
u32 cmdtableidx, u32 cmdtablesz, enum swchnlcmd_id cmdid,
u32 para1, u32 para2, u32 msdelay)
diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192de/phy.h b/drivers/net/wireless/realtek/rtlwifi/rtl8192de/phy.h
index bbe9ef77225e..a9bfe54f2802 100644
--- a/drivers/net/wireless/realtek/rtlwifi/rtl8192de/phy.h
+++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192de/phy.h
@@ -83,7 +83,6 @@ void rtl92d_phy_set_poweron(struct ieee80211_hw *hw);
bool rtl92d_phy_check_poweroff(struct ieee80211_hw *hw);
void rtl92d_phy_lc_calibrate(struct ieee80211_hw *hw, bool is2t);
void rtl92d_update_bbrf_configuration(struct ieee80211_hw *hw);
-void rtl92d_phy_ap_calibrate(struct ieee80211_hw *hw, s8 delta);
void rtl92d_phy_iq_calibrate(struct ieee80211_hw *hw);
void rtl92d_phy_reload_iqk_setting(struct ieee80211_hw *hw, u8 channel);
diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192du/phy.c b/drivers/net/wireless/realtek/rtlwifi/rtl8192du/phy.c
index 289ec71ce3e5..8c2167cc1f13 100644
--- a/drivers/net/wireless/realtek/rtlwifi/rtl8192du/phy.c
+++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192du/phy.c
@@ -2445,11 +2445,6 @@ void rtl92du_phy_lc_calibrate(struct ieee80211_hw *hw, bool is2t)
RTPRINT(rtlpriv, FINIT, INIT_IQK, "LCK:Finish!!!\n");
}
-void rtl92du_phy_ap_calibrate(struct ieee80211_hw *hw, s8 delta)
-{
- /* Nothing to do. */
-}
-
u8 rtl92du_phy_sw_chnl(struct ieee80211_hw *hw)
{
struct rtl_priv *rtlpriv = rtl_priv(hw);
diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192du/phy.h b/drivers/net/wireless/realtek/rtlwifi/rtl8192du/phy.h
index 090a6203db7e..a107a5a76beb 100644
--- a/drivers/net/wireless/realtek/rtlwifi/rtl8192du/phy.h
+++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192du/phy.h
@@ -24,7 +24,6 @@ void rtl92du_phy_set_poweron(struct ieee80211_hw *hw);
bool rtl92du_phy_check_poweroff(struct ieee80211_hw *hw);
void rtl92du_phy_lc_calibrate(struct ieee80211_hw *hw, bool is2t);
void rtl92du_update_bbrf_configuration(struct ieee80211_hw *hw);
-void rtl92du_phy_ap_calibrate(struct ieee80211_hw *hw, s8 delta);
void rtl92du_phy_iq_calibrate(struct ieee80211_hw *hw);
void rtl92du_phy_reload_iqk_setting(struct ieee80211_hw *hw, u8 channel);
void rtl92du_phy_init_pa_bias(struct ieee80211_hw *hw);
diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192ee/phy.c b/drivers/net/wireless/realtek/rtlwifi/rtl8192ee/phy.c
index 73ef602bfb01..1e7f0cd1c86e 100644
--- a/drivers/net/wireless/realtek/rtlwifi/rtl8192ee/phy.c
+++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192ee/phy.c
@@ -2926,10 +2926,6 @@ void rtl92ee_phy_lc_calibrate(struct ieee80211_hw *hw)
rtlphy->lck_inprogress = false;
}
-void rtl92ee_phy_ap_calibrate(struct ieee80211_hw *hw, s8 delta)
-{
-}
-
void rtl92ee_phy_set_rfpath_switch(struct ieee80211_hw *hw, bool bmain)
{
_rtl92ee_phy_set_rfpath_switch(hw, bmain, false);
diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192ee/phy.h b/drivers/net/wireless/realtek/rtlwifi/rtl8192ee/phy.h
index 1a5dbc628379..ec4c26b81c48 100644
--- a/drivers/net/wireless/realtek/rtlwifi/rtl8192ee/phy.h
+++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192ee/phy.h
@@ -119,7 +119,6 @@ void rtl92ee_phy_set_bw_mode(struct ieee80211_hw *hw,
void rtl92ee_phy_sw_chnl_callback(struct ieee80211_hw *hw);
u8 rtl92ee_phy_sw_chnl(struct ieee80211_hw *hw);
void rtl92ee_phy_iq_calibrate(struct ieee80211_hw *hw, bool b_recovery);
-void rtl92ee_phy_ap_calibrate(struct ieee80211_hw *hw, s8 delta);
void rtl92ee_phy_lc_calibrate(struct ieee80211_hw *hw);
void rtl92ee_phy_set_rfpath_switch(struct ieee80211_hw *hw, bool bmain);
bool rtl92ee_phy_config_rf_with_headerfile(struct ieee80211_hw *hw,
diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/phy.c b/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/phy.c
index 9eddbada8af1..13a05066e8a6 100644
--- a/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/phy.c
+++ b/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/phy.c
@@ -4586,10 +4586,6 @@ void rtl8821ae_phy_lc_calibrate(struct ieee80211_hw *hw)
{
}
-void rtl8821ae_phy_ap_calibrate(struct ieee80211_hw *hw, s8 delta)
-{
-}
-
void rtl8821ae_phy_set_rfpath_switch(struct ieee80211_hw *hw, bool bmain)
{
_rtl8821ae_phy_set_rfpath_switch(hw, bmain);
diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/phy.h b/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/phy.h
index 35b7d0f70125..90bf5462a3f8 100644
--- a/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/phy.h
+++ b/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/phy.h
@@ -214,7 +214,6 @@ void rtl8821ae_phy_iq_calibrate(struct ieee80211_hw *hw,
bool b_recovery);
void rtl8812ae_phy_iq_calibrate(struct ieee80211_hw *hw,
bool b_recovery);
-void rtl8821ae_phy_ap_calibrate(struct ieee80211_hw *hw, s8 delta);
void rtl8821ae_phy_lc_calibrate(struct ieee80211_hw *hw);
void rtl8821ae_phy_set_rfpath_switch(struct ieee80211_hw *hw, bool bmain);
bool rtl8812ae_phy_config_rf_with_headerfile(struct ieee80211_hw *hw,
diff --git a/drivers/net/wireless/realtek/rtlwifi/usb.c b/drivers/net/wireless/realtek/rtlwifi/usb.c
index f5718e570011..d35ed56d6db9 100644
--- a/drivers/net/wireless/realtek/rtlwifi/usb.c
+++ b/drivers/net/wireless/realtek/rtlwifi/usb.c
@@ -1077,15 +1077,3 @@ void rtl_usb_disconnect(struct usb_interface *intf)
ieee80211_free_hw(hw);
}
EXPORT_SYMBOL(rtl_usb_disconnect);
-
-int rtl_usb_suspend(struct usb_interface *pusb_intf, pm_message_t message)
-{
- return 0;
-}
-EXPORT_SYMBOL(rtl_usb_suspend);
-
-int rtl_usb_resume(struct usb_interface *pusb_intf)
-{
- return 0;
-}
-EXPORT_SYMBOL(rtl_usb_resume);
diff --git a/drivers/net/wireless/realtek/rtlwifi/usb.h b/drivers/net/wireless/realtek/rtlwifi/usb.h
index b66d6f9ae564..b873bbc9c4c2 100644
--- a/drivers/net/wireless/realtek/rtlwifi/usb.h
+++ b/drivers/net/wireless/realtek/rtlwifi/usb.h
@@ -138,7 +138,5 @@ int rtl_usb_probe(struct usb_interface *intf,
const struct usb_device_id *id,
const struct rtl_hal_cfg *rtl92cu_hal_cfg);
void rtl_usb_disconnect(struct usb_interface *intf);
-int rtl_usb_suspend(struct usb_interface *pusb_intf, pm_message_t message);
-int rtl_usb_resume(struct usb_interface *pusb_intf);
#endif
diff --git a/drivers/net/wireless/realtek/rtw88/Kconfig b/drivers/net/wireless/realtek/rtw88/Kconfig
index ab21b8059e0b..3736f290bd42 100644
--- a/drivers/net/wireless/realtek/rtw88/Kconfig
+++ b/drivers/net/wireless/realtek/rtw88/Kconfig
@@ -54,6 +54,9 @@ config RTW88_8812A
tristate
select RTW88_88XXA
+config RTW88_8814A
+ tristate
+
config RTW88_8822BE
tristate "Realtek 8822BE PCI wireless network adapter"
depends on PCI
@@ -222,6 +225,28 @@ config RTW88_8812AU
802.11ac USB wireless network adapter
+config RTW88_8814AE
+ tristate "Realtek 8814AE PCI wireless network adapter"
+ depends on PCI
+ select RTW88_CORE
+ select RTW88_PCI
+ select RTW88_8814A
+ help
+ Select this option will enable support for 8814AE chipset
+
+ 802.11ac PCIe wireless network adapter
+
+config RTW88_8814AU
+ tristate "Realtek 8814AU USB wireless network adapter"
+ depends on USB
+ select RTW88_CORE
+ select RTW88_USB
+ select RTW88_8814A
+ help
+ Select this option will enable support for 8814AU chipset
+
+ 802.11ac USB wireless network adapter
+
config RTW88_DEBUG
bool "Realtek rtw88 debug support"
depends on RTW88_CORE
diff --git a/drivers/net/wireless/realtek/rtw88/Makefile b/drivers/net/wireless/realtek/rtw88/Makefile
index 0cbbb366e393..0b3da05a2938 100644
--- a/drivers/net/wireless/realtek/rtw88/Makefile
+++ b/drivers/net/wireless/realtek/rtw88/Makefile
@@ -94,6 +94,15 @@ rtw88_8821au-objs := rtw8821au.o
obj-$(CONFIG_RTW88_8812AU) += rtw88_8812au.o
rtw88_8812au-objs := rtw8812au.o
+obj-$(CONFIG_RTW88_8814A) += rtw88_8814a.o
+rtw88_8814a-objs := rtw8814a.o rtw8814a_table.o
+
+obj-$(CONFIG_RTW88_8814AE) += rtw88_8814ae.o
+rtw88_8814ae-objs := rtw8814ae.o
+
+obj-$(CONFIG_RTW88_8814AU) += rtw88_8814au.o
+rtw88_8814au-objs := rtw8814au.o
+
obj-$(CONFIG_RTW88_PCI) += rtw88_pci.o
rtw88_pci-objs := pci.o
diff --git a/drivers/net/wireless/realtek/rtw88/coex.c b/drivers/net/wireless/realtek/rtw88/coex.c
index c929db1e53ca..64904278ddad 100644
--- a/drivers/net/wireless/realtek/rtw88/coex.c
+++ b/drivers/net/wireless/realtek/rtw88/coex.c
@@ -309,7 +309,7 @@ static void rtw_coex_tdma_timer_base(struct rtw_dev *rtwdev, u8 type)
{
struct rtw_coex *coex = &rtwdev->coex;
struct rtw_coex_stat *coex_stat = &coex->stat;
- u8 para[2] = {0};
+ u8 para[6] = {};
u8 times;
u16 tbtt_interval = coex_stat->wl_beacon_interval;
diff --git a/drivers/net/wireless/realtek/rtw88/debug.c b/drivers/net/wireless/realtek/rtw88/debug.c
index 364ec0436d0f..b67d69b01f87 100644
--- a/drivers/net/wireless/realtek/rtw88/debug.c
+++ b/drivers/net/wireless/realtek/rtw88/debug.c
@@ -654,10 +654,10 @@ static void rtw_print_rate(struct seq_file *m, u8 rate)
case DESC_RATE6M...DESC_RATE54M:
rtw_print_ofdm_rate_txt(m, rate);
break;
- case DESC_RATEMCS0...DESC_RATEMCS15:
+ case DESC_RATEMCS0...DESC_RATEMCS31:
rtw_print_ht_rate_txt(m, rate);
break;
- case DESC_RATEVHT1SS_MCS0...DESC_RATEVHT2SS_MCS9:
+ case DESC_RATEVHT1SS_MCS0...DESC_RATEVHT4SS_MCS9:
rtw_print_vht_rate_txt(m, rate);
break;
default:
@@ -692,9 +692,11 @@ static int rtw_debugfs_get_tx_pwr_tbl(struct seq_file *m, void *v)
{
struct rtw_debugfs_priv *debugfs_priv = m->private;
struct rtw_dev *rtwdev = debugfs_priv->rtwdev;
+ struct rtw_power_params pwr_param = {0};
struct rtw_hal *hal = &rtwdev->hal;
+ u8 nss = rtwdev->efuse.hw_cap.nss;
u8 path, rate, bw, ch, regd;
- struct rtw_power_params pwr_param = {0};
+ u8 max_ht_rate, max_rate;
mutex_lock(&rtwdev->mutex);
bw = hal->current_band_width;
@@ -707,19 +709,23 @@ static int rtw_debugfs_get_tx_pwr_tbl(struct seq_file *m, void *v)
seq_printf(m, "%-4s %-10s %-9s %-9s (%-4s %-4s %-4s) %-4s\n",
"path", "rate", "pwr", "base", "byr", "lmt", "sar", "rem");
+ max_ht_rate = DESC_RATEMCS0 + nss * 8 - 1;
+
+ if (rtwdev->chip->vht_supported)
+ max_rate = DESC_RATEVHT1SS_MCS0 + nss * 10 - 1;
+ else
+ max_rate = max_ht_rate;
+
mutex_lock(&hal->tx_power_mutex);
- for (path = RF_PATH_A; path <= RF_PATH_B; path++) {
+ for (path = RF_PATH_A; path < hal->rf_path_num; path++) {
/* there is no CCK rates used in 5G */
if (hal->current_band_type == RTW_BAND_5G)
rate = DESC_RATE6M;
else
rate = DESC_RATE1M;
- /* now, not support vht 3ss and vht 4ss*/
- for (; rate <= DESC_RATEVHT2SS_MCS9; rate++) {
- /* now, not support ht 3ss and ht 4ss*/
- if (rate > DESC_RATEMCS15 &&
- rate < DESC_RATEVHT1SS_MCS0)
+ for (; rate <= max_rate; rate++) {
+ if (rate > max_ht_rate && rate <= DESC_RATEMCS31)
continue;
rtw_get_tx_power_params(rtwdev, path, rate, bw,
@@ -849,20 +855,28 @@ static int rtw_debugfs_get_phy_info(struct seq_file *m, void *v)
last_cnt->num_qry_pkt[rate_id + 9]);
}
- seq_printf(m, "[RSSI(dBm)] = {%d, %d}\n",
+ seq_printf(m, "[RSSI(dBm)] = {%d, %d, %d, %d}\n",
dm_info->rssi[RF_PATH_A] - 100,
- dm_info->rssi[RF_PATH_B] - 100);
- seq_printf(m, "[Rx EVM(dB)] = {-%d, -%d}\n",
+ dm_info->rssi[RF_PATH_B] - 100,
+ dm_info->rssi[RF_PATH_C] - 100,
+ dm_info->rssi[RF_PATH_D] - 100);
+ seq_printf(m, "[Rx EVM(dB)] = {-%d, -%d, -%d, -%d}\n",
dm_info->rx_evm_dbm[RF_PATH_A],
- dm_info->rx_evm_dbm[RF_PATH_B]);
- seq_printf(m, "[Rx SNR] = {%d, %d}\n",
+ dm_info->rx_evm_dbm[RF_PATH_B],
+ dm_info->rx_evm_dbm[RF_PATH_C],
+ dm_info->rx_evm_dbm[RF_PATH_D]);
+ seq_printf(m, "[Rx SNR] = {%d, %d, %d, %d}\n",
dm_info->rx_snr[RF_PATH_A],
- dm_info->rx_snr[RF_PATH_B]);
- seq_printf(m, "[CFO_tail(KHz)] = {%d, %d}\n",
+ dm_info->rx_snr[RF_PATH_B],
+ dm_info->rx_snr[RF_PATH_C],
+ dm_info->rx_snr[RF_PATH_D]);
+ seq_printf(m, "[CFO_tail(KHz)] = {%d, %d, %d, %d}\n",
dm_info->cfo_tail[RF_PATH_A],
- dm_info->cfo_tail[RF_PATH_B]);
+ dm_info->cfo_tail[RF_PATH_B],
+ dm_info->cfo_tail[RF_PATH_C],
+ dm_info->cfo_tail[RF_PATH_D]);
- if (dm_info->curr_rx_rate >= DESC_RATE11M) {
+ if (dm_info->curr_rx_rate >= DESC_RATE6M) {
seq_puts(m, "[Rx Average Status]:\n");
seq_printf(m, " * OFDM, EVM: {-%d}, SNR: {%d}\n",
(u8)ewma_evm_read(&ewma_evm[RTW_EVM_OFDM]),
@@ -875,6 +889,13 @@ static int rtw_debugfs_get_phy_info(struct seq_file *m, void *v)
(u8)ewma_evm_read(&ewma_evm[RTW_EVM_2SS_B]),
(u8)ewma_snr_read(&ewma_snr[RTW_SNR_2SS_A]),
(u8)ewma_snr_read(&ewma_snr[RTW_SNR_2SS_B]));
+ seq_printf(m, " * 3SS, EVM: {-%d, -%d, -%d}, SNR: {%d, %d, %d}\n",
+ (u8)ewma_evm_read(&ewma_evm[RTW_EVM_3SS_A]),
+ (u8)ewma_evm_read(&ewma_evm[RTW_EVM_3SS_B]),
+ (u8)ewma_evm_read(&ewma_evm[RTW_EVM_3SS_C]),
+ (u8)ewma_snr_read(&ewma_snr[RTW_SNR_3SS_A]),
+ (u8)ewma_snr_read(&ewma_snr[RTW_SNR_3SS_B]),
+ (u8)ewma_snr_read(&ewma_snr[RTW_SNR_3SS_C]));
}
seq_puts(m, "[Rx Counter]:\n");
diff --git a/drivers/net/wireless/realtek/rtw88/fw.c b/drivers/net/wireless/realtek/rtw88/fw.c
index 02389b7c6876..4fc78b882080 100644
--- a/drivers/net/wireless/realtek/rtw88/fw.c
+++ b/drivers/net/wireless/realtek/rtw88/fw.c
@@ -735,6 +735,7 @@ void rtw_fw_send_ra_info(struct rtw_dev *rtwdev, struct rtw_sta_info *si,
{
u8 h2c_pkt[H2C_PKT_SIZE] = {0};
bool disable_pt = true;
+ u32 mask_hi;
SET_H2C_CMD_ID_CLASS(h2c_pkt, H2C_CMD_RA_INFO);
@@ -755,6 +756,20 @@ void rtw_fw_send_ra_info(struct rtw_dev *rtwdev, struct rtw_sta_info *si,
si->init_ra_lv = 0;
rtw_fw_send_h2c_command(rtwdev, h2c_pkt);
+
+ if (rtwdev->chip->id != RTW_CHIP_TYPE_8814A)
+ return;
+
+ SET_H2C_CMD_ID_CLASS(h2c_pkt, H2C_CMD_RA_INFO_HI);
+
+ mask_hi = si->ra_mask >> 32;
+
+ SET_RA_INFO_RA_MASK0(h2c_pkt, (mask_hi & 0xff));
+ SET_RA_INFO_RA_MASK1(h2c_pkt, (mask_hi & 0xff00) >> 8);
+ SET_RA_INFO_RA_MASK2(h2c_pkt, (mask_hi & 0xff0000) >> 16);
+ SET_RA_INFO_RA_MASK3(h2c_pkt, (mask_hi & 0xff000000) >> 24);
+
+ rtw_fw_send_h2c_command(rtwdev, h2c_pkt);
}
void rtw_fw_media_status_report(struct rtw_dev *rtwdev, u8 mac_id, bool connect)
@@ -1451,7 +1466,7 @@ void rtw_add_rsvd_page_sta(struct rtw_dev *rtwdev,
int rtw_fw_write_data_rsvd_page(struct rtw_dev *rtwdev, u16 pg_addr,
u8 *buf, u32 size)
{
- u8 bckp[2];
+ u8 bckp[3];
u8 val;
u16 rsvd_pg_head;
u32 bcn_valid_addr;
@@ -1463,6 +1478,8 @@ int rtw_fw_write_data_rsvd_page(struct rtw_dev *rtwdev, u16 pg_addr,
if (!size)
return -EINVAL;
+ bckp[2] = rtw_read8(rtwdev, REG_BCN_CTRL);
+
if (rtw_chip_wcpu_11n(rtwdev)) {
rtw_write32_set(rtwdev, REG_DWBCN0_CTRL, BIT_BCN_VALID);
} else {
@@ -1476,6 +1493,9 @@ int rtw_fw_write_data_rsvd_page(struct rtw_dev *rtwdev, u16 pg_addr,
val |= BIT_ENSWBCN >> 8;
rtw_write8(rtwdev, REG_CR + 1, val);
+ rtw_write8(rtwdev, REG_BCN_CTRL,
+ (bckp[2] & ~BIT_EN_BCN_FUNCTION) | BIT_DIS_TSF_UDT);
+
if (rtw_hci_type(rtwdev) == RTW_HCI_TYPE_PCIE) {
val = rtw_read8(rtwdev, REG_FWHW_TXQ_CTRL + 2);
bckp[1] = val;
@@ -1506,6 +1526,7 @@ restore:
rsvd_pg_head = rtwdev->fifo.rsvd_boundary;
rtw_write16(rtwdev, REG_FIFOPAGE_CTRL_2,
rsvd_pg_head | BIT_BCN_VALID_V1);
+ rtw_write8(rtwdev, REG_BCN_CTRL, bckp[2]);
if (rtw_hci_type(rtwdev) == RTW_HCI_TYPE_PCIE)
rtw_write8(rtwdev, REG_FWHW_TXQ_CTRL + 2, bckp[1]);
rtw_write8(rtwdev, REG_CR + 1, bckp[0]);
diff --git a/drivers/net/wireless/realtek/rtw88/fw.h b/drivers/net/wireless/realtek/rtw88/fw.h
index 404de1b0c407..48ad9ceab6ea 100644
--- a/drivers/net/wireless/realtek/rtw88/fw.h
+++ b/drivers/net/wireless/realtek/rtw88/fw.h
@@ -557,6 +557,7 @@ static inline void rtw_h2c_pkt_set_header(u8 *h2c_pkt, u8 sub_id)
#define H2C_CMD_DEFAULT_PORT 0x2c
#define H2C_CMD_RA_INFO 0x40
#define H2C_CMD_RSSI_MONITOR 0x42
+#define H2C_CMD_RA_INFO_HI 0x46
#define H2C_CMD_BCN_FILTER_OFFLOAD_P0 0x56
#define H2C_CMD_BCN_FILTER_OFFLOAD_P1 0x57
#define H2C_CMD_WL_PHY_INFO 0x58
diff --git a/drivers/net/wireless/realtek/rtw88/hci.h b/drivers/net/wireless/realtek/rtw88/hci.h
index 96aeda26014e..d4bee9c3ecfe 100644
--- a/drivers/net/wireless/realtek/rtw88/hci.h
+++ b/drivers/net/wireless/realtek/rtw88/hci.h
@@ -19,6 +19,8 @@ struct rtw_hci_ops {
void (*link_ps)(struct rtw_dev *rtwdev, bool enter);
void (*interface_cfg)(struct rtw_dev *rtwdev);
void (*dynamic_rx_agg)(struct rtw_dev *rtwdev, bool enable);
+ void (*write_firmware_page)(struct rtw_dev *rtwdev, u32 page,
+ const u8 *data, u32 size);
int (*write_data_rsvd_page)(struct rtw_dev *rtwdev, u8 *buf, u32 size);
int (*write_data_h2c)(struct rtw_dev *rtwdev, u8 *buf, u32 size);
@@ -79,6 +81,12 @@ static inline void rtw_hci_dynamic_rx_agg(struct rtw_dev *rtwdev, bool enable)
rtwdev->hci.ops->dynamic_rx_agg(rtwdev, enable);
}
+static inline void rtw_hci_write_firmware_page(struct rtw_dev *rtwdev, u32 page,
+ const u8 *data, u32 size)
+{
+ rtwdev->hci.ops->write_firmware_page(rtwdev, page, data, size);
+}
+
static inline int
rtw_hci_write_data_rsvd_page(struct rtw_dev *rtwdev, u8 *buf, u32 size)
{
diff --git a/drivers/net/wireless/realtek/rtw88/mac.c b/drivers/net/wireless/realtek/rtw88/mac.c
index cae9cca6dca3..f66d1b302dc5 100644
--- a/drivers/net/wireless/realtek/rtw88/mac.c
+++ b/drivers/net/wireless/realtek/rtw88/mac.c
@@ -291,6 +291,7 @@ static int rtw_mac_power_switch(struct rtw_dev *rtwdev, bool pwr_on)
if (rtw_read8(rtwdev, REG_CR) == 0xea)
cur_pwr = false;
else if (rtw_hci_type(rtwdev) == RTW_HCI_TYPE_USB &&
+ chip->id != RTW_CHIP_TYPE_8814A &&
(rtw_read8(rtwdev, REG_SYS_STATUS1 + 1) & BIT(0)))
cur_pwr = false;
else
@@ -784,7 +785,8 @@ static int __rtw_download_firmware(struct rtw_dev *rtwdev,
if (!check_firmware_size(data, size))
return -EINVAL;
- if (!ltecoex_read_reg(rtwdev, 0x38, &ltecoex_bckp))
+ if (rtwdev->chip->ltecoex_addr &&
+ !ltecoex_read_reg(rtwdev, 0x38, &ltecoex_bckp))
return -EBUSY;
wlan_cpu_enable(rtwdev, false);
@@ -802,7 +804,8 @@ static int __rtw_download_firmware(struct rtw_dev *rtwdev,
wlan_cpu_enable(rtwdev, true);
- if (!ltecoex_reg_write(rtwdev, 0x38, ltecoex_bckp)) {
+ if (rtwdev->chip->ltecoex_addr &&
+ !ltecoex_reg_write(rtwdev, 0x38, ltecoex_bckp)) {
ret = -EBUSY;
goto dlfw_fail;
}
@@ -853,8 +856,8 @@ fwdl_ready:
}
}
-static void
-write_firmware_page(struct rtw_dev *rtwdev, u32 page, const u8 *data, u32 size)
+void rtw_write_firmware_page(struct rtw_dev *rtwdev, u32 page,
+ const u8 *data, u32 size)
{
u32 val32;
u32 block_nr;
@@ -884,6 +887,7 @@ write_firmware_page(struct rtw_dev *rtwdev, u32 page, const u8 *data, u32 size)
rtw_write32(rtwdev, write_addr, le32_to_cpu(remain_data));
}
}
+EXPORT_SYMBOL(rtw_write_firmware_page);
static int
download_firmware_legacy(struct rtw_dev *rtwdev, const u8 *data, u32 size)
@@ -901,11 +905,13 @@ download_firmware_legacy(struct rtw_dev *rtwdev, const u8 *data, u32 size)
rtw_write8_set(rtwdev, REG_MCUFW_CTRL, BIT_FWDL_CHK_RPT);
for (page = 0; page < total_page; page++) {
- write_firmware_page(rtwdev, page, data, DLFW_PAGE_SIZE_LEGACY);
+ rtw_hci_write_firmware_page(rtwdev, page, data,
+ DLFW_PAGE_SIZE_LEGACY);
data += DLFW_PAGE_SIZE_LEGACY;
}
if (last_page_size)
- write_firmware_page(rtwdev, page, data, last_page_size);
+ rtw_hci_write_firmware_page(rtwdev, page, data,
+ last_page_size);
if (!check_hw_ready(rtwdev, REG_MCUFW_CTRL, BIT_FWDL_CHK_RPT, 1)) {
rtw_err(rtwdev, "failed to check download firmware report\n");
diff --git a/drivers/net/wireless/realtek/rtw88/mac.h b/drivers/net/wireless/realtek/rtw88/mac.h
index 6905e2747372..e92b1483728d 100644
--- a/drivers/net/wireless/realtek/rtw88/mac.h
+++ b/drivers/net/wireless/realtek/rtw88/mac.h
@@ -34,6 +34,8 @@ int rtw_pwr_seq_parser(struct rtw_dev *rtwdev,
const struct rtw_pwr_seq_cmd * const *cmd_seq);
int rtw_mac_power_on(struct rtw_dev *rtwdev);
void rtw_mac_power_off(struct rtw_dev *rtwdev);
+void rtw_write_firmware_page(struct rtw_dev *rtwdev, u32 page,
+ const u8 *data, u32 size);
int rtw_download_firmware(struct rtw_dev *rtwdev, struct rtw_fw_state *fw);
int rtw_mac_init(struct rtw_dev *rtwdev);
void rtw_mac_flush_queues(struct rtw_dev *rtwdev, u32 queues, bool drop);
diff --git a/drivers/net/wireless/realtek/rtw88/mac80211.c b/drivers/net/wireless/realtek/rtw88/mac80211.c
index 026fbf4ad9cc..77f9fbe1870c 100644
--- a/drivers/net/wireless/realtek/rtw88/mac80211.c
+++ b/drivers/net/wireless/realtek/rtw88/mac80211.c
@@ -396,6 +396,8 @@ static void rtw_ops_bss_info_changed(struct ieee80211_hw *hw,
if (rtw_bf_support)
rtw_bf_assoc(rtwdev, vif, conf);
+ rtw_set_ampdu_factor(rtwdev, vif, conf);
+
rtw_fw_beacon_filter_config(rtwdev, true, vif);
} else {
rtw_leave_lps(rtwdev);
diff --git a/drivers/net/wireless/realtek/rtw88/main.c b/drivers/net/wireless/realtek/rtw88/main.c
index 0cee0fd8c0ef..c4de5d114eda 100644
--- a/drivers/net/wireless/realtek/rtw88/main.c
+++ b/drivers/net/wireless/realtek/rtw88/main.c
@@ -136,7 +136,7 @@ u16 rtw_desc_to_bitrate(u8 desc_rate)
return rate.bitrate;
}
-static struct ieee80211_supported_band rtw_band_2ghz = {
+static const struct ieee80211_supported_band rtw_band_2ghz = {
.band = NL80211_BAND_2GHZ,
.channels = rtw_channeltable_2g,
@@ -149,7 +149,7 @@ static struct ieee80211_supported_band rtw_band_2ghz = {
.vht_cap = {0},
};
-static struct ieee80211_supported_band rtw_band_5ghz = {
+static const struct ieee80211_supported_band rtw_band_5ghz = {
.band = NL80211_BAND_5GHZ,
.channels = rtw_channeltable_5g,
@@ -1234,7 +1234,9 @@ void rtw_update_sta_info(struct rtw_dev *rtwdev, struct rtw_sta_info *si,
if (sta->deflink.vht_cap.cap & IEEE80211_VHT_CAP_RXLDPC)
ldpc_en = VHT_LDPC_EN;
} else if (sta->deflink.ht_cap.ht_supported) {
- ra_mask |= (sta->deflink.ht_cap.mcs.rx_mask[1] << 20) |
+ ra_mask |= ((u64)sta->deflink.ht_cap.mcs.rx_mask[3] << 36) |
+ ((u64)sta->deflink.ht_cap.mcs.rx_mask[2] << 28) |
+ (sta->deflink.ht_cap.mcs.rx_mask[1] << 20) |
(sta->deflink.ht_cap.mcs.rx_mask[0] << 12);
if (sta->deflink.ht_cap.cap & IEEE80211_HT_CAP_RX_STBC)
stbc_en = HT_STBC_EN;
@@ -1244,6 +1246,9 @@ void rtw_update_sta_info(struct rtw_dev *rtwdev, struct rtw_sta_info *si,
if (efuse->hw_cap.nss == 1 || rtwdev->hal.txrx_1ss)
ra_mask &= RA_MASK_VHT_RATES_1SS | RA_MASK_HT_RATES_1SS;
+ else if (efuse->hw_cap.nss == 2)
+ ra_mask &= RA_MASK_VHT_RATES_2SS | RA_MASK_HT_RATES_2SS |
+ RA_MASK_VHT_RATES_1SS | RA_MASK_HT_RATES_1SS;
if (hal->current_band_type == RTW_BAND_5G) {
ra_mask |= (u64)sta->deflink.supp_rates[NL80211_BAND_5GHZ] << 4;
@@ -1302,10 +1307,9 @@ void rtw_update_sta_info(struct rtw_dev *rtwdev, struct rtw_sta_info *si,
break;
}
- if (sta->deflink.vht_cap.vht_supported && ra_mask & 0xffc00000)
- tx_num = 2;
- else if (sta->deflink.ht_cap.ht_supported && ra_mask & 0xfff00000)
- tx_num = 2;
+ if (sta->deflink.vht_cap.vht_supported ||
+ sta->deflink.ht_cap.ht_supported)
+ tx_num = efuse->hw_cap.nss;
rate_id = get_rate_id(wireless_set, bw_mode, tx_num);
@@ -1561,6 +1565,7 @@ static void rtw_init_ht_cap(struct rtw_dev *rtwdev,
{
const struct rtw_chip_info *chip = rtwdev->chip;
struct rtw_efuse *efuse = &rtwdev->efuse;
+ int i;
ht_cap->ht_supported = true;
ht_cap->cap = 0;
@@ -1580,25 +1585,20 @@ static void rtw_init_ht_cap(struct rtw_dev *rtwdev,
ht_cap->ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K;
ht_cap->ampdu_density = chip->ampdu_density;
ht_cap->mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED;
- if (efuse->hw_cap.nss > 1) {
- ht_cap->mcs.rx_mask[0] = 0xFF;
- ht_cap->mcs.rx_mask[1] = 0xFF;
- ht_cap->mcs.rx_mask[4] = 0x01;
- ht_cap->mcs.rx_highest = cpu_to_le16(300);
- } else {
- ht_cap->mcs.rx_mask[0] = 0xFF;
- ht_cap->mcs.rx_mask[1] = 0x00;
- ht_cap->mcs.rx_mask[4] = 0x01;
- ht_cap->mcs.rx_highest = cpu_to_le16(150);
- }
+
+ for (i = 0; i < efuse->hw_cap.nss; i++)
+ ht_cap->mcs.rx_mask[i] = 0xFF;
+ ht_cap->mcs.rx_mask[4] = 0x01;
+ ht_cap->mcs.rx_highest = cpu_to_le16(150 * efuse->hw_cap.nss);
}
static void rtw_init_vht_cap(struct rtw_dev *rtwdev,
struct ieee80211_sta_vht_cap *vht_cap)
{
struct rtw_efuse *efuse = &rtwdev->efuse;
- u16 mcs_map;
+ u16 mcs_map = 0;
__le16 highest;
+ int i;
if (efuse->hw_cap.ptcl != EFUSE_HW_CAP_IGNORE &&
efuse->hw_cap.ptcl != EFUSE_HW_CAP_PTCL_VHT)
@@ -1621,21 +1621,15 @@ static void rtw_init_vht_cap(struct rtw_dev *rtwdev,
if (rtw_chip_has_rx_ldpc(rtwdev))
vht_cap->cap |= IEEE80211_VHT_CAP_RXLDPC;
- mcs_map = IEEE80211_VHT_MCS_SUPPORT_0_9 << 0 |
- IEEE80211_VHT_MCS_NOT_SUPPORTED << 4 |
- IEEE80211_VHT_MCS_NOT_SUPPORTED << 6 |
- IEEE80211_VHT_MCS_NOT_SUPPORTED << 8 |
- IEEE80211_VHT_MCS_NOT_SUPPORTED << 10 |
- IEEE80211_VHT_MCS_NOT_SUPPORTED << 12 |
- IEEE80211_VHT_MCS_NOT_SUPPORTED << 14;
- if (efuse->hw_cap.nss > 1) {
- highest = cpu_to_le16(780);
- mcs_map |= IEEE80211_VHT_MCS_SUPPORT_0_9 << 2;
- } else {
- highest = cpu_to_le16(390);
- mcs_map |= IEEE80211_VHT_MCS_NOT_SUPPORTED << 2;
+ for (i = 0; i < 8; i++) {
+ if (i < efuse->hw_cap.nss)
+ mcs_map |= IEEE80211_VHT_MCS_SUPPORT_0_9 << (i * 2);
+ else
+ mcs_map |= IEEE80211_VHT_MCS_NOT_SUPPORTED << (i * 2);
}
+ highest = cpu_to_le16(390 * efuse->hw_cap.nss);
+
vht_cap->vht_mcs.rx_mcs_map = cpu_to_le16(mcs_map);
vht_cap->vht_mcs.tx_mcs_map = cpu_to_le16(mcs_map);
vht_cap->vht_mcs.rx_highest = highest;
@@ -2248,7 +2242,8 @@ int rtw_register_hw(struct rtw_dev *rtwdev, struct ieee80211_hw *hw)
ieee80211_hw_set(hw, SUPPORTS_PS);
ieee80211_hw_set(hw, SUPPORTS_DYNAMIC_PS);
ieee80211_hw_set(hw, SUPPORT_FAST_XMIT);
- ieee80211_hw_set(hw, SUPPORTS_AMSDU_IN_AMPDU);
+ if (rtwdev->chip->amsdu_in_ampdu)
+ ieee80211_hw_set(hw, SUPPORTS_AMSDU_IN_AMPDU);
ieee80211_hw_set(hw, HAS_RATE_CONTROL);
ieee80211_hw_set(hw, TX_AMSDU);
ieee80211_hw_set(hw, SINGLE_SCAN_ON_ALL_BANDS);
@@ -2453,6 +2448,38 @@ void rtw_core_enable_beacon(struct rtw_dev *rtwdev, bool enable)
}
}
+void rtw_set_ampdu_factor(struct rtw_dev *rtwdev, struct ieee80211_vif *vif,
+ struct ieee80211_bss_conf *bss_conf)
+{
+ const struct rtw_chip_ops *ops = rtwdev->chip->ops;
+ struct ieee80211_sta *sta;
+ u8 factor = 0xff;
+
+ if (!ops->set_ampdu_factor)
+ return;
+
+ rcu_read_lock();
+
+ sta = ieee80211_find_sta(vif, bss_conf->bssid);
+ if (!sta) {
+ rcu_read_unlock();
+ rtw_warn(rtwdev, "%s: failed to find station %pM\n",
+ __func__, bss_conf->bssid);
+ return;
+ }
+
+ if (sta->deflink.vht_cap.vht_supported)
+ factor = u32_get_bits(sta->deflink.vht_cap.cap,
+ IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK);
+ else if (sta->deflink.ht_cap.ht_supported)
+ factor = sta->deflink.ht_cap.ampdu_factor;
+
+ rcu_read_unlock();
+
+ if (factor != 0xff)
+ ops->set_ampdu_factor(rtwdev, factor);
+}
+
MODULE_AUTHOR("Realtek Corporation");
MODULE_DESCRIPTION("Realtek 802.11ac wireless core module");
MODULE_LICENSE("Dual BSD/GPL");
diff --git a/drivers/net/wireless/realtek/rtw88/main.h b/drivers/net/wireless/realtek/rtw88/main.h
index 62cd4c526301..b0f1fabe9554 100644
--- a/drivers/net/wireless/realtek/rtw88/main.h
+++ b/drivers/net/wireless/realtek/rtw88/main.h
@@ -61,7 +61,7 @@ enum rtw_hci_type {
};
struct rtw_hci {
- struct rtw_hci_ops *ops;
+ const struct rtw_hci_ops *ops;
enum rtw_hci_type type;
u32 rpwm_addr;
@@ -166,9 +166,14 @@ enum rtw_rate_section {
RTW_RATE_SECTION_HT_2S,
RTW_RATE_SECTION_VHT_1S,
RTW_RATE_SECTION_VHT_2S,
+ __RTW_RATE_SECTION_2SS_MAX = RTW_RATE_SECTION_VHT_2S,
+ RTW_RATE_SECTION_HT_3S,
+ RTW_RATE_SECTION_HT_4S,
+ RTW_RATE_SECTION_VHT_3S,
+ RTW_RATE_SECTION_VHT_4S,
/* keep last */
- RTW_RATE_SECTION_MAX,
+ RTW_RATE_SECTION_NUM,
};
enum rtw_wireless_set {
@@ -191,6 +196,7 @@ enum rtw_chip_type {
RTW_CHIP_TYPE_8703B,
RTW_CHIP_TYPE_8821A,
RTW_CHIP_TYPE_8812A,
+ RTW_CHIP_TYPE_8814A,
};
enum rtw_tx_queue_type {
@@ -380,6 +386,9 @@ enum rtw_evm {
RTW_EVM_1SS,
RTW_EVM_2SS_A,
RTW_EVM_2SS_B,
+ RTW_EVM_3SS_A,
+ RTW_EVM_3SS_B,
+ RTW_EVM_3SS_C,
/* keep it last */
RTW_EVM_NUM
};
@@ -397,6 +406,10 @@ enum rtw_snr {
RTW_SNR_2SS_B,
RTW_SNR_2SS_C,
RTW_SNR_2SS_D,
+ RTW_SNR_3SS_A,
+ RTW_SNR_3SS_B,
+ RTW_SNR_3SS_C,
+ RTW_SNR_3SS_D,
/* keep it last */
RTW_SNR_NUM
};
@@ -822,7 +835,7 @@ struct rtw_vif {
};
struct rtw_regulatory {
- char alpha2[2];
+ char alpha2[2] __nonstring;
u8 txpwr_regd_2g;
u8 txpwr_regd_5g;
};
@@ -865,6 +878,7 @@ struct rtw_chip_ops {
u32 antenna_rx);
void (*cfg_ldo25)(struct rtw_dev *rtwdev, bool enable);
void (*efuse_grant)(struct rtw_dev *rtwdev, bool enable);
+ void (*set_ampdu_factor)(struct rtw_dev *rtwdev, u8 factor);
void (*false_alarm_statistics)(struct rtw_dev *rtwdev);
void (*phy_calibration)(struct rtw_dev *rtwdev);
void (*dpk_track)(struct rtw_dev *rtwdev);
@@ -1130,14 +1144,26 @@ struct rtw_rfe_def {
* For 2G there are cck rate and ofdm rate with different settings.
*/
struct rtw_pwr_track_tbl {
+ const u8 *pwrtrk_5gd_n[RTW_PWR_TRK_5G_NUM];
+ const u8 *pwrtrk_5gd_p[RTW_PWR_TRK_5G_NUM];
+ const u8 *pwrtrk_5gc_n[RTW_PWR_TRK_5G_NUM];
+ const u8 *pwrtrk_5gc_p[RTW_PWR_TRK_5G_NUM];
const u8 *pwrtrk_5gb_n[RTW_PWR_TRK_5G_NUM];
const u8 *pwrtrk_5gb_p[RTW_PWR_TRK_5G_NUM];
const u8 *pwrtrk_5ga_n[RTW_PWR_TRK_5G_NUM];
const u8 *pwrtrk_5ga_p[RTW_PWR_TRK_5G_NUM];
+ const u8 *pwrtrk_2gd_n;
+ const u8 *pwrtrk_2gd_p;
+ const u8 *pwrtrk_2gc_n;
+ const u8 *pwrtrk_2gc_p;
const u8 *pwrtrk_2gb_n;
const u8 *pwrtrk_2gb_p;
const u8 *pwrtrk_2ga_n;
const u8 *pwrtrk_2ga_p;
+ const u8 *pwrtrk_2g_cckd_n;
+ const u8 *pwrtrk_2g_cckd_p;
+ const u8 *pwrtrk_2g_cckc_n;
+ const u8 *pwrtrk_2g_cckc_p;
const u8 *pwrtrk_2g_cckb_n;
const u8 *pwrtrk_2g_cckb_p;
const u8 *pwrtrk_2g_ccka_n;
@@ -1204,6 +1230,7 @@ struct rtw_chip_info {
u16 fw_fifo_addr[RTW_FW_FIFO_MAX];
const struct rtw_fwcd_segs *fwcd_segs;
+ bool amsdu_in_ampdu;
u8 usb_tx_agg_desc_num;
bool hw_feature_report;
u8 c2h_ra_report_size;
@@ -1227,8 +1254,8 @@ struct rtw_chip_info {
const struct rtw_hw_reg *dig;
const struct rtw_hw_reg *dig_cck;
- u32 rf_base_addr[2];
- u32 rf_sipi_addr[2];
+ u32 rf_base_addr[RTW_RF_PATH_MAX];
+ u32 rf_sipi_addr[RTW_RF_PATH_MAX];
const struct rtw_rf_sipi_addr *rf_sipi_read_addr;
u8 fix_rf_phy_num;
const struct rtw_ltecoex_addr *ltecoex_addr;
@@ -1924,7 +1951,7 @@ union rtw_sar_cfg {
struct rtw_sar {
enum rtw_sar_sources src;
- union rtw_sar_cfg cfg[RTW_RF_PATH_MAX][RTW_RATE_SECTION_MAX];
+ union rtw_sar_cfg cfg[RTW_RF_PATH_MAX][RTW_RATE_SECTION_NUM];
};
struct rtw_hal {
@@ -1968,16 +1995,16 @@ struct rtw_hal {
s8 tx_pwr_by_rate_offset_5g[RTW_RF_PATH_MAX]
[DESC_RATE_MAX];
s8 tx_pwr_by_rate_base_2g[RTW_RF_PATH_MAX]
- [RTW_RATE_SECTION_MAX];
+ [RTW_RATE_SECTION_NUM];
s8 tx_pwr_by_rate_base_5g[RTW_RF_PATH_MAX]
- [RTW_RATE_SECTION_MAX];
+ [RTW_RATE_SECTION_NUM];
s8 tx_pwr_limit_2g[RTW_REGD_MAX]
[RTW_CHANNEL_WIDTH_MAX]
- [RTW_RATE_SECTION_MAX]
+ [RTW_RATE_SECTION_NUM]
[RTW_MAX_CHANNEL_NUM_2G];
s8 tx_pwr_limit_5g[RTW_REGD_MAX]
[RTW_CHANNEL_WIDTH_MAX]
- [RTW_RATE_SECTION_MAX]
+ [RTW_RATE_SECTION_NUM]
[RTW_MAX_CHANNEL_NUM_5G];
s8 tx_pwr_tbl[RTW_RF_PATH_MAX]
[DESC_RATE_MAX];
@@ -2247,4 +2274,6 @@ void rtw_update_channel(struct rtw_dev *rtwdev, u8 center_channel,
void rtw_core_port_switch(struct rtw_dev *rtwdev, struct ieee80211_vif *vif);
bool rtw_core_check_sta_active(struct rtw_dev *rtwdev);
void rtw_core_enable_beacon(struct rtw_dev *rtwdev, bool enable);
+void rtw_set_ampdu_factor(struct rtw_dev *rtwdev, struct ieee80211_vif *vif,
+ struct ieee80211_bss_conf *bss_conf);
#endif
diff --git a/drivers/net/wireless/realtek/rtw88/pci.c b/drivers/net/wireless/realtek/rtw88/pci.c
index 0ecaefc4c83d..7f2b6dc21f56 100644
--- a/drivers/net/wireless/realtek/rtw88/pci.c
+++ b/drivers/net/wireless/realtek/rtw88/pci.c
@@ -12,6 +12,7 @@
#include "fw.h"
#include "ps.h"
#include "debug.h"
+#include "mac.h"
static bool rtw_disable_msi;
static bool rtw_pci_disable_aspm;
@@ -20,7 +21,7 @@ module_param_named(disable_aspm, rtw_pci_disable_aspm, bool, 0644);
MODULE_PARM_DESC(disable_msi, "Set Y to disable MSI interrupt support");
MODULE_PARM_DESC(disable_aspm, "Set Y to disable PCI ASPM support");
-static u32 rtw_pci_tx_queue_idx_addr[] = {
+static const u32 rtw_pci_tx_queue_idx_addr[] = {
[RTW_TX_QUEUE_BK] = RTK_PCI_TXBD_IDX_BKQ,
[RTW_TX_QUEUE_BE] = RTK_PCI_TXBD_IDX_BEQ,
[RTW_TX_QUEUE_VI] = RTK_PCI_TXBD_IDX_VIQ,
@@ -1591,7 +1592,7 @@ static void rtw_pci_destroy(struct rtw_dev *rtwdev, struct pci_dev *pdev)
rtw_pci_io_unmapping(rtwdev, pdev);
}
-static struct rtw_hci_ops rtw_pci_ops = {
+static const struct rtw_hci_ops rtw_pci_ops = {
.tx_write = rtw_pci_tx_write,
.tx_kick_off = rtw_pci_tx_kick_off,
.flush_queues = rtw_pci_flush_queues,
@@ -1602,6 +1603,7 @@ static struct rtw_hci_ops rtw_pci_ops = {
.link_ps = rtw_pci_link_ps,
.interface_cfg = rtw_pci_interface_cfg,
.dynamic_rx_agg = NULL,
+ .write_firmware_page = rtw_write_firmware_page,
.read8 = rtw_pci_read8,
.read16 = rtw_pci_read16,
diff --git a/drivers/net/wireless/realtek/rtw88/phy.c b/drivers/net/wireless/realtek/rtw88/phy.c
index 8ed20c89d216..55be0d8e0c28 100644
--- a/drivers/net/wireless/realtek/rtw88/phy.c
+++ b/drivers/net/wireless/realtek/rtw88/phy.c
@@ -52,60 +52,93 @@ static const u32 db_invert_table[12][8] = {
1995262315, 2511886432U, 3162277660U, 3981071706U}
};
-u8 rtw_cck_rates[] = { DESC_RATE1M, DESC_RATE2M, DESC_RATE5_5M, DESC_RATE11M };
-u8 rtw_ofdm_rates[] = {
+const u8 rtw_cck_rates[] = { DESC_RATE1M, DESC_RATE2M, DESC_RATE5_5M, DESC_RATE11M };
+
+const u8 rtw_ofdm_rates[] = {
DESC_RATE6M, DESC_RATE9M, DESC_RATE12M,
DESC_RATE18M, DESC_RATE24M, DESC_RATE36M,
DESC_RATE48M, DESC_RATE54M
};
-u8 rtw_ht_1s_rates[] = {
+
+const u8 rtw_ht_1s_rates[] = {
DESC_RATEMCS0, DESC_RATEMCS1, DESC_RATEMCS2,
DESC_RATEMCS3, DESC_RATEMCS4, DESC_RATEMCS5,
DESC_RATEMCS6, DESC_RATEMCS7
};
-u8 rtw_ht_2s_rates[] = {
+
+const u8 rtw_ht_2s_rates[] = {
DESC_RATEMCS8, DESC_RATEMCS9, DESC_RATEMCS10,
DESC_RATEMCS11, DESC_RATEMCS12, DESC_RATEMCS13,
DESC_RATEMCS14, DESC_RATEMCS15
};
-u8 rtw_vht_1s_rates[] = {
+
+const u8 rtw_vht_1s_rates[] = {
DESC_RATEVHT1SS_MCS0, DESC_RATEVHT1SS_MCS1,
DESC_RATEVHT1SS_MCS2, DESC_RATEVHT1SS_MCS3,
DESC_RATEVHT1SS_MCS4, DESC_RATEVHT1SS_MCS5,
DESC_RATEVHT1SS_MCS6, DESC_RATEVHT1SS_MCS7,
DESC_RATEVHT1SS_MCS8, DESC_RATEVHT1SS_MCS9
};
-u8 rtw_vht_2s_rates[] = {
+
+const u8 rtw_vht_2s_rates[] = {
DESC_RATEVHT2SS_MCS0, DESC_RATEVHT2SS_MCS1,
DESC_RATEVHT2SS_MCS2, DESC_RATEVHT2SS_MCS3,
DESC_RATEVHT2SS_MCS4, DESC_RATEVHT2SS_MCS5,
DESC_RATEVHT2SS_MCS6, DESC_RATEVHT2SS_MCS7,
DESC_RATEVHT2SS_MCS8, DESC_RATEVHT2SS_MCS9
};
-u8 *rtw_rate_section[RTW_RATE_SECTION_MAX] = {
+
+const u8 rtw_ht_3s_rates[] = {
+ DESC_RATEMCS16, DESC_RATEMCS17, DESC_RATEMCS18,
+ DESC_RATEMCS19, DESC_RATEMCS20, DESC_RATEMCS21,
+ DESC_RATEMCS22, DESC_RATEMCS23
+};
+
+const u8 rtw_ht_4s_rates[] = {
+ DESC_RATEMCS24, DESC_RATEMCS25, DESC_RATEMCS26,
+ DESC_RATEMCS27, DESC_RATEMCS28, DESC_RATEMCS29,
+ DESC_RATEMCS30, DESC_RATEMCS31
+};
+
+const u8 rtw_vht_3s_rates[] = {
+ DESC_RATEVHT3SS_MCS0, DESC_RATEVHT3SS_MCS1,
+ DESC_RATEVHT3SS_MCS2, DESC_RATEVHT3SS_MCS3,
+ DESC_RATEVHT3SS_MCS4, DESC_RATEVHT3SS_MCS5,
+ DESC_RATEVHT3SS_MCS6, DESC_RATEVHT3SS_MCS7,
+ DESC_RATEVHT3SS_MCS8, DESC_RATEVHT3SS_MCS9
+};
+
+const u8 rtw_vht_4s_rates[] = {
+ DESC_RATEVHT4SS_MCS0, DESC_RATEVHT4SS_MCS1,
+ DESC_RATEVHT4SS_MCS2, DESC_RATEVHT4SS_MCS3,
+ DESC_RATEVHT4SS_MCS4, DESC_RATEVHT4SS_MCS5,
+ DESC_RATEVHT4SS_MCS6, DESC_RATEVHT4SS_MCS7,
+ DESC_RATEVHT4SS_MCS8, DESC_RATEVHT4SS_MCS9
+};
+
+const u8 * const rtw_rate_section[RTW_RATE_SECTION_NUM] = {
rtw_cck_rates, rtw_ofdm_rates,
rtw_ht_1s_rates, rtw_ht_2s_rates,
- rtw_vht_1s_rates, rtw_vht_2s_rates
+ rtw_vht_1s_rates, rtw_vht_2s_rates,
+ rtw_ht_3s_rates, rtw_ht_4s_rates,
+ rtw_vht_3s_rates, rtw_vht_4s_rates
};
EXPORT_SYMBOL(rtw_rate_section);
-u8 rtw_rate_size[RTW_RATE_SECTION_MAX] = {
+const u8 rtw_rate_size[RTW_RATE_SECTION_NUM] = {
ARRAY_SIZE(rtw_cck_rates),
ARRAY_SIZE(rtw_ofdm_rates),
ARRAY_SIZE(rtw_ht_1s_rates),
ARRAY_SIZE(rtw_ht_2s_rates),
ARRAY_SIZE(rtw_vht_1s_rates),
- ARRAY_SIZE(rtw_vht_2s_rates)
+ ARRAY_SIZE(rtw_vht_2s_rates),
+ ARRAY_SIZE(rtw_ht_3s_rates),
+ ARRAY_SIZE(rtw_ht_4s_rates),
+ ARRAY_SIZE(rtw_vht_3s_rates),
+ ARRAY_SIZE(rtw_vht_4s_rates)
};
EXPORT_SYMBOL(rtw_rate_size);
-static const u8 rtw_cck_size = ARRAY_SIZE(rtw_cck_rates);
-static const u8 rtw_ofdm_size = ARRAY_SIZE(rtw_ofdm_rates);
-static const u8 rtw_ht_1s_size = ARRAY_SIZE(rtw_ht_1s_rates);
-static const u8 rtw_ht_2s_size = ARRAY_SIZE(rtw_ht_2s_rates);
-static const u8 rtw_vht_1s_size = ARRAY_SIZE(rtw_vht_1s_rates);
-static const u8 rtw_vht_2s_size = ARRAY_SIZE(rtw_vht_2s_rates);
-
enum rtw_phy_band_type {
PHY_BAND_2G = 0,
PHY_BAND_5G = 1,
@@ -1590,7 +1623,7 @@ static void rtw_phy_set_tx_power_limit(struct rtw_dev *rtwdev, u8 regd, u8 band,
ch_idx = rtw_channel_to_idx(band, ch);
if (regd >= RTW_REGD_MAX || bw >= RTW_CHANNEL_WIDTH_MAX ||
- rs >= RTW_RATE_SECTION_MAX || ch_idx < 0) {
+ rs >= RTW_RATE_SECTION_NUM || ch_idx < 0) {
WARN(1,
"wrong txpwr_lmt regd=%u, band=%u bw=%u, rs=%u, ch_idx=%u, pwr_limit=%d\n",
regd, band, bw, rs, ch_idx, pwr_limit);
@@ -1634,11 +1667,15 @@ rtw_xref_5g_txpwr_lmt(struct rtw_dev *rtwdev, u8 regd,
static void
rtw_xref_txpwr_lmt_by_rs(struct rtw_dev *rtwdev, u8 regd, u8 bw, u8 ch_idx)
{
+ static const u8 rs_cmp[4][2] = {
+ {RTW_RATE_SECTION_HT_1S, RTW_RATE_SECTION_VHT_1S},
+ {RTW_RATE_SECTION_HT_2S, RTW_RATE_SECTION_VHT_2S},
+ {RTW_RATE_SECTION_HT_3S, RTW_RATE_SECTION_VHT_3S},
+ {RTW_RATE_SECTION_HT_4S, RTW_RATE_SECTION_VHT_4S}
+ };
u8 rs_idx, rs_ht, rs_vht;
- u8 rs_cmp[2][2] = {{RTW_RATE_SECTION_HT_1S, RTW_RATE_SECTION_VHT_1S},
- {RTW_RATE_SECTION_HT_2S, RTW_RATE_SECTION_VHT_2S} };
- for (rs_idx = 0; rs_idx < 2; rs_idx++) {
+ for (rs_idx = 0; rs_idx < 4; rs_idx++) {
rs_ht = rs_cmp[rs_idx][0];
rs_vht = rs_cmp[rs_idx][1];
@@ -1695,7 +1732,7 @@ rtw_cfg_txpwr_lmt_by_alt(struct rtw_dev *rtwdev, u8 regd, u8 regd_alt)
u8 bw, rs;
for (bw = 0; bw < RTW_CHANNEL_WIDTH_MAX; bw++)
- for (rs = 0; rs < RTW_RATE_SECTION_MAX; rs++)
+ for (rs = 0; rs < RTW_RATE_SECTION_NUM; rs++)
__cfg_txpwr_lmt_by_alt(&rtwdev->hal, regd, regd_alt,
bw, rs);
}
@@ -1959,10 +1996,10 @@ static u8 rtw_phy_get_2g_tx_power_index(struct rtw_dev *rtwdev,
u8 rate, u8 group)
{
const struct rtw_chip_info *chip = rtwdev->chip;
- u8 tx_power;
- bool mcs_rate;
- bool above_2ss;
+ bool above_2ss, above_3ss, above_4ss;
u8 factor = chip->txgi_factor;
+ bool mcs_rate;
+ u8 tx_power;
if (rate <= DESC_RATE11M)
tx_power = pwr_idx_2g->cck_base[group];
@@ -1972,11 +2009,15 @@ static u8 rtw_phy_get_2g_tx_power_index(struct rtw_dev *rtwdev,
if (rate >= DESC_RATE6M && rate <= DESC_RATE54M)
tx_power += pwr_idx_2g->ht_1s_diff.ofdm * factor;
- mcs_rate = (rate >= DESC_RATEMCS0 && rate <= DESC_RATEMCS15) ||
+ mcs_rate = (rate >= DESC_RATEMCS0 && rate <= DESC_RATEMCS31) ||
(rate >= DESC_RATEVHT1SS_MCS0 &&
- rate <= DESC_RATEVHT2SS_MCS9);
- above_2ss = (rate >= DESC_RATEMCS8 && rate <= DESC_RATEMCS15) ||
+ rate <= DESC_RATEVHT4SS_MCS9);
+ above_2ss = (rate >= DESC_RATEMCS8 && rate <= DESC_RATEMCS31) ||
(rate >= DESC_RATEVHT2SS_MCS0);
+ above_3ss = (rate >= DESC_RATEMCS16 && rate <= DESC_RATEMCS31) ||
+ (rate >= DESC_RATEVHT3SS_MCS0);
+ above_4ss = (rate >= DESC_RATEMCS24 && rate <= DESC_RATEMCS31) ||
+ (rate >= DESC_RATEVHT4SS_MCS0);
if (!mcs_rate)
return tx_power;
@@ -1989,11 +2030,19 @@ static u8 rtw_phy_get_2g_tx_power_index(struct rtw_dev *rtwdev,
tx_power += pwr_idx_2g->ht_1s_diff.bw20 * factor;
if (above_2ss)
tx_power += pwr_idx_2g->ht_2s_diff.bw20 * factor;
+ if (above_3ss)
+ tx_power += pwr_idx_2g->ht_3s_diff.bw20 * factor;
+ if (above_4ss)
+ tx_power += pwr_idx_2g->ht_4s_diff.bw20 * factor;
break;
case RTW_CHANNEL_WIDTH_40:
/* bw40 is the base power */
if (above_2ss)
tx_power += pwr_idx_2g->ht_2s_diff.bw40 * factor;
+ if (above_3ss)
+ tx_power += pwr_idx_2g->ht_3s_diff.bw40 * factor;
+ if (above_4ss)
+ tx_power += pwr_idx_2g->ht_4s_diff.bw40 * factor;
break;
}
@@ -2006,19 +2055,23 @@ static u8 rtw_phy_get_5g_tx_power_index(struct rtw_dev *rtwdev,
u8 rate, u8 group)
{
const struct rtw_chip_info *chip = rtwdev->chip;
- u8 tx_power;
+ bool above_2ss, above_3ss, above_4ss;
+ u8 factor = chip->txgi_factor;
u8 upper, lower;
bool mcs_rate;
- bool above_2ss;
- u8 factor = chip->txgi_factor;
+ u8 tx_power;
tx_power = pwr_idx_5g->bw40_base[group];
- mcs_rate = (rate >= DESC_RATEMCS0 && rate <= DESC_RATEMCS15) ||
+ mcs_rate = (rate >= DESC_RATEMCS0 && rate <= DESC_RATEMCS31) ||
(rate >= DESC_RATEVHT1SS_MCS0 &&
- rate <= DESC_RATEVHT2SS_MCS9);
- above_2ss = (rate >= DESC_RATEMCS8 && rate <= DESC_RATEMCS15) ||
+ rate <= DESC_RATEVHT4SS_MCS9);
+ above_2ss = (rate >= DESC_RATEMCS8 && rate <= DESC_RATEMCS31) ||
(rate >= DESC_RATEVHT2SS_MCS0);
+ above_3ss = (rate >= DESC_RATEMCS16 && rate <= DESC_RATEMCS31) ||
+ (rate >= DESC_RATEVHT3SS_MCS0);
+ above_4ss = (rate >= DESC_RATEMCS24 && rate <= DESC_RATEMCS31) ||
+ (rate >= DESC_RATEVHT4SS_MCS0);
if (!mcs_rate) {
tx_power += pwr_idx_5g->ht_1s_diff.ofdm * factor;
@@ -2033,11 +2086,19 @@ static u8 rtw_phy_get_5g_tx_power_index(struct rtw_dev *rtwdev,
tx_power += pwr_idx_5g->ht_1s_diff.bw20 * factor;
if (above_2ss)
tx_power += pwr_idx_5g->ht_2s_diff.bw20 * factor;
+ if (above_3ss)
+ tx_power += pwr_idx_5g->ht_3s_diff.bw20 * factor;
+ if (above_4ss)
+ tx_power += pwr_idx_5g->ht_4s_diff.bw20 * factor;
break;
case RTW_CHANNEL_WIDTH_40:
/* bw40 is the base power */
if (above_2ss)
tx_power += pwr_idx_5g->ht_2s_diff.bw40 * factor;
+ if (above_3ss)
+ tx_power += pwr_idx_5g->ht_3s_diff.bw40 * factor;
+ if (above_4ss)
+ tx_power += pwr_idx_5g->ht_4s_diff.bw40 * factor;
break;
case RTW_CHANNEL_WIDTH_80:
/* the base idx of bw80 is the average of bw40+/bw40- */
@@ -2048,13 +2109,17 @@ static u8 rtw_phy_get_5g_tx_power_index(struct rtw_dev *rtwdev,
tx_power += pwr_idx_5g->vht_1s_diff.bw80 * factor;
if (above_2ss)
tx_power += pwr_idx_5g->vht_2s_diff.bw80 * factor;
+ if (above_3ss)
+ tx_power += pwr_idx_5g->vht_3s_diff.bw80 * factor;
+ if (above_4ss)
+ tx_power += pwr_idx_5g->vht_4s_diff.bw80 * factor;
break;
}
return tx_power;
}
-/* return RTW_RATE_SECTION_MAX to indicate rate is invalid */
+/* return RTW_RATE_SECTION_NUM to indicate rate is invalid */
static u8 rtw_phy_rate_to_rate_section(u8 rate)
{
if (rate >= DESC_RATE1M && rate <= DESC_RATE11M)
@@ -2065,12 +2130,20 @@ static u8 rtw_phy_rate_to_rate_section(u8 rate)
return RTW_RATE_SECTION_HT_1S;
else if (rate >= DESC_RATEMCS8 && rate <= DESC_RATEMCS15)
return RTW_RATE_SECTION_HT_2S;
+ else if (rate >= DESC_RATEMCS16 && rate <= DESC_RATEMCS23)
+ return RTW_RATE_SECTION_HT_3S;
+ else if (rate >= DESC_RATEMCS24 && rate <= DESC_RATEMCS31)
+ return RTW_RATE_SECTION_HT_4S;
else if (rate >= DESC_RATEVHT1SS_MCS0 && rate <= DESC_RATEVHT1SS_MCS9)
return RTW_RATE_SECTION_VHT_1S;
else if (rate >= DESC_RATEVHT2SS_MCS0 && rate <= DESC_RATEVHT2SS_MCS9)
return RTW_RATE_SECTION_VHT_2S;
+ else if (rate >= DESC_RATEVHT3SS_MCS0 && rate <= DESC_RATEVHT3SS_MCS9)
+ return RTW_RATE_SECTION_VHT_3S;
+ else if (rate >= DESC_RATEVHT4SS_MCS0 && rate <= DESC_RATEVHT4SS_MCS9)
+ return RTW_RATE_SECTION_VHT_4S;
else
- return RTW_RATE_SECTION_MAX;
+ return RTW_RATE_SECTION_NUM;
}
static s8 rtw_phy_get_tx_power_limit(struct rtw_dev *rtwdev, u8 band,
@@ -2088,7 +2161,7 @@ static s8 rtw_phy_get_tx_power_limit(struct rtw_dev *rtwdev, u8 band,
if (regd > RTW_REGD_WW)
return power_limit;
- if (rs == RTW_RATE_SECTION_MAX)
+ if (rs == RTW_RATE_SECTION_NUM)
goto err;
/* only 20M BW with cck and ofdm */
@@ -2096,7 +2169,7 @@ static s8 rtw_phy_get_tx_power_limit(struct rtw_dev *rtwdev, u8 band,
bw = RTW_CHANNEL_WIDTH_20;
/* only 20/40M BW with ht */
- if (rs == RTW_RATE_SECTION_HT_1S || rs == RTW_RATE_SECTION_HT_2S)
+ if (rate >= DESC_RATEMCS0 && rate <= DESC_RATEMCS31)
bw = min_t(u8, bw, RTW_CHANNEL_WIDTH_40);
/* select min power limit among [20M BW ~ current BW] */
@@ -2132,7 +2205,7 @@ static s8 rtw_phy_get_tx_power_sar(struct rtw_dev *rtwdev, u8 sar_band,
.rs = rs,
};
- if (rs == RTW_RATE_SECTION_MAX)
+ if (rs == RTW_RATE_SECTION_NUM)
goto err;
return rtw_query_sar(rtwdev, &arg);
@@ -2214,14 +2287,14 @@ static void rtw_phy_set_tx_power_index_by_rs(struct rtw_dev *rtwdev,
{
struct rtw_hal *hal = &rtwdev->hal;
u8 regd = rtw_regd_get(rtwdev);
- u8 *rates;
+ const u8 *rates;
u8 size;
u8 rate;
u8 pwr_idx;
u8 bw;
int i;
- if (rs >= RTW_RATE_SECTION_MAX)
+ if (rs >= RTW_RATE_SECTION_NUM)
return;
rates = rtw_rate_section[rs];
@@ -2252,7 +2325,7 @@ static void rtw_phy_set_tx_power_level_by_path(struct rtw_dev *rtwdev,
else
rs = RTW_RATE_SECTION_OFDM;
- for (; rs < RTW_RATE_SECTION_MAX; rs++)
+ for (; rs < RTW_RATE_SECTION_NUM; rs++)
rtw_phy_set_tx_power_index_by_rs(rtwdev, ch, path, rs);
}
@@ -2274,13 +2347,13 @@ EXPORT_SYMBOL(rtw_phy_set_tx_power_level);
static void
rtw_phy_tx_power_by_rate_config_by_path(struct rtw_hal *hal, u8 path,
- u8 rs, u8 size, u8 *rates)
+ u8 rs, u8 size, const u8 *rates)
{
u8 rate;
u8 base_idx, rate_idx;
s8 base_2g, base_5g;
- if (rs >= RTW_RATE_SECTION_VHT_1S)
+ if (size == 10) /* VHT rates */
base_idx = rates[size - 3];
else
base_idx = rates[size - 1];
@@ -2297,28 +2370,12 @@ rtw_phy_tx_power_by_rate_config_by_path(struct rtw_hal *hal, u8 path,
void rtw_phy_tx_power_by_rate_config(struct rtw_hal *hal)
{
- u8 path;
+ u8 path, rs;
- for (path = 0; path < RTW_RF_PATH_MAX; path++) {
- rtw_phy_tx_power_by_rate_config_by_path(hal, path,
- RTW_RATE_SECTION_CCK,
- rtw_cck_size, rtw_cck_rates);
- rtw_phy_tx_power_by_rate_config_by_path(hal, path,
- RTW_RATE_SECTION_OFDM,
- rtw_ofdm_size, rtw_ofdm_rates);
- rtw_phy_tx_power_by_rate_config_by_path(hal, path,
- RTW_RATE_SECTION_HT_1S,
- rtw_ht_1s_size, rtw_ht_1s_rates);
- rtw_phy_tx_power_by_rate_config_by_path(hal, path,
- RTW_RATE_SECTION_HT_2S,
- rtw_ht_2s_size, rtw_ht_2s_rates);
- rtw_phy_tx_power_by_rate_config_by_path(hal, path,
- RTW_RATE_SECTION_VHT_1S,
- rtw_vht_1s_size, rtw_vht_1s_rates);
- rtw_phy_tx_power_by_rate_config_by_path(hal, path,
- RTW_RATE_SECTION_VHT_2S,
- rtw_vht_2s_size, rtw_vht_2s_rates);
- }
+ for (path = 0; path < RTW_RF_PATH_MAX; path++)
+ for (rs = 0; rs < RTW_RATE_SECTION_NUM; rs++)
+ rtw_phy_tx_power_by_rate_config_by_path(hal, path, rs,
+ rtw_rate_size[rs], rtw_rate_section[rs]);
}
static void
@@ -2347,7 +2404,7 @@ void rtw_phy_tx_power_limit_config(struct rtw_hal *hal)
for (regd = 0; regd < RTW_REGD_MAX; regd++)
for (bw = 0; bw < RTW_CHANNEL_WIDTH_MAX; bw++)
- for (rs = 0; rs < RTW_RATE_SECTION_MAX; rs++)
+ for (rs = 0; rs < RTW_RATE_SECTION_NUM; rs++)
__rtw_phy_tx_power_limit_config(hal, regd, bw, rs);
}
@@ -2383,7 +2440,7 @@ void rtw_phy_init_tx_power(struct rtw_dev *rtwdev)
/* init tx power limit */
for (regd = 0; regd < RTW_REGD_MAX; regd++)
for (bw = 0; bw < RTW_CHANNEL_WIDTH_MAX; bw++)
- for (rs = 0; rs < RTW_RATE_SECTION_MAX; rs++)
+ for (rs = 0; rs < RTW_RATE_SECTION_NUM; rs++)
rtw_phy_init_tx_power_limit(rtwdev, regd, bw,
rs);
}
@@ -2401,32 +2458,56 @@ void rtw_phy_config_swing_table(struct rtw_dev *rtwdev,
swing_table->n[RF_PATH_A] = tbl->pwrtrk_2g_ccka_n;
swing_table->p[RF_PATH_B] = tbl->pwrtrk_2g_cckb_p;
swing_table->n[RF_PATH_B] = tbl->pwrtrk_2g_cckb_n;
+ swing_table->p[RF_PATH_C] = tbl->pwrtrk_2g_cckc_p;
+ swing_table->n[RF_PATH_C] = tbl->pwrtrk_2g_cckc_n;
+ swing_table->p[RF_PATH_D] = tbl->pwrtrk_2g_cckd_p;
+ swing_table->n[RF_PATH_D] = tbl->pwrtrk_2g_cckd_n;
} else {
swing_table->p[RF_PATH_A] = tbl->pwrtrk_2ga_p;
swing_table->n[RF_PATH_A] = tbl->pwrtrk_2ga_n;
swing_table->p[RF_PATH_B] = tbl->pwrtrk_2gb_p;
swing_table->n[RF_PATH_B] = tbl->pwrtrk_2gb_n;
+ swing_table->p[RF_PATH_C] = tbl->pwrtrk_2gc_p;
+ swing_table->n[RF_PATH_C] = tbl->pwrtrk_2gc_n;
+ swing_table->p[RF_PATH_D] = tbl->pwrtrk_2gd_p;
+ swing_table->n[RF_PATH_D] = tbl->pwrtrk_2gd_n;
}
} else if (IS_CH_5G_BAND_1(channel) || IS_CH_5G_BAND_2(channel)) {
swing_table->p[RF_PATH_A] = tbl->pwrtrk_5ga_p[RTW_PWR_TRK_5G_1];
swing_table->n[RF_PATH_A] = tbl->pwrtrk_5ga_n[RTW_PWR_TRK_5G_1];
swing_table->p[RF_PATH_B] = tbl->pwrtrk_5gb_p[RTW_PWR_TRK_5G_1];
swing_table->n[RF_PATH_B] = tbl->pwrtrk_5gb_n[RTW_PWR_TRK_5G_1];
+ swing_table->p[RF_PATH_C] = tbl->pwrtrk_5gc_p[RTW_PWR_TRK_5G_1];
+ swing_table->n[RF_PATH_C] = tbl->pwrtrk_5gc_n[RTW_PWR_TRK_5G_1];
+ swing_table->p[RF_PATH_D] = tbl->pwrtrk_5gd_p[RTW_PWR_TRK_5G_1];
+ swing_table->n[RF_PATH_D] = tbl->pwrtrk_5gd_n[RTW_PWR_TRK_5G_1];
} else if (IS_CH_5G_BAND_3(channel)) {
swing_table->p[RF_PATH_A] = tbl->pwrtrk_5ga_p[RTW_PWR_TRK_5G_2];
swing_table->n[RF_PATH_A] = tbl->pwrtrk_5ga_n[RTW_PWR_TRK_5G_2];
swing_table->p[RF_PATH_B] = tbl->pwrtrk_5gb_p[RTW_PWR_TRK_5G_2];
swing_table->n[RF_PATH_B] = tbl->pwrtrk_5gb_n[RTW_PWR_TRK_5G_2];
+ swing_table->p[RF_PATH_C] = tbl->pwrtrk_5gc_p[RTW_PWR_TRK_5G_2];
+ swing_table->n[RF_PATH_C] = tbl->pwrtrk_5gc_n[RTW_PWR_TRK_5G_2];
+ swing_table->p[RF_PATH_D] = tbl->pwrtrk_5gd_p[RTW_PWR_TRK_5G_2];
+ swing_table->n[RF_PATH_D] = tbl->pwrtrk_5gd_n[RTW_PWR_TRK_5G_2];
} else if (IS_CH_5G_BAND_4(channel)) {
swing_table->p[RF_PATH_A] = tbl->pwrtrk_5ga_p[RTW_PWR_TRK_5G_3];
swing_table->n[RF_PATH_A] = tbl->pwrtrk_5ga_n[RTW_PWR_TRK_5G_3];
swing_table->p[RF_PATH_B] = tbl->pwrtrk_5gb_p[RTW_PWR_TRK_5G_3];
swing_table->n[RF_PATH_B] = tbl->pwrtrk_5gb_n[RTW_PWR_TRK_5G_3];
+ swing_table->p[RF_PATH_C] = tbl->pwrtrk_5gc_p[RTW_PWR_TRK_5G_3];
+ swing_table->n[RF_PATH_C] = tbl->pwrtrk_5gc_n[RTW_PWR_TRK_5G_3];
+ swing_table->p[RF_PATH_D] = tbl->pwrtrk_5gd_p[RTW_PWR_TRK_5G_3];
+ swing_table->n[RF_PATH_D] = tbl->pwrtrk_5gd_n[RTW_PWR_TRK_5G_3];
} else {
swing_table->p[RF_PATH_A] = tbl->pwrtrk_2ga_p;
swing_table->n[RF_PATH_A] = tbl->pwrtrk_2ga_n;
swing_table->p[RF_PATH_B] = tbl->pwrtrk_2gb_p;
swing_table->n[RF_PATH_B] = tbl->pwrtrk_2gb_n;
+ swing_table->p[RF_PATH_C] = tbl->pwrtrk_2gc_p;
+ swing_table->n[RF_PATH_C] = tbl->pwrtrk_2gc_n;
+ swing_table->p[RF_PATH_D] = tbl->pwrtrk_2gd_p;
+ swing_table->n[RF_PATH_D] = tbl->pwrtrk_2gd_n;
}
}
EXPORT_SYMBOL(rtw_phy_config_swing_table);
diff --git a/drivers/net/wireless/realtek/rtw88/phy.h b/drivers/net/wireless/realtek/rtw88/phy.h
index ccfcbd3ced03..c9e6b869661d 100644
--- a/drivers/net/wireless/realtek/rtw88/phy.h
+++ b/drivers/net/wireless/realtek/rtw88/phy.h
@@ -7,14 +7,18 @@
#include "debug.h"
-extern u8 rtw_cck_rates[];
-extern u8 rtw_ofdm_rates[];
-extern u8 rtw_ht_1s_rates[];
-extern u8 rtw_ht_2s_rates[];
-extern u8 rtw_vht_1s_rates[];
-extern u8 rtw_vht_2s_rates[];
-extern u8 *rtw_rate_section[];
-extern u8 rtw_rate_size[];
+extern const u8 rtw_cck_rates[];
+extern const u8 rtw_ofdm_rates[];
+extern const u8 rtw_ht_1s_rates[];
+extern const u8 rtw_ht_2s_rates[];
+extern const u8 rtw_vht_1s_rates[];
+extern const u8 rtw_vht_2s_rates[];
+extern const u8 rtw_ht_3s_rates[];
+extern const u8 rtw_ht_4s_rates[];
+extern const u8 rtw_vht_3s_rates[];
+extern const u8 rtw_vht_4s_rates[];
+extern const u8 * const rtw_rate_section[];
+extern const u8 rtw_rate_size[];
void rtw_phy_init(struct rtw_dev *rtwdev);
void rtw_phy_dynamic_mechanism(struct rtw_dev *rtwdev);
diff --git a/drivers/net/wireless/realtek/rtw88/reg.h b/drivers/net/wireless/realtek/rtw88/reg.h
index e438405fba56..08e9494977e0 100644
--- a/drivers/net/wireless/realtek/rtw88/reg.h
+++ b/drivers/net/wireless/realtek/rtw88/reg.h
@@ -8,6 +8,7 @@
#define REG_SYS_FUNC_EN 0x0002
#define BIT_FEN_EN_25_1 BIT(13)
#define BIT_FEN_ELDR BIT(12)
+#define BIT_FEN_PCIEA BIT(6)
#define BIT_FEN_CPUEN BIT(2)
#define BIT_FEN_USBA BIT(2)
#define BIT_FEN_BB_GLB_RST BIT(1)
@@ -39,6 +40,9 @@
#define BIT_RF_RSTB BIT(1)
#define BIT_RF_EN BIT(0)
+#define REG_RF_CTRL1 0x0020
+#define REG_RF_CTRL2 0x0021
+
#define REG_AFE_CTRL1 0x0024
#define BIT_MAC_CLK_SEL (BIT(20) | BIT(21))
#define REG_EFUSE_CTRL 0x0030
@@ -73,6 +77,8 @@
#define BIT_BT_PTA_EN BIT(5)
#define BIT_WLRFE_4_5_EN BIT(2)
+#define REG_GPIO_PIN_CTRL 0x0044
+
#define REG_LED_CFG 0x004C
#define BIT_LNAON_SEL_EN BIT(26)
#define BIT_PAPE_SEL_EN BIT(25)
@@ -110,6 +116,7 @@
#define BIT_SDIO_PAD_E5 BIT(18)
#define REG_RF_B_CTRL 0x76
+#define REG_RF_CTRL3 0x0076
#define REG_AFE_CTRL_4 0x0078
#define BIT_CK320M_AFE_EN BIT(4)
@@ -130,6 +137,7 @@
#define BIT_SHIFT_ROM_PGE 16
#define BIT_FW_INIT_RDY BIT(15)
#define BIT_FW_DW_RDY BIT(14)
+#define BIT_CPU_CLK_SEL (BIT(12) | BIT(13))
#define BIT_RPWM_TOGGLE BIT(7)
#define BIT_RAM_DL_SEL BIT(7) /* legacy only */
#define BIT_DMEM_CHKSUM_OK BIT(6)
@@ -147,7 +155,7 @@
BIT_CHECK_SUM_OK)
#define FW_READY_LEGACY (BIT_MCUFWDL_RDY | BIT_FWDL_CHK_RPT | \
BIT_WINTINI_RDY | BIT_RAM_DL_SEL)
-#define FW_READY_MASK 0xffff
+#define FW_READY_MASK (0xffff & ~BIT_CPU_CLK_SEL)
#define REG_MCU_TST_CFG 0x84
#define VAL_FW_TRIGGER 0x1
@@ -602,15 +610,25 @@
#define REG_CCA2ND 0x0838
#define REG_L1PKTH 0x0848
#define REG_CLKTRK 0x0860
+#define REG_CSI_MASK_SETTING1 0x0874
+#define REG_NBI_SETTING 0x087c
+#define BIT_NBI_ENABLE BIT(13)
+#define REG_CSI_FIX_MASK0 0x0880
+#define REG_CSI_FIX_MASK1 0x0884
+#define REG_CSI_FIX_MASK6 0x0898
+#define REG_CSI_FIX_MASK7 0x089c
#define REG_ADCCLK 0x08AC
#define REG_HSSI_READ 0x08B0
#define REG_FPGA0_XCD_RF_PARA 0x08B4
#define REG_RX_MCS_LIMIT 0x08BC
#define REG_ADC160 0x08C4
+#define REG_DBGSEL 0x08fc
#define REG_ANTSEL_SW 0x0900
#define REG_DAC_RSTB 0x090c
+#define REG_PSD 0x0910
+#define BIT_PSD_INI GENMASK(23, 22)
#define REG_SINGLE_TONE_CONT_TX 0x0914
-
+#define REG_AGC_TABLE 0x0958
#define REG_RFE_CTRL_E 0x0974
#define REG_2ND_CCA_CTRL 0x0976
#define REG_IQK_COM00 0x0978
@@ -620,10 +638,18 @@
#define REG_FAS 0x09a4
#define REG_RXSB 0x0a00
+#define BIT_RXSB_ANA_DIV BIT(15)
#define REG_CCK_RX 0x0a04
#define REG_CCK_PD_TH 0x0a0a
-
-#define REG_CCK0_FAREPORT 0xa2c
+#define REG_PRECTRL 0x0a14
+#define BIT_DIS_CO_PATHSEL BIT(7)
+#define BIT_IQ_WGT GENMASK(9, 8)
+#define REG_CCA_MF 0x0a20
+#define BIT_MBC_WIN GENMASK(5, 4)
+#define REG_CCK0_TX_FILTER1 0x0a20
+#define REG_CCK0_TX_FILTER2 0x0a24
+#define REG_CCK0_DEBUG_PORT 0x0a28
+#define REG_CCK0_FAREPORT 0x0a2c
#define BIT_CCK0_2RX BIT(18)
#define BIT_CCK0_MRC BIT(22)
#define REG_FA_CCK 0x0a5c
@@ -642,10 +668,18 @@
#define DIS_DPD_RATEVHT2SS_MCS1 BIT(9)
#define DIS_DPD_RATEALL GENMASK(9, 0)
+#define REG_CCA 0x0a70
+#define BIT_CCA_CO BIT(7)
+#define REG_ANTSEL 0x0a74
+#define BIT_ANT_BYCO BIT(8)
+#define REG_CCKTX 0x0a84
+#define BIT_CMB_CCA_2R BIT(28)
+
#define REG_CNTRST 0x0b58
#define REG_3WIRE_SWA 0x0c00
#define REG_RX_IQC_AB_A 0x0c10
+#define REG_RX_IQC_CD_A 0x0c14
#define REG_TXSCALE_A 0x0c1c
#define BB_SWING_MASK GENMASK(31, 21)
#define REG_TX_AGC_A_CCK_11_CCK_1 0xc20
@@ -673,7 +707,7 @@
#define REG_LSSI_WRITE_A 0x0c90
#define REG_PREDISTA 0x0c90
#define REG_TXAGCIDX 0x0c94
-
+#define REG_TX_AGC_A 0x0c94
#define REG_RFE_PINMUX_A 0x0cb0
#define REG_RFE_INV_A 0x0cb4
#define REG_RFE_CTRL8 0x0cb4
@@ -682,6 +716,7 @@
#define DPDT_CTRL_PIN 0x77
#define RFE_INV_MASK 0x3ff00000
#define REG_RFECTL_A 0x0cb8
+#define REG_RFE_INV0 0x0cbc
#define REG_RFE_INV8 0x0cbd
#define BIT_MASK_RFE_INV89 GENMASK(1, 0)
#define REG_RFE_INV16 0x0cbe
@@ -702,6 +737,7 @@
#define REG_3WIRE_SWB 0x0e00
#define REG_RX_IQC_AB_B 0x0e10
+#define REG_RX_IQC_CD_B 0x0e14
#define REG_TXSCALE_B 0x0e1c
#define REG_TX_AGC_B_CCK_11_CCK_1 0xe20
#define REG_TX_AGC_B_OFDM18_OFDM6 0xe24
@@ -728,6 +764,7 @@
#define REG_LSSI_WRITE_B 0x0e90
#define REG_PREDISTB 0x0e90
#define REG_INIDLYB 0x0e94
+#define REG_TX_AGC_B 0x0e94
#define REG_RFE_PINMUX_B 0x0eb0
#define REG_RFE_INV_B 0x0eb4
#define REG_RFECTL_B 0x0eb8
@@ -743,8 +780,11 @@
#define REG_CRC_HT 0x0f10
#define REG_CRC_OFDM 0x0f14
#define REG_FA_OFDM 0x0f48
+#define REG_DBGRPT 0x0fa0
#define REG_CCA_CCK 0x0fcc
+#define REG_SYS_CFG3_8814A 0x1000
+
#define REG_ANAPARSW_MAC_0 0x1010
#define BIT_CF_L_V2 GENMASK(29, 28)
@@ -862,9 +902,27 @@
#define LTECOEX_WRITE_DATA REG_WL2LTECOEX_INDIRECT_ACCESS_WRITE_DATA_V1
#define LTECOEX_READ_DATA REG_WL2LTECOEX_INDIRECT_ACCESS_READ_DATA_V1
+#define REG_RX_IQC_AB_C 0x1810
+#define REG_RX_IQC_CD_C 0x1814
+#define REG_TXSCALE_C 0x181c
+#define REG_CK_MONHC 0x185c
+#define REG_AFE_PWR1_C 0x1860
#define REG_IGN_GNT_BT1 0x1860
+#define REG_TX_AGC_C 0x1894
+#define REG_RFE_PINMUX_C 0x18b4
#define REG_RFESEL_CTRL 0x1990
+#define REG_AGC_TBL 0x1998
+
+#define REG_RX_IQC_AB_D 0x1a10
+#define REG_RX_IQC_CD_D 0x1a14
+#define REG_TXSCALE_D 0x1a1c
+#define REG_CK_MONHD 0x1a5c
+#define REG_AFE_PWR1_D 0x1a60
+#define REG_TX_AGC_D 0x1a94
+#define REG_RFE_PINMUX_D 0x1ab4
+#define REG_RFE_INVSEL_D 0x1abc
+#define BIT_RFE_SELSW0_D GENMASK(27, 20)
#define REG_NOMASK_TXBT 0x1ca7
#define REG_ANAPAR 0x1c30
@@ -905,6 +963,7 @@
#define RF18_BAND_MASK (BIT(16) | BIT(9) | BIT(8))
#define RF18_CHANNEL_MASK (MASKBYTE0)
#define RF18_RFSI_MASK (BIT(18) | BIT(17))
+#define RF_RCK1_V1 0x1c
#define RF_RCK 0x1d
#define RF_MODE_TABLE_ADDR 0x30
#define RF_MODE_TABLE_DATA0 0x31
diff --git a/drivers/net/wireless/realtek/rtw88/rtw8703b.c b/drivers/net/wireless/realtek/rtw88/rtw8703b.c
index 1d232adbdd7e..9e6700c43a63 100644
--- a/drivers/net/wireless/realtek/rtw88/rtw8703b.c
+++ b/drivers/net/wireless/realtek/rtw88/rtw8703b.c
@@ -519,15 +519,6 @@ static const struct rtw_rqpn rqpn_table_8703b[] = {
RTW_DMA_MAPPING_EXTRA, RTW_DMA_MAPPING_HIGH},
};
-/* Default power index table for RTL8703B, used if EFUSE does not
- * contain valid data. Replaces EFUSE data from offset 0x10 (start of
- * txpwr_idx_table).
- */
-static const u8 rtw8703b_txpwr_idx_table[] = {
- 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D,
- 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x02
-};
-
static void try_mac_from_devicetree(struct rtw_dev *rtwdev)
{
struct device_node *node = rtwdev->dev->of_node;
@@ -544,15 +535,9 @@ static void try_mac_from_devicetree(struct rtw_dev *rtwdev)
}
}
-#define DBG_EFUSE_FIX(rtwdev, name) \
- rtw_dbg(rtwdev, RTW_DBG_EFUSE, "Fixed invalid EFUSE value: " \
- # name "=0x%x\n", rtwdev->efuse.name)
-
static int rtw8703b_read_efuse(struct rtw_dev *rtwdev, u8 *log_map)
{
struct rtw_efuse *efuse = &rtwdev->efuse;
- u8 *pwr = (u8 *)efuse->txpwr_idx_table;
- bool valid = false;
int ret;
ret = rtw8723x_read_efuse(rtwdev, log_map);
@@ -562,51 +547,6 @@ static int rtw8703b_read_efuse(struct rtw_dev *rtwdev, u8 *log_map)
if (!is_valid_ether_addr(efuse->addr))
try_mac_from_devicetree(rtwdev);
- /* If TX power index table in EFUSE is invalid, fall back to
- * built-in table.
- */
- for (int i = 0; i < ARRAY_SIZE(rtw8703b_txpwr_idx_table); i++)
- if (pwr[i] != 0xff) {
- valid = true;
- break;
- }
- if (!valid) {
- for (int i = 0; i < ARRAY_SIZE(rtw8703b_txpwr_idx_table); i++)
- pwr[i] = rtw8703b_txpwr_idx_table[i];
- rtw_dbg(rtwdev, RTW_DBG_EFUSE,
- "Replaced invalid EFUSE TX power index table.");
- rtw8723x_debug_txpwr_limit(rtwdev,
- efuse->txpwr_idx_table, 2);
- }
-
- /* Override invalid antenna settings. */
- if (efuse->bt_setting == 0xff) {
- /* shared antenna */
- efuse->bt_setting |= BIT(0);
- /* RF path A */
- efuse->bt_setting &= ~BIT(6);
- DBG_EFUSE_FIX(rtwdev, bt_setting);
- }
-
- /* Override invalid board options: The coex code incorrectly
- * assumes that if bits 6 & 7 are set the board doesn't
- * support coex. Regd is also derived from rf_board_option and
- * should be 0 if there's no valid data.
- */
- if (efuse->rf_board_option == 0xff) {
- efuse->regd = 0;
- efuse->rf_board_option &= GENMASK(5, 0);
- DBG_EFUSE_FIX(rtwdev, rf_board_option);
- }
-
- /* Override invalid crystal cap setting, default comes from
- * vendor driver. Chip specific.
- */
- if (efuse->crystal_cap == 0xff) {
- efuse->crystal_cap = 0x20;
- DBG_EFUSE_FIX(rtwdev, crystal_cap);
- }
-
return 0;
}
@@ -1904,6 +1844,7 @@ static const struct rtw_chip_ops rtw8703b_ops = {
.set_antenna = NULL,
.cfg_ldo25 = rtw8723x_cfg_ldo25,
.efuse_grant = rtw8723x_efuse_grant,
+ .set_ampdu_factor = NULL,
.false_alarm_statistics = rtw8723x_false_alarm_statistics,
.phy_calibration = rtw8703b_phy_calibration,
.dpk_track = NULL,
diff --git a/drivers/net/wireless/realtek/rtw88/rtw8723cs.c b/drivers/net/wireless/realtek/rtw88/rtw8723cs.c
index 8d38d36be8c0..1f98d35a8dd1 100644
--- a/drivers/net/wireless/realtek/rtw88/rtw8723cs.c
+++ b/drivers/net/wireless/realtek/rtw88/rtw8723cs.c
@@ -19,7 +19,7 @@ static const struct sdio_device_id rtw_8723cs_id_table[] = {
MODULE_DEVICE_TABLE(sdio, rtw_8723cs_id_table);
static struct sdio_driver rtw_8723cs_driver = {
- .name = "rtw8723cs",
+ .name = KBUILD_MODNAME,
.id_table = rtw_8723cs_id_table,
.probe = rtw_sdio_probe,
.remove = rtw_sdio_remove,
diff --git a/drivers/net/wireless/realtek/rtw88/rtw8723d.c b/drivers/net/wireless/realtek/rtw88/rtw8723d.c
index eeca31bf71f1..31876e708f9e 100644
--- a/drivers/net/wireless/realtek/rtw88/rtw8723d.c
+++ b/drivers/net/wireless/realtek/rtw88/rtw8723d.c
@@ -444,7 +444,7 @@ static u8 rtw8723d_iqk_check_tx_failed(struct rtw_dev *rtwdev,
rtw_read32(rtwdev, REG_IQK_RES_TX),
rtw_read32(rtwdev, REG_IQK_RES_TY));
rtw_dbg(rtwdev, RTW_DBG_RFK,
- "[IQK] 0xe90(before IQK)= 0x%x, 0xe98(afer IQK) = 0x%x\n",
+ "[IQK] 0xe90(before IQK)= 0x%x, 0xe98(after IQK) = 0x%x\n",
rtw_read32(rtwdev, 0xe90),
rtw_read32(rtwdev, 0xe98));
@@ -472,7 +472,7 @@ static u8 rtw8723d_iqk_check_rx_failed(struct rtw_dev *rtwdev,
rtw_read32(rtwdev, REG_IQK_RES_RY));
rtw_dbg(rtwdev, RTW_DBG_RFK,
- "[IQK] 0xea0(before IQK)= 0x%x, 0xea8(afer IQK) = 0x%x\n",
+ "[IQK] 0xea0(before IQK)= 0x%x, 0xea8(after IQK) = 0x%x\n",
rtw_read32(rtwdev, 0xea0),
rtw_read32(rtwdev, 0xea8));
@@ -1404,6 +1404,7 @@ static const struct rtw_chip_ops rtw8723d_ops = {
.set_antenna = NULL,
.cfg_ldo25 = rtw8723x_cfg_ldo25,
.efuse_grant = rtw8723x_efuse_grant,
+ .set_ampdu_factor = NULL,
.false_alarm_statistics = rtw8723x_false_alarm_statistics,
.phy_calibration = rtw8723d_phy_calibration,
.cck_pd_set = rtw8723d_phy_cck_pd_set,
diff --git a/drivers/net/wireless/realtek/rtw88/rtw8723de.c b/drivers/net/wireless/realtek/rtw88/rtw8723de.c
index abbaafa32851..87c8bc9d18a9 100644
--- a/drivers/net/wireless/realtek/rtw88/rtw8723de.c
+++ b/drivers/net/wireless/realtek/rtw88/rtw8723de.c
@@ -17,7 +17,7 @@ static const struct pci_device_id rtw_8723de_id_table[] = {
MODULE_DEVICE_TABLE(pci, rtw_8723de_id_table);
static struct pci_driver rtw_8723de_driver = {
- .name = "rtw_8723de",
+ .name = KBUILD_MODNAME,
.id_table = rtw_8723de_id_table,
.probe = rtw_pci_probe,
.remove = rtw_pci_remove,
diff --git a/drivers/net/wireless/realtek/rtw88/rtw8723ds.c b/drivers/net/wireless/realtek/rtw88/rtw8723ds.c
index e5b6960ba0a0..206b77e5b98e 100644
--- a/drivers/net/wireless/realtek/rtw88/rtw8723ds.c
+++ b/drivers/net/wireless/realtek/rtw88/rtw8723ds.c
@@ -25,7 +25,7 @@ static const struct sdio_device_id rtw_8723ds_id_table[] = {
MODULE_DEVICE_TABLE(sdio, rtw_8723ds_id_table);
static struct sdio_driver rtw_8723ds_driver = {
- .name = "rtw_8723ds",
+ .name = KBUILD_MODNAME,
.probe = rtw_sdio_probe,
.remove = rtw_sdio_remove,
.id_table = rtw_8723ds_id_table,
diff --git a/drivers/net/wireless/realtek/rtw88/rtw8723du.c b/drivers/net/wireless/realtek/rtw88/rtw8723du.c
index 322a805da76b..b661a26e0e22 100644
--- a/drivers/net/wireless/realtek/rtw88/rtw8723du.c
+++ b/drivers/net/wireless/realtek/rtw88/rtw8723du.c
@@ -24,7 +24,7 @@ static int rtw8723du_probe(struct usb_interface *intf,
}
static struct usb_driver rtw_8723du_driver = {
- .name = "rtw_8723du",
+ .name = KBUILD_MODNAME,
.id_table = rtw_8723du_id_table,
.probe = rtw8723du_probe,
.disconnect = rtw_usb_disconnect,
diff --git a/drivers/net/wireless/realtek/rtw88/rtw8723x.c b/drivers/net/wireless/realtek/rtw88/rtw8723x.c
index 69f73cb5b4cd..4c77963fdd37 100644
--- a/drivers/net/wireless/realtek/rtw88/rtw8723x.c
+++ b/drivers/net/wireless/realtek/rtw88/rtw8723x.c
@@ -69,6 +69,9 @@ static void __rtw8723x_lck(struct rtw_dev *rtwdev)
#define DBG_EFUSE_2BYTE(rtwdev, map, name) \
rtw_dbg(rtwdev, RTW_DBG_EFUSE, # name "=0x%02x%02x\n", \
(map)->name[0], (map)->name[1])
+#define DBG_EFUSE_FIX(rtwdev, name) \
+ rtw_dbg(rtwdev, RTW_DBG_EFUSE, "Fixed invalid EFUSE value: " \
+ # name "=0x%x\n", rtwdev->efuse.name)
static void rtw8723xe_efuse_debug(struct rtw_dev *rtwdev,
struct rtw8723x_efuse *map)
@@ -238,10 +241,21 @@ static void rtw8723xs_efuse_parsing(struct rtw_efuse *efuse,
ether_addr_copy(efuse->addr, map->s.mac_addr);
}
+/* Default power index table for RTL8703B/RTL8723D, used if EFUSE does
+ * not contain valid data. Replaces EFUSE data from offset 0x10 (start
+ * of txpwr_idx_table).
+ */
+static const u8 rtw8723x_txpwr_idx_table[] = {
+ 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D,
+ 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x02
+};
+
static int __rtw8723x_read_efuse(struct rtw_dev *rtwdev, u8 *log_map)
{
struct rtw_efuse *efuse = &rtwdev->efuse;
+ u8 *pwr = (u8 *)efuse->txpwr_idx_table;
struct rtw8723x_efuse *map;
+ bool valid = false;
int i;
map = (struct rtw8723x_efuse *)log_map;
@@ -279,6 +293,51 @@ static int __rtw8723x_read_efuse(struct rtw_dev *rtwdev, u8 *log_map)
return -EOPNOTSUPP;
}
+ /* If TX power index table in EFUSE is invalid, fall back to
+ * built-in table.
+ */
+ for (i = 0; i < ARRAY_SIZE(rtw8723x_txpwr_idx_table); i++)
+ if (pwr[i] != 0xff) {
+ valid = true;
+ break;
+ }
+ if (!valid) {
+ for (i = 0; i < ARRAY_SIZE(rtw8723x_txpwr_idx_table); i++)
+ pwr[i] = rtw8723x_txpwr_idx_table[i];
+ rtw_dbg(rtwdev, RTW_DBG_EFUSE,
+ "Replaced invalid EFUSE TX power index table.");
+ rtw8723x_debug_txpwr_limit(rtwdev,
+ efuse->txpwr_idx_table, 2);
+ }
+
+ /* Override invalid antenna settings. */
+ if (efuse->bt_setting == 0xff) {
+ /* shared antenna */
+ efuse->bt_setting |= BIT(0);
+ /* RF path A */
+ efuse->bt_setting &= ~BIT(6);
+ DBG_EFUSE_FIX(rtwdev, bt_setting);
+ }
+
+ /* Override invalid board options: The coex code incorrectly
+ * assumes that if bits 6 & 7 are set the board doesn't
+ * support coex. Regd is also derived from rf_board_option and
+ * should be 0 if there's no valid data.
+ */
+ if (efuse->rf_board_option == 0xff) {
+ efuse->regd = 0;
+ efuse->rf_board_option &= GENMASK(5, 0);
+ DBG_EFUSE_FIX(rtwdev, rf_board_option);
+ }
+
+ /* Override invalid crystal cap setting, default comes from
+ * vendor driver. Chip specific.
+ */
+ if (efuse->crystal_cap == 0xff) {
+ efuse->crystal_cap = 0x20;
+ DBG_EFUSE_FIX(rtwdev, crystal_cap);
+ }
+
return 0;
}
diff --git a/drivers/net/wireless/realtek/rtw88/rtw8812a.c b/drivers/net/wireless/realtek/rtw88/rtw8812a.c
index f9ba2aa2928a..c2ef41767ff9 100644
--- a/drivers/net/wireless/realtek/rtw88/rtw8812a.c
+++ b/drivers/net/wireless/realtek/rtw88/rtw8812a.c
@@ -925,6 +925,7 @@ static const struct rtw_chip_ops rtw8812a_ops = {
.set_tx_power_index = rtw88xxa_set_tx_power_index,
.cfg_ldo25 = rtw8812a_cfg_ldo25,
.efuse_grant = rtw88xxa_efuse_grant,
+ .set_ampdu_factor = NULL,
.false_alarm_statistics = rtw88xxa_false_alarm_statistics,
.phy_calibration = rtw8812a_phy_calibration,
.cck_pd_set = rtw88xxa_phy_cck_pd_set,
@@ -1075,6 +1076,7 @@ const struct rtw_chip_info rtw8812a_hw_spec = {
.rfe_defs = rtw8812a_rfe_defs,
.rfe_defs_size = ARRAY_SIZE(rtw8812a_rfe_defs),
.rx_ldpc = false,
+ .amsdu_in_ampdu = true,
.hw_feature_report = false,
.c2h_ra_report_size = 4,
.old_datarate_fb_limit = true,
diff --git a/drivers/net/wireless/realtek/rtw88/rtw8812au.c b/drivers/net/wireless/realtek/rtw88/rtw8812au.c
index e18995f4cc78..dfea89670372 100644
--- a/drivers/net/wireless/realtek/rtw88/rtw8812au.c
+++ b/drivers/net/wireless/realtek/rtw88/rtw8812au.c
@@ -82,7 +82,7 @@ static const struct usb_device_id rtw_8812au_id_table[] = {
MODULE_DEVICE_TABLE(usb, rtw_8812au_id_table);
static struct usb_driver rtw_8812au_driver = {
- .name = "rtw_8812au",
+ .name = KBUILD_MODNAME,
.id_table = rtw_8812au_id_table,
.probe = rtw_usb_probe,
.disconnect = rtw_usb_disconnect,
diff --git a/drivers/net/wireless/realtek/rtw88/rtw8814a.c b/drivers/net/wireless/realtek/rtw88/rtw8814a.c
new file mode 100644
index 000000000000..44dd3090484b
--- /dev/null
+++ b/drivers/net/wireless/realtek/rtw88/rtw8814a.c
@@ -0,0 +1,2269 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
+/* Copyright(c) 2025 Realtek Corporation
+ */
+
+#include <linux/usb.h>
+#include "main.h"
+#include "coex.h"
+#include "tx.h"
+#include "phy.h"
+#include "rtw8814a.h"
+#include "rtw8814a_table.h"
+#include "rtw88xxa.h"
+#include "reg.h"
+#include "debug.h"
+#include "efuse.h"
+#include "regd.h"
+#include "usb.h"
+
+static void rtw8814a_efuse_grant(struct rtw_dev *rtwdev, bool on)
+{
+ if (on) {
+ rtw_write8(rtwdev, REG_EFUSE_ACCESS, EFUSE_ACCESS_ON);
+
+ rtw_write16_set(rtwdev, REG_SYS_FUNC_EN, BIT_FEN_ELDR);
+ rtw_write16_set(rtwdev, REG_SYS_CLKR,
+ BIT_LOADER_CLK_EN | BIT_ANA8M);
+ } else {
+ rtw_write8(rtwdev, REG_EFUSE_ACCESS, EFUSE_ACCESS_OFF);
+ }
+}
+
+static void rtw8814a_read_rfe_type(struct rtw_dev *rtwdev)
+{
+ struct rtw_efuse *efuse = &rtwdev->efuse;
+
+ if (!(efuse->rfe_option & BIT(7)))
+ return;
+
+ if (rtw_hci_type(rtwdev) == RTW_HCI_TYPE_PCIE)
+ efuse->rfe_option = 0;
+ else if (rtw_hci_type(rtwdev) == RTW_HCI_TYPE_USB)
+ efuse->rfe_option = 1;
+}
+
+static void rtw8814a_read_amplifier_type(struct rtw_dev *rtwdev)
+{
+ struct rtw_efuse *efuse = &rtwdev->efuse;
+
+ switch (efuse->rfe_option) {
+ case 1:
+ /* Internal 2G */
+ efuse->pa_type_2g = 0;
+ efuse->lna_type_2g = 0;
+ /* External 5G */
+ efuse->pa_type_5g = BIT(0);
+ efuse->lna_type_5g = BIT(3);
+ break;
+ case 2 ... 5:
+ /* External everything */
+ efuse->pa_type_2g = BIT(4);
+ efuse->lna_type_2g = BIT(3);
+ efuse->pa_type_5g = BIT(0);
+ efuse->lna_type_5g = BIT(3);
+ break;
+ case 6:
+ efuse->lna_type_5g = BIT(3);
+ break;
+ default:
+ break;
+ }
+}
+
+static void rtw8814a_read_rf_type(struct rtw_dev *rtwdev,
+ struct rtw8814a_efuse *map)
+{
+ struct rtw_usb *rtwusb = rtw_get_usb_priv(rtwdev);
+ struct rtw_hal *hal = &rtwdev->hal;
+
+ switch (map->trx_antenna_option) {
+ case 0xff: /* 4T4R */
+ case 0xee: /* 3T3R */
+ if (rtw_hci_type(rtwdev) == RTW_HCI_TYPE_USB &&
+ rtwusb->udev->speed != USB_SPEED_SUPER)
+ hal->rf_type = RF_2T2R;
+ else
+ hal->rf_type = RF_3T3R;
+
+ break;
+ case 0x66: /* 2T2R */
+ case 0x6f: /* 2T4R */
+ default:
+ hal->rf_type = RF_2T2R;
+ break;
+ }
+
+ hal->rf_path_num = 4;
+ hal->rf_phy_num = 4;
+
+ if (hal->rf_type == RF_3T3R) {
+ hal->antenna_rx = BB_PATH_ABC;
+ hal->antenna_tx = BB_PATH_ABC;
+ } else {
+ hal->antenna_rx = BB_PATH_AB;
+ hal->antenna_tx = BB_PATH_AB;
+ }
+}
+
+static void rtw8814a_init_hwcap(struct rtw_dev *rtwdev)
+{
+ struct rtw_efuse *efuse = &rtwdev->efuse;
+ struct rtw_hal *hal = &rtwdev->hal;
+
+ efuse->hw_cap.bw = BIT(RTW_CHANNEL_WIDTH_20) |
+ BIT(RTW_CHANNEL_WIDTH_40) |
+ BIT(RTW_CHANNEL_WIDTH_80);
+ efuse->hw_cap.ptcl = EFUSE_HW_CAP_PTCL_VHT;
+
+ if (hal->rf_type == RF_3T3R)
+ efuse->hw_cap.nss = 3;
+ else
+ efuse->hw_cap.nss = 2;
+
+ rtw_dbg(rtwdev, RTW_DBG_EFUSE,
+ "hw cap: hci=0x%02x, bw=0x%02x, ptcl=0x%02x, ant_num=%d, nss=%d\n",
+ efuse->hw_cap.hci, efuse->hw_cap.bw, efuse->hw_cap.ptcl,
+ efuse->hw_cap.ant_num, efuse->hw_cap.nss);
+}
+
+static int rtw8814a_read_efuse(struct rtw_dev *rtwdev, u8 *log_map)
+{
+ struct rtw_efuse *efuse = &rtwdev->efuse;
+ struct rtw8814a_efuse *map;
+ int i;
+
+ if (rtw_dbg_is_enabled(rtwdev, RTW_DBG_EFUSE))
+ print_hex_dump(KERN_INFO, "", DUMP_PREFIX_OFFSET, 16, 1,
+ log_map, rtwdev->chip->log_efuse_size, true);
+
+ map = (struct rtw8814a_efuse *)log_map;
+
+ efuse->usb_mode_switch = u8_get_bits(map->usb_mode, BIT(4));
+ efuse->rfe_option = map->rfe_option;
+ efuse->rf_board_option = map->rf_board_option;
+ efuse->crystal_cap = map->xtal_k;
+ efuse->channel_plan = map->channel_plan;
+ efuse->country_code[0] = map->country_code[0];
+ efuse->country_code[1] = map->country_code[1];
+ efuse->bt_setting = map->rf_bt_setting;
+ efuse->regd = map->rf_board_option & 0x7;
+ efuse->thermal_meter[RF_PATH_A] = map->thermal_meter;
+ efuse->thermal_meter_k = map->thermal_meter;
+ efuse->tx_bb_swing_setting_2g = map->tx_bb_swing_setting_2g;
+ efuse->tx_bb_swing_setting_5g = map->tx_bb_swing_setting_5g;
+
+ rtw8814a_read_rfe_type(rtwdev);
+ rtw8814a_read_amplifier_type(rtwdev);
+
+ /* Override rtw_chip_parameter_setup() */
+ rtw8814a_read_rf_type(rtwdev, map);
+
+ rtw8814a_init_hwcap(rtwdev);
+
+ for (i = 0; i < 4; i++)
+ efuse->txpwr_idx_table[i] = map->txpwr_idx_table[i];
+
+ switch (rtw_hci_type(rtwdev)) {
+ case RTW_HCI_TYPE_USB:
+ ether_addr_copy(efuse->addr, map->u.mac_addr);
+ break;
+ case RTW_HCI_TYPE_PCIE:
+ ether_addr_copy(efuse->addr, map->e.mac_addr);
+ break;
+ case RTW_HCI_TYPE_SDIO:
+ default:
+ /* unsupported now */
+ return -EOPNOTSUPP;
+ }
+
+ return 0;
+}
+
+static void rtw8814a_init_rfe_reg(struct rtw_dev *rtwdev)
+{
+ u8 rfe_option = rtwdev->efuse.rfe_option;
+
+ if (rfe_option == 2 || rfe_option == 1) {
+ rtw_write32_mask(rtwdev, 0x1994, 0xf, 0xf);
+ rtw_write8_set(rtwdev, REG_GPIO_MUXCFG + 2, 0xf0);
+ } else if (rfe_option == 0) {
+ rtw_write32_mask(rtwdev, 0x1994, 0xf, 0xf);
+ rtw_write8_set(rtwdev, REG_GPIO_MUXCFG + 2, 0xc0);
+ }
+}
+
+#define RTW_TXSCALE_SIZE 37
+static const u32 rtw8814a_txscale_tbl[RTW_TXSCALE_SIZE] = {
+ 0x081, 0x088, 0x090, 0x099, 0x0a2, 0x0ac, 0x0b6, 0x0c0, 0x0cc, 0x0d8,
+ 0x0e5, 0x0f2, 0x101, 0x110, 0x120, 0x131, 0x143, 0x156, 0x16a, 0x180,
+ 0x197, 0x1af, 0x1c8, 0x1e3, 0x200, 0x21e, 0x23e, 0x261, 0x285, 0x2ab,
+ 0x2d3, 0x2fe, 0x32b, 0x35c, 0x38e, 0x3c4, 0x3fe
+};
+
+static u32 rtw8814a_get_bb_swing(struct rtw_dev *rtwdev, u8 band, u8 rf_path)
+{
+ static const u32 swing2setting[4] = {0x200, 0x16a, 0x101, 0x0b6};
+ struct rtw_efuse *efuse = &rtwdev->efuse;
+ u8 tx_bb_swing;
+
+ if (band == RTW_BAND_2G)
+ tx_bb_swing = efuse->tx_bb_swing_setting_2g;
+ else
+ tx_bb_swing = efuse->tx_bb_swing_setting_5g;
+
+ tx_bb_swing >>= 2 * rf_path;
+ tx_bb_swing &= 0x3;
+
+ return swing2setting[tx_bb_swing];
+}
+
+static u8 rtw8814a_get_swing_index(struct rtw_dev *rtwdev)
+{
+ u32 swing, table_value;
+ u8 i;
+
+ swing = rtw8814a_get_bb_swing(rtwdev, rtwdev->hal.current_band_type,
+ RF_PATH_A);
+
+ for (i = 0; i < ARRAY_SIZE(rtw8814a_txscale_tbl); i++) {
+ table_value = rtw8814a_txscale_tbl[i];
+ if (swing == table_value)
+ return i;
+ }
+
+ return 24;
+}
+
+static void rtw8814a_pwrtrack_init(struct rtw_dev *rtwdev)
+{
+ struct rtw_dm_info *dm_info = &rtwdev->dm_info;
+ u8 path;
+
+ dm_info->default_ofdm_index = rtw8814a_get_swing_index(rtwdev);
+
+ for (path = RF_PATH_A; path < rtwdev->hal.rf_path_num; path++) {
+ ewma_thermal_init(&dm_info->avg_thermal[path]);
+ dm_info->delta_power_index[path] = 0;
+ dm_info->delta_power_index_last[path] = 0;
+ }
+ dm_info->pwr_trk_triggered = false;
+ dm_info->pwr_trk_init_trigger = true;
+ dm_info->thermal_meter_k = rtwdev->efuse.thermal_meter_k;
+}
+
+static void rtw8814a_config_trx_path(struct rtw_dev *rtwdev)
+{
+ /* RX CCK disable 2R CCA */
+ rtw_write32_clr(rtwdev, REG_CCK0_FAREPORT,
+ BIT_CCK0_2RX | BIT_CCK0_MRC);
+ /* pathB tx on, path A/C/D tx off */
+ rtw_write32_mask(rtwdev, REG_CCK_RX, 0xf0000000, 0x4);
+ /* pathB rx */
+ rtw_write32_mask(rtwdev, REG_CCK_RX, 0x0f000000, 0x5);
+}
+
+static void rtw8814a_config_cck_rx_antenna_init(struct rtw_dev *rtwdev)
+{
+ /* CCK 2R CCA parameters */
+
+ /* Disable Ant diversity */
+ rtw_write32_mask(rtwdev, REG_RXSB, BIT_RXSB_ANA_DIV, 0x0);
+ /* Concurrent CCA at LSB & USB */
+ rtw_write32_mask(rtwdev, REG_CCA, BIT_CCA_CO, 0);
+ /* RX path diversity enable */
+ rtw_write32_mask(rtwdev, REG_ANTSEL, BIT_ANT_BYCO, 0);
+ /* r_en_mrc_antsel */
+ rtw_write32_mask(rtwdev, REG_PRECTRL, BIT_DIS_CO_PATHSEL, 0);
+ /* MBC weighting */
+ rtw_write32_mask(rtwdev, REG_CCA_MF, BIT_MBC_WIN, 1);
+ /* 2R CCA only */
+ rtw_write32_mask(rtwdev, REG_CCKTX, BIT_CMB_CCA_2R, 1);
+}
+
+static void rtw8814a_phy_set_param(struct rtw_dev *rtwdev)
+{
+ u32 crystal_cap, val32;
+ u8 val8, rf_path;
+
+ /* power on BB/RF domain */
+ if (rtw_hci_type(rtwdev) == RTW_HCI_TYPE_USB)
+ rtw_write8_set(rtwdev, REG_SYS_FUNC_EN, BIT_FEN_USBA);
+ else if (rtw_hci_type(rtwdev) == RTW_HCI_TYPE_PCIE)
+ rtw_write8_set(rtwdev, REG_SYS_FUNC_EN, BIT_FEN_PCIEA);
+
+ rtw_write8_set(rtwdev, REG_SYS_CFG3_8814A + 2,
+ BIT_FEN_BB_GLB_RST | BIT_FEN_BB_RSTB);
+
+ /* Power on RF paths A..D */
+ val8 = BIT_RF_EN | BIT_RF_RSTB | BIT_RF_SDM_RSTB;
+ rtw_write8(rtwdev, REG_RF_CTRL, val8);
+ rtw_write8(rtwdev, REG_RF_CTRL1, val8);
+ rtw_write8(rtwdev, REG_RF_CTRL2, val8);
+ rtw_write8(rtwdev, REG_RF_CTRL3, val8);
+
+ rtw_load_table(rtwdev, rtwdev->chip->bb_tbl);
+ rtw_load_table(rtwdev, rtwdev->chip->agc_tbl);
+
+ crystal_cap = rtwdev->efuse.crystal_cap & 0x3F;
+ crystal_cap |= crystal_cap << 6;
+ rtw_write32_mask(rtwdev, REG_AFE_CTRL3, 0x07ff8000, crystal_cap);
+
+ rtw8814a_config_trx_path(rtwdev);
+
+ for (rf_path = 0; rf_path < rtwdev->hal.rf_path_num; rf_path++)
+ rtw_load_table(rtwdev, rtwdev->chip->rf_tbl[rf_path]);
+
+ val32 = rtw_read_rf(rtwdev, RF_PATH_A, RF_RCK1_V1, RFREG_MASK);
+ rtw_write_rf(rtwdev, RF_PATH_B, RF_RCK1_V1, RFREG_MASK, val32);
+ rtw_write_rf(rtwdev, RF_PATH_C, RF_RCK1_V1, RFREG_MASK, val32);
+ rtw_write_rf(rtwdev, RF_PATH_D, RF_RCK1_V1, RFREG_MASK, val32);
+
+ rtw_write32_set(rtwdev, REG_RXPSEL, BIT_RX_PSEL_RST);
+
+ rtw_write8(rtwdev, REG_HWSEQ_CTRL, 0xFF);
+
+ rtw_write32(rtwdev, REG_BAR_MODE_CTRL, 0x0201ffff);
+
+ rtw_write8(rtwdev, REG_MISC_CTRL, BIT_DIS_SECOND_CCA);
+
+ rtw_write8(rtwdev, REG_NAV_CTRL + 2, 0);
+
+ rtw_write8_clr(rtwdev, REG_GPIO_MUXCFG, BIT(5));
+
+ rtw8814a_config_cck_rx_antenna_init(rtwdev);
+
+ rtw_phy_init(rtwdev);
+ rtw8814a_pwrtrack_init(rtwdev);
+
+ rtw8814a_init_rfe_reg(rtwdev);
+
+ rtw_write8_clr(rtwdev, REG_QUEUE_CTRL, BIT(3));
+
+ rtw_write8(rtwdev, REG_NAV_CTRL + 2, 235);
+
+ /* enable Tx report. */
+ rtw_write8(rtwdev, REG_FWHW_TXQ_CTRL + 1, 0x1F);
+
+ if (rtw_hci_type(rtwdev) == RTW_HCI_TYPE_USB) {
+ /* Reset USB mode switch setting */
+ rtw_write8(rtwdev, REG_SYS_SDIO_CTRL, 0x0);
+ rtw_write8(rtwdev, REG_ACLK_MON, 0x0);
+ }
+}
+
+static void rtw8814ae_enable_rf_1_2v(struct rtw_dev *rtwdev)
+{
+ /* This is for fullsize card, because GPIO7 there is floating.
+ * We should pull GPIO7 high to enable RF 1.2V Switch Power Supply
+ */
+
+ /* 1. set 0x40[1:0] to 0, BIT_GPIOSEL=0, select pin as GPIO */
+ rtw_write8_clr(rtwdev, REG_GPIO_MUXCFG, BIT(1) | BIT(0));
+
+ /* 2. set 0x44[31] to 0
+ * mode=0: data port;
+ * mode=1 and BIT_GPIO_IO_SEL=0: interrupt mode;
+ */
+ rtw_write8_clr(rtwdev, REG_GPIO_PIN_CTRL + 3, BIT(7));
+
+ /* 3. data mode
+ * 3.1 set 0x44[23] to 1
+ * sel=0: input;
+ * sel=1: output;
+ */
+ rtw_write8_set(rtwdev, REG_GPIO_PIN_CTRL + 2, BIT(7));
+
+ /* 3.2 set 0x44[15] to 1
+ * output high value;
+ */
+ rtw_write8_set(rtwdev, REG_GPIO_PIN_CTRL + 1, BIT(7));
+}
+
+static int rtw8814a_mac_init(struct rtw_dev *rtwdev)
+{
+ struct rtw_usb *rtwusb = rtw_get_usb_priv(rtwdev);
+
+ rtw_write16(rtwdev, REG_CR,
+ MAC_TRX_ENABLE | BIT_MAC_SEC_EN | BIT_32K_CAL_TMR_EN);
+
+ rtw_load_table(rtwdev, rtwdev->chip->mac_tbl);
+
+ if (rtw_hci_type(rtwdev) == RTW_HCI_TYPE_USB)
+ rtw_write8(rtwdev, REG_AUTO_LLT_V1 + 3,
+ rtwdev->chip->usb_tx_agg_desc_num << 1);
+
+ rtw_write32(rtwdev, REG_HIMR0, 0);
+ rtw_write32(rtwdev, REG_HIMR1, 0);
+
+ rtw_write32_mask(rtwdev, REG_RRSR, 0xfffff, 0xfffff);
+
+ rtw_write16(rtwdev, REG_RETRY_LIMIT, 0x3030);
+
+ rtw_write16(rtwdev, REG_RXFLTMAP0, 0xffff);
+ rtw_write16(rtwdev, REG_RXFLTMAP1, 0x0400);
+ rtw_write16(rtwdev, REG_RXFLTMAP2, 0xffff);
+
+ rtw_write8(rtwdev, REG_MAX_AGGR_NUM, 0x36);
+ rtw_write8(rtwdev, REG_MAX_AGGR_NUM + 1, 0x36);
+
+ /* Set Spec SIFS (used in NAV) */
+ rtw_write16(rtwdev, REG_SPEC_SIFS, 0x100a);
+ rtw_write16(rtwdev, REG_MAC_SPEC_SIFS, 0x100a);
+
+ /* Set SIFS for CCK */
+ rtw_write16(rtwdev, REG_SIFS, 0x100a);
+
+ /* Set SIFS for OFDM */
+ rtw_write16(rtwdev, REG_SIFS + 2, 0x100a);
+
+ /* TXOP */
+ rtw_write32(rtwdev, REG_EDCA_BE_PARAM, 0x005EA42B);
+ rtw_write32(rtwdev, REG_EDCA_BK_PARAM, 0x0000A44F);
+ rtw_write32(rtwdev, REG_EDCA_VI_PARAM, 0x005EA324);
+ rtw_write32(rtwdev, REG_EDCA_VO_PARAM, 0x002FA226);
+
+ rtw_write8_set(rtwdev, REG_FWHW_TXQ_CTRL, BIT(7));
+
+ rtw_write8(rtwdev, REG_ACKTO, 0x80);
+
+ rtw_write16(rtwdev, REG_BCN_CTRL,
+ BIT_DIS_TSF_UDT | (BIT_DIS_TSF_UDT << 8));
+ rtw_write32_mask(rtwdev, REG_TBTT_PROHIBIT, 0xfffff, WLAN_TBTT_TIME);
+ rtw_write8(rtwdev, REG_DRVERLYINT, 0x05);
+ rtw_write8(rtwdev, REG_BCNDMATIM, WLAN_BCN_DMA_TIME);
+ rtw_write16(rtwdev, REG_BCNTCFG, 0x4413);
+ rtw_write8(rtwdev, REG_BCN_MAX_ERR, 0xFF);
+
+ rtw_write32(rtwdev, REG_FAST_EDCA_VOVI_SETTING, 0x08070807);
+ rtw_write32(rtwdev, REG_FAST_EDCA_BEBK_SETTING, 0x08070807);
+
+ if (rtw_hci_type(rtwdev) == RTW_HCI_TYPE_USB &&
+ rtwusb->udev->speed == USB_SPEED_SUPER) {
+ /* Disable U1/U2 Mode to avoid 2.5G spur in USB3.0. */
+ rtw_write8_clr(rtwdev, REG_USB_MOD, BIT(4) | BIT(3));
+ /* To avoid usb 3.0 H2C fail. */
+ rtw_write16(rtwdev, 0xf002, 0);
+
+ rtw_write8_clr(rtwdev, REG_SW_AMPDU_BURST_MODE_CTRL,
+ BIT_PRE_TX_CMD);
+ } else if (rtw_hci_type(rtwdev) == RTW_HCI_TYPE_PCIE) {
+ rtw8814ae_enable_rf_1_2v(rtwdev);
+
+ /* Force the antenna b to wifi. */
+ rtw_write8_set(rtwdev, REG_PAD_CTRL1, BIT(2));
+ rtw_write8_set(rtwdev, REG_PAD_CTRL1 + 1, BIT(0));
+ rtw_write8_set(rtwdev, REG_LED_CFG + 3,
+ (BIT(27) | BIT_DPDT_WL_SEL) >> 24);
+ }
+
+ return 0;
+}
+
+static void rtw8814a_set_rfe_reg_24g(struct rtw_dev *rtwdev)
+{
+ switch (rtwdev->efuse.rfe_option) {
+ case 2:
+ rtw_write32(rtwdev, REG_RFE_PINMUX_A, 0x72707270);
+ rtw_write32(rtwdev, REG_RFE_PINMUX_B, 0x72707270);
+ rtw_write32(rtwdev, REG_RFE_PINMUX_C, 0x72707270);
+ rtw_write32(rtwdev, REG_RFE_PINMUX_D, 0x77707770);
+
+ rtw_write32_mask(rtwdev, REG_RFE_INVSEL_D,
+ BIT_RFE_SELSW0_D, 0x72);
+
+ break;
+ case 1:
+ rtw_write32(rtwdev, REG_RFE_PINMUX_A, 0x77777777);
+ rtw_write32(rtwdev, REG_RFE_PINMUX_B, 0x77777777);
+ rtw_write32(rtwdev, REG_RFE_PINMUX_C, 0x77777777);
+ rtw_write32(rtwdev, REG_RFE_PINMUX_D, 0x77777777);
+
+ rtw_write32_mask(rtwdev, REG_RFE_INVSEL_D,
+ BIT_RFE_SELSW0_D, 0x77);
+
+ break;
+ case 0:
+ default:
+ rtw_write32(rtwdev, REG_RFE_PINMUX_A, 0x77777777);
+ rtw_write32(rtwdev, REG_RFE_PINMUX_B, 0x77777777);
+ rtw_write32(rtwdev, REG_RFE_PINMUX_C, 0x77777777);
+ /* Is it not necessary to set REG_RFE_PINMUX_D ? */
+
+ rtw_write32_mask(rtwdev, REG_RFE_INVSEL_D,
+ BIT_RFE_SELSW0_D, 0x77);
+
+ break;
+ }
+}
+
+static void rtw8814a_set_rfe_reg_5g(struct rtw_dev *rtwdev)
+{
+ switch (rtwdev->efuse.rfe_option) {
+ case 2:
+ rtw_write32(rtwdev, REG_RFE_PINMUX_A, 0x37173717);
+ rtw_write32(rtwdev, REG_RFE_PINMUX_B, 0x37173717);
+ rtw_write32(rtwdev, REG_RFE_PINMUX_C, 0x37173717);
+ rtw_write32(rtwdev, REG_RFE_PINMUX_D, 0x77177717);
+
+ rtw_write32_mask(rtwdev, REG_RFE_INVSEL_D,
+ BIT_RFE_SELSW0_D, 0x37);
+
+ break;
+ case 1:
+ rtw_write32(rtwdev, REG_RFE_PINMUX_A, 0x33173317);
+ rtw_write32(rtwdev, REG_RFE_PINMUX_B, 0x33173317);
+ rtw_write32(rtwdev, REG_RFE_PINMUX_C, 0x33173317);
+ rtw_write32(rtwdev, REG_RFE_PINMUX_D, 0x77177717);
+
+ rtw_write32_mask(rtwdev, REG_RFE_INVSEL_D,
+ BIT_RFE_SELSW0_D, 0x33);
+
+ break;
+ case 0:
+ default:
+ rtw_write32(rtwdev, REG_RFE_PINMUX_A, 0x54775477);
+ rtw_write32(rtwdev, REG_RFE_PINMUX_B, 0x54775477);
+ rtw_write32(rtwdev, REG_RFE_PINMUX_C, 0x54775477);
+ rtw_write32(rtwdev, REG_RFE_PINMUX_D, 0x54775477);
+
+ rtw_write32_mask(rtwdev, REG_RFE_INVSEL_D,
+ BIT_RFE_SELSW0_D, 0x54);
+
+ break;
+ }
+}
+
+static void rtw8814a_set_channel_bb_swing(struct rtw_dev *rtwdev, u8 band)
+{
+ rtw_write32_mask(rtwdev, REG_TXSCALE_A, BB_SWING_MASK,
+ rtw8814a_get_bb_swing(rtwdev, band, RF_PATH_A));
+ rtw_write32_mask(rtwdev, REG_TXSCALE_B, BB_SWING_MASK,
+ rtw8814a_get_bb_swing(rtwdev, band, RF_PATH_B));
+ rtw_write32_mask(rtwdev, REG_TXSCALE_C, BB_SWING_MASK,
+ rtw8814a_get_bb_swing(rtwdev, band, RF_PATH_C));
+ rtw_write32_mask(rtwdev, REG_TXSCALE_D, BB_SWING_MASK,
+ rtw8814a_get_bb_swing(rtwdev, band, RF_PATH_D));
+ rtw8814a_pwrtrack_init(rtwdev);
+}
+
+static void rtw8814a_set_bw_reg_adc(struct rtw_dev *rtwdev, u8 bw)
+{
+ u32 adc = 0;
+
+ if (bw == RTW_CHANNEL_WIDTH_20)
+ adc = 0;
+ else if (bw == RTW_CHANNEL_WIDTH_40)
+ adc = 1;
+ else if (bw == RTW_CHANNEL_WIDTH_80)
+ adc = 2;
+
+ rtw_write32_mask(rtwdev, REG_ADCCLK, BIT(1) | BIT(0), adc);
+}
+
+static void rtw8814a_set_bw_reg_agc(struct rtw_dev *rtwdev, u8 new_band, u8 bw)
+{
+ u32 agc = 7;
+
+ if (bw == RTW_CHANNEL_WIDTH_20) {
+ agc = 6;
+ } else if (bw == RTW_CHANNEL_WIDTH_40) {
+ if (new_band == RTW_BAND_5G)
+ agc = 8;
+ else
+ agc = 7;
+ } else if (bw == RTW_CHANNEL_WIDTH_80) {
+ agc = 3;
+ }
+
+ rtw_write32_mask(rtwdev, REG_CCASEL, 0xf000, agc);
+}
+
+static void rtw8814a_switch_band(struct rtw_dev *rtwdev, u8 new_band, u8 bw)
+{
+ /* Clear 0x1000[16], When this bit is set to 0, CCK and OFDM
+ * are disabled, and clock are gated. Otherwise, CCK and OFDM
+ * are enabled.
+ */
+ rtw_write8_clr(rtwdev, REG_SYS_CFG3_8814A + 2, BIT_FEN_BB_RSTB);
+
+ if (new_band == RTW_BAND_2G) {
+ rtw_write32_mask(rtwdev, REG_AGC_TABLE, 0x1f, 0);
+
+ rtw8814a_set_rfe_reg_24g(rtwdev);
+
+ rtw_write32_mask(rtwdev, REG_TXPSEL, 0xf0, 0x2);
+ rtw_write32_mask(rtwdev, REG_CCK_RX, 0x0f000000, 0x5);
+
+ rtw_write32_mask(rtwdev, REG_RXPSEL, BIT_RX_PSEL_RST, 0x3);
+
+ rtw_write8(rtwdev, REG_CCK_CHECK, 0);
+
+ rtw_write32_mask(rtwdev, 0xa80, BIT(18), 0);
+ } else {
+ rtw_write8(rtwdev, REG_CCK_CHECK, BIT_CHECK_CCK_EN);
+
+ /* Enable CCK Tx function, even when CCK is off */
+ rtw_write32_mask(rtwdev, 0xa80, BIT(18), 1);
+
+ rtw8814a_set_rfe_reg_5g(rtwdev);
+
+ rtw_write32_mask(rtwdev, REG_TXPSEL, 0xf0, 0x0);
+ rtw_write32_mask(rtwdev, REG_CCK_RX, 0x0f000000, 0xf);
+
+ rtw_write32_mask(rtwdev, REG_RXPSEL, BIT_RX_PSEL_RST, 0x2);
+ }
+
+ rtw8814a_set_channel_bb_swing(rtwdev, new_band);
+
+ rtw8814a_set_bw_reg_adc(rtwdev, bw);
+ rtw8814a_set_bw_reg_agc(rtwdev, new_band, bw);
+
+ rtw_write8_set(rtwdev, REG_SYS_CFG3_8814A + 2, BIT_FEN_BB_RSTB);
+}
+
+static void rtw8814a_switch_channel(struct rtw_dev *rtwdev, u8 channel)
+{
+ struct rtw_hal *hal = &rtwdev->hal;
+ u32 fc_area, rf_mod_ag, cfgch;
+ u8 path;
+
+ switch (channel) {
+ case 36 ... 48:
+ fc_area = 0x494;
+ break;
+ case 50 ... 64:
+ fc_area = 0x453;
+ break;
+ case 100 ... 116:
+ fc_area = 0x452;
+ break;
+ default:
+ if (channel >= 118)
+ fc_area = 0x412;
+ else
+ fc_area = 0x96a;
+ break;
+ }
+
+ rtw_write32_mask(rtwdev, REG_CLKTRK, 0x1ffe0000, fc_area);
+
+ for (path = 0; path < hal->rf_path_num; path++) {
+ switch (channel) {
+ case 36 ... 64:
+ rf_mod_ag = 0x101;
+ break;
+ case 100 ... 140:
+ rf_mod_ag = 0x301;
+ break;
+ default:
+ if (channel > 140)
+ rf_mod_ag = 0x501;
+ else
+ rf_mod_ag = 0x000;
+ break;
+ }
+
+ cfgch = (rf_mod_ag << 8) | channel;
+
+ rtw_write_rf(rtwdev, path, RF_CFGCH,
+ RF18_RFSI_MASK | RF18_BAND_MASK | RF18_CHANNEL_MASK, cfgch);
+ }
+
+ switch (channel) {
+ case 36 ... 64:
+ rtw_write32_mask(rtwdev, REG_AGC_TABLE, 0x1f, 1);
+ break;
+ case 100 ... 144:
+ rtw_write32_mask(rtwdev, REG_AGC_TABLE, 0x1f, 2);
+ break;
+ default:
+ if (channel >= 149)
+ rtw_write32_mask(rtwdev, REG_AGC_TABLE, 0x1f, 3);
+
+ break;
+ }
+}
+
+static void rtw8814a_24g_cck_tx_dfir(struct rtw_dev *rtwdev, u8 channel)
+{
+ if (channel >= 1 && channel <= 11) {
+ rtw_write32(rtwdev, REG_CCK0_TX_FILTER1, 0x1a1b0030);
+ rtw_write32(rtwdev, REG_CCK0_TX_FILTER2, 0x090e1317);
+ rtw_write32(rtwdev, REG_CCK0_DEBUG_PORT, 0x00000204);
+ } else if (channel >= 12 && channel <= 13) {
+ rtw_write32(rtwdev, REG_CCK0_TX_FILTER1, 0x1a1b0030);
+ rtw_write32(rtwdev, REG_CCK0_TX_FILTER2, 0x090e1217);
+ rtw_write32(rtwdev, REG_CCK0_DEBUG_PORT, 0x00000305);
+ } else if (channel == 14) {
+ rtw_write32(rtwdev, REG_CCK0_TX_FILTER1, 0x1a1b0030);
+ rtw_write32(rtwdev, REG_CCK0_TX_FILTER2, 0x00000E17);
+ rtw_write32(rtwdev, REG_CCK0_DEBUG_PORT, 0x00000000);
+ }
+}
+
+static void rtw8814a_set_bw_reg_mac(struct rtw_dev *rtwdev, u8 bw)
+{
+ u16 val16 = rtw_read16(rtwdev, REG_WMAC_TRXPTCL_CTL);
+
+ val16 &= ~BIT_RFMOD;
+ if (bw == RTW_CHANNEL_WIDTH_80)
+ val16 |= BIT_RFMOD_80M;
+ else if (bw == RTW_CHANNEL_WIDTH_40)
+ val16 |= BIT_RFMOD_40M;
+
+ rtw_write16(rtwdev, REG_WMAC_TRXPTCL_CTL, val16);
+}
+
+static void rtw8814a_set_bw_rf(struct rtw_dev *rtwdev, u8 bw)
+{
+ u8 path;
+
+ for (path = RF_PATH_A; path < rtwdev->hal.rf_path_num; path++) {
+ switch (bw) {
+ case RTW_CHANNEL_WIDTH_5:
+ case RTW_CHANNEL_WIDTH_10:
+ case RTW_CHANNEL_WIDTH_20:
+ default:
+ rtw_write_rf(rtwdev, path, RF_CFGCH, RF18_BW_MASK, 3);
+ break;
+ case RTW_CHANNEL_WIDTH_40:
+ rtw_write_rf(rtwdev, path, RF_CFGCH, RF18_BW_MASK, 1);
+ break;
+ case RTW_CHANNEL_WIDTH_80:
+ rtw_write_rf(rtwdev, path, RF_CFGCH, RF18_BW_MASK, 0);
+ break;
+ }
+ }
+}
+
+static void rtw8814a_adc_clk(struct rtw_dev *rtwdev)
+{
+ static const u32 rxiqc_reg[2][4] = {
+ { REG_RX_IQC_AB_A, REG_RX_IQC_AB_B,
+ REG_RX_IQC_AB_C, REG_RX_IQC_AB_D },
+ { REG_RX_IQC_CD_A, REG_RX_IQC_CD_B,
+ REG_RX_IQC_CD_C, REG_RX_IQC_CD_D }
+ };
+ u32 bb_reg_8fc, bb_reg_808, rxiqc[4];
+ u32 i = 0, mac_active = 1;
+ u8 mac_reg_522;
+
+ if (rtwdev->hal.cut_version != RTW_CHIP_VER_CUT_A)
+ return;
+
+ /* 1 Step1. MAC TX pause */
+ mac_reg_522 = rtw_read8(rtwdev, REG_TXPAUSE);
+ bb_reg_8fc = rtw_read32(rtwdev, REG_DBGSEL);
+ bb_reg_808 = rtw_read32(rtwdev, REG_RXPSEL);
+ rtw_write8(rtwdev, REG_TXPAUSE, 0x3f);
+
+ /* 1 Step 2. Backup rxiqc & rxiqc = 0 */
+ for (i = 0; i < 4; i++) {
+ rxiqc[i] = rtw_read32(rtwdev, rxiqc_reg[0][i]);
+ rtw_write32(rtwdev, rxiqc_reg[0][i], 0x0);
+ rtw_write32(rtwdev, rxiqc_reg[1][i], 0x0);
+ }
+ rtw_write32_mask(rtwdev, REG_PRECTRL, BIT_IQ_WGT, 0x3);
+ i = 0;
+
+ /* 1 Step 3. Monitor MAC IDLE */
+ rtw_write32(rtwdev, REG_DBGSEL, 0x0);
+ while (mac_active) {
+ mac_active = rtw_read32(rtwdev, REG_DBGRPT) & 0x803e0008;
+ i++;
+ if (i > 1000)
+ break;
+ }
+
+ /* 1 Step 4. ADC clk flow */
+ rtw_write8(rtwdev, REG_RXPSEL, 0x11);
+ rtw_write32_mask(rtwdev, REG_DAC_RSTB, BIT(13), 0x1);
+ rtw_write8_mask(rtwdev, REG_GNT_BT, BIT(2) | BIT(1), 0x3);
+ rtw_write32_mask(rtwdev, REG_CCK_RPT_FORMAT, BIT(2), 0x1);
+
+ /* 0xc1c/0xe1c/0x181c/0x1a1c[4] must=1 to ensure table can be
+ * written when bbrstb=0
+ * 0xc60/0xe60/0x1860/0x1a60[15] always = 1 after this line
+ * 0xc60/0xe60/0x1860/0x1a60[14] always = 0 bcz its error in A-cut
+ */
+
+ /* power_off/clk_off @ anapar_state=idle mode */
+ rtw_write32(rtwdev, REG_AFE_PWR1_A, 0x15800002);
+ rtw_write32(rtwdev, REG_AFE_PWR1_A, 0x01808003);
+ rtw_write32(rtwdev, REG_AFE_PWR1_B, 0x15800002);
+ rtw_write32(rtwdev, REG_AFE_PWR1_B, 0x01808003);
+ rtw_write32(rtwdev, REG_AFE_PWR1_C, 0x15800002);
+ rtw_write32(rtwdev, REG_AFE_PWR1_C, 0x01808003);
+ rtw_write32(rtwdev, REG_AFE_PWR1_D, 0x15800002);
+ rtw_write32(rtwdev, REG_AFE_PWR1_D, 0x01808003);
+
+ rtw_write8_mask(rtwdev, REG_GNT_BT, BIT(2), 0x0);
+ rtw_write32_mask(rtwdev, REG_CCK_RPT_FORMAT, BIT(2), 0x0);
+ /* [19] = 1 to turn off ADC */
+ rtw_write32(rtwdev, REG_CK_MONHA, 0x0D080058);
+ rtw_write32(rtwdev, REG_CK_MONHB, 0x0D080058);
+ rtw_write32(rtwdev, REG_CK_MONHC, 0x0D080058);
+ rtw_write32(rtwdev, REG_CK_MONHD, 0x0D080058);
+
+ /* power_on/clk_off */
+ /* [19] = 0 to turn on ADC */
+ rtw_write32(rtwdev, REG_CK_MONHA, 0x0D000058);
+ rtw_write32(rtwdev, REG_CK_MONHB, 0x0D000058);
+ rtw_write32(rtwdev, REG_CK_MONHC, 0x0D000058);
+ rtw_write32(rtwdev, REG_CK_MONHD, 0x0D000058);
+
+ /* power_on/clk_on @ anapar_state=BT mode */
+ rtw_write32(rtwdev, REG_AFE_PWR1_A, 0x05808032);
+ rtw_write32(rtwdev, REG_AFE_PWR1_B, 0x05808032);
+ rtw_write32(rtwdev, REG_AFE_PWR1_C, 0x05808032);
+ rtw_write32(rtwdev, REG_AFE_PWR1_D, 0x05808032);
+ rtw_write8_mask(rtwdev, REG_GNT_BT, BIT(2), 0x1);
+ rtw_write32_mask(rtwdev, REG_CCK_RPT_FORMAT, BIT(2), 0x1);
+
+ /* recover original setting @ anapar_state=BT mode */
+ rtw_write32(rtwdev, REG_AFE_PWR1_A, 0x05808032);
+ rtw_write32(rtwdev, REG_AFE_PWR1_B, 0x05808032);
+ rtw_write32(rtwdev, REG_AFE_PWR1_C, 0x05808032);
+ rtw_write32(rtwdev, REG_AFE_PWR1_D, 0x05808032);
+
+ rtw_write32(rtwdev, REG_AFE_PWR1_A, 0x05800002);
+ rtw_write32(rtwdev, REG_AFE_PWR1_A, 0x07808003);
+ rtw_write32(rtwdev, REG_AFE_PWR1_B, 0x05800002);
+ rtw_write32(rtwdev, REG_AFE_PWR1_B, 0x07808003);
+ rtw_write32(rtwdev, REG_AFE_PWR1_C, 0x05800002);
+ rtw_write32(rtwdev, REG_AFE_PWR1_C, 0x07808003);
+ rtw_write32(rtwdev, REG_AFE_PWR1_D, 0x05800002);
+ rtw_write32(rtwdev, REG_AFE_PWR1_D, 0x07808003);
+
+ rtw_write8_mask(rtwdev, REG_GNT_BT, BIT(2) | BIT(1), 0x0);
+ rtw_write32_mask(rtwdev, REG_CCK_RPT_FORMAT, BIT(2), 0x0);
+ rtw_write32_mask(rtwdev, REG_DAC_RSTB, BIT(13), 0x0);
+
+ /* 1 Step 5. Recover MAC TX & IQC */
+ rtw_write8(rtwdev, REG_TXPAUSE, mac_reg_522);
+ rtw_write32(rtwdev, REG_DBGSEL, bb_reg_8fc);
+ rtw_write32(rtwdev, REG_RXPSEL, bb_reg_808);
+ for (i = 0; i < 4; i++) {
+ rtw_write32(rtwdev, rxiqc_reg[0][i], rxiqc[i]);
+ rtw_write32(rtwdev, rxiqc_reg[1][i], 0x01000000);
+ }
+ rtw_write32_mask(rtwdev, REG_PRECTRL, BIT_IQ_WGT, 0x0);
+}
+
+static void rtw8814a_spur_calibration_ch140(struct rtw_dev *rtwdev, u8 channel)
+{
+ struct rtw_hal *hal = &rtwdev->hal;
+
+ /* Add for 8814AE module ch140 MP Rx */
+ if (channel == 140) {
+ if (hal->ch_param[0] == 0)
+ hal->ch_param[0] = rtw_read32(rtwdev, REG_CCASEL);
+ if (hal->ch_param[1] == 0)
+ hal->ch_param[1] = rtw_read32(rtwdev, REG_PDMFTH);
+
+ rtw_write32(rtwdev, REG_CCASEL, 0x75438170);
+ rtw_write32(rtwdev, REG_PDMFTH, 0x79a18a0a);
+ } else {
+ if (rtw_read32(rtwdev, REG_CCASEL) == 0x75438170 &&
+ hal->ch_param[0] != 0)
+ rtw_write32(rtwdev, REG_CCASEL, hal->ch_param[0]);
+
+ if (rtw_read32(rtwdev, REG_PDMFTH) == 0x79a18a0a &&
+ hal->ch_param[1] != 0)
+ rtw_write32(rtwdev, REG_PDMFTH, hal->ch_param[1]);
+
+ hal->ch_param[0] = rtw_read32(rtwdev, REG_CCASEL);
+ hal->ch_param[1] = rtw_read32(rtwdev, REG_PDMFTH);
+ }
+}
+
+static void rtw8814a_set_nbi_reg(struct rtw_dev *rtwdev, u32 tone_idx)
+{
+ /* tone_idx X 10 */
+ static const u32 nbi_128[] = {
+ 25, 55, 85, 115, 135,
+ 155, 185, 205, 225, 245,
+ 265, 285, 305, 335, 355,
+ 375, 395, 415, 435, 455,
+ 485, 505, 525, 555, 585, 615, 635
+ };
+ u32 reg_idx = 0;
+ u32 i;
+
+ for (i = 0; i < ARRAY_SIZE(nbi_128); i++) {
+ if (tone_idx < nbi_128[i]) {
+ reg_idx = i + 1;
+ break;
+ }
+ }
+
+ rtw_write32_mask(rtwdev, REG_NBI_SETTING, 0xfc000, reg_idx);
+}
+
+static void rtw8814a_nbi_setting(struct rtw_dev *rtwdev, u32 ch, u32 f_intf)
+{
+ u32 fc, int_distance, tone_idx;
+
+ fc = 2412 + (ch - 1) * 5;
+ int_distance = abs_diff(fc, f_intf);
+
+ /* 10 * (int_distance / 0.3125) */
+ tone_idx = int_distance << 5;
+
+ rtw8814a_set_nbi_reg(rtwdev, tone_idx);
+
+ rtw_write32_mask(rtwdev, REG_NBI_SETTING, BIT_NBI_ENABLE, 1);
+}
+
+static void rtw8814a_spur_nbi_setting(struct rtw_dev *rtwdev)
+{
+ u8 primary_channel = rtwdev->hal.primary_channel;
+ u8 rfe_type = rtwdev->efuse.rfe_option;
+
+ if (rfe_type != 0 && rfe_type != 1 && rfe_type != 6 && rfe_type != 7)
+ return;
+
+ if (primary_channel == 14)
+ rtw8814a_nbi_setting(rtwdev, primary_channel, 2480);
+ else if (primary_channel >= 4 && primary_channel <= 8)
+ rtw8814a_nbi_setting(rtwdev, primary_channel, 2440);
+ else
+ rtw_write32_mask(rtwdev, REG_NBI_SETTING, BIT_NBI_ENABLE, 0);
+}
+
+/* A workaround to eliminate the 5280 MHz & 5600 MHz & 5760 MHz spur of 8814A */
+static void rtw8814a_spur_calibration(struct rtw_dev *rtwdev, u8 channel, u8 bw)
+{
+ u8 rfe_type = rtwdev->efuse.rfe_option;
+ bool reset_nbi_csi = true;
+
+ if (rfe_type == 0) {
+ switch (bw) {
+ case RTW_CHANNEL_WIDTH_40:
+ if (channel == 54 || channel == 118) {
+ rtw_write32_mask(rtwdev, REG_NBI_SETTING,
+ 0x000fe000, 0x3e >> 1);
+ rtw_write32_mask(rtwdev, REG_CSI_MASK_SETTING1,
+ BIT(0), 1);
+ rtw_write32(rtwdev, REG_CSI_FIX_MASK0, 0);
+ rtw_write32_mask(rtwdev, REG_CSI_FIX_MASK1,
+ BIT(0), 1);
+ rtw_write32(rtwdev, REG_CSI_FIX_MASK6, 0);
+ rtw_write32(rtwdev, REG_CSI_FIX_MASK7, 0);
+
+ reset_nbi_csi = false;
+ } else if (channel == 151) {
+ rtw_write32_mask(rtwdev, REG_NBI_SETTING,
+ 0x000fe000, 0x1e >> 1);
+ rtw_write32_mask(rtwdev, REG_CSI_MASK_SETTING1,
+ BIT(0), 1);
+ rtw_write32_mask(rtwdev, REG_CSI_FIX_MASK0,
+ BIT(16), 1);
+ rtw_write32(rtwdev, REG_CSI_FIX_MASK1, 0);
+ rtw_write32(rtwdev, REG_CSI_FIX_MASK6, 0);
+ rtw_write32(rtwdev, REG_CSI_FIX_MASK7, 0);
+
+ reset_nbi_csi = false;
+ }
+ break;
+ case RTW_CHANNEL_WIDTH_80:
+ if (channel == 58 || channel == 122) {
+ rtw_write32_mask(rtwdev, REG_NBI_SETTING,
+ 0x000fe000, 0x3a >> 1);
+ rtw_write32_mask(rtwdev, REG_CSI_MASK_SETTING1,
+ BIT(0), 1);
+ rtw_write32(rtwdev, REG_CSI_FIX_MASK0, 0);
+ rtw_write32(rtwdev, REG_CSI_FIX_MASK1, 0);
+ rtw_write32(rtwdev, REG_CSI_FIX_MASK6, 0);
+ rtw_write32_mask(rtwdev, REG_CSI_FIX_MASK7,
+ BIT(0), 1);
+
+ reset_nbi_csi = false;
+ } else if (channel == 155) {
+ rtw_write32_mask(rtwdev, REG_NBI_SETTING,
+ 0x000fe000, 0x5a >> 1);
+ rtw_write32_mask(rtwdev, REG_CSI_MASK_SETTING1,
+ BIT(0), 1);
+ rtw_write32(rtwdev, REG_CSI_FIX_MASK0, 0);
+ rtw_write32(rtwdev, REG_CSI_FIX_MASK1, 0);
+ rtw_write32_mask(rtwdev, REG_CSI_FIX_MASK6,
+ BIT(16), 1);
+ rtw_write32(rtwdev, REG_CSI_FIX_MASK7, 0);
+
+ reset_nbi_csi = false;
+ }
+ break;
+ case RTW_CHANNEL_WIDTH_20:
+ if (channel == 153) {
+ rtw_write32_mask(rtwdev, REG_NBI_SETTING,
+ 0x000fe000, 0x1e >> 1);
+ rtw_write32_mask(rtwdev, REG_CSI_MASK_SETTING1,
+ BIT(0), 1);
+ rtw_write32(rtwdev, REG_CSI_FIX_MASK0, 0);
+ rtw_write32(rtwdev, REG_CSI_FIX_MASK1, 0);
+ rtw_write32(rtwdev, REG_CSI_FIX_MASK6, 0);
+ rtw_write32_mask(rtwdev, REG_CSI_FIX_MASK7,
+ BIT(16), 1);
+
+ reset_nbi_csi = false;
+ }
+
+ rtw8814a_spur_calibration_ch140(rtwdev, channel);
+ break;
+ default:
+ break;
+ }
+ } else if (rfe_type == 1 || rfe_type == 2) {
+ switch (bw) {
+ case RTW_CHANNEL_WIDTH_20:
+ if (channel == 153) {
+ rtw_write32_mask(rtwdev, REG_NBI_SETTING,
+ 0x000fe000, 0x1E >> 1);
+ rtw_write32_mask(rtwdev, REG_CSI_MASK_SETTING1,
+ BIT(0), 1);
+ rtw_write32(rtwdev, REG_CSI_FIX_MASK0, 0);
+ rtw_write32(rtwdev, REG_CSI_FIX_MASK1, 0);
+ rtw_write32(rtwdev, REG_CSI_FIX_MASK6, 0);
+ rtw_write32_mask(rtwdev, REG_CSI_FIX_MASK7,
+ BIT(16), 1);
+
+ reset_nbi_csi = false;
+ }
+ break;
+ case RTW_CHANNEL_WIDTH_40:
+ if (channel == 151) {
+ rtw_write32_mask(rtwdev, REG_NBI_SETTING,
+ 0x000fe000, 0x1e >> 1);
+ rtw_write32_mask(rtwdev, REG_CSI_MASK_SETTING1,
+ BIT(0), 1);
+ rtw_write32_mask(rtwdev, REG_CSI_FIX_MASK0,
+ BIT(16), 1);
+ rtw_write32(rtwdev, REG_CSI_FIX_MASK1, 0);
+ rtw_write32(rtwdev, REG_CSI_FIX_MASK6, 0);
+ rtw_write32(rtwdev, REG_CSI_FIX_MASK7, 0);
+
+ reset_nbi_csi = false;
+ }
+ break;
+ case RTW_CHANNEL_WIDTH_80:
+ if (channel == 155) {
+ rtw_write32_mask(rtwdev, REG_NBI_SETTING,
+ 0x000fe000, 0x5a >> 1);
+ rtw_write32_mask(rtwdev, REG_CSI_MASK_SETTING1,
+ BIT(0), 1);
+ rtw_write32(rtwdev, REG_CSI_FIX_MASK0, 0);
+ rtw_write32(rtwdev, REG_CSI_FIX_MASK1, 0);
+ rtw_write32_mask(rtwdev, REG_CSI_FIX_MASK6,
+ BIT(16), 1);
+ rtw_write32(rtwdev, REG_CSI_FIX_MASK7, 0);
+
+ reset_nbi_csi = false;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (reset_nbi_csi) {
+ rtw_write32_mask(rtwdev, REG_NBI_SETTING,
+ 0x000fe000, 0xfc >> 1);
+ rtw_write32_mask(rtwdev, REG_CSI_MASK_SETTING1, BIT(0), 0);
+ rtw_write32(rtwdev, REG_CSI_FIX_MASK0, 0);
+ rtw_write32(rtwdev, REG_CSI_FIX_MASK1, 0);
+ rtw_write32(rtwdev, REG_CSI_FIX_MASK6, 0);
+ rtw_write32(rtwdev, REG_CSI_FIX_MASK7, 0);
+ }
+
+ rtw8814a_spur_nbi_setting(rtwdev);
+}
+
+static void rtw8814a_set_bw_mode(struct rtw_dev *rtwdev, u8 new_band,
+ u8 channel, u8 bw, u8 primary_chan_idx)
+{
+ u8 txsc40 = 0, txsc20, txsc;
+
+ rtw8814a_set_bw_reg_mac(rtwdev, bw);
+
+ txsc20 = primary_chan_idx;
+ if (bw == RTW_CHANNEL_WIDTH_80) {
+ if (txsc20 == RTW_SC_20_UPPER || txsc20 == RTW_SC_20_UPMOST)
+ txsc40 = RTW_SC_40_UPPER;
+ else
+ txsc40 = RTW_SC_40_LOWER;
+ }
+
+ txsc = BIT_TXSC_20M(txsc20) | BIT_TXSC_40M(txsc40);
+ rtw_write8(rtwdev, REG_DATA_SC, txsc);
+
+ rtw8814a_set_bw_reg_adc(rtwdev, bw);
+ rtw8814a_set_bw_reg_agc(rtwdev, new_band, bw);
+
+ if (bw == RTW_CHANNEL_WIDTH_80) {
+ rtw_write32_mask(rtwdev, REG_ADCCLK, 0x3c, txsc);
+ } else if (bw == RTW_CHANNEL_WIDTH_40) {
+ rtw_write32_mask(rtwdev, REG_ADCCLK, 0x3c, txsc);
+
+ if (txsc == RTW_SC_20_UPPER)
+ rtw_write32_set(rtwdev, REG_RXSB, BIT(4));
+ else
+ rtw_write32_clr(rtwdev, REG_RXSB, BIT(4));
+ }
+
+ rtw8814a_set_bw_rf(rtwdev, bw);
+
+ rtw8814a_adc_clk(rtwdev);
+
+ rtw8814a_spur_calibration(rtwdev, channel, bw);
+}
+
+static void rtw8814a_set_channel(struct rtw_dev *rtwdev, u8 channel, u8 bw,
+ u8 primary_chan_idx)
+{
+ u8 old_band, new_band;
+
+ if (rtw_read8(rtwdev, REG_CCK_CHECK) & BIT_CHECK_CCK_EN)
+ old_band = RTW_BAND_5G;
+ else
+ old_band = RTW_BAND_2G;
+
+ if (channel > 14)
+ new_band = RTW_BAND_5G;
+ else
+ new_band = RTW_BAND_2G;
+
+ if (new_band != old_band)
+ rtw8814a_switch_band(rtwdev, new_band, bw);
+
+ rtw8814a_switch_channel(rtwdev, channel);
+
+ rtw8814a_24g_cck_tx_dfir(rtwdev, channel);
+
+ rtw8814a_set_bw_mode(rtwdev, new_band, channel, bw, primary_chan_idx);
+}
+
+static s8 rtw8814a_cck_rx_pwr(u8 lna_idx, u8 vga_idx)
+{
+ s8 rx_pwr_all = 0;
+
+ switch (lna_idx) {
+ case 7:
+ rx_pwr_all = -38 - 2 * vga_idx;
+ break;
+ case 5:
+ rx_pwr_all = -28 - 2 * vga_idx;
+ break;
+ case 3:
+ rx_pwr_all = -8 - 2 * vga_idx;
+ break;
+ case 2:
+ rx_pwr_all = -1 - 2 * vga_idx;
+ break;
+ default:
+ break;
+ }
+
+ return rx_pwr_all;
+}
+
+static void rtw8814a_query_phy_status(struct rtw_dev *rtwdev, u8 *phy_status,
+ struct rtw_rx_pkt_stat *pkt_stat)
+{
+ struct rtw_dm_info *dm_info = &rtwdev->dm_info;
+ struct rtw_jaguar_phy_status_rpt *rpt;
+ u8 gain[RTW_RF_PATH_MAX], rssi, i;
+ s8 rx_pwr_db, middle1, middle2;
+ s8 snr[RTW_RF_PATH_MAX];
+ s8 evm[RTW_RF_PATH_MAX];
+ u8 rfmode, subchannel;
+ u8 lna, vga;
+ s8 cfo[2];
+
+ rpt = (struct rtw_jaguar_phy_status_rpt *)phy_status;
+
+ pkt_stat->bw = RTW_CHANNEL_WIDTH_20;
+
+ if (pkt_stat->rate <= DESC_RATE11M) {
+ lna = le32_get_bits(rpt->w1, RTW_JGRPHY_W1_AGC_RPT_LNA_IDX);
+ vga = le32_get_bits(rpt->w1, RTW_JGRPHY_W1_AGC_RPT_VGA_IDX);
+
+ rx_pwr_db = rtw8814a_cck_rx_pwr(lna, vga);
+
+ pkt_stat->rx_power[RF_PATH_A] = rx_pwr_db;
+ pkt_stat->rssi = rtw_phy_rf_power_2_rssi(pkt_stat->rx_power, 1);
+ dm_info->rssi[RF_PATH_A] = pkt_stat->rssi;
+ pkt_stat->signal_power = rx_pwr_db;
+ } else { /* OFDM rate */
+ gain[RF_PATH_A] = le32_get_bits(rpt->w0, RTW_JGRPHY_W0_GAIN_A);
+ gain[RF_PATH_B] = le32_get_bits(rpt->w0, RTW_JGRPHY_W0_GAIN_B);
+ gain[RF_PATH_C] = le32_get_bits(rpt->w5, RTW_JGRPHY_W5_GAIN_C);
+ gain[RF_PATH_D] = le32_get_bits(rpt->w6, RTW_JGRPHY_W6_GAIN_D);
+
+ snr[RF_PATH_A] = le32_get_bits(rpt->w3, RTW_JGRPHY_W3_RXSNR_A);
+ snr[RF_PATH_B] = le32_get_bits(rpt->w4, RTW_JGRPHY_W4_RXSNR_B);
+ snr[RF_PATH_C] = le32_get_bits(rpt->w5, RTW_JGRPHY_W5_RXSNR_C);
+ snr[RF_PATH_D] = le32_get_bits(rpt->w5, RTW_JGRPHY_W5_RXSNR_D);
+
+ evm[RF_PATH_A] = le32_get_bits(rpt->w3, RTW_JGRPHY_W3_RXEVM_1);
+ evm[RF_PATH_B] = le32_get_bits(rpt->w3, RTW_JGRPHY_W3_RXEVM_2);
+ evm[RF_PATH_C] = le32_get_bits(rpt->w4, RTW_JGRPHY_W4_RXEVM_3);
+ evm[RF_PATH_D] = le32_get_bits(rpt->w5, RTW_JGRPHY_W5_RXEVM_4);
+
+ if (pkt_stat->rate <= DESC_RATE54M)
+ evm[RF_PATH_A] = le32_get_bits(rpt->w6,
+ RTW_JGRPHY_W6_SIGEVM);
+
+ for (i = RF_PATH_A; i < RTW_RF_PATH_MAX; i++) {
+ pkt_stat->rx_power[i] = gain[i] - 110;
+
+ rssi = rtw_phy_rf_power_2_rssi(&pkt_stat->rx_power[i], 1);
+ dm_info->rssi[i] = rssi;
+
+ pkt_stat->rx_snr[i] = snr[i];
+ dm_info->rx_snr[i] = snr[i] >> 1;
+
+ pkt_stat->rx_evm[i] = evm[i];
+ evm[i] = max_t(s8, -127, evm[i]);
+ dm_info->rx_evm_dbm[i] = abs(evm[i]) >> 1;
+ }
+
+ rssi = rtw_phy_rf_power_2_rssi(pkt_stat->rx_power,
+ RTW_RF_PATH_MAX);
+ pkt_stat->rssi = rssi;
+
+ /* When power saving is enabled the hardware sometimes
+ * reports unbelievably high gain for paths A and C
+ * (e.g. one frame 64 68 68 72, the next frame 106 66 88 72,
+ * the next 66 66 68 72), so use the second lowest gain
+ * instead of the highest.
+ */
+ middle1 = max(min(gain[RF_PATH_A], gain[RF_PATH_B]),
+ min(gain[RF_PATH_C], gain[RF_PATH_D]));
+ middle2 = min(max(gain[RF_PATH_A], gain[RF_PATH_B]),
+ max(gain[RF_PATH_C], gain[RF_PATH_D]));
+ rx_pwr_db = min(middle1, middle2);
+ rx_pwr_db -= 110;
+ pkt_stat->signal_power = rx_pwr_db;
+
+ rfmode = le32_get_bits(rpt->w0, RTW_JGRPHY_W0_R_RFMOD);
+ subchannel = le32_get_bits(rpt->w0, RTW_JGRPHY_W0_SUB_CHNL);
+
+ if (rfmode == 1 && subchannel == 0) {
+ pkt_stat->bw = RTW_CHANNEL_WIDTH_40;
+ } else if (rfmode == 2) {
+ if (subchannel == 0)
+ pkt_stat->bw = RTW_CHANNEL_WIDTH_80;
+ else if (subchannel == 9 || subchannel == 10)
+ pkt_stat->bw = RTW_CHANNEL_WIDTH_40;
+ }
+
+ cfo[RF_PATH_A] = le32_get_bits(rpt->w2, RTW_JGRPHY_W2_CFO_TAIL_A);
+ cfo[RF_PATH_B] = le32_get_bits(rpt->w2, RTW_JGRPHY_W2_CFO_TAIL_B);
+
+ for (i = RF_PATH_A; i < 2; i++) {
+ pkt_stat->cfo_tail[i] = cfo[i];
+ dm_info->cfo_tail[i] = (cfo[i] * 5) >> 1;
+ }
+ }
+}
+
+static void
+rtw8814a_set_tx_power_index_by_rate(struct rtw_dev *rtwdev, u8 path, u8 rs)
+{
+ struct rtw_hal *hal = &rtwdev->hal;
+ u32 txagc_table_wd;
+ u8 rate, pwr_index;
+ int j;
+
+ for (j = 0; j < rtw_rate_size[rs]; j++) {
+ rate = rtw_rate_section[rs][j];
+
+ pwr_index = hal->tx_pwr_tbl[path][rate] + 2;
+ if (pwr_index > rtwdev->chip->max_power_index)
+ pwr_index = rtwdev->chip->max_power_index;
+
+ txagc_table_wd = 0x00801000;
+ txagc_table_wd |= (pwr_index << 24) | (path << 8) | rate;
+
+ rtw_write32(rtwdev, REG_AGC_TBL, txagc_table_wd);
+
+ /* first time to turn on the txagc table
+ * second to write the addr0
+ */
+ if (rate == DESC_RATE1M)
+ rtw_write32(rtwdev, REG_AGC_TBL, txagc_table_wd);
+ }
+}
+
+static void rtw8814a_set_tx_power_index(struct rtw_dev *rtwdev)
+{
+ struct rtw_hal *hal = &rtwdev->hal;
+ int path;
+
+ for (path = 0; path < hal->rf_path_num; path++) {
+ if (hal->current_band_type == RTW_BAND_2G)
+ rtw8814a_set_tx_power_index_by_rate(rtwdev, path,
+ RTW_RATE_SECTION_CCK);
+
+ rtw8814a_set_tx_power_index_by_rate(rtwdev, path,
+ RTW_RATE_SECTION_OFDM);
+
+ if (test_bit(RTW_FLAG_SCANNING, rtwdev->flags))
+ continue;
+
+ rtw8814a_set_tx_power_index_by_rate(rtwdev, path,
+ RTW_RATE_SECTION_HT_1S);
+ rtw8814a_set_tx_power_index_by_rate(rtwdev, path,
+ RTW_RATE_SECTION_VHT_1S);
+
+ rtw8814a_set_tx_power_index_by_rate(rtwdev, path,
+ RTW_RATE_SECTION_HT_2S);
+ rtw8814a_set_tx_power_index_by_rate(rtwdev, path,
+ RTW_RATE_SECTION_VHT_2S);
+
+ rtw8814a_set_tx_power_index_by_rate(rtwdev, path,
+ RTW_RATE_SECTION_HT_3S);
+ rtw8814a_set_tx_power_index_by_rate(rtwdev, path,
+ RTW_RATE_SECTION_VHT_3S);
+ }
+}
+
+static void rtw8814a_cfg_ldo25(struct rtw_dev *rtwdev, bool enable)
+{
+}
+
+/* Without this RTL8814A sends too many frames and (some?) 11n AP
+ * can't handle it, resulting in low TX speed. Other chips seem fine.
+ */
+static void rtw8814a_set_ampdu_factor(struct rtw_dev *rtwdev, u8 factor)
+{
+ factor = min_t(u8, factor, IEEE80211_VHT_MAX_AMPDU_256K);
+
+ rtw_write32(rtwdev, REG_AMPDU_MAX_LENGTH, (8192 << factor) - 1);
+}
+
+static void rtw8814a_false_alarm_statistics(struct rtw_dev *rtwdev)
+{
+ struct rtw_dm_info *dm_info = &rtwdev->dm_info;
+ u32 cck_fa_cnt, ofdm_fa_cnt;
+ u32 crc32_cnt, cca32_cnt;
+ u32 cck_enable;
+
+ cck_enable = rtw_read32(rtwdev, REG_RXPSEL) & BIT(28);
+ cck_fa_cnt = rtw_read16(rtwdev, REG_FA_CCK);
+ ofdm_fa_cnt = rtw_read16(rtwdev, REG_FA_OFDM);
+
+ dm_info->cck_fa_cnt = cck_fa_cnt;
+ dm_info->ofdm_fa_cnt = ofdm_fa_cnt;
+ dm_info->total_fa_cnt = ofdm_fa_cnt;
+ if (cck_enable)
+ dm_info->total_fa_cnt += cck_fa_cnt;
+
+ crc32_cnt = rtw_read32(rtwdev, REG_CRC_CCK);
+ dm_info->cck_ok_cnt = u32_get_bits(crc32_cnt, MASKLWORD);
+ dm_info->cck_err_cnt = u32_get_bits(crc32_cnt, MASKHWORD);
+
+ crc32_cnt = rtw_read32(rtwdev, REG_CRC_OFDM);
+ dm_info->ofdm_ok_cnt = u32_get_bits(crc32_cnt, MASKLWORD);
+ dm_info->ofdm_err_cnt = u32_get_bits(crc32_cnt, MASKHWORD);
+
+ crc32_cnt = rtw_read32(rtwdev, REG_CRC_HT);
+ dm_info->ht_ok_cnt = u32_get_bits(crc32_cnt, MASKLWORD);
+ dm_info->ht_err_cnt = u32_get_bits(crc32_cnt, MASKHWORD);
+
+ crc32_cnt = rtw_read32(rtwdev, REG_CRC_VHT);
+ dm_info->vht_ok_cnt = u32_get_bits(crc32_cnt, MASKLWORD);
+ dm_info->vht_err_cnt = u32_get_bits(crc32_cnt, MASKHWORD);
+
+ cca32_cnt = rtw_read32(rtwdev, REG_CCA_OFDM);
+ dm_info->ofdm_cca_cnt = u32_get_bits(cca32_cnt, MASKHWORD);
+ dm_info->total_cca_cnt = dm_info->ofdm_cca_cnt;
+ if (cck_enable) {
+ cca32_cnt = rtw_read32(rtwdev, REG_CCA_CCK);
+ dm_info->cck_cca_cnt = u32_get_bits(cca32_cnt, MASKLWORD);
+ dm_info->total_cca_cnt += dm_info->cck_cca_cnt;
+ }
+
+ rtw_write32_set(rtwdev, REG_FAS, BIT(17));
+ rtw_write32_clr(rtwdev, REG_FAS, BIT(17));
+ rtw_write32_clr(rtwdev, REG_CCK0_FAREPORT, BIT(15));
+ rtw_write32_set(rtwdev, REG_CCK0_FAREPORT, BIT(15));
+ rtw_write32_set(rtwdev, REG_CNTRST, BIT(0));
+ rtw_write32_clr(rtwdev, REG_CNTRST, BIT(0));
+}
+
+#define MAC_REG_NUM_8814 2
+#define BB_REG_NUM_8814 14
+#define RF_REG_NUM_8814 1
+
+static void rtw8814a_iqk_backup_mac_bb(struct rtw_dev *rtwdev,
+ u32 *mac_backup, u32 *bb_backup,
+ const u32 *mac_regs,
+ const u32 *bb_regs)
+{
+ u32 i;
+
+ /* save MACBB default value */
+ for (i = 0; i < MAC_REG_NUM_8814; i++)
+ mac_backup[i] = rtw_read32(rtwdev, mac_regs[i]);
+
+ for (i = 0; i < BB_REG_NUM_8814; i++)
+ bb_backup[i] = rtw_read32(rtwdev, bb_regs[i]);
+}
+
+static void rtw8814a_iqk_backup_rf(struct rtw_dev *rtwdev,
+ u32 rf_backup[][4], const u32 *rf_regs)
+{
+ u32 i;
+
+ /* Save RF Parameters */
+ for (i = 0; i < RF_REG_NUM_8814; i++) {
+ rf_backup[i][RF_PATH_A] = rtw_read_rf(rtwdev, RF_PATH_A,
+ rf_regs[i], RFREG_MASK);
+ rf_backup[i][RF_PATH_B] = rtw_read_rf(rtwdev, RF_PATH_B,
+ rf_regs[i], RFREG_MASK);
+ rf_backup[i][RF_PATH_C] = rtw_read_rf(rtwdev, RF_PATH_C,
+ rf_regs[i], RFREG_MASK);
+ rf_backup[i][RF_PATH_D] = rtw_read_rf(rtwdev, RF_PATH_D,
+ rf_regs[i], RFREG_MASK);
+ }
+}
+
+static void rtw8814a_iqk_afe_setting(struct rtw_dev *rtwdev, bool do_iqk)
+{
+ if (do_iqk) {
+ /* IQK AFE setting RX_WAIT_CCA mode */
+ rtw_write32(rtwdev, REG_AFE_PWR1_A, 0x0e808003);
+ rtw_write32(rtwdev, REG_AFE_PWR1_B, 0x0e808003);
+ rtw_write32(rtwdev, REG_AFE_PWR1_C, 0x0e808003);
+ rtw_write32(rtwdev, REG_AFE_PWR1_D, 0x0e808003);
+ } else {
+ rtw_write32(rtwdev, REG_AFE_PWR1_A, 0x07808003);
+ rtw_write32(rtwdev, REG_AFE_PWR1_B, 0x07808003);
+ rtw_write32(rtwdev, REG_AFE_PWR1_C, 0x07808003);
+ rtw_write32(rtwdev, REG_AFE_PWR1_D, 0x07808003);
+ }
+
+ rtw_write32_mask(rtwdev, REG_DAC_RSTB, BIT(13), 0x1);
+
+ rtw_write8_set(rtwdev, REG_GNT_BT, BIT(2) | BIT(1));
+ rtw_write8_clr(rtwdev, REG_GNT_BT, BIT(2) | BIT(1));
+
+ rtw_write32_set(rtwdev, REG_CCK_RPT_FORMAT, BIT(2));
+ rtw_write32_clr(rtwdev, REG_CCK_RPT_FORMAT, BIT(2));
+}
+
+static void rtw8814a_iqk_restore_mac_bb(struct rtw_dev *rtwdev,
+ u32 *mac_backup, u32 *bb_backup,
+ const u32 *mac_regs,
+ const u32 *bb_regs)
+{
+ u32 i;
+
+ /* Reload MacBB Parameters */
+ for (i = 0; i < MAC_REG_NUM_8814; i++)
+ rtw_write32(rtwdev, mac_regs[i], mac_backup[i]);
+
+ for (i = 0; i < BB_REG_NUM_8814; i++)
+ rtw_write32(rtwdev, bb_regs[i], bb_backup[i]);
+}
+
+static void rtw8814a_iqk_restore_rf(struct rtw_dev *rtwdev,
+ const u32 rf_backup[][4],
+ const u32 *rf_regs)
+{
+ u32 i;
+
+ rtw_write_rf(rtwdev, RF_PATH_A, RF_LUTWE, RFREG_MASK, 0x0);
+ rtw_write_rf(rtwdev, RF_PATH_B, RF_LUTWE, RFREG_MASK, 0x0);
+ rtw_write_rf(rtwdev, RF_PATH_C, RF_LUTWE, RFREG_MASK, 0x0);
+ rtw_write_rf(rtwdev, RF_PATH_D, RF_LUTWE, RFREG_MASK, 0x0);
+
+ rtw_write_rf(rtwdev, RF_PATH_A, RF_RXBB2, RFREG_MASK, 0x88001);
+ rtw_write_rf(rtwdev, RF_PATH_B, RF_RXBB2, RFREG_MASK, 0x88001);
+ rtw_write_rf(rtwdev, RF_PATH_C, RF_RXBB2, RFREG_MASK, 0x88001);
+ rtw_write_rf(rtwdev, RF_PATH_D, RF_RXBB2, RFREG_MASK, 0x88001);
+
+ for (i = 0; i < RF_REG_NUM_8814; i++) {
+ rtw_write_rf(rtwdev, RF_PATH_A, rf_regs[i],
+ RFREG_MASK, rf_backup[i][RF_PATH_A]);
+ rtw_write_rf(rtwdev, RF_PATH_B, rf_regs[i],
+ RFREG_MASK, rf_backup[i][RF_PATH_B]);
+ rtw_write_rf(rtwdev, RF_PATH_C, rf_regs[i],
+ RFREG_MASK, rf_backup[i][RF_PATH_C]);
+ rtw_write_rf(rtwdev, RF_PATH_D, rf_regs[i],
+ RFREG_MASK, rf_backup[i][RF_PATH_D]);
+ }
+}
+
+static void rtw8814a_iqk_reset_nctl(struct rtw_dev *rtwdev)
+{
+ rtw_write32(rtwdev, 0x1b00, 0xf8000000);
+ rtw_write32(rtwdev, 0x1b80, 0x00000006);
+
+ rtw_write32(rtwdev, 0x1b00, 0xf8000000);
+ rtw_write32(rtwdev, 0x1b80, 0x00000002);
+}
+
+static void rtw8814a_iqk_configure_mac(struct rtw_dev *rtwdev)
+{
+ rtw_write8(rtwdev, REG_TXPAUSE, 0x3f);
+ rtw_write32_clr(rtwdev, REG_BCN_CTRL,
+ (BIT_EN_BCN_FUNCTION << 8) | BIT_EN_BCN_FUNCTION);
+
+ /* RX ante off */
+ rtw_write8(rtwdev, REG_RXPSEL, 0x00);
+ /* CCA off */
+ rtw_write32_mask(rtwdev, REG_CCA2ND, 0xf, 0xe);
+ /* CCK RX path off */
+ rtw_write32_set(rtwdev, REG_PRECTRL, BIT_IQ_WGT);
+ rtw_write32(rtwdev, REG_RFE_PINMUX_A, 0x77777777);
+ rtw_write32(rtwdev, REG_RFE_PINMUX_B, 0x77777777);
+ rtw_write32(rtwdev, REG_RFE_PINMUX_C, 0x77777777);
+ rtw_write32(rtwdev, REG_RFE_PINMUX_D, 0x77777777);
+ rtw_write32_mask(rtwdev, REG_RFE_INVSEL_D, BIT_RFE_SELSW0_D, 0x77);
+ rtw_write32_mask(rtwdev, REG_PSD, BIT_PSD_INI, 0x0);
+
+ rtw_write32_mask(rtwdev, REG_RFE_INV0, 0xf, 0x0);
+}
+
+static void rtw8814a_lok_one_shot(struct rtw_dev *rtwdev, u8 path)
+{
+ u32 lok_temp1, lok_temp2;
+ bool lok_ready;
+ u8 ii;
+
+ /* ADC Clock source */
+ rtw_write32_mask(rtwdev, REG_FAS, BIT(21) | BIT(20), path);
+ /* LOK: CMD ID = 0
+ * {0xf8000011, 0xf8000021, 0xf8000041, 0xf8000081}
+ */
+ rtw_write32(rtwdev, 0x1b00, 0xf8000001 | (BIT(path) << 4));
+
+ usleep_range(1000, 1100);
+
+ if (read_poll_timeout(!rtw_read32_mask, lok_ready, lok_ready,
+ 1000, 10000, false,
+ rtwdev, 0x1b00, BIT(0))) {
+ rtw_dbg(rtwdev, RTW_DBG_RFK, "==>S%d LOK timed out\n", path);
+
+ rtw8814a_iqk_reset_nctl(rtwdev);
+
+ rtw_write_rf(rtwdev, path, RF_DTXLOK, RFREG_MASK, 0x08400);
+
+ return;
+ }
+
+ rtw_write32(rtwdev, 0x1b00, 0xf8000000 | (path << 1));
+ rtw_write32(rtwdev, 0x1bd4, 0x003f0001);
+
+ lok_temp2 = rtw_read32_mask(rtwdev, 0x1bfc, 0x003e0000);
+ lok_temp2 = (lok_temp2 + 0x10) & 0x1f;
+
+ lok_temp1 = rtw_read32_mask(rtwdev, 0x1bfc, 0x0000003e);
+ lok_temp1 = (lok_temp1 + 0x10) & 0x1f;
+
+ for (ii = 1; ii < 5; ii++) {
+ lok_temp1 += (lok_temp1 & BIT(4 - ii)) << (ii * 2);
+ lok_temp2 += (lok_temp2 & BIT(4 - ii)) << (ii * 2);
+ }
+
+ rtw_dbg(rtwdev, RTW_DBG_RFK,
+ "path %d lok_temp1 = %#x, lok_temp2 = %#x\n",
+ path, lok_temp1 >> 4, lok_temp2 >> 4);
+
+ rtw_write_rf(rtwdev, path, RF_DTXLOK, 0x07c00, lok_temp1 >> 4);
+ rtw_write_rf(rtwdev, path, RF_DTXLOK, 0xf8000, lok_temp2 >> 4);
+}
+
+static void rtw8814a_iqk_tx_one_shot(struct rtw_dev *rtwdev, u8 path,
+ u32 *tx_matrix, bool *tx_ok)
+{
+ u8 bw = rtwdev->hal.current_band_width;
+ u8 cal_retry;
+ u32 iqk_cmd;
+
+ for (cal_retry = 0; cal_retry < 4; cal_retry++) {
+ rtw_write32_mask(rtwdev, REG_FAS, BIT(21) | BIT(20), path);
+
+ iqk_cmd = 0xf8000001 | ((bw + 3) << 8) | (BIT(path) << 4);
+
+ rtw_dbg(rtwdev, RTW_DBG_RFK, "TXK_Trigger = %#x\n", iqk_cmd);
+
+ rtw_write32(rtwdev, 0x1b00, iqk_cmd);
+
+ usleep_range(10000, 11000);
+
+ if (read_poll_timeout(!rtw_read32_mask, *tx_ok, *tx_ok,
+ 1000, 20000, false,
+ rtwdev, 0x1b00, BIT(0))) {
+ rtw_dbg(rtwdev, RTW_DBG_RFK,
+ "tx iqk S%d timed out\n", path);
+
+ rtw8814a_iqk_reset_nctl(rtwdev);
+ } else {
+ *tx_ok = !rtw_read32_mask(rtwdev, 0x1b08, BIT(26));
+
+ if (*tx_ok)
+ break;
+ }
+ }
+
+ rtw_dbg(rtwdev, RTW_DBG_RFK, "S%d tx ==> 0x1b00 = 0x%x\n",
+ path, rtw_read32(rtwdev, 0x1b00));
+ rtw_dbg(rtwdev, RTW_DBG_RFK, "S%d tx ==> 0x1b08 = 0x%x\n",
+ path, rtw_read32(rtwdev, 0x1b08));
+ rtw_dbg(rtwdev, RTW_DBG_RFK, "S%d tx ==> cal_retry = %x\n",
+ path, cal_retry);
+
+ rtw_write32(rtwdev, 0x1b00, 0xf8000000 | (path << 1));
+
+ if (*tx_ok) {
+ *tx_matrix = rtw_read32(rtwdev, 0x1b38);
+
+ rtw_dbg(rtwdev, RTW_DBG_RFK, "S%d_IQC = 0x%x\n",
+ path, *tx_matrix);
+ }
+}
+
+static void rtw8814a_iqk_rx_one_shot(struct rtw_dev *rtwdev, u8 path,
+ u32 *tx_matrix, bool *tx_ok)
+{
+ static const u16 iqk_apply[RTW_RF_PATH_MAX] = {
+ REG_TXAGCIDX, REG_TX_AGC_B, REG_TX_AGC_C, REG_TX_AGC_D
+ };
+ u8 band = rtwdev->hal.current_band_type;
+ u8 bw = rtwdev->hal.current_band_width;
+ u32 rx_matrix;
+ u8 cal_retry;
+ u32 iqk_cmd;
+ bool rx_ok;
+
+ for (cal_retry = 0; cal_retry < 4; cal_retry++) {
+ rtw_write32_mask(rtwdev, REG_FAS, BIT(21) | BIT(20), path);
+
+ if (band == RTW_BAND_2G) {
+ rtw_write_rf(rtwdev, path, RF_LUTDBG, BIT(11), 0x1);
+ rtw_write_rf(rtwdev, path, RF_GAINTX, 0xfffff, 0x51ce1);
+
+ switch (path) {
+ case 0:
+ case 1:
+ rtw_write32(rtwdev, REG_RFE_PINMUX_B,
+ 0x54775477);
+ break;
+ case 2:
+ rtw_write32(rtwdev, REG_RFE_PINMUX_C,
+ 0x54775477);
+ break;
+ case 3:
+ rtw_write32(rtwdev, REG_RFE_INVSEL_D, 0x75400000);
+ rtw_write32(rtwdev, REG_RFE_PINMUX_D,
+ 0x77777777);
+ break;
+ }
+ }
+
+ iqk_cmd = 0xf8000001 | ((9 - bw) << 8) | (BIT(path) << 4);
+
+ rtw_dbg(rtwdev, RTW_DBG_RFK, "RXK_Trigger = 0x%x\n", iqk_cmd);
+
+ rtw_write32(rtwdev, 0x1b00, iqk_cmd);
+
+ usleep_range(10000, 11000);
+
+ if (read_poll_timeout(!rtw_read32_mask, rx_ok, rx_ok,
+ 1000, 20000, false,
+ rtwdev, 0x1b00, BIT(0))) {
+ rtw_dbg(rtwdev, RTW_DBG_RFK,
+ "rx iqk S%d timed out\n", path);
+
+ rtw8814a_iqk_reset_nctl(rtwdev);
+ } else {
+ rx_ok = !rtw_read32_mask(rtwdev, 0x1b08, BIT(26));
+
+ if (rx_ok)
+ break;
+ }
+ }
+
+ rtw_dbg(rtwdev, RTW_DBG_RFK, "S%d rx ==> 0x1b00 = 0x%x\n",
+ path, rtw_read32(rtwdev, 0x1b00));
+ rtw_dbg(rtwdev, RTW_DBG_RFK, "S%d rx ==> 0x1b08 = 0x%x\n",
+ path, rtw_read32(rtwdev, 0x1b08));
+ rtw_dbg(rtwdev, RTW_DBG_RFK, "S%d rx ==> cal_retry = %x\n",
+ path, cal_retry);
+
+ rtw_write32(rtwdev, 0x1b00, 0xf8000000 | (path << 1));
+
+ if (rx_ok) {
+ rtw_write32(rtwdev, 0x1b3c, 0x20000000);
+ rx_matrix = rtw_read32(rtwdev, 0x1b3c);
+
+ rtw_dbg(rtwdev, RTW_DBG_RFK, "S%d_IQC = 0x%x\n",
+ path, rx_matrix);
+ }
+
+ if (*tx_ok)
+ rtw_write32(rtwdev, 0x1b38, *tx_matrix);
+ else
+ rtw_write32_mask(rtwdev, iqk_apply[path], BIT(0), 0x0);
+
+ if (!rx_ok)
+ rtw_write32_mask(rtwdev, iqk_apply[path],
+ BIT(11) | BIT(10), 0x0);
+
+ if (band == RTW_BAND_2G)
+ rtw_write_rf(rtwdev, path, RF_LUTDBG, BIT(11), 0x0);
+}
+
+static void rtw8814a_iqk(struct rtw_dev *rtwdev)
+{
+ u8 band = rtwdev->hal.current_band_type;
+ u8 bw = rtwdev->hal.current_band_width;
+ u32 tx_matrix[RTW_RF_PATH_MAX];
+ bool tx_ok[RTW_RF_PATH_MAX];
+ u8 path;
+
+ rtw_dbg(rtwdev, RTW_DBG_RFK, "IQK band = %d GHz bw = %d MHz\n",
+ band == RTW_BAND_2G ? 2 : 5, (1 << (bw + 1)) * 10);
+
+ rtw_write_rf(rtwdev, RF_PATH_A, RF_TXMOD, BIT(19), 0x1);
+ rtw_write_rf(rtwdev, RF_PATH_B, RF_TXMOD, BIT(19), 0x1);
+ rtw_write_rf(rtwdev, RF_PATH_C, RF_TXMOD, BIT(19), 0x1);
+ rtw_write_rf(rtwdev, RF_PATH_D, RF_TXMOD, BIT(19), 0x1);
+
+ rtw_write32_mask(rtwdev, REG_TXAGCIDX,
+ (BIT(11) | BIT(10) | BIT(0)), 0x401);
+ rtw_write32_mask(rtwdev, REG_TX_AGC_B,
+ (BIT(11) | BIT(10) | BIT(0)), 0x401);
+ rtw_write32_mask(rtwdev, REG_TX_AGC_C,
+ (BIT(11) | BIT(10) | BIT(0)), 0x401);
+ rtw_write32_mask(rtwdev, REG_TX_AGC_D,
+ (BIT(11) | BIT(10) | BIT(0)), 0x401);
+
+ if (band == RTW_BAND_5G)
+ rtw_write32(rtwdev, 0x1b00, 0xf8000ff1);
+ else
+ rtw_write32(rtwdev, 0x1b00, 0xf8000ef1);
+
+ usleep_range(1000, 1100);
+
+ rtw_write32(rtwdev, 0x810, 0x20101063);
+ rtw_write32(rtwdev, REG_DAC_RSTB, 0x0B00C000);
+
+ for (path = RF_PATH_A; path < RTW_RF_PATH_MAX; path++)
+ rtw8814a_lok_one_shot(rtwdev, path);
+
+ for (path = RF_PATH_A; path < RTW_RF_PATH_MAX; path++)
+ rtw8814a_iqk_tx_one_shot(rtwdev, path,
+ &tx_matrix[path], &tx_ok[path]);
+
+ for (path = RF_PATH_A; path < RTW_RF_PATH_MAX; path++)
+ rtw8814a_iqk_rx_one_shot(rtwdev, path,
+ &tx_matrix[path], &tx_ok[path]);
+}
+
+static void rtw8814a_do_iqk(struct rtw_dev *rtwdev)
+{
+ static const u32 backup_mac_reg[MAC_REG_NUM_8814] = {0x520, 0x550};
+ static const u32 backup_bb_reg[BB_REG_NUM_8814] = {
+ 0xa14, 0x808, 0x838, 0x90c, 0x810, 0xcb0, 0xeb0,
+ 0x18b4, 0x1ab4, 0x1abc, 0x9a4, 0x764, 0xcbc, 0x910
+ };
+ static const u32 backup_rf_reg[RF_REG_NUM_8814] = {0x0};
+ u32 rf_backup[RF_REG_NUM_8814][RTW_RF_PATH_MAX];
+ u32 mac_backup[MAC_REG_NUM_8814];
+ u32 bb_backup[BB_REG_NUM_8814];
+
+ rtw8814a_iqk_backup_mac_bb(rtwdev, mac_backup, bb_backup,
+ backup_mac_reg, backup_bb_reg);
+ rtw8814a_iqk_afe_setting(rtwdev, true);
+ rtw8814a_iqk_backup_rf(rtwdev, rf_backup, backup_rf_reg);
+ rtw8814a_iqk_configure_mac(rtwdev);
+ rtw8814a_iqk(rtwdev);
+ rtw8814a_iqk_reset_nctl(rtwdev); /* for 3-wire to BB use */
+ rtw8814a_iqk_afe_setting(rtwdev, false);
+ rtw8814a_iqk_restore_mac_bb(rtwdev, mac_backup, bb_backup,
+ backup_mac_reg, backup_bb_reg);
+ rtw8814a_iqk_restore_rf(rtwdev, rf_backup, backup_rf_reg);
+}
+
+static void rtw8814a_phy_calibration(struct rtw_dev *rtwdev)
+{
+ rtw8814a_do_iqk(rtwdev);
+}
+
+static void rtw8814a_coex_cfg_init(struct rtw_dev *rtwdev)
+{
+}
+
+static void rtw8814a_coex_cfg_ant_switch(struct rtw_dev *rtwdev, u8 ctrl_type,
+ u8 pos_type)
+{
+ /* Override rtw_coex_coex_ctrl_owner(). RF path C does not
+ * function when BIT_LTE_MUX_CTRL_PATH is set.
+ */
+ rtw_write8_clr(rtwdev, REG_SYS_SDIO_CTRL + 3,
+ BIT_LTE_MUX_CTRL_PATH >> 24);
+}
+
+static void rtw8814a_coex_cfg_gnt_fix(struct rtw_dev *rtwdev)
+{
+}
+
+static void rtw8814a_coex_cfg_gnt_debug(struct rtw_dev *rtwdev)
+{
+}
+
+static void rtw8814a_coex_cfg_rfe_type(struct rtw_dev *rtwdev)
+{
+ struct rtw_coex *coex = &rtwdev->coex;
+ struct rtw_coex_rfe *coex_rfe = &coex->rfe;
+
+ /* Only needed to make rtw8814a_coex_cfg_ant_switch() run. */
+ coex_rfe->ant_switch_exist = true;
+}
+
+static void rtw8814a_coex_cfg_wl_tx_power(struct rtw_dev *rtwdev, u8 wl_pwr)
+{
+}
+
+static void rtw8814a_coex_cfg_wl_rx_gain(struct rtw_dev *rtwdev, bool low_gain)
+{
+}
+
+static void rtw8814a_txagc_swing_offset(struct rtw_dev *rtwdev, u8 path,
+ u8 tx_pwr_idx_offset,
+ s8 *txagc_idx, u8 *swing_idx)
+{
+ struct rtw_dm_info *dm_info = &rtwdev->dm_info;
+ u8 swing_upper_bound = dm_info->default_ofdm_index + 10;
+ s8 delta_pwr_idx = dm_info->delta_power_index[path];
+ u8 swing_index = dm_info->default_ofdm_index;
+ u8 max_tx_pwr_idx_offset = 0xf;
+ u8 swing_lower_bound = 0;
+ s8 agc_index = 0;
+
+ tx_pwr_idx_offset = min_t(u8, tx_pwr_idx_offset, max_tx_pwr_idx_offset);
+
+ if (delta_pwr_idx >= 0) {
+ if (delta_pwr_idx <= tx_pwr_idx_offset) {
+ agc_index = delta_pwr_idx;
+ swing_index = dm_info->default_ofdm_index;
+ } else if (delta_pwr_idx > tx_pwr_idx_offset) {
+ agc_index = tx_pwr_idx_offset;
+ swing_index = dm_info->default_ofdm_index +
+ delta_pwr_idx - tx_pwr_idx_offset;
+ swing_index = min_t(u8, swing_index, swing_upper_bound);
+ }
+ } else {
+ if (dm_info->default_ofdm_index > abs(delta_pwr_idx))
+ swing_index =
+ dm_info->default_ofdm_index + delta_pwr_idx;
+ else
+ swing_index = swing_lower_bound;
+ swing_index = max_t(u8, swing_index, swing_lower_bound);
+
+ agc_index = 0;
+ }
+
+ if (swing_index >= RTW_TXSCALE_SIZE) {
+ rtw_warn(rtwdev, "swing index overflow\n");
+ swing_index = RTW_TXSCALE_SIZE - 1;
+ }
+ *txagc_idx = agc_index;
+ *swing_idx = swing_index;
+}
+
+static void rtw8814a_pwrtrack_set_pwr(struct rtw_dev *rtwdev, u8 path,
+ u8 pwr_idx_offset)
+{
+ static const u32 txagc_reg[RTW_RF_PATH_MAX] = {
+ REG_TX_AGC_A, REG_TX_AGC_B, REG_TX_AGC_C, REG_TX_AGC_D
+ };
+ static const u32 txscale_reg[RTW_RF_PATH_MAX] = {
+ REG_TXSCALE_A, REG_TXSCALE_B, REG_TXSCALE_C, REG_TXSCALE_D
+ };
+ s8 txagc_idx;
+ u8 swing_idx;
+
+ rtw8814a_txagc_swing_offset(rtwdev, path, pwr_idx_offset,
+ &txagc_idx, &swing_idx);
+ rtw_write32_mask(rtwdev, txagc_reg[path], GENMASK(29, 25),
+ txagc_idx);
+ rtw_write32_mask(rtwdev, txscale_reg[path], BB_SWING_MASK,
+ rtw8814a_txscale_tbl[swing_idx]);
+}
+
+static void rtw8814a_pwrtrack_set(struct rtw_dev *rtwdev, u8 path)
+{
+ u8 max_pwr_idx = rtwdev->chip->max_power_index;
+ u8 band_width = rtwdev->hal.current_band_width;
+ u8 channel = rtwdev->hal.current_channel;
+ u8 tx_rate = rtwdev->dm_info.tx_rate;
+ u8 regd = rtw_regd_get(rtwdev);
+ u8 pwr_idx_offset, tx_pwr_idx;
+
+ tx_pwr_idx = rtw_phy_get_tx_power_index(rtwdev, path, tx_rate,
+ band_width, channel, regd);
+
+ tx_pwr_idx = min_t(u8, tx_pwr_idx, max_pwr_idx);
+
+ pwr_idx_offset = max_pwr_idx - tx_pwr_idx;
+
+ rtw8814a_pwrtrack_set_pwr(rtwdev, path, pwr_idx_offset);
+}
+
+static void rtw8814a_phy_pwrtrack_path(struct rtw_dev *rtwdev,
+ struct rtw_swing_table *swing_table,
+ u8 path)
+{
+ struct rtw_dm_info *dm_info = &rtwdev->dm_info;
+ u8 power_idx_cur, power_idx_last;
+ u8 delta;
+
+ /* 8814A only has one thermal meter at PATH A */
+ delta = rtw_phy_pwrtrack_get_delta(rtwdev, RF_PATH_A);
+
+ power_idx_last = dm_info->delta_power_index[path];
+ power_idx_cur = rtw_phy_pwrtrack_get_pwridx(rtwdev, swing_table,
+ path, RF_PATH_A, delta);
+
+ /* if delta of power indexes are the same, just skip */
+ if (power_idx_cur == power_idx_last)
+ return;
+
+ dm_info->delta_power_index[path] = power_idx_cur;
+ rtw8814a_pwrtrack_set(rtwdev, path);
+}
+
+static void rtw8814a_phy_pwrtrack(struct rtw_dev *rtwdev)
+{
+ struct rtw_dm_info *dm_info = &rtwdev->dm_info;
+ struct rtw_swing_table swing_table;
+ u8 thermal_value, path;
+
+ rtw_phy_config_swing_table(rtwdev, &swing_table);
+
+ if (rtwdev->efuse.thermal_meter[RF_PATH_A] == 0xff)
+ return;
+
+ thermal_value = rtw_read_rf(rtwdev, RF_PATH_A, RF_T_METER, 0xfc00);
+
+ rtw_phy_pwrtrack_avg(rtwdev, thermal_value, RF_PATH_A);
+
+ if (dm_info->pwr_trk_init_trigger)
+ dm_info->pwr_trk_init_trigger = false;
+ else if (!rtw_phy_pwrtrack_thermal_changed(rtwdev, thermal_value,
+ RF_PATH_A))
+ goto iqk;
+
+ for (path = RF_PATH_A; path < rtwdev->hal.rf_path_num; path++)
+ rtw8814a_phy_pwrtrack_path(rtwdev, &swing_table, path);
+
+iqk:
+ if (rtw_phy_pwrtrack_need_iqk(rtwdev))
+ rtw8814a_do_iqk(rtwdev);
+}
+
+static void rtw8814a_pwr_track(struct rtw_dev *rtwdev)
+{
+ struct rtw_dm_info *dm_info = &rtwdev->dm_info;
+
+ if (!dm_info->pwr_trk_triggered) {
+ rtw_write_rf(rtwdev, RF_PATH_A, RF_T_METER,
+ GENMASK(17, 16), 0x03);
+ dm_info->pwr_trk_triggered = true;
+ return;
+ }
+
+ rtw8814a_phy_pwrtrack(rtwdev);
+ dm_info->pwr_trk_triggered = false;
+}
+
+static void rtw8814a_phy_cck_pd_set(struct rtw_dev *rtwdev, u8 new_lvl)
+{
+ static const u8 pd[CCK_PD_LV_MAX] = {0x40, 0x83, 0xcd, 0xdd, 0xed};
+ struct rtw_dm_info *dm_info = &rtwdev->dm_info;
+
+ /* Override rtw_phy_cck_pd_lv_link(). It implements something
+ * like type 2/3/4. We need type 1 here.
+ */
+ if (rtw_is_assoc(rtwdev)) {
+ if (dm_info->min_rssi > 60) {
+ new_lvl = CCK_PD_LV3;
+ } else if (dm_info->min_rssi > 35) {
+ new_lvl = CCK_PD_LV2;
+ } else if (dm_info->min_rssi > 20) {
+ if (dm_info->cck_fa_avg > 500)
+ new_lvl = CCK_PD_LV2;
+ else if (dm_info->cck_fa_avg < 250)
+ new_lvl = CCK_PD_LV1;
+ else
+ return;
+ } else {
+ new_lvl = CCK_PD_LV1;
+ }
+ }
+
+ rtw_dbg(rtwdev, RTW_DBG_PHY, "lv: (%d) -> (%d)\n",
+ dm_info->cck_pd_lv[RTW_CHANNEL_WIDTH_20][RF_PATH_A], new_lvl);
+
+ if (dm_info->cck_pd_lv[RTW_CHANNEL_WIDTH_20][RF_PATH_A] == new_lvl)
+ return;
+
+ dm_info->cck_fa_avg = CCK_FA_AVG_RESET;
+ dm_info->cck_pd_lv[RTW_CHANNEL_WIDTH_20][RF_PATH_A] = new_lvl;
+
+ rtw_write8(rtwdev, REG_CCK_PD_TH, pd[new_lvl]);
+}
+
+static void rtw8814a_led_set(struct led_classdev *led,
+ enum led_brightness brightness)
+{
+ struct rtw_dev *rtwdev = container_of(led, struct rtw_dev, led_cdev);
+ u32 led_gpio_cfg;
+
+ led_gpio_cfg = rtw_read32(rtwdev, REG_GPIO_PIN_CTRL_2);
+ led_gpio_cfg |= BIT(16) | BIT(17) | BIT(21) | BIT(22);
+
+ if (brightness == LED_OFF) {
+ led_gpio_cfg |= BIT(8) | BIT(9) | BIT(13) | BIT(14);
+ } else {
+ led_gpio_cfg &= ~(BIT(8) | BIT(9) | BIT(13) | BIT(14));
+ led_gpio_cfg &= ~(BIT(0) | BIT(1) | BIT(5) | BIT(6));
+ }
+
+ rtw_write32(rtwdev, REG_GPIO_PIN_CTRL_2, led_gpio_cfg);
+}
+
+static void rtw8814a_fill_txdesc_checksum(struct rtw_dev *rtwdev,
+ struct rtw_tx_pkt_info *pkt_info,
+ u8 *txdesc)
+{
+ size_t words = 32 / 2; /* calculate the first 32 bytes (16 words) */
+
+ fill_txdesc_checksum_common(txdesc, words);
+}
+
+static const struct rtw_chip_ops rtw8814a_ops = {
+ .power_on = rtw_power_on,
+ .power_off = rtw_power_off,
+ .phy_set_param = rtw8814a_phy_set_param,
+ .read_efuse = rtw8814a_read_efuse,
+ .query_phy_status = rtw8814a_query_phy_status,
+ .set_channel = rtw8814a_set_channel,
+ .mac_init = rtw8814a_mac_init,
+ .read_rf = rtw_phy_read_rf,
+ .write_rf = rtw_phy_write_rf_reg_sipi,
+ .set_tx_power_index = rtw8814a_set_tx_power_index,
+ .set_antenna = NULL,
+ .cfg_ldo25 = rtw8814a_cfg_ldo25,
+ .efuse_grant = rtw8814a_efuse_grant,
+ .set_ampdu_factor = rtw8814a_set_ampdu_factor,
+ .false_alarm_statistics = rtw8814a_false_alarm_statistics,
+ .phy_calibration = rtw8814a_phy_calibration,
+ .cck_pd_set = rtw8814a_phy_cck_pd_set,
+ .pwr_track = rtw8814a_pwr_track,
+ .config_bfee = NULL,
+ .set_gid_table = NULL,
+ .cfg_csi_rate = NULL,
+ .led_set = rtw8814a_led_set,
+ .fill_txdesc_checksum = rtw8814a_fill_txdesc_checksum,
+
+ .coex_set_init = rtw8814a_coex_cfg_init,
+ .coex_set_ant_switch = rtw8814a_coex_cfg_ant_switch,
+ .coex_set_gnt_fix = rtw8814a_coex_cfg_gnt_fix,
+ .coex_set_gnt_debug = rtw8814a_coex_cfg_gnt_debug,
+ .coex_set_rfe_type = rtw8814a_coex_cfg_rfe_type,
+ .coex_set_wl_tx_power = rtw8814a_coex_cfg_wl_tx_power,
+ .coex_set_wl_rx_gain = rtw8814a_coex_cfg_wl_rx_gain,
+};
+
+static const struct rtw_rqpn rqpn_table_8814a[] = {
+ /* SDIO */
+ {RTW_DMA_MAPPING_NORMAL, RTW_DMA_MAPPING_NORMAL, /* vo vi */
+ RTW_DMA_MAPPING_LOW, RTW_DMA_MAPPING_LOW, /* be bk */
+ RTW_DMA_MAPPING_EXTRA, RTW_DMA_MAPPING_HIGH}, /* mg hi */
+ /* PCIE */
+ {RTW_DMA_MAPPING_HIGH, RTW_DMA_MAPPING_NORMAL,
+ RTW_DMA_MAPPING_LOW, RTW_DMA_MAPPING_LOW,
+ RTW_DMA_MAPPING_HIGH, RTW_DMA_MAPPING_HIGH},
+ /* USB, 2 bulk out */
+ {RTW_DMA_MAPPING_HIGH, RTW_DMA_MAPPING_HIGH,
+ RTW_DMA_MAPPING_NORMAL, RTW_DMA_MAPPING_NORMAL,
+ RTW_DMA_MAPPING_HIGH, RTW_DMA_MAPPING_HIGH},
+ /* USB, 3 bulk out */
+ {RTW_DMA_MAPPING_HIGH, RTW_DMA_MAPPING_NORMAL,
+ RTW_DMA_MAPPING_LOW, RTW_DMA_MAPPING_LOW,
+ RTW_DMA_MAPPING_HIGH, RTW_DMA_MAPPING_HIGH},
+ /* USB, 4 bulk out */
+ {RTW_DMA_MAPPING_HIGH, RTW_DMA_MAPPING_NORMAL,
+ RTW_DMA_MAPPING_LOW, RTW_DMA_MAPPING_LOW,
+ RTW_DMA_MAPPING_HIGH, RTW_DMA_MAPPING_HIGH},
+};
+
+static const struct rtw_prioq_addrs prioq_addrs_8814a = {
+ .prio[RTW_DMA_MAPPING_EXTRA] = {
+ .rsvd = REG_FIFOPAGE_INFO_4, .avail = REG_FIFOPAGE_INFO_4 + 2,
+ },
+ .prio[RTW_DMA_MAPPING_LOW] = {
+ .rsvd = REG_FIFOPAGE_INFO_2, .avail = REG_FIFOPAGE_INFO_2 + 2,
+ },
+ .prio[RTW_DMA_MAPPING_NORMAL] = {
+ .rsvd = REG_FIFOPAGE_INFO_3, .avail = REG_FIFOPAGE_INFO_3 + 2,
+ },
+ .prio[RTW_DMA_MAPPING_HIGH] = {
+ .rsvd = REG_FIFOPAGE_INFO_1, .avail = REG_FIFOPAGE_INFO_1 + 2,
+ },
+ .wsize = true,
+};
+
+static const struct rtw_page_table page_table_8814a[] = {
+ /* SDIO */
+ {0, 0, 0, 0, 0}, /* hq nq lq exq gapq */
+ /* PCIE */
+ {32, 32, 32, 32, 0},
+ /* USB, 2 bulk out */
+ {32, 32, 32, 32, 0},
+ /* USB, 3 bulk out */
+ {32, 32, 32, 32, 0},
+ /* USB, 4 bulk out */
+ {32, 32, 32, 32, 0},
+};
+
+static const struct rtw_intf_phy_para_table phy_para_table_8814a = {};
+
+static const struct rtw_hw_reg rtw8814a_dig[] = {
+ [0] = { .addr = 0xc50, .mask = 0x7f },
+ [1] = { .addr = 0xe50, .mask = 0x7f },
+ [2] = { .addr = 0x1850, .mask = 0x7f },
+ [3] = { .addr = 0x1a50, .mask = 0x7f },
+};
+
+static const struct rtw_rfe_def rtw8814a_rfe_defs[] = {
+ [0] = { .phy_pg_tbl = &rtw8814a_bb_pg_type0_tbl,
+ .txpwr_lmt_tbl = &rtw8814a_txpwr_lmt_type0_tbl,
+ .pwr_track_tbl = &rtw8814a_rtw_pwrtrk_type0_tbl },
+ [1] = { .phy_pg_tbl = &rtw8814a_bb_pg_tbl,
+ .txpwr_lmt_tbl = &rtw8814a_txpwr_lmt_type1_tbl,
+ .pwr_track_tbl = &rtw8814a_rtw_pwrtrk_tbl },
+};
+
+/* rssi in percentage % (dbm = % - 100) */
+static const u8 wl_rssi_step_8814a[] = {60, 50, 44, 30};
+static const u8 bt_rssi_step_8814a[] = {30, 30, 30, 30};
+
+/* wl_tx_dec_power, bt_tx_dec_power, wl_rx_gain, bt_rx_lna_constrain */
+static const struct coex_rf_para rf_para_tx_8814a[] = {
+ {0, 0, false, 7}, /* for normal */
+ {0, 16, false, 7}, /* for WL-CPT */
+ {4, 0, true, 1},
+ {3, 6, true, 1},
+ {2, 9, true, 1},
+ {1, 13, true, 1}
+};
+
+static const struct coex_rf_para rf_para_rx_8814a[] = {
+ {0, 0, false, 7}, /* for normal */
+ {0, 16, false, 7}, /* for WL-CPT */
+ {4, 0, true, 1},
+ {3, 6, true, 1},
+ {2, 9, true, 1},
+ {1, 13, true, 1}
+};
+
+static_assert(ARRAY_SIZE(rf_para_tx_8814a) == ARRAY_SIZE(rf_para_rx_8814a));
+
+const struct rtw_chip_info rtw8814a_hw_spec = {
+ .ops = &rtw8814a_ops,
+ .id = RTW_CHIP_TYPE_8814A,
+ .fw_name = "rtw88/rtw8814a_fw.bin",
+ .wlan_cpu = RTW_WCPU_11AC,
+ .tx_pkt_desc_sz = 40,
+ .tx_buf_desc_sz = 16,
+ .rx_pkt_desc_sz = 24,
+ .rx_buf_desc_sz = 8,
+ .phy_efuse_size = 1024,
+ .log_efuse_size = 512,
+ .ptct_efuse_size = 0,
+ .txff_size = (2048 - 10) * TX_PAGE_SIZE,
+ .rxff_size = 23552,
+ .rsvd_drv_pg_num = 8,
+ .band = RTW_BAND_2G | RTW_BAND_5G,
+ .page_size = TX_PAGE_SIZE,
+ .csi_buf_pg_num = 0,
+ .dig_min = 0x1c,
+ .txgi_factor = 1,
+ .is_pwr_by_rate_dec = true,
+ .rx_ldpc = true,
+ .max_power_index = 0x3f,
+ .ampdu_density = IEEE80211_HT_MPDU_DENSITY_2,
+ .amsdu_in_ampdu = false, /* RX speed is better without AMSDU */
+ .usb_tx_agg_desc_num = 3,
+ .hw_feature_report = false,
+ .c2h_ra_report_size = 6,
+ .old_datarate_fb_limit = false,
+ .ht_supported = true,
+ .vht_supported = true,
+ .lps_deep_mode_supported = BIT(LPS_DEEP_MODE_LCLK),
+ .sys_func_en = 0xDC,
+ .pwr_on_seq = card_enable_flow_8814a,
+ .pwr_off_seq = card_disable_flow_8814a,
+ .rqpn_table = rqpn_table_8814a,
+ .prioq_addrs = &prioq_addrs_8814a,
+ .page_table = page_table_8814a,
+ .intf_table = &phy_para_table_8814a,
+ .dig = rtw8814a_dig,
+ .dig_cck = NULL,
+ .rf_base_addr = {0x2800, 0x2c00, 0x3800, 0x3c00},
+ .rf_sipi_addr = {0xc90, 0xe90, 0x1890, 0x1a90},
+ .ltecoex_addr = NULL,
+ .mac_tbl = &rtw8814a_mac_tbl,
+ .agc_tbl = &rtw8814a_agc_tbl,
+ .bb_tbl = &rtw8814a_bb_tbl,
+ .rf_tbl = {&rtw8814a_rf_a_tbl, &rtw8814a_rf_b_tbl,
+ &rtw8814a_rf_c_tbl, &rtw8814a_rf_d_tbl},
+ .rfe_defs = rtw8814a_rfe_defs,
+ .rfe_defs_size = ARRAY_SIZE(rtw8814a_rfe_defs),
+ .iqk_threshold = 8,
+ .max_scan_ie_len = IEEE80211_MAX_DATA_LEN,
+
+ .coex_para_ver = 0,
+ .bt_desired_ver = 0,
+ .scbd_support = false,
+ .new_scbd10_def = false,
+ .ble_hid_profile_support = false,
+ .wl_mimo_ps_support = false,
+ .pstdma_type = COEX_PSTDMA_FORCE_LPSOFF,
+ .bt_rssi_type = COEX_BTRSSI_RATIO,
+ .ant_isolation = 15,
+ .rssi_tolerance = 2,
+ .wl_rssi_step = wl_rssi_step_8814a,
+ .bt_rssi_step = bt_rssi_step_8814a,
+ .table_sant_num = 0,
+ .table_sant = NULL,
+ .table_nsant_num = 0,
+ .table_nsant = NULL,
+ .tdma_sant_num = 0,
+ .tdma_sant = NULL,
+ .tdma_nsant_num = 0,
+ .tdma_nsant = NULL,
+ .wl_rf_para_num = ARRAY_SIZE(rf_para_tx_8814a),
+ .wl_rf_para_tx = rf_para_tx_8814a,
+ .wl_rf_para_rx = rf_para_rx_8814a,
+ .bt_afh_span_bw20 = 0x24,
+ .bt_afh_span_bw40 = 0x36,
+ .afh_5g_num = 0,
+ .afh_5g = NULL,
+ .coex_info_hw_regs_num = 0,
+ .coex_info_hw_regs = NULL,
+};
+EXPORT_SYMBOL(rtw8814a_hw_spec);
+
+MODULE_FIRMWARE("rtw88/rtw8814a_fw.bin");
+
+MODULE_AUTHOR("Bitterblue Smith <rtl8821cerfe2@gmail.com>");
+MODULE_DESCRIPTION("Realtek 802.11ac wireless 8814a driver");
+MODULE_LICENSE("Dual BSD/GPL");
diff --git a/drivers/net/wireless/realtek/rtw88/rtw8814a.h b/drivers/net/wireless/realtek/rtw88/rtw8814a.h
new file mode 100644
index 000000000000..c57c7c8f915e
--- /dev/null
+++ b/drivers/net/wireless/realtek/rtw88/rtw8814a.h
@@ -0,0 +1,62 @@
+/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
+/* Copyright(c) 2025 Realtek Corporation
+ */
+
+#ifndef __RTW8814A_H__
+#define __RTW8814A_H__
+
+struct rtw8814au_efuse {
+ u8 vid[2]; /* 0xd0 */
+ u8 pid[2]; /* 0xd2 */
+ u8 res[4]; /* 0xd4 */
+ u8 mac_addr[ETH_ALEN]; /* 0xd8 */
+} __packed;
+
+struct rtw8814ae_efuse {
+ u8 mac_addr[ETH_ALEN]; /* 0xd0 */
+ u8 vid[2]; /* 0xd6 */
+ u8 did[2]; /* 0xd8 */
+ u8 svid[2]; /* 0xda */
+ u8 smid[2]; /* 0xdc */
+} __packed;
+
+struct rtw8814a_efuse {
+ __le16 rtl_id;
+ u8 res0[0x0c];
+ u8 usb_mode; /* 0x0e */
+ u8 res1;
+
+ /* power index for four RF paths */
+ struct rtw_txpwr_idx txpwr_idx_table[4];
+
+ u8 channel_plan; /* 0xb8 */
+ u8 xtal_k; /* 0xb9 */
+ u8 thermal_meter; /* 0xba */
+ u8 iqk_lck; /* 0xbb */
+ u8 pa_type; /* 0xbc */
+ u8 lna_type_2g[2]; /* 0xbd */
+ u8 lna_type_5g[2]; /* 0xbf */
+ u8 rf_board_option; /* 0xc1 */
+ u8 res2;
+ u8 rf_bt_setting; /* 0xc3 */
+ u8 eeprom_version; /* 0xc4 */
+ u8 eeprom_customer_id; /* 0xc5 */
+ u8 tx_bb_swing_setting_2g; /* 0xc6 */
+ u8 tx_bb_swing_setting_5g; /* 0xc7 */
+ u8 res3;
+ u8 trx_antenna_option; /* 0xc9 */
+ u8 rfe_option; /* 0xca */
+ u8 country_code[2]; /* 0xcb */
+ u8 res4[3];
+ union {
+ struct rtw8814au_efuse u;
+ struct rtw8814ae_efuse e;
+ };
+ u8 res5[0x122]; /* 0xde */
+} __packed;
+
+static_assert(sizeof(struct rtw8814a_efuse) == 512);
+
+extern const struct rtw_chip_info rtw8814a_hw_spec;
+
+#endif
diff --git a/drivers/net/wireless/realtek/rtw88/rtw8814a_table.c b/drivers/net/wireless/realtek/rtw88/rtw8814a_table.c
new file mode 100644
index 000000000000..b9ce51a09fc8
--- /dev/null
+++ b/drivers/net/wireless/realtek/rtw88/rtw8814a_table.c
@@ -0,0 +1,23930 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
+/* Copyright(c) 2025 Realtek Corporation
+ */
+
+#include "main.h"
+#include "phy.h"
+#include "rtw8814a_table.h"
+
+static const u32 rtw8814a_mac[] = {
+ 0x010, 0x0000007C,
+ 0x014, 0x000000DB,
+ 0x016, 0x00000002,
+ 0x073, 0x00000010,
+ 0x420, 0x00000080,
+ 0x421, 0x0000000F,
+ 0x428, 0x0000000A,
+ 0x429, 0x00000010,
+ 0x430, 0x00000000,
+ 0x431, 0x00000000,
+ 0x432, 0x00000000,
+ 0x433, 0x00000001,
+ 0x434, 0x00000004,
+ 0x435, 0x00000005,
+ 0x436, 0x00000007,
+ 0x437, 0x00000008,
+ 0x43C, 0x00000004,
+ 0x43D, 0x00000005,
+ 0x43E, 0x00000007,
+ 0x43F, 0x00000008,
+ 0x440, 0x0000005D,
+ 0x441, 0x00000001,
+ 0x442, 0x00000000,
+ 0x444, 0x00000010,
+ 0x445, 0x000000F0,
+ 0x446, 0x00000001,
+ 0x447, 0x000000FE,
+ 0x448, 0x00000000,
+ 0x449, 0x00000000,
+ 0x44A, 0x00000000,
+ 0x44B, 0x00000040,
+ 0x44C, 0x00000010,
+ 0x44D, 0x000000F0,
+ 0x44E, 0x0000003F,
+ 0x44F, 0x00000000,
+ 0x450, 0x00000000,
+ 0x451, 0x00000000,
+ 0x452, 0x00000000,
+ 0x453, 0x00000040,
+ 0x45E, 0x00000004,
+ 0x49C, 0x00000010,
+ 0x49D, 0x000000F0,
+ 0x49E, 0x00000000,
+ 0x49F, 0x00000006,
+ 0x4A0, 0x000000E0,
+ 0x4A1, 0x00000003,
+ 0x4A2, 0x00000000,
+ 0x4A3, 0x00000040,
+ 0x4A4, 0x00000015,
+ 0x4A5, 0x000000F0,
+ 0x4A6, 0x00000000,
+ 0x4A7, 0x00000006,
+ 0x4A8, 0x000000E0,
+ 0x4A9, 0x00000000,
+ 0x4AA, 0x00000000,
+ 0x4AB, 0x00000000,
+ 0x7DA, 0x00000008,
+ 0x1448, 0x00000006,
+ 0x144A, 0x00000006,
+ 0x144C, 0x00000006,
+ 0x144E, 0x00000006,
+ 0x4C8, 0x000000FF,
+ 0x4C9, 0x00000008,
+ 0x4CA, 0x0000003C,
+ 0x4CB, 0x0000003C,
+ 0x4CC, 0x000000FF,
+ 0x4CD, 0x000000FF,
+ 0x4CE, 0x00000001,
+ 0x4CF, 0x00000008,
+ 0x500, 0x00000026,
+ 0x501, 0x000000A2,
+ 0x502, 0x0000002F,
+ 0x503, 0x00000000,
+ 0x504, 0x00000028,
+ 0x505, 0x000000A3,
+ 0x506, 0x0000005E,
+ 0x507, 0x00000000,
+ 0x508, 0x0000002B,
+ 0x509, 0x000000A4,
+ 0x50A, 0x0000005E,
+ 0x50B, 0x00000000,
+ 0x50C, 0x0000004F,
+ 0x50D, 0x000000A4,
+ 0x50E, 0x00000000,
+ 0x50F, 0x00000000,
+ 0x512, 0x0000001C,
+ 0x514, 0x0000000A,
+ 0x516, 0x0000000A,
+ 0x521, 0x0000002F,
+ 0x525, 0x00000047,
+ 0x550, 0x00000010,
+ 0x551, 0x00000010,
+ 0x559, 0x00000002,
+ 0x55C, 0x00000064,
+ 0x55D, 0x000000FF,
+ 0x577, 0x00000003,
+ 0x5BE, 0x00000064,
+ 0x604, 0x00000001,
+ 0x605, 0x00000030,
+ 0x607, 0x00000001,
+ 0x608, 0x0000000E,
+ 0x609, 0x0000002A,
+ 0x60A, 0x00000000,
+ 0x60C, 0x00000018,
+ 0x60D, 0x00000050,
+ 0x6A0, 0x000000FF,
+ 0x6A1, 0x000000FF,
+ 0x6A2, 0x000000FF,
+ 0x6A3, 0x000000FF,
+ 0x6A4, 0x000000FF,
+ 0x6A5, 0x000000FF,
+ 0x6DE, 0x00000084,
+ 0x620, 0x000000FF,
+ 0x621, 0x000000FF,
+ 0x622, 0x000000FF,
+ 0x623, 0x000000FF,
+ 0x624, 0x000000FF,
+ 0x625, 0x000000FF,
+ 0x626, 0x000000FF,
+ 0x627, 0x000000FF,
+ 0x638, 0x00000064,
+ 0x63C, 0x0000000A,
+ 0x63D, 0x0000000A,
+ 0x63E, 0x0000000E,
+ 0x63F, 0x0000000E,
+ 0x640, 0x00000040,
+ 0x642, 0x00000040,
+ 0x643, 0x00000000,
+ 0x652, 0x000000C8,
+ 0x66E, 0x00000005,
+ 0x700, 0x00000021,
+ 0x701, 0x00000043,
+ 0x702, 0x00000065,
+ 0x703, 0x00000087,
+ 0x708, 0x00000021,
+ 0x709, 0x00000043,
+ 0x70A, 0x00000065,
+ 0x70B, 0x00000087,
+ 0x718, 0x00000040,
+ 0x7D5, 0x000000BC,
+ 0x7D8, 0x00000028,
+ 0x7D9, 0x00000000,
+ 0x7DA, 0x0000000B,
+};
+
+RTW_DECL_TABLE_PHY_COND(rtw8814a_mac, rtw_phy_cfg_mac);
+
+static const u32 rtw8814a_agc[] = {
+ 0x80000002, 0x00000000, 0x40000000, 0x00000000,
+ 0x81C, 0xFE000003,
+ 0x90000003, 0x00000000, 0x40000000, 0x00000000,
+ 0x81C, 0xFE000003,
+ 0x90000004, 0x00000000, 0x40000000, 0x00000000,
+ 0x81C, 0xFE000003,
+ 0x90000005, 0x00000000, 0x40000000, 0x00000000,
+ 0x81C, 0xFE000003,
+ 0x90000007, 0x00000000, 0x40000000, 0x00000000,
+ 0x81C, 0xFF000003,
+ 0x90000008, 0x00000000, 0x40000000, 0x00000000,
+ 0x81C, 0xFE000003,
+ 0x90000009, 0x00000000, 0x40000000, 0x00000000,
+ 0x81C, 0xFF000003,
+ 0x9000000a, 0x00000000, 0x40000000, 0x00000000,
+ 0x81C, 0xFE000003,
+ 0x9000000b, 0x00000000, 0x40000000, 0x00000000,
+ 0x81C, 0xFE000003,
+ 0xA0000000, 0x00000000,
+ 0x81C, 0xFF000003,
+ 0xB0000000, 0x00000000,
+ 0x80000001, 0x00000000, 0x40000000, 0x00000000,
+ 0x81C, 0xFE000003,
+ 0x81C, 0xFD020003,
+ 0x81C, 0xFC040003,
+ 0x81C, 0xFB060003,
+ 0x81C, 0xFA080003,
+ 0x81C, 0xF90A0003,
+ 0x81C, 0xF80C0003,
+ 0x81C, 0xF70E0003,
+ 0x81C, 0xF6100003,
+ 0x81C, 0xF5120003,
+ 0x81C, 0xF4140003,
+ 0x81C, 0xF3160003,
+ 0x81C, 0xF2180003,
+ 0x81C, 0xF11A0003,
+ 0x81C, 0xF01C0003,
+ 0x81C, 0xEF1E0003,
+ 0x81C, 0xEE200003,
+ 0x81C, 0xED220003,
+ 0x81C, 0xCF240003,
+ 0x81C, 0xCE260003,
+ 0x81C, 0xCD280003,
+ 0x81C, 0xCC2A0003,
+ 0x81C, 0xCB2C0003,
+ 0x81C, 0xCA2E0003,
+ 0x81C, 0xC9300003,
+ 0x81C, 0xC8320003,
+ 0x81C, 0xC7340003,
+ 0x81C, 0xC6360003,
+ 0x81C, 0xC5380003,
+ 0x81C, 0xC43A0003,
+ 0x81C, 0xA63C0003,
+ 0x81C, 0xA53E0003,
+ 0x81C, 0xA4400003,
+ 0x81C, 0xA3420003,
+ 0x81C, 0xA2440003,
+ 0x81C, 0xA1460003,
+ 0x81C, 0x86480003,
+ 0x81C, 0x854A0003,
+ 0x81C, 0x844C0003,
+ 0x81C, 0x834E0003,
+ 0x81C, 0x66500003,
+ 0x81C, 0x65520003,
+ 0x81C, 0x64540003,
+ 0x81C, 0x63560003,
+ 0x81C, 0x62580003,
+ 0x81C, 0x615A0003,
+ 0x81C, 0x435C0003,
+ 0x81C, 0x425E0003,
+ 0x81C, 0x41600003,
+ 0x81C, 0x27620003,
+ 0x81C, 0x26640003,
+ 0x81C, 0x25660003,
+ 0x81C, 0x24680003,
+ 0x81C, 0x236A0003,
+ 0x81C, 0x226C0003,
+ 0x81C, 0x216E0003,
+ 0x81C, 0x21700003,
+ 0x81C, 0x21720003,
+ 0x81C, 0x21740003,
+ 0x81C, 0x21760003,
+ 0x81C, 0x21780003,
+ 0x81C, 0x217A0003,
+ 0x81C, 0x217C0003,
+ 0x81C, 0x217E0003,
+ 0x90000002, 0x00000000, 0x40000000, 0x00000000,
+ 0x81C, 0xFF000003,
+ 0x81C, 0xFE020003,
+ 0x81C, 0xFD040003,
+ 0x81C, 0xFC060003,
+ 0x81C, 0xFB080003,
+ 0x81C, 0xFA0A0003,
+ 0x81C, 0xF90C0003,
+ 0x81C, 0xF80E0003,
+ 0x81C, 0xF7100003,
+ 0x81C, 0xF6120003,
+ 0x81C, 0xF5140003,
+ 0x81C, 0xF4160003,
+ 0x81C, 0xF3180003,
+ 0x81C, 0xF21A0003,
+ 0x81C, 0xF11C0003,
+ 0x81C, 0xF01E0003,
+ 0x81C, 0xEF200003,
+ 0x81C, 0xEE220003,
+ 0x81C, 0xED240003,
+ 0x81C, 0xEC260003,
+ 0x81C, 0xEB280003,
+ 0x81C, 0xEA2A0003,
+ 0x81C, 0xE92C0003,
+ 0x81C, 0xE82E0003,
+ 0x81C, 0xE7300003,
+ 0x81C, 0xE6320003,
+ 0x81C, 0xE5340003,
+ 0x81C, 0xE4360003,
+ 0x81C, 0xE3380003,
+ 0x81C, 0xC53A0003,
+ 0x81C, 0xC43C0003,
+ 0x81C, 0xC33E0003,
+ 0x81C, 0xC2400003,
+ 0x81C, 0xC1420003,
+ 0x81C, 0xA8440003,
+ 0x81C, 0xA7460003,
+ 0x81C, 0xA6480003,
+ 0x81C, 0xA54A0003,
+ 0x81C, 0xA44C0003,
+ 0x81C, 0xA34E0003,
+ 0x81C, 0xA2500003,
+ 0x81C, 0x65520003,
+ 0x81C, 0x64540003,
+ 0x81C, 0x63560003,
+ 0x81C, 0x62580003,
+ 0x81C, 0x615A0003,
+ 0x81C, 0x475C0003,
+ 0x81C, 0x465E0003,
+ 0x81C, 0x45600003,
+ 0x81C, 0x44620003,
+ 0x81C, 0x43640003,
+ 0x81C, 0x42660003,
+ 0x81C, 0x41680003,
+ 0x81C, 0x416A0003,
+ 0x81C, 0x416C0003,
+ 0x81C, 0x416E0003,
+ 0x81C, 0x41700003,
+ 0x81C, 0x41720003,
+ 0x81C, 0x41740003,
+ 0x81C, 0x41760003,
+ 0x81C, 0x41780003,
+ 0x81C, 0x417A0003,
+ 0x81C, 0x417C0003,
+ 0x81C, 0x417E0003,
+ 0x90000003, 0x00000000, 0x40000000, 0x00000000,
+ 0x81C, 0xFE000003,
+ 0x81C, 0xFD020003,
+ 0x81C, 0xFC040003,
+ 0x81C, 0xFB060003,
+ 0x81C, 0xFA080003,
+ 0x81C, 0xF90A0003,
+ 0x81C, 0xF80C0003,
+ 0x81C, 0xF70E0003,
+ 0x81C, 0xF6100003,
+ 0x81C, 0xF5120003,
+ 0x81C, 0xF4140003,
+ 0x81C, 0xF3160003,
+ 0x81C, 0xF2180003,
+ 0x81C, 0xF11A0003,
+ 0x81C, 0xF01C0003,
+ 0x81C, 0xEF1E0003,
+ 0x81C, 0xEE200003,
+ 0x81C, 0xED220003,
+ 0x81C, 0xEC240003,
+ 0x81C, 0xEB260003,
+ 0x81C, 0xEA280003,
+ 0x81C, 0xE92A0003,
+ 0x81C, 0xE82C0003,
+ 0x81C, 0xE72E0003,
+ 0x81C, 0xE6300003,
+ 0x81C, 0xE5320003,
+ 0x81C, 0xE4340003,
+ 0x81C, 0xE3360003,
+ 0x81C, 0xC6380003,
+ 0x81C, 0xC53A0003,
+ 0x81C, 0xC43C0003,
+ 0x81C, 0xC33E0003,
+ 0x81C, 0xC2400003,
+ 0x81C, 0xA9420003,
+ 0x81C, 0xA8440003,
+ 0x81C, 0xA7460003,
+ 0x81C, 0xA6480003,
+ 0x81C, 0xA54A0003,
+ 0x81C, 0xA44C0003,
+ 0x81C, 0xA34E0003,
+ 0x81C, 0x66500003,
+ 0x81C, 0x65520003,
+ 0x81C, 0x64540003,
+ 0x81C, 0x63560003,
+ 0x81C, 0x49580003,
+ 0x81C, 0x485A0003,
+ 0x81C, 0x475C0003,
+ 0x81C, 0x465E0003,
+ 0x81C, 0x45600003,
+ 0x81C, 0x44620003,
+ 0x81C, 0x43640003,
+ 0x81C, 0x42660003,
+ 0x81C, 0x41680003,
+ 0x81C, 0x416A0003,
+ 0x81C, 0x416C0003,
+ 0x81C, 0x416E0003,
+ 0x81C, 0x41700003,
+ 0x81C, 0x41720003,
+ 0x81C, 0x41740003,
+ 0x81C, 0x41760003,
+ 0x81C, 0x41780003,
+ 0x81C, 0x417A0003,
+ 0x81C, 0x417C0003,
+ 0x81C, 0x417E0003,
+ 0x90000004, 0x00000000, 0x40000000, 0x00000000,
+ 0x81C, 0xFF000003,
+ 0x81C, 0xFE020003,
+ 0x81C, 0xFD040003,
+ 0x81C, 0xFC060003,
+ 0x81C, 0xFB080003,
+ 0x81C, 0xFA0A0003,
+ 0x81C, 0xF90C0003,
+ 0x81C, 0xF80E0003,
+ 0x81C, 0xF7100003,
+ 0x81C, 0xF6120003,
+ 0x81C, 0xF5140003,
+ 0x81C, 0xF4160003,
+ 0x81C, 0xF3180003,
+ 0x81C, 0xF21A0003,
+ 0x81C, 0xF11C0003,
+ 0x81C, 0xF01E0003,
+ 0x81C, 0xEF200003,
+ 0x81C, 0xEE220003,
+ 0x81C, 0xED240003,
+ 0x81C, 0xEC260003,
+ 0x81C, 0xEB280003,
+ 0x81C, 0xEA2A0003,
+ 0x81C, 0xE92C0003,
+ 0x81C, 0xE82E0003,
+ 0x81C, 0xE7300003,
+ 0x81C, 0xE6320003,
+ 0x81C, 0xE5340003,
+ 0x81C, 0xE4360003,
+ 0x81C, 0xE3380003,
+ 0x81C, 0xC53A0003,
+ 0x81C, 0xC43C0003,
+ 0x81C, 0xC33E0003,
+ 0x81C, 0xC2400003,
+ 0x81C, 0xC1420003,
+ 0x81C, 0xA8440003,
+ 0x81C, 0xA7460003,
+ 0x81C, 0xA6480003,
+ 0x81C, 0xA54A0003,
+ 0x81C, 0xA44C0003,
+ 0x81C, 0xA34E0003,
+ 0x81C, 0xA2500003,
+ 0x81C, 0x65520003,
+ 0x81C, 0x64540003,
+ 0x81C, 0x63560003,
+ 0x81C, 0x62580003,
+ 0x81C, 0x615A0003,
+ 0x81C, 0x475C0003,
+ 0x81C, 0x465E0003,
+ 0x81C, 0x45600003,
+ 0x81C, 0x44620003,
+ 0x81C, 0x43640003,
+ 0x81C, 0x42660003,
+ 0x81C, 0x41680003,
+ 0x81C, 0x416A0003,
+ 0x81C, 0x416C0003,
+ 0x81C, 0x416E0003,
+ 0x81C, 0x41700003,
+ 0x81C, 0x41720003,
+ 0x81C, 0x41740003,
+ 0x81C, 0x41760003,
+ 0x81C, 0x41780003,
+ 0x81C, 0x417A0003,
+ 0x81C, 0x417C0003,
+ 0x81C, 0x417E0003,
+ 0x90000005, 0x00000000, 0x40000000, 0x00000000,
+ 0x81C, 0xFE000003,
+ 0x81C, 0xFD020003,
+ 0x81C, 0xFC040003,
+ 0x81C, 0xFB060003,
+ 0x81C, 0xFA080003,
+ 0x81C, 0xF90A0003,
+ 0x81C, 0xF80C0003,
+ 0x81C, 0xF70E0003,
+ 0x81C, 0xF6100003,
+ 0x81C, 0xF5120003,
+ 0x81C, 0xF4140003,
+ 0x81C, 0xF3160003,
+ 0x81C, 0xF2180003,
+ 0x81C, 0xF11A0003,
+ 0x81C, 0xF01C0003,
+ 0x81C, 0xEF1E0003,
+ 0x81C, 0xEE200003,
+ 0x81C, 0xED220003,
+ 0x81C, 0xEC240003,
+ 0x81C, 0xEB260003,
+ 0x81C, 0xEA280003,
+ 0x81C, 0xE92A0003,
+ 0x81C, 0xE82C0003,
+ 0x81C, 0xE72E0003,
+ 0x81C, 0xE6300003,
+ 0x81C, 0xE5320003,
+ 0x81C, 0xE4340003,
+ 0x81C, 0xE3360003,
+ 0x81C, 0xC6380003,
+ 0x81C, 0xC53A0003,
+ 0x81C, 0xC43C0003,
+ 0x81C, 0xC33E0003,
+ 0x81C, 0xC2400003,
+ 0x81C, 0xA9420003,
+ 0x81C, 0xA8440003,
+ 0x81C, 0xA7460003,
+ 0x81C, 0xA6480003,
+ 0x81C, 0xA54A0003,
+ 0x81C, 0xA44C0003,
+ 0x81C, 0xA34E0003,
+ 0x81C, 0x66500003,
+ 0x81C, 0x65520003,
+ 0x81C, 0x64540003,
+ 0x81C, 0x63560003,
+ 0x81C, 0x49580003,
+ 0x81C, 0x485A0003,
+ 0x81C, 0x475C0003,
+ 0x81C, 0x465E0003,
+ 0x81C, 0x45600003,
+ 0x81C, 0x44620003,
+ 0x81C, 0x43640003,
+ 0x81C, 0x42660003,
+ 0x81C, 0x41680003,
+ 0x81C, 0x416A0003,
+ 0x81C, 0x416C0003,
+ 0x81C, 0x416E0003,
+ 0x81C, 0x41700003,
+ 0x81C, 0x41720003,
+ 0x81C, 0x41740003,
+ 0x81C, 0x41760003,
+ 0x81C, 0x41780003,
+ 0x81C, 0x417A0003,
+ 0x81C, 0x417C0003,
+ 0x81C, 0x417E0003,
+ 0x90000007, 0x00000000, 0x40000000, 0x00000000,
+ 0x81C, 0xDF000003,
+ 0x81C, 0xDF020003,
+ 0x81C, 0xDF040003,
+ 0x81C, 0xDE060003,
+ 0x81C, 0xDD080003,
+ 0x81C, 0xDC0A0003,
+ 0x81C, 0xDB0C0003,
+ 0x81C, 0xDA0E0003,
+ 0x81C, 0xD9100003,
+ 0x81C, 0xD8120003,
+ 0x81C, 0xD7140003,
+ 0x81C, 0xD6160003,
+ 0x81C, 0xD5180003,
+ 0x81C, 0xD41A0003,
+ 0x81C, 0xD31C0003,
+ 0x81C, 0xD21E0003,
+ 0x81C, 0xD1200003,
+ 0x81C, 0xD0220003,
+ 0x81C, 0xCF240003,
+ 0x81C, 0xCE260003,
+ 0x81C, 0xCD280003,
+ 0x81C, 0xCC2A0003,
+ 0x81C, 0xCB2C0003,
+ 0x81C, 0xCA2E0003,
+ 0x81C, 0xC9300003,
+ 0x81C, 0xC8320003,
+ 0x81C, 0xC7340003,
+ 0x81C, 0xC6360003,
+ 0x81C, 0xC5380003,
+ 0x81C, 0xA73A0003,
+ 0x81C, 0xA63C0003,
+ 0x81C, 0xA53E0003,
+ 0x81C, 0xA4400003,
+ 0x81C, 0xA3420003,
+ 0x81C, 0xA2440003,
+ 0x81C, 0x87460003,
+ 0x81C, 0x86480003,
+ 0x81C, 0x854A0003,
+ 0x81C, 0x844C0003,
+ 0x81C, 0x834E0003,
+ 0x81C, 0x82500003,
+ 0x81C, 0x81520003,
+ 0x81C, 0x64540003,
+ 0x81C, 0x63560003,
+ 0x81C, 0x62580003,
+ 0x81C, 0x615A0003,
+ 0x81C, 0x445C0003,
+ 0x81C, 0x435E0003,
+ 0x81C, 0x42600003,
+ 0x81C, 0x41620003,
+ 0x81C, 0x27640003,
+ 0x81C, 0x26660003,
+ 0x81C, 0x25680003,
+ 0x81C, 0x246A0003,
+ 0x81C, 0x236C0003,
+ 0x81C, 0x226E0003,
+ 0x81C, 0x21700003,
+ 0x81C, 0x21720003,
+ 0x81C, 0x21740003,
+ 0x81C, 0x21760003,
+ 0x81C, 0x21780003,
+ 0x81C, 0x217A0003,
+ 0x81C, 0x217C0003,
+ 0x81C, 0x217E0003,
+ 0x90000008, 0x00000000, 0x40000000, 0x00000000,
+ 0x81C, 0xFF000003,
+ 0x81C, 0xFE020003,
+ 0x81C, 0xFD040003,
+ 0x81C, 0xFC060003,
+ 0x81C, 0xFB080003,
+ 0x81C, 0xFA0A0003,
+ 0x81C, 0xF90C0003,
+ 0x81C, 0xF80E0003,
+ 0x81C, 0xF7100003,
+ 0x81C, 0xF6120003,
+ 0x81C, 0xF5140003,
+ 0x81C, 0xF4160003,
+ 0x81C, 0xF3180003,
+ 0x81C, 0xF21A0003,
+ 0x81C, 0xF11C0003,
+ 0x81C, 0xF01E0003,
+ 0x81C, 0xEF200003,
+ 0x81C, 0xEE220003,
+ 0x81C, 0xED240003,
+ 0x81C, 0xEC260003,
+ 0x81C, 0xEB280003,
+ 0x81C, 0xEA2A0003,
+ 0x81C, 0xE92C0003,
+ 0x81C, 0xE82E0003,
+ 0x81C, 0xE7300003,
+ 0x81C, 0xE6320003,
+ 0x81C, 0xE5340003,
+ 0x81C, 0xE4360003,
+ 0x81C, 0xE3380003,
+ 0x81C, 0xC53A0003,
+ 0x81C, 0xC43C0003,
+ 0x81C, 0xC33E0003,
+ 0x81C, 0xC2400003,
+ 0x81C, 0xC1420003,
+ 0x81C, 0xA8440003,
+ 0x81C, 0xA7460003,
+ 0x81C, 0xA6480003,
+ 0x81C, 0xA54A0003,
+ 0x81C, 0xA44C0003,
+ 0x81C, 0xA34E0003,
+ 0x81C, 0xA2500003,
+ 0x81C, 0x65520003,
+ 0x81C, 0x64540003,
+ 0x81C, 0x63560003,
+ 0x81C, 0x62580003,
+ 0x81C, 0x615A0003,
+ 0x81C, 0x475C0003,
+ 0x81C, 0x465E0003,
+ 0x81C, 0x45600003,
+ 0x81C, 0x44620003,
+ 0x81C, 0x43640003,
+ 0x81C, 0x42660003,
+ 0x81C, 0x41680003,
+ 0x81C, 0x416A0003,
+ 0x81C, 0x416C0003,
+ 0x81C, 0x416E0003,
+ 0x81C, 0x41700003,
+ 0x81C, 0x41720003,
+ 0x81C, 0x41740003,
+ 0x81C, 0x41760003,
+ 0x81C, 0x41780003,
+ 0x81C, 0x417A0003,
+ 0x81C, 0x417C0003,
+ 0x81C, 0x417E0003,
+ 0x90000009, 0x00000000, 0x40000000, 0x00000000,
+ 0x81C, 0xDF000003,
+ 0x81C, 0xDF020003,
+ 0x81C, 0xDF040003,
+ 0x81C, 0xDE060003,
+ 0x81C, 0xDD080003,
+ 0x81C, 0xDC0A0003,
+ 0x81C, 0xDB0C0003,
+ 0x81C, 0xDA0E0003,
+ 0x81C, 0xD9100003,
+ 0x81C, 0xD8120003,
+ 0x81C, 0xD7140003,
+ 0x81C, 0xD6160003,
+ 0x81C, 0xD5180003,
+ 0x81C, 0xD41A0003,
+ 0x81C, 0xD31C0003,
+ 0x81C, 0xD21E0003,
+ 0x81C, 0xD1200003,
+ 0x81C, 0xD0220003,
+ 0x81C, 0xCF240003,
+ 0x81C, 0xCE260003,
+ 0x81C, 0xCD280003,
+ 0x81C, 0xCC2A0003,
+ 0x81C, 0xCB2C0003,
+ 0x81C, 0xCA2E0003,
+ 0x81C, 0xC9300003,
+ 0x81C, 0xC8320003,
+ 0x81C, 0xC7340003,
+ 0x81C, 0xC6360003,
+ 0x81C, 0xC5380003,
+ 0x81C, 0xA73A0003,
+ 0x81C, 0xA63C0003,
+ 0x81C, 0xA53E0003,
+ 0x81C, 0xA4400003,
+ 0x81C, 0xA3420003,
+ 0x81C, 0xA2440003,
+ 0x81C, 0x87460003,
+ 0x81C, 0x86480003,
+ 0x81C, 0x854A0003,
+ 0x81C, 0x844C0003,
+ 0x81C, 0x834E0003,
+ 0x81C, 0x82500003,
+ 0x81C, 0x81520003,
+ 0x81C, 0x64540003,
+ 0x81C, 0x63560003,
+ 0x81C, 0x62580003,
+ 0x81C, 0x615A0003,
+ 0x81C, 0x445C0003,
+ 0x81C, 0x435E0003,
+ 0x81C, 0x42600003,
+ 0x81C, 0x41620003,
+ 0x81C, 0x27640003,
+ 0x81C, 0x26660003,
+ 0x81C, 0x25680003,
+ 0x81C, 0x246A0003,
+ 0x81C, 0x236C0003,
+ 0x81C, 0x226E0003,
+ 0x81C, 0x21700003,
+ 0x81C, 0x21720003,
+ 0x81C, 0x21740003,
+ 0x81C, 0x21760003,
+ 0x81C, 0x21780003,
+ 0x81C, 0x217A0003,
+ 0x81C, 0x217C0003,
+ 0x81C, 0x217E0003,
+ 0x9000000a, 0x00000000, 0x40000000, 0x00000000,
+ 0x81C, 0xFE000003,
+ 0x81C, 0xFE020003,
+ 0x81C, 0xFD040003,
+ 0x81C, 0xFC060003,
+ 0x81C, 0xFB080003,
+ 0x81C, 0xFA0A0003,
+ 0x81C, 0xF90C0003,
+ 0x81C, 0xF80E0003,
+ 0x81C, 0xF7100003,
+ 0x81C, 0xF6120003,
+ 0x81C, 0xF5140003,
+ 0x81C, 0xF4160003,
+ 0x81C, 0xF3180003,
+ 0x81C, 0xF21A0003,
+ 0x81C, 0xF11C0003,
+ 0x81C, 0xF01E0003,
+ 0x81C, 0xEF200003,
+ 0x81C, 0xEE220003,
+ 0x81C, 0xED240003,
+ 0x81C, 0x0F260003,
+ 0x81C, 0x0E280003,
+ 0x81C, 0x0D2A0003,
+ 0x81C, 0x0C2C0003,
+ 0x81C, 0x0B2E0003,
+ 0x81C, 0x0A300003,
+ 0x81C, 0x09320003,
+ 0x81C, 0x08340003,
+ 0x81C, 0x07360003,
+ 0x81C, 0x06380003,
+ 0x81C, 0x053A0003,
+ 0x81C, 0x043C0003,
+ 0x81C, 0x033E0003,
+ 0x81C, 0x23400003,
+ 0x81C, 0x22420003,
+ 0x81C, 0xA8440003,
+ 0x81C, 0xA7460003,
+ 0x81C, 0xA6480003,
+ 0x81C, 0xA54A0003,
+ 0x81C, 0xA44C0003,
+ 0x81C, 0x684E0003,
+ 0x81C, 0x67500003,
+ 0x81C, 0x66520003,
+ 0x81C, 0x65540003,
+ 0x81C, 0x64560003,
+ 0x81C, 0x63580003,
+ 0x81C, 0x625A0003,
+ 0x81C, 0x615C0003,
+ 0x81C, 0x475E0003,
+ 0x81C, 0x46600003,
+ 0x81C, 0x45620003,
+ 0x81C, 0x44640003,
+ 0x81C, 0x43660003,
+ 0x81C, 0x42680003,
+ 0x81C, 0x416A0003,
+ 0x81C, 0x416C0003,
+ 0x81C, 0x416E0003,
+ 0x81C, 0x41700003,
+ 0x81C, 0x41720003,
+ 0x81C, 0x41740003,
+ 0x81C, 0x41760003,
+ 0x81C, 0x41780003,
+ 0x81C, 0x417A0003,
+ 0x81C, 0x417C0003,
+ 0x81C, 0x417E0003,
+ 0x9000000b, 0x00000000, 0x40000000, 0x00000000,
+ 0x81C, 0xFE000003,
+ 0x81C, 0xFE020003,
+ 0x81C, 0xFD040003,
+ 0x81C, 0xFC060003,
+ 0x81C, 0xFB080003,
+ 0x81C, 0xFA0A0003,
+ 0x81C, 0xF90C0003,
+ 0x81C, 0xF80E0003,
+ 0x81C, 0xF7100003,
+ 0x81C, 0xF6120003,
+ 0x81C, 0xF5140003,
+ 0x81C, 0xF4160003,
+ 0x81C, 0xF3180003,
+ 0x81C, 0xF21A0003,
+ 0x81C, 0xF11C0003,
+ 0x81C, 0xF01E0003,
+ 0x81C, 0xEF200003,
+ 0x81C, 0xEE220003,
+ 0x81C, 0xED240003,
+ 0x81C, 0xEC260003,
+ 0x81C, 0xEB280003,
+ 0x81C, 0xEA2A0003,
+ 0x81C, 0xE92C0003,
+ 0x81C, 0xE72E0003,
+ 0x81C, 0xE6300003,
+ 0x81C, 0xE5320003,
+ 0x81C, 0x08340003,
+ 0x81C, 0x07360003,
+ 0x81C, 0x06380003,
+ 0x81C, 0x053A0003,
+ 0x81C, 0x043C0003,
+ 0x81C, 0x033E0003,
+ 0x81C, 0x02400003,
+ 0x81C, 0xA9420003,
+ 0x81C, 0xA8440003,
+ 0x81C, 0xA7460003,
+ 0x81C, 0xA6480003,
+ 0x81C, 0xA54A0003,
+ 0x81C, 0x684C0003,
+ 0x81C, 0x674E0003,
+ 0x81C, 0x66500003,
+ 0x81C, 0x65520003,
+ 0x81C, 0x64540003,
+ 0x81C, 0x63560003,
+ 0x81C, 0x62580003,
+ 0x81C, 0x615A0003,
+ 0x81C, 0x475C0003,
+ 0x81C, 0x465E0003,
+ 0x81C, 0x45600003,
+ 0x81C, 0x44620003,
+ 0x81C, 0x43640003,
+ 0x81C, 0x42660003,
+ 0x81C, 0x41680003,
+ 0x81C, 0x416A0003,
+ 0x81C, 0x416C0003,
+ 0x81C, 0x416E0003,
+ 0x81C, 0x41700003,
+ 0x81C, 0x41720003,
+ 0x81C, 0x41740003,
+ 0x81C, 0x41760003,
+ 0x81C, 0x41780003,
+ 0x81C, 0x417A0003,
+ 0x81C, 0x417C0003,
+ 0x81C, 0x417E0003,
+ 0xA0000000, 0x00000000,
+ 0x81C, 0xFE000003,
+ 0x81C, 0xFD020003,
+ 0x81C, 0xFC040003,
+ 0x81C, 0xFB060003,
+ 0x81C, 0xFA080003,
+ 0x81C, 0xF90A0003,
+ 0x81C, 0xF80C0003,
+ 0x81C, 0xF70E0003,
+ 0x81C, 0xF6100003,
+ 0x81C, 0xF5120003,
+ 0x81C, 0xF4140003,
+ 0x81C, 0xF3160003,
+ 0x81C, 0xF2180003,
+ 0x81C, 0xF11A0003,
+ 0x81C, 0xF01C0003,
+ 0x81C, 0xEF1E0003,
+ 0x81C, 0xEE200003,
+ 0x81C, 0xED220003,
+ 0x81C, 0xCF240003,
+ 0x81C, 0xCE260003,
+ 0x81C, 0xCD280003,
+ 0x81C, 0xCC2A0003,
+ 0x81C, 0xCB2C0003,
+ 0x81C, 0xCA2E0003,
+ 0x81C, 0xC9300003,
+ 0x81C, 0xC8320003,
+ 0x81C, 0xC7340003,
+ 0x81C, 0xC6360003,
+ 0x81C, 0xC5380003,
+ 0x81C, 0xC43A0003,
+ 0x81C, 0xA63C0003,
+ 0x81C, 0xA53E0003,
+ 0x81C, 0xA4400003,
+ 0x81C, 0xA3420003,
+ 0x81C, 0xA2440003,
+ 0x81C, 0xA1460003,
+ 0x81C, 0x86480003,
+ 0x81C, 0x854A0003,
+ 0x81C, 0x844C0003,
+ 0x81C, 0x834E0003,
+ 0x81C, 0x66500003,
+ 0x81C, 0x65520003,
+ 0x81C, 0x64540003,
+ 0x81C, 0x63560003,
+ 0x81C, 0x62580003,
+ 0x81C, 0x615A0003,
+ 0x81C, 0x435C0003,
+ 0x81C, 0x425E0003,
+ 0x81C, 0x41600003,
+ 0x81C, 0x27620003,
+ 0x81C, 0x26640003,
+ 0x81C, 0x25660003,
+ 0x81C, 0x24680003,
+ 0x81C, 0x236A0003,
+ 0x81C, 0x226C0003,
+ 0x81C, 0x216E0003,
+ 0x81C, 0x21700003,
+ 0x81C, 0x21720003,
+ 0x81C, 0x21740003,
+ 0x81C, 0x21760003,
+ 0x81C, 0x21780003,
+ 0x81C, 0x217A0003,
+ 0x81C, 0x217C0003,
+ 0x81C, 0x217E0003,
+ 0xB0000000, 0x00000000,
+ 0x80000001, 0x00000000, 0x40000000, 0x00000000,
+ 0x81C, 0xF9000103,
+ 0x81C, 0xF8020103,
+ 0x81C, 0xF7040103,
+ 0x81C, 0xF6060103,
+ 0x81C, 0xF5080103,
+ 0x81C, 0xF40A0103,
+ 0x81C, 0xF30C0103,
+ 0x81C, 0xF20E0103,
+ 0x81C, 0xF1100103,
+ 0x81C, 0xF0120103,
+ 0x81C, 0xEF140103,
+ 0x81C, 0xEE160103,
+ 0x81C, 0xED180103,
+ 0x81C, 0xEC1A0103,
+ 0x81C, 0xEB1C0103,
+ 0x81C, 0xEA1E0103,
+ 0x81C, 0xE9200103,
+ 0x81C, 0xE8220103,
+ 0x81C, 0xE7240103,
+ 0x81C, 0xE6260103,
+ 0x81C, 0xE5280103,
+ 0x81C, 0xE42A0103,
+ 0x81C, 0xE32C0103,
+ 0x81C, 0xC32E0103,
+ 0x81C, 0xC2300103,
+ 0x81C, 0xC1320103,
+ 0x81C, 0xA5340103,
+ 0x81C, 0xA4360103,
+ 0x81C, 0xA3380103,
+ 0x81C, 0xA23A0103,
+ 0x81C, 0xA13C0103,
+ 0x81C, 0x843E0103,
+ 0x81C, 0x83400103,
+ 0x81C, 0x82420103,
+ 0x81C, 0x81440103,
+ 0x81C, 0x64460103,
+ 0x81C, 0x63480103,
+ 0x81C, 0x624A0103,
+ 0x81C, 0x614C0103,
+ 0x81C, 0x444E0103,
+ 0x81C, 0x43500103,
+ 0x81C, 0x42520103,
+ 0x81C, 0x41540103,
+ 0x81C, 0x25560103,
+ 0x81C, 0x24580103,
+ 0x81C, 0x235A0103,
+ 0x81C, 0x065C0103,
+ 0x81C, 0x055E0103,
+ 0x81C, 0x04600103,
+ 0x81C, 0x03620103,
+ 0x81C, 0x02640103,
+ 0x81C, 0x01660103,
+ 0x81C, 0x01680103,
+ 0x81C, 0x016A0103,
+ 0x81C, 0x016C0103,
+ 0x81C, 0x016E0103,
+ 0x81C, 0x01700103,
+ 0x81C, 0x01720103,
+ 0x81C, 0x01740103,
+ 0x81C, 0x01760103,
+ 0x81C, 0x01780103,
+ 0x81C, 0x017A0103,
+ 0x81C, 0x017C0103,
+ 0x81C, 0x017E0103,
+ 0x90000002, 0x00000000, 0x40000000, 0x00000000,
+ 0x81C, 0xF8000103,
+ 0x81C, 0xF7020103,
+ 0x81C, 0xF6040103,
+ 0x81C, 0xF5060103,
+ 0x81C, 0xF4080103,
+ 0x81C, 0xF30A0103,
+ 0x81C, 0xF20C0103,
+ 0x81C, 0xF10E0103,
+ 0x81C, 0xF0100103,
+ 0x81C, 0xEF120103,
+ 0x81C, 0xEE140103,
+ 0x81C, 0xED160103,
+ 0x81C, 0xEC180103,
+ 0x81C, 0xEB1A0103,
+ 0x81C, 0xEA1C0103,
+ 0x81C, 0xE91E0103,
+ 0x81C, 0xE8200103,
+ 0x81C, 0xE7220103,
+ 0x81C, 0xE6240103,
+ 0x81C, 0xE5260103,
+ 0x81C, 0xE4280103,
+ 0x81C, 0xE32A0103,
+ 0x81C, 0xE22C0103,
+ 0x81C, 0xE12E0103,
+ 0x81C, 0xA5300103,
+ 0x81C, 0xA4320103,
+ 0x81C, 0xA3340103,
+ 0x81C, 0xA2360103,
+ 0x81C, 0xA1380103,
+ 0x81C, 0x843A0103,
+ 0x81C, 0x833C0103,
+ 0x81C, 0x823E0103,
+ 0x81C, 0x81400103,
+ 0x81C, 0x64420103,
+ 0x81C, 0x63440103,
+ 0x81C, 0x62460103,
+ 0x81C, 0x61480103,
+ 0x81C, 0x454A0103,
+ 0x81C, 0x444C0103,
+ 0x81C, 0x434E0103,
+ 0x81C, 0x42500103,
+ 0x81C, 0x25520103,
+ 0x81C, 0x24540103,
+ 0x81C, 0x23560103,
+ 0x81C, 0x06580103,
+ 0x81C, 0x055A0103,
+ 0x81C, 0x045C0103,
+ 0x81C, 0x035E0103,
+ 0x81C, 0x02600103,
+ 0x81C, 0x01620103,
+ 0x81C, 0x01640103,
+ 0x81C, 0x01660103,
+ 0x81C, 0x01680103,
+ 0x81C, 0x016A0103,
+ 0x81C, 0x016C0103,
+ 0x81C, 0x016E0103,
+ 0x81C, 0x01700103,
+ 0x81C, 0x01720103,
+ 0x81C, 0x01740103,
+ 0x81C, 0x01760103,
+ 0x81C, 0x01780103,
+ 0x81C, 0x017A0103,
+ 0x81C, 0x017C0103,
+ 0x81C, 0x017E0103,
+ 0x90000003, 0x00000000, 0x40000000, 0x00000000,
+ 0x81C, 0xFC000103,
+ 0x81C, 0xFB020103,
+ 0x81C, 0xFA040103,
+ 0x81C, 0xF9060103,
+ 0x81C, 0xF8080103,
+ 0x81C, 0xF70A0103,
+ 0x81C, 0xF60C0103,
+ 0x81C, 0xF50E0103,
+ 0x81C, 0xF4100103,
+ 0x81C, 0xF3120103,
+ 0x81C, 0xF2140103,
+ 0x81C, 0xF1160103,
+ 0x81C, 0xF0180103,
+ 0x81C, 0xEF1A0103,
+ 0x81C, 0xEE1C0103,
+ 0x81C, 0xED1E0103,
+ 0x81C, 0xEC200103,
+ 0x81C, 0xEB220103,
+ 0x81C, 0xEA240103,
+ 0x81C, 0xE9260103,
+ 0x81C, 0xE8280103,
+ 0x81C, 0xE72A0103,
+ 0x81C, 0xE62C0103,
+ 0x81C, 0xE52E0103,
+ 0x81C, 0xE4300103,
+ 0x81C, 0xE3320103,
+ 0x81C, 0xE2340103,
+ 0x81C, 0xE1360103,
+ 0x81C, 0x87380103,
+ 0x81C, 0x863A0103,
+ 0x81C, 0x853C0103,
+ 0x81C, 0x843E0103,
+ 0x81C, 0x83400103,
+ 0x81C, 0x82420103,
+ 0x81C, 0x81440103,
+ 0x81C, 0x64460103,
+ 0x81C, 0x63480103,
+ 0x81C, 0x624A0103,
+ 0x81C, 0x464C0103,
+ 0x81C, 0x454E0103,
+ 0x81C, 0x44500103,
+ 0x81C, 0x43520103,
+ 0x81C, 0x26540103,
+ 0x81C, 0x25560103,
+ 0x81C, 0x24580103,
+ 0x81C, 0x075A0103,
+ 0x81C, 0x065C0103,
+ 0x81C, 0x055E0103,
+ 0x81C, 0x04600103,
+ 0x81C, 0x03620103,
+ 0x81C, 0x02640103,
+ 0x81C, 0x01660103,
+ 0x81C, 0x01680103,
+ 0x81C, 0x016A0103,
+ 0x81C, 0x016C0103,
+ 0x81C, 0x016E0103,
+ 0x81C, 0x01700103,
+ 0x81C, 0x01720103,
+ 0x81C, 0x01740103,
+ 0x81C, 0x01760103,
+ 0x81C, 0x01780103,
+ 0x81C, 0x017A0103,
+ 0x81C, 0x017C0103,
+ 0x81C, 0x017E0103,
+ 0x90000004, 0x00000000, 0x40000000, 0x00000000,
+ 0x81C, 0xF9000103,
+ 0x81C, 0xF8020103,
+ 0x81C, 0xF7040103,
+ 0x81C, 0xF6060103,
+ 0x81C, 0xF5080103,
+ 0x81C, 0xF40A0103,
+ 0x81C, 0xF30C0103,
+ 0x81C, 0xF20E0103,
+ 0x81C, 0xF1100103,
+ 0x81C, 0xF0120103,
+ 0x81C, 0xEF140103,
+ 0x81C, 0xEE160103,
+ 0x81C, 0xED180103,
+ 0x81C, 0xEC1A0103,
+ 0x81C, 0xEB1C0103,
+ 0x81C, 0xEA1E0103,
+ 0x81C, 0xE9200103,
+ 0x81C, 0xE8220103,
+ 0x81C, 0xE7240103,
+ 0x81C, 0xE6260103,
+ 0x81C, 0xE5280103,
+ 0x81C, 0xE42A0103,
+ 0x81C, 0xE32C0103,
+ 0x81C, 0xE22E0103,
+ 0x81C, 0xA6300103,
+ 0x81C, 0xA5320103,
+ 0x81C, 0xA4340103,
+ 0x81C, 0xA3360103,
+ 0x81C, 0xA2380103,
+ 0x81C, 0xA13A0103,
+ 0x81C, 0x843C0103,
+ 0x81C, 0x833E0103,
+ 0x81C, 0x82400103,
+ 0x81C, 0x81420103,
+ 0x81C, 0x64440103,
+ 0x81C, 0x63460103,
+ 0x81C, 0x62480103,
+ 0x81C, 0x614A0103,
+ 0x81C, 0x444C0103,
+ 0x81C, 0x434E0103,
+ 0x81C, 0x42500103,
+ 0x81C, 0x41520103,
+ 0x81C, 0x25540103,
+ 0x81C, 0x24560103,
+ 0x81C, 0x23580103,
+ 0x81C, 0x225A0103,
+ 0x81C, 0x055C0103,
+ 0x81C, 0x045E0103,
+ 0x81C, 0x03600103,
+ 0x81C, 0x02620103,
+ 0x81C, 0x01640103,
+ 0x81C, 0x01660103,
+ 0x81C, 0x01680103,
+ 0x81C, 0x016A0103,
+ 0x81C, 0x016C0103,
+ 0x81C, 0x016E0103,
+ 0x81C, 0x01700103,
+ 0x81C, 0x01720103,
+ 0x81C, 0x01740103,
+ 0x81C, 0x01760103,
+ 0x81C, 0x01780103,
+ 0x81C, 0x017A0103,
+ 0x81C, 0x017C0103,
+ 0x81C, 0x017E0103,
+ 0x90000005, 0x00000000, 0x40000000, 0x00000000,
+ 0x81C, 0xFD000103,
+ 0x81C, 0xFC020103,
+ 0x81C, 0xFB040103,
+ 0x81C, 0xFA060103,
+ 0x81C, 0xF9080103,
+ 0x81C, 0xF80A0103,
+ 0x81C, 0xF70C0103,
+ 0x81C, 0xF60E0103,
+ 0x81C, 0xF5100103,
+ 0x81C, 0xF4120103,
+ 0x81C, 0xF3140103,
+ 0x81C, 0xF2160103,
+ 0x81C, 0xF1180103,
+ 0x81C, 0xF01A0103,
+ 0x81C, 0xEF1C0103,
+ 0x81C, 0xEE1E0103,
+ 0x81C, 0xED200103,
+ 0x81C, 0xEC220103,
+ 0x81C, 0xEB240103,
+ 0x81C, 0xEA260103,
+ 0x81C, 0xE9280103,
+ 0x81C, 0xE82A0103,
+ 0x81C, 0xE72C0103,
+ 0x81C, 0xE62E0103,
+ 0x81C, 0xE5300103,
+ 0x81C, 0xE4320103,
+ 0x81C, 0xE3340103,
+ 0x81C, 0xE2360103,
+ 0x81C, 0xE1380103,
+ 0x81C, 0xA33A0103,
+ 0x81C, 0xA23C0103,
+ 0x81C, 0xA13E0103,
+ 0x81C, 0x84400103,
+ 0x81C, 0x83420103,
+ 0x81C, 0x82440103,
+ 0x81C, 0x81460103,
+ 0x81C, 0x64480103,
+ 0x81C, 0x634A0103,
+ 0x81C, 0x624C0103,
+ 0x81C, 0x614E0103,
+ 0x81C, 0x45500103,
+ 0x81C, 0x44520103,
+ 0x81C, 0x43540103,
+ 0x81C, 0x42560103,
+ 0x81C, 0x25580103,
+ 0x81C, 0x245A0103,
+ 0x81C, 0x235C0103,
+ 0x81C, 0x065E0103,
+ 0x81C, 0x05600103,
+ 0x81C, 0x04620103,
+ 0x81C, 0x03640103,
+ 0x81C, 0x02660103,
+ 0x81C, 0x01680103,
+ 0x81C, 0x016A0103,
+ 0x81C, 0x016C0103,
+ 0x81C, 0x016E0103,
+ 0x81C, 0x01700103,
+ 0x81C, 0x01720103,
+ 0x81C, 0x01740103,
+ 0x81C, 0x01760103,
+ 0x81C, 0x01780103,
+ 0x81C, 0x017A0103,
+ 0x81C, 0x017C0103,
+ 0x81C, 0x017E0103,
+ 0x90000006, 0x00000000, 0x40000000, 0x00000000,
+ 0x81C, 0xFA000103,
+ 0x81C, 0xF9020103,
+ 0x81C, 0xF8040103,
+ 0x81C, 0xF7060103,
+ 0x81C, 0xF6080103,
+ 0x81C, 0xF50A0103,
+ 0x81C, 0xF40C0103,
+ 0x81C, 0xF30E0103,
+ 0x81C, 0xF2100103,
+ 0x81C, 0xF1120103,
+ 0x81C, 0xF0140103,
+ 0x81C, 0xEF160103,
+ 0x81C, 0xEE180103,
+ 0x81C, 0xED1A0103,
+ 0x81C, 0xEC1C0103,
+ 0x81C, 0xEB1E0103,
+ 0x81C, 0xEA200103,
+ 0x81C, 0xE9220103,
+ 0x81C, 0xE8240103,
+ 0x81C, 0xE7260103,
+ 0x81C, 0xE6280103,
+ 0x81C, 0xE52A0103,
+ 0x81C, 0xE42C0103,
+ 0x81C, 0xE32E0103,
+ 0x81C, 0xE2300103,
+ 0x81C, 0xE1320103,
+ 0x81C, 0xA5340103,
+ 0x81C, 0xA4360103,
+ 0x81C, 0xA3380103,
+ 0x81C, 0xA23A0103,
+ 0x81C, 0xA13C0103,
+ 0x81C, 0x843E0103,
+ 0x81C, 0x83400103,
+ 0x81C, 0x82420103,
+ 0x81C, 0x81440103,
+ 0x81C, 0x64460103,
+ 0x81C, 0x63480103,
+ 0x81C, 0x624A0103,
+ 0x81C, 0x614C0103,
+ 0x81C, 0x454E0103,
+ 0x81C, 0x44500103,
+ 0x81C, 0x43520103,
+ 0x81C, 0x42540103,
+ 0x81C, 0x41560103,
+ 0x81C, 0x24580103,
+ 0x81C, 0x235A0103,
+ 0x81C, 0x225C0103,
+ 0x81C, 0x055E0103,
+ 0x81C, 0x04600103,
+ 0x81C, 0x03620103,
+ 0x81C, 0x02640103,
+ 0x81C, 0x01660103,
+ 0x81C, 0x01680103,
+ 0x81C, 0x016A0103,
+ 0x81C, 0x016C0103,
+ 0x81C, 0x016E0103,
+ 0x81C, 0x01700103,
+ 0x81C, 0x01720103,
+ 0x81C, 0x01740103,
+ 0x81C, 0x01760103,
+ 0x81C, 0x01780103,
+ 0x81C, 0x017A0103,
+ 0x81C, 0x017C0103,
+ 0x81C, 0x017E0103,
+ 0x90000007, 0x00000000, 0x40000000, 0x00000000,
+ 0x81C, 0xFF000103,
+ 0x81C, 0xFF020103,
+ 0x81C, 0xFE040103,
+ 0x81C, 0xFD060103,
+ 0x81C, 0xFC080103,
+ 0x81C, 0xFB0A0103,
+ 0x81C, 0xFA0C0103,
+ 0x81C, 0xF90E0103,
+ 0x81C, 0xF8100103,
+ 0x81C, 0xF7120103,
+ 0x81C, 0xF6140103,
+ 0x81C, 0xF5160103,
+ 0x81C, 0xF4180103,
+ 0x81C, 0xF31A0103,
+ 0x81C, 0xF21C0103,
+ 0x81C, 0xF11E0103,
+ 0x81C, 0xF0200103,
+ 0x81C, 0xEF220103,
+ 0x81C, 0xEE240103,
+ 0x81C, 0xED260103,
+ 0x81C, 0xEC280103,
+ 0x81C, 0xEB2A0103,
+ 0x81C, 0xEA2C0103,
+ 0x81C, 0xE92E0103,
+ 0x81C, 0xE8300103,
+ 0x81C, 0xE7320103,
+ 0x81C, 0xE6340103,
+ 0x81C, 0xE5360103,
+ 0x81C, 0xE4380103,
+ 0x81C, 0xE33A0103,
+ 0x81C, 0xA53C0103,
+ 0x81C, 0xA43E0103,
+ 0x81C, 0xA3400103,
+ 0x81C, 0xA2420103,
+ 0x81C, 0xA1440103,
+ 0x81C, 0x85460103,
+ 0x81C, 0x84480103,
+ 0x81C, 0x834A0103,
+ 0x81C, 0x824C0103,
+ 0x81C, 0x814E0103,
+ 0x81C, 0x64500103,
+ 0x81C, 0x63520103,
+ 0x81C, 0x62540103,
+ 0x81C, 0x44560103,
+ 0x81C, 0x43580103,
+ 0x81C, 0x425A0103,
+ 0x81C, 0x265C0103,
+ 0x81C, 0x255E0103,
+ 0x81C, 0x24600103,
+ 0x81C, 0x07620103,
+ 0x81C, 0x06640103,
+ 0x81C, 0x05660103,
+ 0x81C, 0x04680103,
+ 0x81C, 0x036A0103,
+ 0x81C, 0x026C0103,
+ 0x81C, 0x016E0103,
+ 0x81C, 0x01700103,
+ 0x81C, 0x01720103,
+ 0x81C, 0x01740103,
+ 0x81C, 0x01760103,
+ 0x81C, 0x01780103,
+ 0x81C, 0x017A0103,
+ 0x81C, 0x017C0103,
+ 0x81C, 0x017E0103,
+ 0x90000008, 0x00000000, 0x40000000, 0x00000000,
+ 0x81C, 0xF8000103,
+ 0x81C, 0xF7020103,
+ 0x81C, 0xF6040103,
+ 0x81C, 0xF5060103,
+ 0x81C, 0xF4080103,
+ 0x81C, 0xF30A0103,
+ 0x81C, 0xF20C0103,
+ 0x81C, 0xF10E0103,
+ 0x81C, 0xF0100103,
+ 0x81C, 0xEF120103,
+ 0x81C, 0xEE140103,
+ 0x81C, 0xED160103,
+ 0x81C, 0xEC180103,
+ 0x81C, 0xEB1A0103,
+ 0x81C, 0xEA1C0103,
+ 0x81C, 0xE91E0103,
+ 0x81C, 0xE8200103,
+ 0x81C, 0xE7220103,
+ 0x81C, 0xE6240103,
+ 0x81C, 0xE5260103,
+ 0x81C, 0xE4280103,
+ 0x81C, 0xE32A0103,
+ 0x81C, 0xE22C0103,
+ 0x81C, 0xE12E0103,
+ 0x81C, 0xA4300103,
+ 0x81C, 0xA3320103,
+ 0x81C, 0xA2340103,
+ 0x81C, 0xA1360103,
+ 0x81C, 0x85380103,
+ 0x81C, 0x843A0103,
+ 0x81C, 0x833C0103,
+ 0x81C, 0x823E0103,
+ 0x81C, 0x65400103,
+ 0x81C, 0x64420103,
+ 0x81C, 0x63440103,
+ 0x81C, 0x62460103,
+ 0x81C, 0x45480103,
+ 0x81C, 0x444A0103,
+ 0x81C, 0x434C0103,
+ 0x81C, 0x264E0103,
+ 0x81C, 0x25500103,
+ 0x81C, 0x24520103,
+ 0x81C, 0x08540103,
+ 0x81C, 0x07560103,
+ 0x81C, 0x06580103,
+ 0x81C, 0x055A0103,
+ 0x81C, 0x045C0103,
+ 0x81C, 0x035E0103,
+ 0x81C, 0x02600103,
+ 0x81C, 0x01620103,
+ 0x81C, 0x01640103,
+ 0x81C, 0x01660103,
+ 0x81C, 0x01680103,
+ 0x81C, 0x016A0103,
+ 0x81C, 0x016C0103,
+ 0x81C, 0x016E0103,
+ 0x81C, 0x01700103,
+ 0x81C, 0x01720103,
+ 0x81C, 0x01740103,
+ 0x81C, 0x01760103,
+ 0x81C, 0x01780103,
+ 0x81C, 0x017A0103,
+ 0x81C, 0x017C0103,
+ 0x81C, 0x017E0103,
+ 0x90000009, 0x00000000, 0x40000000, 0x00000000,
+ 0x81C, 0xFF000103,
+ 0x81C, 0xFF020103,
+ 0x81C, 0xFE040103,
+ 0x81C, 0xFD060103,
+ 0x81C, 0xFC080103,
+ 0x81C, 0xFB0A0103,
+ 0x81C, 0xFA0C0103,
+ 0x81C, 0xF90E0103,
+ 0x81C, 0xF8100103,
+ 0x81C, 0xF7120103,
+ 0x81C, 0xF6140103,
+ 0x81C, 0xF5160103,
+ 0x81C, 0xF4180103,
+ 0x81C, 0xF31A0103,
+ 0x81C, 0xF21C0103,
+ 0x81C, 0xF11E0103,
+ 0x81C, 0xF0200103,
+ 0x81C, 0xEF220103,
+ 0x81C, 0xEE240103,
+ 0x81C, 0xED260103,
+ 0x81C, 0xEC280103,
+ 0x81C, 0xEB2A0103,
+ 0x81C, 0xEA2C0103,
+ 0x81C, 0xE92E0103,
+ 0x81C, 0xE8300103,
+ 0x81C, 0xE7320103,
+ 0x81C, 0xE6340103,
+ 0x81C, 0xE5360103,
+ 0x81C, 0xE4380103,
+ 0x81C, 0xE33A0103,
+ 0x81C, 0xA53C0103,
+ 0x81C, 0xA43E0103,
+ 0x81C, 0xA3400103,
+ 0x81C, 0xA2420103,
+ 0x81C, 0xA1440103,
+ 0x81C, 0x85460103,
+ 0x81C, 0x84480103,
+ 0x81C, 0x834A0103,
+ 0x81C, 0x824C0103,
+ 0x81C, 0x814E0103,
+ 0x81C, 0x64500103,
+ 0x81C, 0x63520103,
+ 0x81C, 0x62540103,
+ 0x81C, 0x44560103,
+ 0x81C, 0x43580103,
+ 0x81C, 0x425A0103,
+ 0x81C, 0x265C0103,
+ 0x81C, 0x255E0103,
+ 0x81C, 0x24600103,
+ 0x81C, 0x07620103,
+ 0x81C, 0x06640103,
+ 0x81C, 0x05660103,
+ 0x81C, 0x04680103,
+ 0x81C, 0x036A0103,
+ 0x81C, 0x026C0103,
+ 0x81C, 0x016E0103,
+ 0x81C, 0x01700103,
+ 0x81C, 0x01720103,
+ 0x81C, 0x01740103,
+ 0x81C, 0x01760103,
+ 0x81C, 0x01780103,
+ 0x81C, 0x017A0103,
+ 0x81C, 0x017C0103,
+ 0x81C, 0x017E0103,
+ 0x9000000a, 0x00000000, 0x40000000, 0x00000000,
+ 0x81C, 0xF9000103,
+ 0x81C, 0xF8020103,
+ 0x81C, 0xF7040103,
+ 0x81C, 0xF6060103,
+ 0x81C, 0xF5080103,
+ 0x81C, 0xF40A0103,
+ 0x81C, 0xF30C0103,
+ 0x81C, 0xF20E0103,
+ 0x81C, 0xF1100103,
+ 0x81C, 0xF0120103,
+ 0x81C, 0xEF140103,
+ 0x81C, 0xEE160103,
+ 0x81C, 0xED180103,
+ 0x81C, 0xEC1A0103,
+ 0x81C, 0xEB1C0103,
+ 0x81C, 0xEA1E0103,
+ 0x81C, 0xE9200103,
+ 0x81C, 0xE8220103,
+ 0x81C, 0xE7240103,
+ 0x81C, 0xE6260103,
+ 0x81C, 0xE5280103,
+ 0x81C, 0xE42A0103,
+ 0x81C, 0xE32C0103,
+ 0x81C, 0xE22E0103,
+ 0x81C, 0xA6300103,
+ 0x81C, 0xA5320103,
+ 0x81C, 0xA4340103,
+ 0x81C, 0xA3360103,
+ 0x81C, 0xA2380103,
+ 0x81C, 0xA13A0103,
+ 0x81C, 0x843C0103,
+ 0x81C, 0x833E0103,
+ 0x81C, 0x82400103,
+ 0x81C, 0x81420103,
+ 0x81C, 0x64440103,
+ 0x81C, 0x63460103,
+ 0x81C, 0x62480103,
+ 0x81C, 0x614A0103,
+ 0x81C, 0x444C0103,
+ 0x81C, 0x434E0103,
+ 0x81C, 0x42500103,
+ 0x81C, 0x41520103,
+ 0x81C, 0x25540103,
+ 0x81C, 0x24560103,
+ 0x81C, 0x23580103,
+ 0x81C, 0x225A0103,
+ 0x81C, 0x055C0103,
+ 0x81C, 0x045E0103,
+ 0x81C, 0x03600103,
+ 0x81C, 0x02620103,
+ 0x81C, 0x01640103,
+ 0x81C, 0x01660103,
+ 0x81C, 0x01680103,
+ 0x81C, 0x016A0103,
+ 0x81C, 0x016C0103,
+ 0x81C, 0x016E0103,
+ 0x81C, 0x01700103,
+ 0x81C, 0x01720103,
+ 0x81C, 0x01740103,
+ 0x81C, 0x01760103,
+ 0x81C, 0x01780103,
+ 0x81C, 0x017A0103,
+ 0x81C, 0x017C0103,
+ 0x81C, 0x017E0103,
+ 0x9000000b, 0x00000000, 0x40000000, 0x00000000,
+ 0x81C, 0xFA000103,
+ 0x81C, 0xF9020103,
+ 0x81C, 0xF8040103,
+ 0x81C, 0xF7060103,
+ 0x81C, 0xF6080103,
+ 0x81C, 0xF50A0103,
+ 0x81C, 0xF40C0103,
+ 0x81C, 0xF30E0103,
+ 0x81C, 0xF2100103,
+ 0x81C, 0xF1120103,
+ 0x81C, 0xF0140103,
+ 0x81C, 0xEF160103,
+ 0x81C, 0xEE180103,
+ 0x81C, 0xED1A0103,
+ 0x81C, 0xCC1C0103,
+ 0x81C, 0xCB1E0103,
+ 0x81C, 0xCA200103,
+ 0x81C, 0xE9220103,
+ 0x81C, 0xE8240103,
+ 0x81C, 0xE7260103,
+ 0x81C, 0xE6280103,
+ 0x81C, 0xE42A0103,
+ 0x81C, 0xE32C0103,
+ 0x81C, 0xE22E0103,
+ 0x81C, 0xA7300103,
+ 0x81C, 0xA6320103,
+ 0x81C, 0xA5340103,
+ 0x81C, 0xA4360103,
+ 0x81C, 0xA3380103,
+ 0x81C, 0xA23A0103,
+ 0x81C, 0xA13C0103,
+ 0x81C, 0x843E0103,
+ 0x81C, 0x83400103,
+ 0x81C, 0x82420103,
+ 0x81C, 0x65440103,
+ 0x81C, 0x64460103,
+ 0x81C, 0x63480103,
+ 0x81C, 0x624A0103,
+ 0x81C, 0x614C0103,
+ 0x81C, 0x444E0103,
+ 0x81C, 0x43500103,
+ 0x81C, 0x42520103,
+ 0x81C, 0x41540103,
+ 0x81C, 0x24560103,
+ 0x81C, 0x23580103,
+ 0x81C, 0x055A0103,
+ 0x81C, 0x045C0103,
+ 0x81C, 0x035E0103,
+ 0x81C, 0x02600103,
+ 0x81C, 0x01620103,
+ 0x81C, 0x01640103,
+ 0x81C, 0x01660103,
+ 0x81C, 0x01680103,
+ 0x81C, 0x016A0103,
+ 0x81C, 0x016C0103,
+ 0x81C, 0x016E0103,
+ 0x81C, 0x01700103,
+ 0x81C, 0x01720103,
+ 0x81C, 0x01740103,
+ 0x81C, 0x01760103,
+ 0x81C, 0x01780103,
+ 0x81C, 0x017A0103,
+ 0x81C, 0x017C0103,
+ 0x81C, 0x017E0103,
+ 0xA0000000, 0x00000000,
+ 0x81C, 0xFF000103,
+ 0x81C, 0xFE020103,
+ 0x81C, 0xFD040103,
+ 0x81C, 0xFC060103,
+ 0x81C, 0xFB080103,
+ 0x81C, 0xFA0A0103,
+ 0x81C, 0xF90C0103,
+ 0x81C, 0xF80E0103,
+ 0x81C, 0xF7100103,
+ 0x81C, 0xF6120103,
+ 0x81C, 0xF5140103,
+ 0x81C, 0xF4160103,
+ 0x81C, 0xF3180103,
+ 0x81C, 0xF21A0103,
+ 0x81C, 0xF11C0103,
+ 0x81C, 0xF01E0103,
+ 0x81C, 0xEF200103,
+ 0x81C, 0xEE220103,
+ 0x81C, 0xED240103,
+ 0x81C, 0xEC260103,
+ 0x81C, 0xEB280103,
+ 0x81C, 0xEA2A0103,
+ 0x81C, 0xE92C0103,
+ 0x81C, 0xE82E0103,
+ 0x81C, 0xE7300103,
+ 0x81C, 0xE6320103,
+ 0x81C, 0xE5340103,
+ 0x81C, 0xE4360103,
+ 0x81C, 0xE3380103,
+ 0x81C, 0xE23A0103,
+ 0x81C, 0xE13C0103,
+ 0x81C, 0xA43E0103,
+ 0x81C, 0xA3400103,
+ 0x81C, 0xA2420103,
+ 0x81C, 0xA1440103,
+ 0x81C, 0x86460103,
+ 0x81C, 0x85480103,
+ 0x81C, 0x844A0103,
+ 0x81C, 0x834C0103,
+ 0x81C, 0x824E0103,
+ 0x81C, 0x81500103,
+ 0x81C, 0x64520103,
+ 0x81C, 0x63540103,
+ 0x81C, 0x62560103,
+ 0x81C, 0x61580103,
+ 0x81C, 0x435A0103,
+ 0x81C, 0x425C0103,
+ 0x81C, 0x415E0103,
+ 0x81C, 0x25600103,
+ 0x81C, 0x24620103,
+ 0x81C, 0x06640103,
+ 0x81C, 0x05660103,
+ 0x81C, 0x04680103,
+ 0x81C, 0x036A0103,
+ 0x81C, 0x026C0103,
+ 0x81C, 0x016E0103,
+ 0x81C, 0x01700103,
+ 0x81C, 0x01720103,
+ 0x81C, 0x01740103,
+ 0x81C, 0x01760103,
+ 0x81C, 0x01780103,
+ 0x81C, 0x017A0103,
+ 0x81C, 0x017C0103,
+ 0x81C, 0x017E0103,
+ 0xB0000000, 0x00000000,
+ 0x80000001, 0x00000000, 0x40000000, 0x00000000,
+ 0x81C, 0xFA000203,
+ 0x81C, 0xF9020203,
+ 0x81C, 0xF8040203,
+ 0x81C, 0xF7060203,
+ 0x81C, 0xF6080203,
+ 0x81C, 0xF50A0203,
+ 0x81C, 0xF40C0203,
+ 0x81C, 0xF30E0203,
+ 0x81C, 0xF2100203,
+ 0x81C, 0xF1120203,
+ 0x81C, 0xF0140203,
+ 0x81C, 0xEF160203,
+ 0x81C, 0xEE180203,
+ 0x81C, 0xED1A0203,
+ 0x81C, 0xEC1C0203,
+ 0x81C, 0xEB1E0203,
+ 0x81C, 0xEA200203,
+ 0x81C, 0xE9220203,
+ 0x81C, 0xE8240203,
+ 0x81C, 0xE7260203,
+ 0x81C, 0xE6280203,
+ 0x81C, 0xE52A0203,
+ 0x81C, 0xE42C0203,
+ 0x81C, 0xE32E0203,
+ 0x81C, 0xE2300203,
+ 0x81C, 0xE1320203,
+ 0x81C, 0xA5340203,
+ 0x81C, 0xA4360203,
+ 0x81C, 0xA3380203,
+ 0x81C, 0xA23A0203,
+ 0x81C, 0xA13C0203,
+ 0x81C, 0x843E0203,
+ 0x81C, 0x83400203,
+ 0x81C, 0x82420203,
+ 0x81C, 0x81440203,
+ 0x81C, 0x63460203,
+ 0x81C, 0x62480203,
+ 0x81C, 0x614A0203,
+ 0x81C, 0x464C0203,
+ 0x81C, 0x454E0203,
+ 0x81C, 0x44500203,
+ 0x81C, 0x43520203,
+ 0x81C, 0x42540203,
+ 0x81C, 0x41560203,
+ 0x81C, 0x24580203,
+ 0x81C, 0x235A0203,
+ 0x81C, 0x065C0203,
+ 0x81C, 0x055E0203,
+ 0x81C, 0x04600203,
+ 0x81C, 0x03620203,
+ 0x81C, 0x02640203,
+ 0x81C, 0x01660203,
+ 0x81C, 0x01680203,
+ 0x81C, 0x016A0203,
+ 0x81C, 0x016C0203,
+ 0x81C, 0x016E0203,
+ 0x81C, 0x01700203,
+ 0x81C, 0x01720203,
+ 0x81C, 0x01740203,
+ 0x81C, 0x01760203,
+ 0x81C, 0x01780203,
+ 0x81C, 0x017A0203,
+ 0x81C, 0x017C0203,
+ 0x81C, 0x017E0203,
+ 0x90000002, 0x00000000, 0x40000000, 0x00000000,
+ 0x81C, 0xF8000203,
+ 0x81C, 0xF7020203,
+ 0x81C, 0xF6040203,
+ 0x81C, 0xF5060203,
+ 0x81C, 0xF4080203,
+ 0x81C, 0xF30A0203,
+ 0x81C, 0xF20C0203,
+ 0x81C, 0xF10E0203,
+ 0x81C, 0xF0100203,
+ 0x81C, 0xEF120203,
+ 0x81C, 0xEE140203,
+ 0x81C, 0xED160203,
+ 0x81C, 0xEC180203,
+ 0x81C, 0xEB1A0203,
+ 0x81C, 0xEA1C0203,
+ 0x81C, 0xE91E0203,
+ 0x81C, 0xE8200203,
+ 0x81C, 0xE7220203,
+ 0x81C, 0xE6240203,
+ 0x81C, 0xE5260203,
+ 0x81C, 0xE4280203,
+ 0x81C, 0xE32A0203,
+ 0x81C, 0xE22C0203,
+ 0x81C, 0xE12E0203,
+ 0x81C, 0xA6300203,
+ 0x81C, 0xA5320203,
+ 0x81C, 0xA4340203,
+ 0x81C, 0xA3360203,
+ 0x81C, 0xA2380203,
+ 0x81C, 0x853A0203,
+ 0x81C, 0x843C0203,
+ 0x81C, 0x833E0203,
+ 0x81C, 0x82400203,
+ 0x81C, 0x81420203,
+ 0x81C, 0x64440203,
+ 0x81C, 0x63460203,
+ 0x81C, 0x62480203,
+ 0x81C, 0x614A0203,
+ 0x81C, 0x444C0203,
+ 0x81C, 0x434E0203,
+ 0x81C, 0x42500203,
+ 0x81C, 0x25520203,
+ 0x81C, 0x24540203,
+ 0x81C, 0x23560203,
+ 0x81C, 0x06580203,
+ 0x81C, 0x055A0203,
+ 0x81C, 0x045C0203,
+ 0x81C, 0x035E0203,
+ 0x81C, 0x02600203,
+ 0x81C, 0x01620203,
+ 0x81C, 0x01640203,
+ 0x81C, 0x01660203,
+ 0x81C, 0x01680203,
+ 0x81C, 0x016A0203,
+ 0x81C, 0x016C0203,
+ 0x81C, 0x016E0203,
+ 0x81C, 0x01700203,
+ 0x81C, 0x01720203,
+ 0x81C, 0x01740203,
+ 0x81C, 0x01760203,
+ 0x81C, 0x01780203,
+ 0x81C, 0x017A0203,
+ 0x81C, 0x017C0203,
+ 0x81C, 0x017E0203,
+ 0x90000003, 0x00000000, 0x40000000, 0x00000000,
+ 0x81C, 0xFC000203,
+ 0x81C, 0xFB020203,
+ 0x81C, 0xFA040203,
+ 0x81C, 0xF9060203,
+ 0x81C, 0xF8080203,
+ 0x81C, 0xF70A0203,
+ 0x81C, 0xF60C0203,
+ 0x81C, 0xF50E0203,
+ 0x81C, 0xF4100203,
+ 0x81C, 0xF3120203,
+ 0x81C, 0xF2140203,
+ 0x81C, 0xF1160203,
+ 0x81C, 0xF0180203,
+ 0x81C, 0xEF1A0203,
+ 0x81C, 0xEE1C0203,
+ 0x81C, 0xED1E0203,
+ 0x81C, 0xEC200203,
+ 0x81C, 0xEB220203,
+ 0x81C, 0xEA240203,
+ 0x81C, 0xE9260203,
+ 0x81C, 0xE8280203,
+ 0x81C, 0xE72A0203,
+ 0x81C, 0xE62C0203,
+ 0x81C, 0xE52E0203,
+ 0x81C, 0xE4300203,
+ 0x81C, 0xE3320203,
+ 0x81C, 0xE2340203,
+ 0x81C, 0xE1360203,
+ 0x81C, 0x87380203,
+ 0x81C, 0x863A0203,
+ 0x81C, 0x853C0203,
+ 0x81C, 0x843E0203,
+ 0x81C, 0x83400203,
+ 0x81C, 0x82420203,
+ 0x81C, 0x81440203,
+ 0x81C, 0x64460203,
+ 0x81C, 0x63480203,
+ 0x81C, 0x624A0203,
+ 0x81C, 0x474C0203,
+ 0x81C, 0x464E0203,
+ 0x81C, 0x45500203,
+ 0x81C, 0x44520203,
+ 0x81C, 0x43540203,
+ 0x81C, 0x42560203,
+ 0x81C, 0x24580203,
+ 0x81C, 0x235A0203,
+ 0x81C, 0x075C0203,
+ 0x81C, 0x065E0203,
+ 0x81C, 0x05600203,
+ 0x81C, 0x04620203,
+ 0x81C, 0x03640203,
+ 0x81C, 0x02660203,
+ 0x81C, 0x01680203,
+ 0x81C, 0x016A0203,
+ 0x81C, 0x016C0203,
+ 0x81C, 0x016E0203,
+ 0x81C, 0x01700203,
+ 0x81C, 0x01720203,
+ 0x81C, 0x01740203,
+ 0x81C, 0x01760203,
+ 0x81C, 0x01780203,
+ 0x81C, 0x017A0203,
+ 0x81C, 0x017C0203,
+ 0x81C, 0x017E0203,
+ 0x90000004, 0x00000000, 0x40000000, 0x00000000,
+ 0x81C, 0xF8000203,
+ 0x81C, 0xF7020203,
+ 0x81C, 0xF6040203,
+ 0x81C, 0xF5060203,
+ 0x81C, 0xF4080203,
+ 0x81C, 0xF30A0203,
+ 0x81C, 0xF20C0203,
+ 0x81C, 0xF10E0203,
+ 0x81C, 0xF0100203,
+ 0x81C, 0xEF120203,
+ 0x81C, 0xEE140203,
+ 0x81C, 0xED160203,
+ 0x81C, 0xEC180203,
+ 0x81C, 0xEB1A0203,
+ 0x81C, 0xEA1C0203,
+ 0x81C, 0xE91E0203,
+ 0x81C, 0xE8200203,
+ 0x81C, 0xE7220203,
+ 0x81C, 0xE6240203,
+ 0x81C, 0xE5260203,
+ 0x81C, 0xE4280203,
+ 0x81C, 0xE32A0203,
+ 0x81C, 0xE22C0203,
+ 0x81C, 0xE12E0203,
+ 0x81C, 0xA6300203,
+ 0x81C, 0xA5320203,
+ 0x81C, 0xA4340203,
+ 0x81C, 0xA3360203,
+ 0x81C, 0xA2380203,
+ 0x81C, 0xA13A0203,
+ 0x81C, 0x843C0203,
+ 0x81C, 0x833E0203,
+ 0x81C, 0x82400203,
+ 0x81C, 0x81420203,
+ 0x81C, 0x64440203,
+ 0x81C, 0x63460203,
+ 0x81C, 0x62480203,
+ 0x81C, 0x614A0203,
+ 0x81C, 0x444C0203,
+ 0x81C, 0x434E0203,
+ 0x81C, 0x42500203,
+ 0x81C, 0x41520203,
+ 0x81C, 0x25540203,
+ 0x81C, 0x24560203,
+ 0x81C, 0x23580203,
+ 0x81C, 0x065A0203,
+ 0x81C, 0x055C0203,
+ 0x81C, 0x045E0203,
+ 0x81C, 0x03600203,
+ 0x81C, 0x02620203,
+ 0x81C, 0x01640203,
+ 0x81C, 0x01660203,
+ 0x81C, 0x01680203,
+ 0x81C, 0x016A0203,
+ 0x81C, 0x016C0203,
+ 0x81C, 0x016E0203,
+ 0x81C, 0x01700203,
+ 0x81C, 0x01720203,
+ 0x81C, 0x01740203,
+ 0x81C, 0x01760203,
+ 0x81C, 0x01780203,
+ 0x81C, 0x017A0203,
+ 0x81C, 0x017C0203,
+ 0x81C, 0x017E0203,
+ 0x90000005, 0x00000000, 0x40000000, 0x00000000,
+ 0x81C, 0xFB000203,
+ 0x81C, 0xFA020203,
+ 0x81C, 0xF9040203,
+ 0x81C, 0xF8060203,
+ 0x81C, 0xF7080203,
+ 0x81C, 0xF60A0203,
+ 0x81C, 0xF50C0203,
+ 0x81C, 0xF40E0203,
+ 0x81C, 0xF3100203,
+ 0x81C, 0xF2120203,
+ 0x81C, 0xF1140203,
+ 0x81C, 0xF0160203,
+ 0x81C, 0xEF180203,
+ 0x81C, 0xEE1A0203,
+ 0x81C, 0xED1C0203,
+ 0x81C, 0xEC1E0203,
+ 0x81C, 0xEB200203,
+ 0x81C, 0xEA220203,
+ 0x81C, 0xE9240203,
+ 0x81C, 0xE8260203,
+ 0x81C, 0xE7280203,
+ 0x81C, 0xE62A0203,
+ 0x81C, 0xE52C0203,
+ 0x81C, 0xE42E0203,
+ 0x81C, 0xE3300203,
+ 0x81C, 0xE2320203,
+ 0x81C, 0xE1340203,
+ 0x81C, 0xA5360203,
+ 0x81C, 0xA4380203,
+ 0x81C, 0xA33A0203,
+ 0x81C, 0xA23C0203,
+ 0x81C, 0x843E0203,
+ 0x81C, 0x83400203,
+ 0x81C, 0x82420203,
+ 0x81C, 0x81440203,
+ 0x81C, 0x64460203,
+ 0x81C, 0x63480203,
+ 0x81C, 0x624A0203,
+ 0x81C, 0x614C0203,
+ 0x81C, 0x474E0203,
+ 0x81C, 0x46500203,
+ 0x81C, 0x45520203,
+ 0x81C, 0x44540203,
+ 0x81C, 0x43560203,
+ 0x81C, 0x25580203,
+ 0x81C, 0x245A0203,
+ 0x81C, 0x235C0203,
+ 0x81C, 0x075E0203,
+ 0x81C, 0x06600203,
+ 0x81C, 0x05620203,
+ 0x81C, 0x04640203,
+ 0x81C, 0x03660203,
+ 0x81C, 0x02680203,
+ 0x81C, 0x016A0203,
+ 0x81C, 0x016C0203,
+ 0x81C, 0x016E0203,
+ 0x81C, 0x01700203,
+ 0x81C, 0x01720203,
+ 0x81C, 0x01740203,
+ 0x81C, 0x01760203,
+ 0x81C, 0x01780203,
+ 0x81C, 0x017A0203,
+ 0x81C, 0x017C0203,
+ 0x81C, 0x017E0203,
+ 0x90000006, 0x00000000, 0x40000000, 0x00000000,
+ 0x81C, 0xFC000203,
+ 0x81C, 0xFB020203,
+ 0x81C, 0xFA040203,
+ 0x81C, 0xF9060203,
+ 0x81C, 0xF8080203,
+ 0x81C, 0xF70A0203,
+ 0x81C, 0xF60C0203,
+ 0x81C, 0xF50E0203,
+ 0x81C, 0xF4100203,
+ 0x81C, 0xF3120203,
+ 0x81C, 0xF2140203,
+ 0x81C, 0xF1160203,
+ 0x81C, 0xF0180203,
+ 0x81C, 0xEF1A0203,
+ 0x81C, 0xEE1C0203,
+ 0x81C, 0xED1E0203,
+ 0x81C, 0xEC200203,
+ 0x81C, 0xEB220203,
+ 0x81C, 0xEA240203,
+ 0x81C, 0xE9260203,
+ 0x81C, 0xE8280203,
+ 0x81C, 0xE72A0203,
+ 0x81C, 0xE62C0203,
+ 0x81C, 0xE52E0203,
+ 0x81C, 0xE4300203,
+ 0x81C, 0xE3320203,
+ 0x81C, 0xE2340203,
+ 0x81C, 0xE1360203,
+ 0x81C, 0xA5380203,
+ 0x81C, 0xA43A0203,
+ 0x81C, 0xA33C0203,
+ 0x81C, 0x853E0203,
+ 0x81C, 0x84400203,
+ 0x81C, 0x83420203,
+ 0x81C, 0x82440203,
+ 0x81C, 0x81460203,
+ 0x81C, 0x64480203,
+ 0x81C, 0x634A0203,
+ 0x81C, 0x624C0203,
+ 0x81C, 0x614E0203,
+ 0x81C, 0x46500203,
+ 0x81C, 0x45520203,
+ 0x81C, 0x44540203,
+ 0x81C, 0x43560203,
+ 0x81C, 0x25580203,
+ 0x81C, 0x245A0203,
+ 0x81C, 0x235C0203,
+ 0x81C, 0x075E0203,
+ 0x81C, 0x06600203,
+ 0x81C, 0x05620203,
+ 0x81C, 0x04640203,
+ 0x81C, 0x03660203,
+ 0x81C, 0x02680203,
+ 0x81C, 0x016A0203,
+ 0x81C, 0x016C0203,
+ 0x81C, 0x016E0203,
+ 0x81C, 0x01700203,
+ 0x81C, 0x01720203,
+ 0x81C, 0x01740203,
+ 0x81C, 0x01760203,
+ 0x81C, 0x01780203,
+ 0x81C, 0x017A0203,
+ 0x81C, 0x017C0203,
+ 0x81C, 0x017E0203,
+ 0x90000007, 0x00000000, 0x40000000, 0x00000000,
+ 0x81C, 0xFF000203,
+ 0x81C, 0xFF020203,
+ 0x81C, 0xFE040203,
+ 0x81C, 0xFD060203,
+ 0x81C, 0xFC080203,
+ 0x81C, 0xFB0A0203,
+ 0x81C, 0xFA0C0203,
+ 0x81C, 0xF90E0203,
+ 0x81C, 0xF8100203,
+ 0x81C, 0xF7120203,
+ 0x81C, 0xF6140203,
+ 0x81C, 0xF5160203,
+ 0x81C, 0xF4180203,
+ 0x81C, 0xF31A0203,
+ 0x81C, 0xF21C0203,
+ 0x81C, 0xF11E0203,
+ 0x81C, 0xF0200203,
+ 0x81C, 0xEF220203,
+ 0x81C, 0xEE240203,
+ 0x81C, 0xED260203,
+ 0x81C, 0xEC280203,
+ 0x81C, 0xEB2A0203,
+ 0x81C, 0xEA2C0203,
+ 0x81C, 0xE92E0203,
+ 0x81C, 0xE8300203,
+ 0x81C, 0xE7320203,
+ 0x81C, 0xE6340203,
+ 0x81C, 0xE5360203,
+ 0x81C, 0xE4380203,
+ 0x81C, 0xE33A0203,
+ 0x81C, 0xE23C0203,
+ 0x81C, 0xE13E0203,
+ 0x81C, 0xA4400203,
+ 0x81C, 0xA3420203,
+ 0x81C, 0xA2440203,
+ 0x81C, 0xA1460203,
+ 0x81C, 0x84480203,
+ 0x81C, 0x834A0203,
+ 0x81C, 0x824C0203,
+ 0x81C, 0x814E0203,
+ 0x81C, 0x64500203,
+ 0x81C, 0x63520203,
+ 0x81C, 0x62540203,
+ 0x81C, 0x61560203,
+ 0x81C, 0x45580203,
+ 0x81C, 0x445A0203,
+ 0x81C, 0x435C0203,
+ 0x81C, 0x425E0203,
+ 0x81C, 0x24600203,
+ 0x81C, 0x23620203,
+ 0x81C, 0x07640203,
+ 0x81C, 0x06660203,
+ 0x81C, 0x05680203,
+ 0x81C, 0x046A0203,
+ 0x81C, 0x036C0203,
+ 0x81C, 0x026E0203,
+ 0x81C, 0x01700203,
+ 0x81C, 0x01720203,
+ 0x81C, 0x01740203,
+ 0x81C, 0x01760203,
+ 0x81C, 0x01780203,
+ 0x81C, 0x017A0203,
+ 0x81C, 0x017C0203,
+ 0x81C, 0x017E0203,
+ 0x90000008, 0x00000000, 0x40000000, 0x00000000,
+ 0x81C, 0xF7000203,
+ 0x81C, 0xF6020203,
+ 0x81C, 0xF5040203,
+ 0x81C, 0xF4060203,
+ 0x81C, 0xF3080203,
+ 0x81C, 0xF20A0203,
+ 0x81C, 0xF10C0203,
+ 0x81C, 0xF00E0203,
+ 0x81C, 0xEF100203,
+ 0x81C, 0xEE120203,
+ 0x81C, 0xED140203,
+ 0x81C, 0xEC160203,
+ 0x81C, 0xEB180203,
+ 0x81C, 0xEA1A0203,
+ 0x81C, 0xE91C0203,
+ 0x81C, 0xE81E0203,
+ 0x81C, 0xE7200203,
+ 0x81C, 0xE6220203,
+ 0x81C, 0xE5240203,
+ 0x81C, 0xE4260203,
+ 0x81C, 0xE3280203,
+ 0x81C, 0xE22A0203,
+ 0x81C, 0xA62C0203,
+ 0x81C, 0xA52E0203,
+ 0x81C, 0xA4300203,
+ 0x81C, 0xA3320203,
+ 0x81C, 0xA2340203,
+ 0x81C, 0xA1360203,
+ 0x81C, 0x86380203,
+ 0x81C, 0x853A0203,
+ 0x81C, 0x843C0203,
+ 0x81C, 0x833E0203,
+ 0x81C, 0x65400203,
+ 0x81C, 0x64420203,
+ 0x81C, 0x63440203,
+ 0x81C, 0x46460203,
+ 0x81C, 0x45480203,
+ 0x81C, 0x444A0203,
+ 0x81C, 0x434C0203,
+ 0x81C, 0x264E0203,
+ 0x81C, 0x25500203,
+ 0x81C, 0x24520203,
+ 0x81C, 0x08540203,
+ 0x81C, 0x07560203,
+ 0x81C, 0x06580203,
+ 0x81C, 0x055A0203,
+ 0x81C, 0x045C0203,
+ 0x81C, 0x035E0203,
+ 0x81C, 0x02600203,
+ 0x81C, 0x01620203,
+ 0x81C, 0x01640203,
+ 0x81C, 0x01660203,
+ 0x81C, 0x01680203,
+ 0x81C, 0x016A0203,
+ 0x81C, 0x016C0203,
+ 0x81C, 0x016E0203,
+ 0x81C, 0x01700203,
+ 0x81C, 0x01720203,
+ 0x81C, 0x01740203,
+ 0x81C, 0x01760203,
+ 0x81C, 0x01780203,
+ 0x81C, 0x017A0203,
+ 0x81C, 0x017C0203,
+ 0x81C, 0x017E0203,
+ 0x90000009, 0x00000000, 0x40000000, 0x00000000,
+ 0x81C, 0xFF000203,
+ 0x81C, 0xFF020203,
+ 0x81C, 0xFE040203,
+ 0x81C, 0xFD060203,
+ 0x81C, 0xFC080203,
+ 0x81C, 0xFB0A0203,
+ 0x81C, 0xFA0C0203,
+ 0x81C, 0xF90E0203,
+ 0x81C, 0xF8100203,
+ 0x81C, 0xF7120203,
+ 0x81C, 0xF6140203,
+ 0x81C, 0xF5160203,
+ 0x81C, 0xF4180203,
+ 0x81C, 0xF31A0203,
+ 0x81C, 0xF21C0203,
+ 0x81C, 0xF11E0203,
+ 0x81C, 0xF0200203,
+ 0x81C, 0xEF220203,
+ 0x81C, 0xEE240203,
+ 0x81C, 0xED260203,
+ 0x81C, 0xEC280203,
+ 0x81C, 0xEB2A0203,
+ 0x81C, 0xEA2C0203,
+ 0x81C, 0xE92E0203,
+ 0x81C, 0xE8300203,
+ 0x81C, 0xE7320203,
+ 0x81C, 0xE6340203,
+ 0x81C, 0xE5360203,
+ 0x81C, 0xE4380203,
+ 0x81C, 0xE33A0203,
+ 0x81C, 0xE23C0203,
+ 0x81C, 0xE13E0203,
+ 0x81C, 0xA4400203,
+ 0x81C, 0xA3420203,
+ 0x81C, 0xA2440203,
+ 0x81C, 0xA1460203,
+ 0x81C, 0x84480203,
+ 0x81C, 0x834A0203,
+ 0x81C, 0x824C0203,
+ 0x81C, 0x814E0203,
+ 0x81C, 0x64500203,
+ 0x81C, 0x63520203,
+ 0x81C, 0x62540203,
+ 0x81C, 0x61560203,
+ 0x81C, 0x45580203,
+ 0x81C, 0x445A0203,
+ 0x81C, 0x435C0203,
+ 0x81C, 0x425E0203,
+ 0x81C, 0x24600203,
+ 0x81C, 0x23620203,
+ 0x81C, 0x07640203,
+ 0x81C, 0x06660203,
+ 0x81C, 0x05680203,
+ 0x81C, 0x046A0203,
+ 0x81C, 0x036C0203,
+ 0x81C, 0x026E0203,
+ 0x81C, 0x01700203,
+ 0x81C, 0x01720203,
+ 0x81C, 0x01740203,
+ 0x81C, 0x01760203,
+ 0x81C, 0x01780203,
+ 0x81C, 0x017A0203,
+ 0x81C, 0x017C0203,
+ 0x81C, 0x017E0203,
+ 0x9000000a, 0x00000000, 0x40000000, 0x00000000,
+ 0x81C, 0xF8000203,
+ 0x81C, 0xF7020203,
+ 0x81C, 0xF6040203,
+ 0x81C, 0xF5060203,
+ 0x81C, 0xF4080203,
+ 0x81C, 0xF30A0203,
+ 0x81C, 0xF20C0203,
+ 0x81C, 0xF10E0203,
+ 0x81C, 0xF0100203,
+ 0x81C, 0xEF120203,
+ 0x81C, 0xEE140203,
+ 0x81C, 0xED160203,
+ 0x81C, 0xEC180203,
+ 0x81C, 0xEB1A0203,
+ 0x81C, 0xEA1C0203,
+ 0x81C, 0xE91E0203,
+ 0x81C, 0xE8200203,
+ 0x81C, 0xE7220203,
+ 0x81C, 0xE6240203,
+ 0x81C, 0xE5260203,
+ 0x81C, 0xE4280203,
+ 0x81C, 0xE32A0203,
+ 0x81C, 0xE22C0203,
+ 0x81C, 0xE12E0203,
+ 0x81C, 0xA6300203,
+ 0x81C, 0xA5320203,
+ 0x81C, 0xA4340203,
+ 0x81C, 0xA3360203,
+ 0x81C, 0xA2380203,
+ 0x81C, 0xA13A0203,
+ 0x81C, 0x843C0203,
+ 0x81C, 0x833E0203,
+ 0x81C, 0x82400203,
+ 0x81C, 0x81420203,
+ 0x81C, 0x64440203,
+ 0x81C, 0x63460203,
+ 0x81C, 0x62480203,
+ 0x81C, 0x614A0203,
+ 0x81C, 0x444C0203,
+ 0x81C, 0x434E0203,
+ 0x81C, 0x42500203,
+ 0x81C, 0x41520203,
+ 0x81C, 0x25540203,
+ 0x81C, 0x24560203,
+ 0x81C, 0x23580203,
+ 0x81C, 0x065A0203,
+ 0x81C, 0x055C0203,
+ 0x81C, 0x045E0203,
+ 0x81C, 0x03600203,
+ 0x81C, 0x02620203,
+ 0x81C, 0x01640203,
+ 0x81C, 0x01660203,
+ 0x81C, 0x01680203,
+ 0x81C, 0x016A0203,
+ 0x81C, 0x016C0203,
+ 0x81C, 0x016E0203,
+ 0x81C, 0x01700203,
+ 0x81C, 0x01720203,
+ 0x81C, 0x01740203,
+ 0x81C, 0x01760203,
+ 0x81C, 0x01780203,
+ 0x81C, 0x017A0203,
+ 0x81C, 0x017C0203,
+ 0x81C, 0x017E0203,
+ 0x9000000b, 0x00000000, 0x40000000, 0x00000000,
+ 0x81C, 0xF9000203,
+ 0x81C, 0xF8020203,
+ 0x81C, 0xF7040203,
+ 0x81C, 0xF6060203,
+ 0x81C, 0xF5080203,
+ 0x81C, 0xF40A0203,
+ 0x81C, 0xF30C0203,
+ 0x81C, 0xF20E0203,
+ 0x81C, 0xF1100203,
+ 0x81C, 0xF0120203,
+ 0x81C, 0xEF140203,
+ 0x81C, 0xCE160203,
+ 0x81C, 0xCD180203,
+ 0x81C, 0xCC1A0203,
+ 0x81C, 0xCB1C0203,
+ 0x81C, 0xCA1E0203,
+ 0x81C, 0xC9200203,
+ 0x81C, 0xC8220203,
+ 0x81C, 0xC7240203,
+ 0x81C, 0xC6260203,
+ 0x81C, 0xC5280203,
+ 0x81C, 0xC42A0203,
+ 0x81C, 0xC32C0203,
+ 0x81C, 0xC22E0203,
+ 0x81C, 0xC1300203,
+ 0x81C, 0xA5320203,
+ 0x81C, 0xA4340203,
+ 0x81C, 0xA3360203,
+ 0x81C, 0xA2380203,
+ 0x81C, 0xA13A0203,
+ 0x81C, 0x853C0203,
+ 0x81C, 0x843E0203,
+ 0x81C, 0x83400203,
+ 0x81C, 0x82420203,
+ 0x81C, 0x81440203,
+ 0x81C, 0x64460203,
+ 0x81C, 0x63480203,
+ 0x81C, 0x624A0203,
+ 0x81C, 0x614C0203,
+ 0x81C, 0x444E0203,
+ 0x81C, 0x43500203,
+ 0x81C, 0x42520203,
+ 0x81C, 0x41540203,
+ 0x81C, 0x24560203,
+ 0x81C, 0x23580203,
+ 0x81C, 0x075A0203,
+ 0x81C, 0x065C0203,
+ 0x81C, 0x055E0203,
+ 0x81C, 0x04600203,
+ 0x81C, 0x03620203,
+ 0x81C, 0x02640203,
+ 0x81C, 0x01660203,
+ 0x81C, 0x01680203,
+ 0x81C, 0x016A0203,
+ 0x81C, 0x016C0203,
+ 0x81C, 0x016E0203,
+ 0x81C, 0x01700203,
+ 0x81C, 0x01720203,
+ 0x81C, 0x01740203,
+ 0x81C, 0x01760203,
+ 0x81C, 0x01780203,
+ 0x81C, 0x017A0203,
+ 0x81C, 0x017C0203,
+ 0x81C, 0x017E0203,
+ 0xA0000000, 0x00000000,
+ 0x81C, 0xFF000203,
+ 0x81C, 0xFF020203,
+ 0x81C, 0xFE040203,
+ 0x81C, 0xFD060203,
+ 0x81C, 0xFC080203,
+ 0x81C, 0xFB0A0203,
+ 0x81C, 0xFA0C0203,
+ 0x81C, 0xF90E0203,
+ 0x81C, 0xF8100203,
+ 0x81C, 0xF7120203,
+ 0x81C, 0xF6140203,
+ 0x81C, 0xF5160203,
+ 0x81C, 0xF4180203,
+ 0x81C, 0xF31A0203,
+ 0x81C, 0xF21C0203,
+ 0x81C, 0xF11E0203,
+ 0x81C, 0xF0200203,
+ 0x81C, 0xEF220203,
+ 0x81C, 0xEE240203,
+ 0x81C, 0xED260203,
+ 0x81C, 0xEC280203,
+ 0x81C, 0xEB2A0203,
+ 0x81C, 0xEA2C0203,
+ 0x81C, 0xE92E0203,
+ 0x81C, 0xE8300203,
+ 0x81C, 0xE7320203,
+ 0x81C, 0xE6340203,
+ 0x81C, 0xE5360203,
+ 0x81C, 0xE4380203,
+ 0x81C, 0xE33A0203,
+ 0x81C, 0xE23C0203,
+ 0x81C, 0xE13E0203,
+ 0x81C, 0xA4400203,
+ 0x81C, 0xA3420203,
+ 0x81C, 0xA2440203,
+ 0x81C, 0xA1460203,
+ 0x81C, 0x85480203,
+ 0x81C, 0x844A0203,
+ 0x81C, 0x834C0203,
+ 0x81C, 0x824E0203,
+ 0x81C, 0x81500203,
+ 0x81C, 0x64520203,
+ 0x81C, 0x63540203,
+ 0x81C, 0x62560203,
+ 0x81C, 0x61580203,
+ 0x81C, 0x445A0203,
+ 0x81C, 0x435C0203,
+ 0x81C, 0x425E0203,
+ 0x81C, 0x25600203,
+ 0x81C, 0x24620203,
+ 0x81C, 0x06640203,
+ 0x81C, 0x05660203,
+ 0x81C, 0x04680203,
+ 0x81C, 0x036A0203,
+ 0x81C, 0x026C0203,
+ 0x81C, 0x016E0203,
+ 0x81C, 0x01700203,
+ 0x81C, 0x01720203,
+ 0x81C, 0x01740203,
+ 0x81C, 0x01760203,
+ 0x81C, 0x01780203,
+ 0x81C, 0x017A0203,
+ 0x81C, 0x017C0203,
+ 0x81C, 0x017E0203,
+ 0xB0000000, 0x00000000,
+ 0x80000001, 0x00000000, 0x40000000, 0x00000000,
+ 0x81C, 0xF8000303,
+ 0x81C, 0xF7020303,
+ 0x81C, 0xF6040303,
+ 0x81C, 0xF5060303,
+ 0x81C, 0xF4080303,
+ 0x81C, 0xF30A0303,
+ 0x81C, 0xF20C0303,
+ 0x81C, 0xF10E0303,
+ 0x81C, 0xF0100303,
+ 0x81C, 0xEF120303,
+ 0x81C, 0xEE140303,
+ 0x81C, 0xED160303,
+ 0x81C, 0xEC180303,
+ 0x81C, 0xEB1A0303,
+ 0x81C, 0xEA1C0303,
+ 0x81C, 0xE91E0303,
+ 0x81C, 0xE8200303,
+ 0x81C, 0xE7220303,
+ 0x81C, 0xE6240303,
+ 0x81C, 0xE5260303,
+ 0x81C, 0xE4280303,
+ 0x81C, 0xE32A0303,
+ 0x81C, 0xE22C0303,
+ 0x81C, 0xE12E0303,
+ 0x81C, 0xA6300303,
+ 0x81C, 0xA5320303,
+ 0x81C, 0xA4340303,
+ 0x81C, 0xA3360303,
+ 0x81C, 0xA2380303,
+ 0x81C, 0xA13A0303,
+ 0x81C, 0x843C0303,
+ 0x81C, 0x833E0303,
+ 0x81C, 0x82400303,
+ 0x81C, 0x81420303,
+ 0x81C, 0x64440303,
+ 0x81C, 0x63460303,
+ 0x81C, 0x62480303,
+ 0x81C, 0x614A0303,
+ 0x81C, 0x454C0303,
+ 0x81C, 0x444E0303,
+ 0x81C, 0x43500303,
+ 0x81C, 0x42520303,
+ 0x81C, 0x41540303,
+ 0x81C, 0x24560303,
+ 0x81C, 0x23580303,
+ 0x81C, 0x065A0303,
+ 0x81C, 0x055C0303,
+ 0x81C, 0x045E0303,
+ 0x81C, 0x03600303,
+ 0x81C, 0x02620303,
+ 0x81C, 0x01640303,
+ 0x81C, 0x01660303,
+ 0x81C, 0x01680303,
+ 0x81C, 0x016A0303,
+ 0x81C, 0x016C0303,
+ 0x81C, 0x016E0303,
+ 0x81C, 0x01700303,
+ 0x81C, 0x01720303,
+ 0x81C, 0x01740303,
+ 0x81C, 0x01760303,
+ 0x81C, 0x01780303,
+ 0x81C, 0x017A0303,
+ 0x81C, 0x017C0303,
+ 0x81C, 0x017E0303,
+ 0x90000002, 0x00000000, 0x40000000, 0x00000000,
+ 0x81C, 0xF7000303,
+ 0x81C, 0xF6020303,
+ 0x81C, 0xF5040303,
+ 0x81C, 0xF4060303,
+ 0x81C, 0xF3080303,
+ 0x81C, 0xF20A0303,
+ 0x81C, 0xF10C0303,
+ 0x81C, 0xF00E0303,
+ 0x81C, 0xEF100303,
+ 0x81C, 0xEE120303,
+ 0x81C, 0xED140303,
+ 0x81C, 0xEC160303,
+ 0x81C, 0xEB180303,
+ 0x81C, 0xEA1A0303,
+ 0x81C, 0xE91C0303,
+ 0x81C, 0xE81E0303,
+ 0x81C, 0xE7200303,
+ 0x81C, 0xE6220303,
+ 0x81C, 0xE5240303,
+ 0x81C, 0xE4260303,
+ 0x81C, 0xE3280303,
+ 0x81C, 0xC32A0303,
+ 0x81C, 0xC22C0303,
+ 0x81C, 0xC12E0303,
+ 0x81C, 0xA5300303,
+ 0x81C, 0xA4320303,
+ 0x81C, 0xA3340303,
+ 0x81C, 0xA2360303,
+ 0x81C, 0xA1380303,
+ 0x81C, 0x853A0303,
+ 0x81C, 0x843C0303,
+ 0x81C, 0x833E0303,
+ 0x81C, 0x82400303,
+ 0x81C, 0x81420303,
+ 0x81C, 0x64440303,
+ 0x81C, 0x63460303,
+ 0x81C, 0x62480303,
+ 0x81C, 0x614A0303,
+ 0x81C, 0x454C0303,
+ 0x81C, 0x444E0303,
+ 0x81C, 0x43500303,
+ 0x81C, 0x25520303,
+ 0x81C, 0x24540303,
+ 0x81C, 0x23560303,
+ 0x81C, 0x06580303,
+ 0x81C, 0x055A0303,
+ 0x81C, 0x045C0303,
+ 0x81C, 0x035E0303,
+ 0x81C, 0x02600303,
+ 0x81C, 0x01620303,
+ 0x81C, 0x01640303,
+ 0x81C, 0x01660303,
+ 0x81C, 0x01680303,
+ 0x81C, 0x016A0303,
+ 0x81C, 0x016C0303,
+ 0x81C, 0x016E0303,
+ 0x81C, 0x01700303,
+ 0x81C, 0x01720303,
+ 0x81C, 0x01740303,
+ 0x81C, 0x01760303,
+ 0x81C, 0x01780303,
+ 0x81C, 0x017A0303,
+ 0x81C, 0x017C0303,
+ 0x81C, 0x017E0303,
+ 0x90000003, 0x00000000, 0x40000000, 0x00000000,
+ 0x81C, 0xF9000303,
+ 0x81C, 0xF8020303,
+ 0x81C, 0xF7040303,
+ 0x81C, 0xF6060303,
+ 0x81C, 0xF5080303,
+ 0x81C, 0xF40A0303,
+ 0x81C, 0xF30C0303,
+ 0x81C, 0xF20E0303,
+ 0x81C, 0xF1100303,
+ 0x81C, 0xF0120303,
+ 0x81C, 0xEF140303,
+ 0x81C, 0xEE160303,
+ 0x81C, 0xED180303,
+ 0x81C, 0xEC1A0303,
+ 0x81C, 0xEB1C0303,
+ 0x81C, 0xEA1E0303,
+ 0x81C, 0xE9200303,
+ 0x81C, 0xE8220303,
+ 0x81C, 0xE7240303,
+ 0x81C, 0xE6260303,
+ 0x81C, 0xE5280303,
+ 0x81C, 0xE42A0303,
+ 0x81C, 0xE32C0303,
+ 0x81C, 0xE22E0303,
+ 0x81C, 0xE1300303,
+ 0x81C, 0xA4320303,
+ 0x81C, 0xA3340303,
+ 0x81C, 0xA2360303,
+ 0x81C, 0xA1380303,
+ 0x81C, 0x853A0303,
+ 0x81C, 0x843C0303,
+ 0x81C, 0x833E0303,
+ 0x81C, 0x82400303,
+ 0x81C, 0x81420303,
+ 0x81C, 0x64440303,
+ 0x81C, 0x63460303,
+ 0x81C, 0x62480303,
+ 0x81C, 0x614A0303,
+ 0x81C, 0x444C0303,
+ 0x81C, 0x434E0303,
+ 0x81C, 0x42500303,
+ 0x81C, 0x25520303,
+ 0x81C, 0x24540303,
+ 0x81C, 0x23560303,
+ 0x81C, 0x07580303,
+ 0x81C, 0x065A0303,
+ 0x81C, 0x055C0303,
+ 0x81C, 0x045E0303,
+ 0x81C, 0x03600303,
+ 0x81C, 0x02620303,
+ 0x81C, 0x01640303,
+ 0x81C, 0x01660303,
+ 0x81C, 0x01680303,
+ 0x81C, 0x016A0303,
+ 0x81C, 0x016C0303,
+ 0x81C, 0x016E0303,
+ 0x81C, 0x01700303,
+ 0x81C, 0x01720303,
+ 0x81C, 0x01740303,
+ 0x81C, 0x01760303,
+ 0x81C, 0x01780303,
+ 0x81C, 0x017A0303,
+ 0x81C, 0x017C0303,
+ 0x81C, 0x017E0303,
+ 0x90000004, 0x00000000, 0x40000000, 0x00000000,
+ 0x81C, 0xF7000303,
+ 0x81C, 0xF6020303,
+ 0x81C, 0xF5040303,
+ 0x81C, 0xF4060303,
+ 0x81C, 0xF3080303,
+ 0x81C, 0xF20A0303,
+ 0x81C, 0xF10C0303,
+ 0x81C, 0xF00E0303,
+ 0x81C, 0xEF100303,
+ 0x81C, 0xEE120303,
+ 0x81C, 0xED140303,
+ 0x81C, 0xEC160303,
+ 0x81C, 0xEB180303,
+ 0x81C, 0xEA1A0303,
+ 0x81C, 0xE91C0303,
+ 0x81C, 0xE81E0303,
+ 0x81C, 0xE7200303,
+ 0x81C, 0xE6220303,
+ 0x81C, 0xE5240303,
+ 0x81C, 0xE4260303,
+ 0x81C, 0xE3280303,
+ 0x81C, 0xE22A0303,
+ 0x81C, 0xE12C0303,
+ 0x81C, 0xA72E0303,
+ 0x81C, 0xA6300303,
+ 0x81C, 0xA5320303,
+ 0x81C, 0xA4340303,
+ 0x81C, 0xA3360303,
+ 0x81C, 0xA2380303,
+ 0x81C, 0xA13A0303,
+ 0x81C, 0x843C0303,
+ 0x81C, 0x833E0303,
+ 0x81C, 0x82400303,
+ 0x81C, 0x81420303,
+ 0x81C, 0x64440303,
+ 0x81C, 0x63460303,
+ 0x81C, 0x62480303,
+ 0x81C, 0x614A0303,
+ 0x81C, 0x454C0303,
+ 0x81C, 0x444E0303,
+ 0x81C, 0x43500303,
+ 0x81C, 0x42520303,
+ 0x81C, 0x41540303,
+ 0x81C, 0x24560303,
+ 0x81C, 0x23580303,
+ 0x81C, 0x065A0303,
+ 0x81C, 0x055C0303,
+ 0x81C, 0x045E0303,
+ 0x81C, 0x03600303,
+ 0x81C, 0x02620303,
+ 0x81C, 0x01640303,
+ 0x81C, 0x01660303,
+ 0x81C, 0x01680303,
+ 0x81C, 0x016A0303,
+ 0x81C, 0x016C0303,
+ 0x81C, 0x016E0303,
+ 0x81C, 0x01700303,
+ 0x81C, 0x01720303,
+ 0x81C, 0x01740303,
+ 0x81C, 0x01760303,
+ 0x81C, 0x01780303,
+ 0x81C, 0x017A0303,
+ 0x81C, 0x017C0303,
+ 0x81C, 0x017E0303,
+ 0x90000005, 0x00000000, 0x40000000, 0x00000000,
+ 0x81C, 0xFB000303,
+ 0x81C, 0xFA020303,
+ 0x81C, 0xF9040303,
+ 0x81C, 0xF8060303,
+ 0x81C, 0xF7080303,
+ 0x81C, 0xF60A0303,
+ 0x81C, 0xF50C0303,
+ 0x81C, 0xF40E0303,
+ 0x81C, 0xF3100303,
+ 0x81C, 0xF2120303,
+ 0x81C, 0xF1140303,
+ 0x81C, 0xF0160303,
+ 0x81C, 0xEF180303,
+ 0x81C, 0xEE1A0303,
+ 0x81C, 0xED1C0303,
+ 0x81C, 0xEC1E0303,
+ 0x81C, 0xEB200303,
+ 0x81C, 0xEA220303,
+ 0x81C, 0xE9240303,
+ 0x81C, 0xE8260303,
+ 0x81C, 0xE7280303,
+ 0x81C, 0xE62A0303,
+ 0x81C, 0xE52C0303,
+ 0x81C, 0xE42E0303,
+ 0x81C, 0xE3300303,
+ 0x81C, 0xE2320303,
+ 0x81C, 0xE1340303,
+ 0x81C, 0xC2360303,
+ 0x81C, 0xC1380303,
+ 0x81C, 0xA33A0303,
+ 0x81C, 0xA23C0303,
+ 0x81C, 0x853E0303,
+ 0x81C, 0x84400303,
+ 0x81C, 0x83420303,
+ 0x81C, 0x66440303,
+ 0x81C, 0x65460303,
+ 0x81C, 0x64480303,
+ 0x81C, 0x634A0303,
+ 0x81C, 0x624C0303,
+ 0x81C, 0x614E0303,
+ 0x81C, 0x45500303,
+ 0x81C, 0x44520303,
+ 0x81C, 0x43540303,
+ 0x81C, 0x42560303,
+ 0x81C, 0x25580303,
+ 0x81C, 0x245A0303,
+ 0x81C, 0x235C0303,
+ 0x81C, 0x065E0303,
+ 0x81C, 0x05600303,
+ 0x81C, 0x04620303,
+ 0x81C, 0x03640303,
+ 0x81C, 0x02660303,
+ 0x81C, 0x01680303,
+ 0x81C, 0x016A0303,
+ 0x81C, 0x016C0303,
+ 0x81C, 0x016E0303,
+ 0x81C, 0x01700303,
+ 0x81C, 0x01720303,
+ 0x81C, 0x01740303,
+ 0x81C, 0x01760303,
+ 0x81C, 0x01780303,
+ 0x81C, 0x017A0303,
+ 0x81C, 0x017C0303,
+ 0x81C, 0x017E0303,
+ 0x90000006, 0x00000000, 0x40000000, 0x00000000,
+ 0x81C, 0xF9000303,
+ 0x81C, 0xF8020303,
+ 0x81C, 0xF7040303,
+ 0x81C, 0xF6060303,
+ 0x81C, 0xF5080303,
+ 0x81C, 0xF40A0303,
+ 0x81C, 0xF30C0303,
+ 0x81C, 0xF20E0303,
+ 0x81C, 0xF1100303,
+ 0x81C, 0xF0120303,
+ 0x81C, 0xEF140303,
+ 0x81C, 0xEE160303,
+ 0x81C, 0xED180303,
+ 0x81C, 0xEC1A0303,
+ 0x81C, 0xEB1C0303,
+ 0x81C, 0xEA1E0303,
+ 0x81C, 0xE9200303,
+ 0x81C, 0xE8220303,
+ 0x81C, 0xE7240303,
+ 0x81C, 0xE6260303,
+ 0x81C, 0xE5280303,
+ 0x81C, 0xE42A0303,
+ 0x81C, 0xE32C0303,
+ 0x81C, 0xE22E0303,
+ 0x81C, 0xE1300303,
+ 0x81C, 0xA6320303,
+ 0x81C, 0xA5340303,
+ 0x81C, 0xA4360303,
+ 0x81C, 0xA3380303,
+ 0x81C, 0xA23A0303,
+ 0x81C, 0xA13C0303,
+ 0x81C, 0x853E0303,
+ 0x81C, 0x84400303,
+ 0x81C, 0x83420303,
+ 0x81C, 0x82440303,
+ 0x81C, 0x81460303,
+ 0x81C, 0x64480303,
+ 0x81C, 0x634A0303,
+ 0x81C, 0x624C0303,
+ 0x81C, 0x614E0303,
+ 0x81C, 0x44500303,
+ 0x81C, 0x43520303,
+ 0x81C, 0x42540303,
+ 0x81C, 0x41560303,
+ 0x81C, 0x25580303,
+ 0x81C, 0x245A0303,
+ 0x81C, 0x235C0303,
+ 0x81C, 0x055E0303,
+ 0x81C, 0x04600303,
+ 0x81C, 0x03620303,
+ 0x81C, 0x02640303,
+ 0x81C, 0x01660303,
+ 0x81C, 0x01680303,
+ 0x81C, 0x016A0303,
+ 0x81C, 0x016C0303,
+ 0x81C, 0x016E0303,
+ 0x81C, 0x01700303,
+ 0x81C, 0x01720303,
+ 0x81C, 0x01740303,
+ 0x81C, 0x01760303,
+ 0x81C, 0x01780303,
+ 0x81C, 0x017A0303,
+ 0x81C, 0x017C0303,
+ 0x81C, 0x017E0303,
+ 0x90000007, 0x00000000, 0x40000000, 0x00000000,
+ 0x81C, 0xFE000303,
+ 0x81C, 0xFD020303,
+ 0x81C, 0xFC040303,
+ 0x81C, 0xFB060303,
+ 0x81C, 0xFA080303,
+ 0x81C, 0xF90A0303,
+ 0x81C, 0xF80C0303,
+ 0x81C, 0xF70E0303,
+ 0x81C, 0xF6100303,
+ 0x81C, 0xF5120303,
+ 0x81C, 0xF4140303,
+ 0x81C, 0xF3160303,
+ 0x81C, 0xF2180303,
+ 0x81C, 0xF11A0303,
+ 0x81C, 0xF01C0303,
+ 0x81C, 0xEF1E0303,
+ 0x81C, 0xEE200303,
+ 0x81C, 0xED220303,
+ 0x81C, 0xEC240303,
+ 0x81C, 0xEB260303,
+ 0x81C, 0xEA280303,
+ 0x81C, 0xE92A0303,
+ 0x81C, 0xE82C0303,
+ 0x81C, 0xE72E0303,
+ 0x81C, 0xE6300303,
+ 0x81C, 0xE5320303,
+ 0x81C, 0xE4340303,
+ 0x81C, 0xE3360303,
+ 0x81C, 0xC3380303,
+ 0x81C, 0xC23A0303,
+ 0x81C, 0xC13C0303,
+ 0x81C, 0xA43E0303,
+ 0x81C, 0xA3400303,
+ 0x81C, 0xA2420303,
+ 0x81C, 0xA1440303,
+ 0x81C, 0x85460303,
+ 0x81C, 0x84480303,
+ 0x81C, 0x834A0303,
+ 0x81C, 0x824C0303,
+ 0x81C, 0x814E0303,
+ 0x81C, 0x64500303,
+ 0x81C, 0x63520303,
+ 0x81C, 0x62540303,
+ 0x81C, 0x61560303,
+ 0x81C, 0x44580303,
+ 0x81C, 0x435A0303,
+ 0x81C, 0x425C0303,
+ 0x81C, 0x265E0303,
+ 0x81C, 0x25600303,
+ 0x81C, 0x24620303,
+ 0x81C, 0x06640303,
+ 0x81C, 0x05660303,
+ 0x81C, 0x04680303,
+ 0x81C, 0x036A0303,
+ 0x81C, 0x026C0303,
+ 0x81C, 0x016E0303,
+ 0x81C, 0x01700303,
+ 0x81C, 0x01720303,
+ 0x81C, 0x01740303,
+ 0x81C, 0x01760303,
+ 0x81C, 0x01780303,
+ 0x81C, 0x017A0303,
+ 0x81C, 0x017C0303,
+ 0x81C, 0x017E0303,
+ 0x90000008, 0x00000000, 0x40000000, 0x00000000,
+ 0x81C, 0xF7000303,
+ 0x81C, 0xF6020303,
+ 0x81C, 0xF5040303,
+ 0x81C, 0xF4060303,
+ 0x81C, 0xF3080303,
+ 0x81C, 0xF20A0303,
+ 0x81C, 0xF10C0303,
+ 0x81C, 0xF00E0303,
+ 0x81C, 0xEF100303,
+ 0x81C, 0xEE120303,
+ 0x81C, 0xED140303,
+ 0x81C, 0xEC160303,
+ 0x81C, 0xEB180303,
+ 0x81C, 0xEA1A0303,
+ 0x81C, 0xE91C0303,
+ 0x81C, 0xE81E0303,
+ 0x81C, 0xE7200303,
+ 0x81C, 0xE6220303,
+ 0x81C, 0xE5240303,
+ 0x81C, 0xE4260303,
+ 0x81C, 0xE3280303,
+ 0x81C, 0xE22A0303,
+ 0x81C, 0xA62C0303,
+ 0x81C, 0xA52E0303,
+ 0x81C, 0xA4300303,
+ 0x81C, 0xA3320303,
+ 0x81C, 0xA2340303,
+ 0x81C, 0x87360303,
+ 0x81C, 0x86380303,
+ 0x81C, 0x853A0303,
+ 0x81C, 0x843C0303,
+ 0x81C, 0x833E0303,
+ 0x81C, 0x66400303,
+ 0x81C, 0x65420303,
+ 0x81C, 0x64440303,
+ 0x81C, 0x45460303,
+ 0x81C, 0x44480303,
+ 0x81C, 0x434A0303,
+ 0x81C, 0x274C0303,
+ 0x81C, 0x264E0303,
+ 0x81C, 0x25500303,
+ 0x81C, 0x24520303,
+ 0x81C, 0x23540303,
+ 0x81C, 0x08560303,
+ 0x81C, 0x07580303,
+ 0x81C, 0x065A0303,
+ 0x81C, 0x055C0303,
+ 0x81C, 0x045E0303,
+ 0x81C, 0x03600303,
+ 0x81C, 0x02620303,
+ 0x81C, 0x01640303,
+ 0x81C, 0x01660303,
+ 0x81C, 0x01680303,
+ 0x81C, 0x016A0303,
+ 0x81C, 0x016C0303,
+ 0x81C, 0x016E0303,
+ 0x81C, 0x01700303,
+ 0x81C, 0x01720303,
+ 0x81C, 0x01740303,
+ 0x81C, 0x01760303,
+ 0x81C, 0x01780303,
+ 0x81C, 0x017A0303,
+ 0x81C, 0x017C0303,
+ 0x81C, 0x017E0303,
+ 0x90000009, 0x00000000, 0x40000000, 0x00000000,
+ 0x81C, 0xFE000303,
+ 0x81C, 0xFD020303,
+ 0x81C, 0xFC040303,
+ 0x81C, 0xFB060303,
+ 0x81C, 0xFA080303,
+ 0x81C, 0xF90A0303,
+ 0x81C, 0xF80C0303,
+ 0x81C, 0xF70E0303,
+ 0x81C, 0xF6100303,
+ 0x81C, 0xF5120303,
+ 0x81C, 0xF4140303,
+ 0x81C, 0xF3160303,
+ 0x81C, 0xF2180303,
+ 0x81C, 0xF11A0303,
+ 0x81C, 0xF01C0303,
+ 0x81C, 0xEF1E0303,
+ 0x81C, 0xEE200303,
+ 0x81C, 0xED220303,
+ 0x81C, 0xEC240303,
+ 0x81C, 0xEB260303,
+ 0x81C, 0xEA280303,
+ 0x81C, 0xE92A0303,
+ 0x81C, 0xE82C0303,
+ 0x81C, 0xE72E0303,
+ 0x81C, 0xE6300303,
+ 0x81C, 0xE5320303,
+ 0x81C, 0xE4340303,
+ 0x81C, 0xE3360303,
+ 0x81C, 0xC3380303,
+ 0x81C, 0xC23A0303,
+ 0x81C, 0xC13C0303,
+ 0x81C, 0xA43E0303,
+ 0x81C, 0xA3400303,
+ 0x81C, 0xA2420303,
+ 0x81C, 0xA1440303,
+ 0x81C, 0x85460303,
+ 0x81C, 0x84480303,
+ 0x81C, 0x834A0303,
+ 0x81C, 0x824C0303,
+ 0x81C, 0x814E0303,
+ 0x81C, 0x64500303,
+ 0x81C, 0x63520303,
+ 0x81C, 0x62540303,
+ 0x81C, 0x61560303,
+ 0x81C, 0x44580303,
+ 0x81C, 0x435A0303,
+ 0x81C, 0x425C0303,
+ 0x81C, 0x265E0303,
+ 0x81C, 0x25600303,
+ 0x81C, 0x24620303,
+ 0x81C, 0x06640303,
+ 0x81C, 0x05660303,
+ 0x81C, 0x04680303,
+ 0x81C, 0x036A0303,
+ 0x81C, 0x026C0303,
+ 0x81C, 0x016E0303,
+ 0x81C, 0x01700303,
+ 0x81C, 0x01720303,
+ 0x81C, 0x01740303,
+ 0x81C, 0x01760303,
+ 0x81C, 0x01780303,
+ 0x81C, 0x017A0303,
+ 0x81C, 0x017C0303,
+ 0x81C, 0x017E0303,
+ 0x9000000a, 0x00000000, 0x40000000, 0x00000000,
+ 0x81C, 0xF7000303,
+ 0x81C, 0xF6020303,
+ 0x81C, 0xF5040303,
+ 0x81C, 0xF4060303,
+ 0x81C, 0xF3080303,
+ 0x81C, 0xF20A0303,
+ 0x81C, 0xF10C0303,
+ 0x81C, 0xF00E0303,
+ 0x81C, 0xEF100303,
+ 0x81C, 0xEE120303,
+ 0x81C, 0xED140303,
+ 0x81C, 0xEC160303,
+ 0x81C, 0xEB180303,
+ 0x81C, 0xEA1A0303,
+ 0x81C, 0xE91C0303,
+ 0x81C, 0xE81E0303,
+ 0x81C, 0xE7200303,
+ 0x81C, 0xE6220303,
+ 0x81C, 0xE5240303,
+ 0x81C, 0xE4260303,
+ 0x81C, 0xE3280303,
+ 0x81C, 0xE22A0303,
+ 0x81C, 0xE12C0303,
+ 0x81C, 0xA72E0303,
+ 0x81C, 0xA6300303,
+ 0x81C, 0xA5320303,
+ 0x81C, 0xA4340303,
+ 0x81C, 0xA3360303,
+ 0x81C, 0xA2380303,
+ 0x81C, 0xA13A0303,
+ 0x81C, 0x843C0303,
+ 0x81C, 0x833E0303,
+ 0x81C, 0x82400303,
+ 0x81C, 0x81420303,
+ 0x81C, 0x64440303,
+ 0x81C, 0x63460303,
+ 0x81C, 0x62480303,
+ 0x81C, 0x614A0303,
+ 0x81C, 0x454C0303,
+ 0x81C, 0x444E0303,
+ 0x81C, 0x43500303,
+ 0x81C, 0x42520303,
+ 0x81C, 0x41540303,
+ 0x81C, 0x24560303,
+ 0x81C, 0x23580303,
+ 0x81C, 0x065A0303,
+ 0x81C, 0x055C0303,
+ 0x81C, 0x045E0303,
+ 0x81C, 0x03600303,
+ 0x81C, 0x02620303,
+ 0x81C, 0x01640303,
+ 0x81C, 0x01660303,
+ 0x81C, 0x01680303,
+ 0x81C, 0x016A0303,
+ 0x81C, 0x016C0303,
+ 0x81C, 0x016E0303,
+ 0x81C, 0x01700303,
+ 0x81C, 0x01720303,
+ 0x81C, 0x01740303,
+ 0x81C, 0x01760303,
+ 0x81C, 0x01780303,
+ 0x81C, 0x017A0303,
+ 0x81C, 0x017C0303,
+ 0x81C, 0x017E0303,
+ 0x9000000b, 0x00000000, 0x40000000, 0x00000000,
+ 0x81C, 0xF7000303,
+ 0x81C, 0xF6020303,
+ 0x81C, 0xF5040303,
+ 0x81C, 0xF4060303,
+ 0x81C, 0xF3080303,
+ 0x81C, 0xF20A0303,
+ 0x81C, 0xF10C0303,
+ 0x81C, 0xF00E0303,
+ 0x81C, 0xEF100303,
+ 0x81C, 0xEE120303,
+ 0x81C, 0xED140303,
+ 0x81C, 0xEC160303,
+ 0x81C, 0xEB180303,
+ 0x81C, 0xEA1A0303,
+ 0x81C, 0xAF1C0303,
+ 0x81C, 0xAE1E0303,
+ 0x81C, 0xAD200303,
+ 0x81C, 0xAC220303,
+ 0x81C, 0xAB240303,
+ 0x81C, 0xAA260303,
+ 0x81C, 0xC5280303,
+ 0x81C, 0xC42A0303,
+ 0x81C, 0xC32C0303,
+ 0x81C, 0xC22E0303,
+ 0x81C, 0xA5300303,
+ 0x81C, 0xA4320303,
+ 0x81C, 0xA3340303,
+ 0x81C, 0xA2360303,
+ 0x81C, 0xA1380303,
+ 0x81C, 0x853A0303,
+ 0x81C, 0x843C0303,
+ 0x81C, 0x833E0303,
+ 0x81C, 0x82400303,
+ 0x81C, 0x81420303,
+ 0x81C, 0x64440303,
+ 0x81C, 0x63460303,
+ 0x81C, 0x62480303,
+ 0x81C, 0x614A0303,
+ 0x81C, 0x444C0303,
+ 0x81C, 0x434E0303,
+ 0x81C, 0x42500303,
+ 0x81C, 0x41520303,
+ 0x81C, 0x25540303,
+ 0x81C, 0x24560303,
+ 0x81C, 0x06580303,
+ 0x81C, 0x055A0303,
+ 0x81C, 0x045C0303,
+ 0x81C, 0x035E0303,
+ 0x81C, 0x02600303,
+ 0x81C, 0x01620303,
+ 0x81C, 0x01640303,
+ 0x81C, 0x01660303,
+ 0x81C, 0x01680303,
+ 0x81C, 0x016A0303,
+ 0x81C, 0x016C0303,
+ 0x81C, 0x016E0303,
+ 0x81C, 0x01700303,
+ 0x81C, 0x01720303,
+ 0x81C, 0x01740303,
+ 0x81C, 0x01760303,
+ 0x81C, 0x01780303,
+ 0x81C, 0x017A0303,
+ 0x81C, 0x017C0303,
+ 0x81C, 0x017E0303,
+ 0xA0000000, 0x00000000,
+ 0x81C, 0xFD000303,
+ 0x81C, 0xFC020303,
+ 0x81C, 0xFB040303,
+ 0x81C, 0xFA060303,
+ 0x81C, 0xF9080303,
+ 0x81C, 0xF80A0303,
+ 0x81C, 0xF70C0303,
+ 0x81C, 0xF60E0303,
+ 0x81C, 0xF5100303,
+ 0x81C, 0xF4120303,
+ 0x81C, 0xF3140303,
+ 0x81C, 0xF2160303,
+ 0x81C, 0xF1180303,
+ 0x81C, 0xF01A0303,
+ 0x81C, 0xEF1C0303,
+ 0x81C, 0xEE1E0303,
+ 0x81C, 0xED200303,
+ 0x81C, 0xEC220303,
+ 0x81C, 0xEB240303,
+ 0x81C, 0xEA260303,
+ 0x81C, 0xE9280303,
+ 0x81C, 0xE82A0303,
+ 0x81C, 0xE72C0303,
+ 0x81C, 0xE62E0303,
+ 0x81C, 0xE5300303,
+ 0x81C, 0xE4320303,
+ 0x81C, 0xE3340303,
+ 0x81C, 0xE2360303,
+ 0x81C, 0xE1380303,
+ 0x81C, 0xA53A0303,
+ 0x81C, 0xA43C0303,
+ 0x81C, 0xA33E0303,
+ 0x81C, 0xA2400303,
+ 0x81C, 0xA1420303,
+ 0x81C, 0x87440303,
+ 0x81C, 0x86460303,
+ 0x81C, 0x85480303,
+ 0x81C, 0x844A0303,
+ 0x81C, 0x834C0303,
+ 0x81C, 0x824E0303,
+ 0x81C, 0x81500303,
+ 0x81C, 0x64520303,
+ 0x81C, 0x63540303,
+ 0x81C, 0x62560303,
+ 0x81C, 0x61580303,
+ 0x81C, 0x435A0303,
+ 0x81C, 0x425C0303,
+ 0x81C, 0x415E0303,
+ 0x81C, 0x07600303,
+ 0x81C, 0x06620303,
+ 0x81C, 0x05640303,
+ 0x81C, 0x04660303,
+ 0x81C, 0x03680303,
+ 0x81C, 0x026A0303,
+ 0x81C, 0x016C0303,
+ 0x81C, 0x016E0303,
+ 0x81C, 0x01700303,
+ 0x81C, 0x01720303,
+ 0x81C, 0x01740303,
+ 0x81C, 0x01760303,
+ 0x81C, 0x01780303,
+ 0x81C, 0x017A0303,
+ 0x81C, 0x017C0303,
+ 0x81C, 0x017E0303,
+ 0xB0000000, 0x00000000,
+ 0xC50, 0x00000022,
+ 0xC50, 0x00000020,
+ 0xE50, 0x00000022,
+ 0xE50, 0x00000020,
+ 0x1850, 0x00000022,
+ 0x1850, 0x00000020,
+ 0x1A50, 0x00000022,
+ 0x1A50, 0x00000020,
+};
+
+RTW_DECL_TABLE_PHY_COND(rtw8814a_agc, rtw_phy_cfg_agc);
+
+static const u32 rtw8814a_bb[] = {
+ 0x800, 0x9020D010,
+ 0x804, 0x08011280,
+ 0x808, 0x0E0282FF,
+ 0x80C, 0x1000002F,
+ 0x80000003, 0x00000000, 0x40000000, 0x00000000,
+ 0x810, 0x33303265,
+ 0xA0000000, 0x00000000,
+ 0x810, 0x33303265,
+ 0xB0000000, 0x00000000,
+ 0x814, 0x020C3D10,
+ 0x818, 0x04A10385,
+ 0x820, 0x00000000,
+ 0x824, 0x00033E40,
+ 0x828, 0x00000000,
+ 0x82C, 0x73985170,
+ 0x830, 0x79A0EA08,
+ 0x834, 0x042E708A,
+ 0x80000002, 0x00000000, 0x40000000, 0x00000000,
+ 0x838, 0x86667640,
+ 0x90000004, 0x00000000, 0x40000000, 0x00000000,
+ 0x838, 0x86667641,
+ 0x9000000a, 0x00000000, 0x40000000, 0x00000000,
+ 0x838, 0x86667641,
+ 0x9000000b, 0x00000000, 0x40000000, 0x00000000,
+ 0x838, 0x86667641,
+ 0xA0000000, 0x00000000,
+ 0x838, 0x86667640,
+ 0xB0000000, 0x00000000,
+ 0x83C, 0x9798B9B9,
+ 0x840, 0x17578F60,
+ 0x844, 0x4BBDFCDE,
+ 0x848, 0x5CD07F8B,
+ 0x84C, 0x6CFBF7B5,
+ 0x850, 0x28834706,
+ 0x854, 0x0001520C,
+ 0x858, 0x4060C000,
+ 0x85C, 0x74210368,
+ 0x860, 0x6929C321,
+ 0x864, 0x79727432,
+ 0x868, 0x8CA7A314,
+ 0x86C, 0x438C2878,
+ 0x870, 0x44444444,
+ 0x874, 0x21612C2E,
+ 0x878, 0x00003152,
+ 0x87C, 0x000FC000,
+ 0x8A0, 0x00000013,
+ 0x8A4, 0x7F7F7F7F,
+ 0x8A8, 0xA202033E,
+ 0x8AC, 0xF40F550A,
+ 0x8B0, 0x00000600,
+ 0x8B4, 0x000FC080,
+ 0x8B8, 0xEC0057FF,
+ 0x8BC, 0x8CA520C3,
+ 0x8C0, 0x3FF00020,
+ 0x8C4, 0x44C00000,
+ 0x80000009, 0x00000000, 0x40000000, 0x00000000,
+ 0x8C8, 0x80025969,
+ 0xA0000000, 0x00000000,
+ 0x8C8, 0x80025167,
+ 0xB0000000, 0x00000000,
+ 0x8CC, 0x08250492,
+ 0x8D0, 0x0000B800,
+ 0x8D4, 0x940008A0,
+ 0x8D8, 0x290B5612,
+ 0x8DC, 0x00000000,
+ 0x8E0, 0x32316407,
+ 0x8E4, 0x4A092925,
+ 0x8E8, 0xFFFFC42C,
+ 0x8EC, 0x99999999,
+ 0x8F0, 0x00009999,
+ 0x8F4, 0x00F80FA1,
+ 0x8F8, 0x400082C0,
+ 0x8FC, 0x00000000,
+ 0x900, 0x00400700,
+ 0x90C, 0x09004000,
+ 0x910, 0x0000FC00,
+ 0x914, 0xD6400404,
+ 0x918, 0x1C1028C0,
+ 0x91C, 0x64B11A1C,
+ 0x920, 0xE0767233,
+ 0x924, 0x055AA500,
+ 0x928, 0x4AB0E4E4,
+ 0x92C, 0xFFFE0000,
+ 0x930, 0xFFFFFFFE,
+ 0x934, 0x001FFFFF,
+ 0x938, 0x00008400,
+ 0x93C, 0x932C0642,
+ 0x940, 0x093E9360,
+ 0x944, 0x08000000,
+ 0x948, 0x04000000,
+ 0x950, 0x02010080,
+ 0x954, 0x86510080,
+ 0x960, 0x00000000,
+ 0x964, 0x00000000,
+ 0x968, 0x00000000,
+ 0x96C, 0x00000000,
+ 0x970, 0x801FFFFF,
+ 0x978, 0x00000000,
+ 0x97C, 0x00000000,
+ 0x980, 0x00000000,
+ 0x984, 0x00000000,
+ 0x988, 0x00000000,
+ 0x98C, 0x03440000,
+ 0x990, 0x27100000,
+ 0x994, 0xFFFF0100,
+ 0x998, 0xFFFFFF5C,
+ 0x99C, 0xFFFFFFFF,
+ 0x9A0, 0x000000FF,
+ 0x9A4, 0x00080080,
+ 0x9A8, 0x0C2F0000,
+ 0x9AC, 0x00560000,
+ 0x9B0, 0x81081008,
+ 0x9B4, 0x00000000,
+ 0x9B8, 0x01081008,
+ 0x9BC, 0x01081008,
+ 0x9D0, 0x00000000,
+ 0x9D4, 0x00000000,
+ 0x9D8, 0x00000000,
+ 0x9DC, 0x00000000,
+ 0x9E4, 0x00000002,
+ 0x9E8, 0x000022D5,
+ 0x9FC, 0xEFFFF7FF,
+ 0xB00, 0xE3100000,
+ 0xB04, 0x0000B000,
+ 0xB0C, 0x31EAA006,
+ 0xB5C, 0x41CFFFFF,
+ 0xC00, 0x00000007,
+ 0xC04, 0x00042020,
+ 0xC08, 0x80410231,
+ 0xC0C, 0x00000000,
+ 0xC10, 0x00000100,
+ 0xC14, 0x01000000,
+ 0xC1C, 0x40000053,
+ 0xC50, 0x00000020,
+ 0xC54, 0x00000000,
+ 0x80000002, 0x00000000, 0x40000000, 0x00000000,
+ 0xC58, 0x3C0A0C14,
+ 0x90000004, 0x00000000, 0x40000000, 0x00000000,
+ 0xC58, 0x3C0A0C14,
+ 0x9000000a, 0x00000000, 0x40000000, 0x00000000,
+ 0xC58, 0x3C0A0C14,
+ 0x9000000b, 0x00000000, 0x40000000, 0x00000000,
+ 0xC58, 0x3C0A0C14,
+ 0xA0000000, 0x00000000,
+ 0xC58, 0x3C020C14,
+ 0xB0000000, 0x00000000,
+ 0xC5C, 0x0D000058,
+ 0xC60, 0x1B800000,
+ 0xC60, 0x0B800001,
+ 0xC60, 0x05800002,
+ 0xC60, 0x07800003,
+ 0xC60, 0x1A800004,
+ 0xC60, 0x0B800005,
+ 0xC60, 0x05800006,
+ 0xC60, 0x0E800007,
+ 0xC60, 0x1A800008,
+ 0xC60, 0x0B800009,
+ 0xC60, 0x1580000A,
+ 0xC60, 0x0880000B,
+ 0xC60, 0x1A80000C,
+ 0xC60, 0x0B80000D,
+ 0xC60, 0x0580000E,
+ 0xC60, 0x0E80000F,
+ 0xC60, 0x1A800010,
+ 0xC60, 0x0B800011,
+ 0xC60, 0x15800012,
+ 0xC60, 0x08800013,
+ 0xC60, 0x1A800014,
+ 0xC60, 0x0B800015,
+ 0xC60, 0x05800016,
+ 0xC60, 0x07800017,
+ 0xC60, 0x1A800018,
+ 0xC60, 0x0B800019,
+ 0xC60, 0x1580001A,
+ 0xC60, 0x0880001B,
+ 0xC60, 0x1B80001C,
+ 0xC60, 0x0B80001D,
+ 0xC60, 0x0580001E,
+ 0xC60, 0x0780001F,
+ 0xC60, 0x1B800020,
+ 0xC60, 0x0B800021,
+ 0xC60, 0x05800022,
+ 0xC60, 0x07800023,
+ 0xC60, 0x1B800024,
+ 0xC60, 0x0B800025,
+ 0xC60, 0x05800026,
+ 0xC60, 0x07800027,
+ 0xC60, 0x1B800028,
+ 0xC60, 0x0B800029,
+ 0xC60, 0x0580002A,
+ 0xC60, 0x0780002B,
+ 0xC60, 0x1B800030,
+ 0xC60, 0x0B800031,
+ 0xC60, 0x05800032,
+ 0xC60, 0x00800033,
+ 0xC60, 0x1B800034,
+ 0xC60, 0x0B800035,
+ 0xC60, 0x05800036,
+ 0xC60, 0x00800037,
+ 0xC60, 0x1B800038,
+ 0xC60, 0x0B800039,
+ 0xC60, 0x0580003A,
+ 0xC60, 0x0E80803B,
+ 0xC94, 0x01000401,
+ 0xC98, 0x00188000,
+ 0xCA0, 0x00002929,
+ 0xCA4, 0x08040201,
+ 0xCA8, 0x80402010,
+ 0xCAC, 0x77777000,
+ 0xCB0, 0x54775477,
+ 0xCB4, 0x54775477,
+ 0xCB8, 0x00500000,
+ 0xCBC, 0x77700000,
+ 0xCC0, 0x00000010,
+ 0xCC8, 0x00000010,
+ 0xE00, 0x00000007,
+ 0xE04, 0x00042020,
+ 0xE08, 0x80410231,
+ 0xE0C, 0x00000000,
+ 0xE10, 0x00000100,
+ 0xE14, 0x01000000,
+ 0xE1C, 0x40000053,
+ 0xE50, 0x00000020,
+ 0xE54, 0x00000000,
+ 0x80000002, 0x00000000, 0x40000000, 0x00000000,
+ 0xE58, 0x3C0A0C14,
+ 0x90000004, 0x00000000, 0x40000000, 0x00000000,
+ 0xE58, 0x3C0A0C14,
+ 0x9000000a, 0x00000000, 0x40000000, 0x00000000,
+ 0xE58, 0x3C0A0C14,
+ 0x9000000b, 0x00000000, 0x40000000, 0x00000000,
+ 0xE58, 0x3C0A0C14,
+ 0xA0000000, 0x00000000,
+ 0xE58, 0x3C020C14,
+ 0xB0000000, 0x00000000,
+ 0xE5C, 0x0D000058,
+ 0xE60, 0x1B800000,
+ 0xE60, 0x0B800001,
+ 0xE60, 0x05800002,
+ 0xE60, 0x07800003,
+ 0xE60, 0x1A800004,
+ 0xE60, 0x0B800005,
+ 0xE60, 0x05800006,
+ 0xE60, 0x0E800007,
+ 0xE60, 0x1A800008,
+ 0xE60, 0x0B800009,
+ 0xE60, 0x1580000A,
+ 0xE60, 0x0880000B,
+ 0xE60, 0x1A80000C,
+ 0xE60, 0x0B80000D,
+ 0xE60, 0x0580000E,
+ 0xE60, 0x0E80000F,
+ 0xE60, 0x1A800010,
+ 0xE60, 0x0B800011,
+ 0xE60, 0x15800012,
+ 0xE60, 0x08800013,
+ 0xE60, 0x1A800014,
+ 0xE60, 0x0B800015,
+ 0xE60, 0x05800016,
+ 0xE60, 0x07800017,
+ 0xE60, 0x1A800018,
+ 0xE60, 0x0B800019,
+ 0xE60, 0x1580001A,
+ 0xE60, 0x0880001B,
+ 0xE60, 0x1B80001C,
+ 0xE60, 0x0B80001D,
+ 0xE60, 0x0580001E,
+ 0xE60, 0x0780001F,
+ 0xE60, 0x1B800020,
+ 0xE60, 0x0B800021,
+ 0xE60, 0x05800022,
+ 0xE60, 0x07800023,
+ 0xE60, 0x1B800024,
+ 0xE60, 0x0B800025,
+ 0xE60, 0x05800026,
+ 0xE60, 0x07800027,
+ 0xE60, 0x1B800028,
+ 0xE60, 0x0B800029,
+ 0xE60, 0x0580002A,
+ 0xE60, 0x0780002B,
+ 0xE60, 0x1B800030,
+ 0xE60, 0x0B800031,
+ 0xE60, 0x05800032,
+ 0xE60, 0x00800033,
+ 0xE60, 0x1B800034,
+ 0xE60, 0x0B800035,
+ 0xE60, 0x05800036,
+ 0xE60, 0x00800037,
+ 0xE60, 0x1B800038,
+ 0xE60, 0x0B800039,
+ 0xE60, 0x0580003A,
+ 0xE60, 0x0E80803B,
+ 0xE94, 0x01000401,
+ 0xE98, 0x00188000,
+ 0xEA0, 0x00002929,
+ 0xEA4, 0x08040201,
+ 0xEA8, 0x80402010,
+ 0xEAC, 0x77777000,
+ 0xEB0, 0x54775477,
+ 0xEB4, 0x54775477,
+ 0xEB8, 0x00500000,
+ 0xEBC, 0x77700000,
+ 0x1800, 0x00000007,
+ 0x1804, 0x00042020,
+ 0x1808, 0x80410231,
+ 0x180C, 0x00000000,
+ 0x1810, 0x00000100,
+ 0x1814, 0x01000000,
+ 0x181C, 0x40000053,
+ 0x1850, 0x00000020,
+ 0x1854, 0x00000000,
+ 0x80000002, 0x00000000, 0x40000000, 0x00000000,
+ 0x1858, 0x3C0A0C14,
+ 0x90000004, 0x00000000, 0x40000000, 0x00000000,
+ 0x1858, 0x3C0A0C14,
+ 0x9000000a, 0x00000000, 0x40000000, 0x00000000,
+ 0x1858, 0x3C0A0C14,
+ 0x9000000b, 0x00000000, 0x40000000, 0x00000000,
+ 0x1858, 0x3C0A0C14,
+ 0xA0000000, 0x00000000,
+ 0x1858, 0x3C020C14,
+ 0xB0000000, 0x00000000,
+ 0x185C, 0x0D000058,
+ 0x1860, 0x1B800000,
+ 0x1860, 0x0B800001,
+ 0x1860, 0x05800002,
+ 0x1860, 0x07800003,
+ 0x1860, 0x1A800004,
+ 0x1860, 0x0B800005,
+ 0x1860, 0x05800006,
+ 0x1860, 0x0E800007,
+ 0x1860, 0x1A800008,
+ 0x1860, 0x0B800009,
+ 0x1860, 0x1580000A,
+ 0x1860, 0x0880000B,
+ 0x1860, 0x1A80000C,
+ 0x1860, 0x0B80000D,
+ 0x1860, 0x0580000E,
+ 0x1860, 0x0E80000F,
+ 0x1860, 0x1A800010,
+ 0x1860, 0x0B800011,
+ 0x1860, 0x15800012,
+ 0x1860, 0x08800013,
+ 0x1860, 0x1A800014,
+ 0x1860, 0x0B800015,
+ 0x1860, 0x05800016,
+ 0x1860, 0x07800017,
+ 0x1860, 0x1A800018,
+ 0x1860, 0x0B800019,
+ 0x1860, 0x1580001A,
+ 0x1860, 0x0880001B,
+ 0x1860, 0x1B80001C,
+ 0x1860, 0x0B80001D,
+ 0x1860, 0x0580001E,
+ 0x1860, 0x0780001F,
+ 0x1860, 0x1B800020,
+ 0x1860, 0x0B800021,
+ 0x1860, 0x05800022,
+ 0x1860, 0x07800023,
+ 0x1860, 0x1B800024,
+ 0x1860, 0x0B800025,
+ 0x1860, 0x05800026,
+ 0x1860, 0x07800027,
+ 0x1860, 0x1B800028,
+ 0x1860, 0x0B800029,
+ 0x1860, 0x0580002A,
+ 0x1860, 0x0780002B,
+ 0x1860, 0x1B800030,
+ 0x1860, 0x0B800031,
+ 0x1860, 0x05800032,
+ 0x1860, 0x00800033,
+ 0x1860, 0x1B800034,
+ 0x1860, 0x0B800035,
+ 0x1860, 0x05800036,
+ 0x1860, 0x00800037,
+ 0x1860, 0x1B800038,
+ 0x1860, 0x0B800039,
+ 0x1860, 0x0580003A,
+ 0x1860, 0x0E80803B,
+ 0x1894, 0x01000401,
+ 0x1898, 0x00188000,
+ 0x18A0, 0x00002929,
+ 0x18A4, 0x08040201,
+ 0x18A8, 0x80402010,
+ 0x18AC, 0x77777000,
+ 0x18B0, 0x54775477,
+ 0x18B4, 0x54775477,
+ 0x18B8, 0x00500000,
+ 0x18BC, 0x77700000,
+ 0x1A00, 0x00000007,
+ 0x1A04, 0x00042020,
+ 0x1A08, 0x80410231,
+ 0x1A0C, 0x00000000,
+ 0x1A10, 0x00000100,
+ 0x1A14, 0x01000000,
+ 0x1A1C, 0x40000053,
+ 0x1A50, 0x00000020,
+ 0x1A54, 0x00000000,
+ 0x80000002, 0x00000000, 0x40000000, 0x00000000,
+ 0x1A58, 0x3C0A0C14,
+ 0x90000004, 0x00000000, 0x40000000, 0x00000000,
+ 0x1A58, 0x3C0A0C14,
+ 0x9000000a, 0x00000000, 0x40000000, 0x00000000,
+ 0x1A58, 0x3C0A0C14,
+ 0x9000000b, 0x00000000, 0x40000000, 0x00000000,
+ 0x1A58, 0x3C0A0C14,
+ 0xA0000000, 0x00000000,
+ 0x1A58, 0x3C020C14,
+ 0xB0000000, 0x00000000,
+ 0x1A5C, 0x0D000058,
+ 0x1A60, 0x1B800000,
+ 0x1A60, 0x0B800001,
+ 0x1A60, 0x05800002,
+ 0x1A60, 0x07800003,
+ 0x1A60, 0x1A800004,
+ 0x1A60, 0x0B800005,
+ 0x1A60, 0x05800006,
+ 0x1A60, 0x0E800007,
+ 0x1A60, 0x1A800008,
+ 0x1A60, 0x0B800009,
+ 0x1A60, 0x1580000A,
+ 0x1A60, 0x0880000B,
+ 0x1A60, 0x1A80000C,
+ 0x1A60, 0x0B80000D,
+ 0x1A60, 0x0580000E,
+ 0x1A60, 0x0E80000F,
+ 0x1A60, 0x1A800010,
+ 0x1A60, 0x0B800011,
+ 0x1A60, 0x15800012,
+ 0x1A60, 0x08800013,
+ 0x1A60, 0x1A800014,
+ 0x1A60, 0x0B800015,
+ 0x1A60, 0x05800016,
+ 0x1A60, 0x07800017,
+ 0x1A60, 0x1A800018,
+ 0x1A60, 0x0B800019,
+ 0x1A60, 0x1580001A,
+ 0x1A60, 0x0880001B,
+ 0x1A60, 0x1B80001C,
+ 0x1A60, 0x0B80001D,
+ 0x1A60, 0x0580001E,
+ 0x1A60, 0x0780001F,
+ 0x1A60, 0x1B800020,
+ 0x1A60, 0x0B800021,
+ 0x1A60, 0x05800022,
+ 0x1A60, 0x07800023,
+ 0x1A60, 0x1B800024,
+ 0x1A60, 0x0B800025,
+ 0x1A60, 0x05800026,
+ 0x1A60, 0x07800027,
+ 0x1A60, 0x1B800028,
+ 0x1A60, 0x0B800029,
+ 0x1A60, 0x0580002A,
+ 0x1A60, 0x0780002B,
+ 0x1A60, 0x1B800030,
+ 0x1A60, 0x0B800031,
+ 0x1A60, 0x05800032,
+ 0x1A60, 0x00800033,
+ 0x1A60, 0x1B800034,
+ 0x1A60, 0x0B800035,
+ 0x1A60, 0x05800036,
+ 0x1A60, 0x00800037,
+ 0x1A60, 0x1B800038,
+ 0x1A60, 0x0B800039,
+ 0x1A60, 0x0580003A,
+ 0x1A60, 0x0E80803B,
+ 0x1A94, 0x01000401,
+ 0x1A98, 0x00188000,
+ 0x1AA0, 0x00002929,
+ 0x1AA4, 0x08040201,
+ 0x1AA8, 0x80402010,
+ 0x1AAC, 0x77777000,
+ 0x1AB0, 0x54775477,
+ 0x1AB4, 0x54775477,
+ 0x1AB8, 0x00500000,
+ 0x1ABC, 0x77700000,
+ 0x1904, 0x00030000,
+ 0x1914, 0x00030000,
+ 0x1984, 0x03000000,
+ 0x1988, 0x00000087,
+ 0x198C, 0x00000007,
+ 0x1990, 0xFFAA5500,
+ 0x1994, 0x00000077,
+ 0x1998, 0x12801000,
+ 0x1998, 0x12801000,
+ 0x1998, 0x12801001,
+ 0x1998, 0x12801002,
+ 0x1998, 0x12801003,
+ 0x1998, 0x12801004,
+ 0x1998, 0x12801005,
+ 0x1998, 0x12801006,
+ 0x1998, 0x12801007,
+ 0x1998, 0x12801008,
+ 0x1998, 0x12801009,
+ 0x1998, 0x1280100A,
+ 0x1998, 0x1280100B,
+ 0x1998, 0x1280100C,
+ 0x1998, 0x1280100D,
+ 0x1998, 0x1280100E,
+ 0x1998, 0x1280100F,
+ 0x1998, 0x12801010,
+ 0x1998, 0x12801011,
+ 0x1998, 0x12801012,
+ 0x1998, 0x12801013,
+ 0x1998, 0x12801014,
+ 0x1998, 0x12801015,
+ 0x1998, 0x12801016,
+ 0x1998, 0x12801017,
+ 0x1998, 0x12801018,
+ 0x1998, 0x12801019,
+ 0x1998, 0x1280101A,
+ 0x1998, 0x1280101B,
+ 0x1998, 0x1280101C,
+ 0x1998, 0x1280101D,
+ 0x1998, 0x1280101E,
+ 0x1998, 0x1280101F,
+ 0x1998, 0x12801020,
+ 0x1998, 0x12801021,
+ 0x1998, 0x12801022,
+ 0x1998, 0x12801023,
+ 0x1998, 0x1280102C,
+ 0x1998, 0x1280102D,
+ 0x1998, 0x1280102E,
+ 0x1998, 0x1280102F,
+ 0x1998, 0x12801030,
+ 0x1998, 0x12801031,
+ 0x1998, 0x12801032,
+ 0x1998, 0x12801033,
+ 0x1998, 0x12801034,
+ 0x1998, 0x12801035,
+ 0x1998, 0x12801036,
+ 0x1998, 0x12801037,
+ 0x1998, 0x12801038,
+ 0x1998, 0x12801039,
+ 0x1998, 0x1280103A,
+ 0x1998, 0x1280103B,
+ 0x1998, 0x1280103C,
+ 0x1998, 0x1280103D,
+ 0x1998, 0x1280103E,
+ 0x1998, 0x1280103F,
+ 0x1998, 0x12801040,
+ 0x1998, 0x12801041,
+ 0x1998, 0x12801042,
+ 0x1998, 0x12801043,
+ 0x1998, 0x12801044,
+ 0x1998, 0x12801045,
+ 0x1998, 0x12801046,
+ 0x1998, 0x12801047,
+ 0x1998, 0x12801048,
+ 0x1998, 0x12801049,
+ 0x1998, 0x12801100,
+ 0x1998, 0x12801101,
+ 0x1998, 0x12801102,
+ 0x1998, 0x12801103,
+ 0x1998, 0x12801104,
+ 0x1998, 0x12801105,
+ 0x1998, 0x12801106,
+ 0x1998, 0x12801107,
+ 0x1998, 0x12801108,
+ 0x1998, 0x12801109,
+ 0x1998, 0x1280110A,
+ 0x1998, 0x1280110B,
+ 0x1998, 0x1280110C,
+ 0x1998, 0x1280110D,
+ 0x1998, 0x1280110E,
+ 0x1998, 0x1280110F,
+ 0x1998, 0x12801110,
+ 0x1998, 0x12801111,
+ 0x1998, 0x12801112,
+ 0x1998, 0x12801113,
+ 0x1998, 0x12801114,
+ 0x1998, 0x12801115,
+ 0x1998, 0x12801116,
+ 0x1998, 0x12801117,
+ 0x1998, 0x12801118,
+ 0x1998, 0x12801119,
+ 0x1998, 0x1280111A,
+ 0x1998, 0x1280111B,
+ 0x1998, 0x1280111C,
+ 0x1998, 0x1280111D,
+ 0x1998, 0x1280111E,
+ 0x1998, 0x1280111F,
+ 0x1998, 0x12801120,
+ 0x1998, 0x12801121,
+ 0x1998, 0x12801122,
+ 0x1998, 0x12801123,
+ 0x1998, 0x1280112C,
+ 0x1998, 0x1280112D,
+ 0x1998, 0x1280112E,
+ 0x1998, 0x1280112F,
+ 0x1998, 0x12801130,
+ 0x1998, 0x12801131,
+ 0x1998, 0x12801132,
+ 0x1998, 0x12801133,
+ 0x1998, 0x12801134,
+ 0x1998, 0x12801135,
+ 0x1998, 0x12801136,
+ 0x1998, 0x12801137,
+ 0x1998, 0x12801138,
+ 0x1998, 0x12801139,
+ 0x1998, 0x1280113A,
+ 0x1998, 0x1280113B,
+ 0x1998, 0x1280113C,
+ 0x1998, 0x1280113D,
+ 0x1998, 0x1280113E,
+ 0x1998, 0x1280113F,
+ 0x1998, 0x12801140,
+ 0x1998, 0x12801141,
+ 0x1998, 0x12801142,
+ 0x1998, 0x12801143,
+ 0x1998, 0x12801144,
+ 0x1998, 0x12801145,
+ 0x1998, 0x12801146,
+ 0x1998, 0x12801147,
+ 0x1998, 0x12801148,
+ 0x1998, 0x12801149,
+ 0x1998, 0x12801200,
+ 0x1998, 0x12801201,
+ 0x1998, 0x12801202,
+ 0x1998, 0x12801203,
+ 0x1998, 0x12801204,
+ 0x1998, 0x12801205,
+ 0x1998, 0x12801206,
+ 0x1998, 0x12801207,
+ 0x1998, 0x12801208,
+ 0x1998, 0x12801209,
+ 0x1998, 0x1280120A,
+ 0x1998, 0x1280120B,
+ 0x1998, 0x1280120C,
+ 0x1998, 0x1280120D,
+ 0x1998, 0x1280120E,
+ 0x1998, 0x1280120F,
+ 0x1998, 0x12801210,
+ 0x1998, 0x12801211,
+ 0x1998, 0x12801212,
+ 0x1998, 0x12801213,
+ 0x1998, 0x12801214,
+ 0x1998, 0x12801215,
+ 0x1998, 0x12801216,
+ 0x1998, 0x12801217,
+ 0x1998, 0x12801218,
+ 0x1998, 0x12801219,
+ 0x1998, 0x1280121A,
+ 0x1998, 0x1280121B,
+ 0x1998, 0x1280121C,
+ 0x1998, 0x1280121D,
+ 0x1998, 0x1280121E,
+ 0x1998, 0x1280121F,
+ 0x1998, 0x12801220,
+ 0x1998, 0x12801221,
+ 0x1998, 0x12801222,
+ 0x1998, 0x12801223,
+ 0x1998, 0x1280122C,
+ 0x1998, 0x1280122D,
+ 0x1998, 0x1280122E,
+ 0x1998, 0x1280122F,
+ 0x1998, 0x12801230,
+ 0x1998, 0x12801231,
+ 0x1998, 0x12801232,
+ 0x1998, 0x12801233,
+ 0x1998, 0x12801234,
+ 0x1998, 0x12801235,
+ 0x1998, 0x12801236,
+ 0x1998, 0x12801237,
+ 0x1998, 0x12801238,
+ 0x1998, 0x12801239,
+ 0x1998, 0x1280123A,
+ 0x1998, 0x1280123B,
+ 0x1998, 0x1280123C,
+ 0x1998, 0x1280123D,
+ 0x1998, 0x1280123E,
+ 0x1998, 0x1280123F,
+ 0x1998, 0x12801240,
+ 0x1998, 0x12801241,
+ 0x1998, 0x12801242,
+ 0x1998, 0x12801243,
+ 0x1998, 0x12801244,
+ 0x1998, 0x12801245,
+ 0x1998, 0x12801246,
+ 0x1998, 0x12801247,
+ 0x1998, 0x12801248,
+ 0x1998, 0x12801249,
+ 0x1998, 0x12801300,
+ 0x1998, 0x12801301,
+ 0x1998, 0x12801302,
+ 0x1998, 0x12801303,
+ 0x1998, 0x12801304,
+ 0x1998, 0x12801305,
+ 0x1998, 0x12801306,
+ 0x1998, 0x12801307,
+ 0x1998, 0x12801308,
+ 0x1998, 0x12801309,
+ 0x1998, 0x1280130A,
+ 0x1998, 0x1280130B,
+ 0x1998, 0x1280130C,
+ 0x1998, 0x1280130D,
+ 0x1998, 0x1280130E,
+ 0x1998, 0x1280130F,
+ 0x1998, 0x12801310,
+ 0x1998, 0x12801311,
+ 0x1998, 0x12801312,
+ 0x1998, 0x12801313,
+ 0x1998, 0x12801314,
+ 0x1998, 0x12801315,
+ 0x1998, 0x12801316,
+ 0x1998, 0x12801317,
+ 0x1998, 0x12801318,
+ 0x1998, 0x12801319,
+ 0x1998, 0x1280131A,
+ 0x1998, 0x1280131B,
+ 0x1998, 0x1280131C,
+ 0x1998, 0x1280131D,
+ 0x1998, 0x1280131E,
+ 0x1998, 0x1280131F,
+ 0x1998, 0x12801320,
+ 0x1998, 0x12801321,
+ 0x1998, 0x12801322,
+ 0x1998, 0x12801323,
+ 0x1998, 0x1280132C,
+ 0x1998, 0x1280132D,
+ 0x1998, 0x1280132E,
+ 0x1998, 0x1280132F,
+ 0x1998, 0x12801330,
+ 0x1998, 0x12801331,
+ 0x1998, 0x12801332,
+ 0x1998, 0x12801333,
+ 0x1998, 0x12801334,
+ 0x1998, 0x12801335,
+ 0x1998, 0x12801336,
+ 0x1998, 0x12801337,
+ 0x1998, 0x12801338,
+ 0x1998, 0x12801339,
+ 0x1998, 0x1280133A,
+ 0x1998, 0x1280133B,
+ 0x1998, 0x1280133C,
+ 0x1998, 0x1280133D,
+ 0x1998, 0x1280133E,
+ 0x1998, 0x1280133F,
+ 0x1998, 0x12801340,
+ 0x1998, 0x12801341,
+ 0x1998, 0x12801342,
+ 0x1998, 0x12801343,
+ 0x1998, 0x12801344,
+ 0x1998, 0x12801345,
+ 0x1998, 0x12801346,
+ 0x1998, 0x12801347,
+ 0x1998, 0x12801348,
+ 0x1998, 0x12801349,
+ 0x19D4, 0x88888888,
+ 0x19D8, 0x00000888,
+ 0xB00, 0xE3100100,
+ 0xB00, 0xE7100100,
+ 0xC60, 0x15808002,
+ 0xC60, 0x01808003,
+ 0xE60, 0x15808002,
+ 0xE60, 0x01808003,
+ 0x1860, 0x15808002,
+ 0x1860, 0x01808003,
+ 0x1A60, 0x15808002,
+ 0x1A60, 0x01808003,
+ 0xB00, 0xE3100100,
+ 0xC5C, 0x0D080058,
+ 0xE5C, 0x0D080058,
+ 0x185C, 0x0D080058,
+ 0x1A5C, 0x0D080058,
+ 0xC5C, 0x0D000058,
+ 0xE5C, 0x0D000058,
+ 0x185C, 0x0D000058,
+ 0x1A5C, 0x0D000058,
+ 0xC60, 0x05808002,
+ 0xC60, 0x0E808003,
+ 0xE60, 0x05808002,
+ 0xE60, 0x0E808003,
+ 0x1860, 0x05808002,
+ 0x1860, 0x0E808003,
+ 0x1A60, 0x05808002,
+ 0x1A60, 0x0E808003,
+ 0xB00, 0xE7100100,
+ 0xB00, 0xE3100100,
+ 0xB00, 0xE3100000,
+ 0x1C38, 0x00000002,
+ 0xA00, 0x00D047C8,
+ 0xA04, 0x46FF800C,
+ 0xA08, 0x8C838300,
+ 0xA0C, 0x2E7E000F,
+ 0xA10, 0x9500BB78,
+ 0xA14, 0x11144028,
+ 0xA18, 0x00881117,
+ 0xA1C, 0x89140F00,
+ 0xA20, 0x1A1B0030,
+ 0xA24, 0x090E1317,
+ 0xA28, 0x00000204,
+ 0xA2C, 0x00900000,
+ 0xA70, 0x101FFF00,
+ 0xA74, 0x00000128,
+ 0xA78, 0x00000900,
+ 0xA7C, 0x225B0606,
+ 0xA80, 0x218075B2,
+ 0xA84, 0x9C1F8C00,
+ 0x1B04, 0xE24628D2,
+ 0x1B10, 0x88010D46,
+ 0x1B14, 0x00000000,
+ 0x1B18, 0x00292903,
+ 0x1B00, 0xF8000000,
+ 0x1B00, 0xF800D000,
+ 0x1B00, 0xF801F000,
+ 0x1B1C, 0xA2123DB2,
+ 0x1B20, 0x07040001,
+ 0x1B24, 0x07060807,
+ 0x80000001, 0x00000000, 0x40000000, 0x00000000,
+ 0x1B28, 0xC0060324,
+ 0x90000002, 0x00000000, 0x40000000, 0x00000000,
+ 0x1B28, 0xC0060324,
+ 0x90000004, 0x00000000, 0x40000000, 0x00000000,
+ 0x1B28, 0xC0060324,
+ 0x90000003, 0x00000000, 0x40000000, 0x00000000,
+ 0x1B28, 0xC0060324,
+ 0x90000005, 0x00000000, 0x40000000, 0x00000000,
+ 0x1B28, 0xC0060324,
+ 0x90000006, 0x00000000, 0x40000000, 0x00000000,
+ 0x1B28, 0xC0060324,
+ 0x9000000a, 0x00000000, 0x40000000, 0x00000000,
+ 0x1B28, 0xC0060324,
+ 0x9000000b, 0x00000000, 0x40000000, 0x00000000,
+ 0x1B28, 0xC0060324,
+ 0xA0000000, 0x00000000,
+ 0x1B28, 0xC0060348,
+ 0xB0000000, 0x00000000,
+ 0x1B2C, 0x20000003,
+ 0x1B30, 0x20000000,
+ 0x1B38, 0x20000000,
+ 0x1B3C, 0x20000000,
+ 0x1BD4, 0x00000001,
+ 0x1B94, 0x80000000,
+ 0x1B34, 0x00000000,
+ 0x1B34, 0x00000002,
+ 0x1B34, 0x00000000,
+ 0x1B00, 0xF8000002,
+ 0x1B00, 0xF800D002,
+ 0x1B00, 0xF801F002,
+ 0x1B1C, 0xA2123DB2,
+ 0x1B20, 0x07040001,
+ 0x1B24, 0x07060807,
+ 0x80000001, 0x00000000, 0x40000000, 0x00000000,
+ 0x1B28, 0xC0060324,
+ 0x90000002, 0x00000000, 0x40000000, 0x00000000,
+ 0x1B28, 0xC0060324,
+ 0x90000004, 0x00000000, 0x40000000, 0x00000000,
+ 0x1B28, 0xC0060324,
+ 0x90000003, 0x00000000, 0x40000000, 0x00000000,
+ 0x1B28, 0xC0060324,
+ 0x90000005, 0x00000000, 0x40000000, 0x00000000,
+ 0x1B28, 0xC0060324,
+ 0x90000006, 0x00000000, 0x40000000, 0x00000000,
+ 0x1B28, 0xC0060324,
+ 0x9000000a, 0x00000000, 0x40000000, 0x00000000,
+ 0x1B28, 0xC0060324,
+ 0x9000000b, 0x00000000, 0x40000000, 0x00000000,
+ 0x1B28, 0xC0060324,
+ 0xA0000000, 0x00000000,
+ 0x1B28, 0xC0060348,
+ 0xB0000000, 0x00000000,
+ 0x1B2C, 0x20000003,
+ 0x1B30, 0x20000000,
+ 0x1B38, 0x20000000,
+ 0x1B3C, 0x20000000,
+ 0x1BD4, 0x00000001,
+ 0x1B94, 0x80000000,
+ 0x1B34, 0x00000000,
+ 0x1B34, 0x00000002,
+ 0x1B34, 0x00000000,
+ 0x1B00, 0xF8000004,
+ 0x1B00, 0xF800D004,
+ 0x1B00, 0xF801F004,
+ 0x1B1C, 0xA2123DB2,
+ 0x1B20, 0x07040001,
+ 0x1B24, 0x07060807,
+ 0x80000001, 0x00000000, 0x40000000, 0x00000000,
+ 0x1B28, 0xC0060324,
+ 0x90000002, 0x00000000, 0x40000000, 0x00000000,
+ 0x1B28, 0xC0060324,
+ 0x90000004, 0x00000000, 0x40000000, 0x00000000,
+ 0x1B28, 0xC0060324,
+ 0x90000003, 0x00000000, 0x40000000, 0x00000000,
+ 0x1B28, 0xC0060324,
+ 0x90000005, 0x00000000, 0x40000000, 0x00000000,
+ 0x1B28, 0xC0060324,
+ 0x90000006, 0x00000000, 0x40000000, 0x00000000,
+ 0x1B28, 0xC0060324,
+ 0x9000000a, 0x00000000, 0x40000000, 0x00000000,
+ 0x1B28, 0xC0060324,
+ 0x9000000b, 0x00000000, 0x40000000, 0x00000000,
+ 0x1B28, 0xC0060324,
+ 0xA0000000, 0x00000000,
+ 0x1B28, 0xC0060348,
+ 0xB0000000, 0x00000000,
+ 0x1B2C, 0x20000003,
+ 0x1B30, 0x20000000,
+ 0x1B38, 0x20000000,
+ 0x1B3C, 0x20000000,
+ 0x1BD4, 0x00000001,
+ 0x1B94, 0x80000000,
+ 0x1B34, 0x00000000,
+ 0x1B34, 0x00000002,
+ 0x1B34, 0x00000000,
+ 0x1B00, 0xF8000006,
+ 0x1B00, 0xF800D006,
+ 0x1B00, 0xF801F006,
+ 0x1B1C, 0xA2123DB2,
+ 0x1B20, 0x07040001,
+ 0x1B24, 0x07060807,
+ 0x80000001, 0x00000000, 0x40000000, 0x00000000,
+ 0x1B28, 0xC0060324,
+ 0x90000002, 0x00000000, 0x40000000, 0x00000000,
+ 0x1B28, 0xC0060324,
+ 0x90000004, 0x00000000, 0x40000000, 0x00000000,
+ 0x1B28, 0xC0060324,
+ 0x90000003, 0x00000000, 0x40000000, 0x00000000,
+ 0x1B28, 0xC0060324,
+ 0x90000005, 0x00000000, 0x40000000, 0x00000000,
+ 0x1B28, 0xC0060324,
+ 0x90000006, 0x00000000, 0x40000000, 0x00000000,
+ 0x1B28, 0xC0060324,
+ 0x9000000a, 0x00000000, 0x40000000, 0x00000000,
+ 0x1B28, 0xC0060324,
+ 0x9000000b, 0x00000000, 0x40000000, 0x00000000,
+ 0x1B28, 0xC0060324,
+ 0xA0000000, 0x00000000,
+ 0x1B28, 0xC0060348,
+ 0xB0000000, 0x00000000,
+ 0x1B2C, 0x20000003,
+ 0x1B30, 0x20000000,
+ 0x1B38, 0x20000000,
+ 0x1B3C, 0x20000000,
+ 0x1BD4, 0x00000001,
+ 0x1B94, 0x80000000,
+ 0x1B34, 0x00000000,
+ 0x1B34, 0x00000002,
+ 0x1B34, 0x00000000,
+ 0x1B00, 0xF8000000,
+ 0x1B80, 0x00000007,
+ 0x1B80, 0x09060005,
+ 0x1B80, 0x09060007,
+ 0x1B80, 0x0FFE0015,
+ 0x1B80, 0x0FFE0017,
+ 0x1B80, 0x00240025,
+ 0x1B80, 0x00240027,
+ 0x1B80, 0x00040035,
+ 0x1B80, 0x00040037,
+ 0x1B80, 0x05C00045,
+ 0x1B80, 0x05C00047,
+ 0x1B80, 0x00070055,
+ 0x1B80, 0x00070057,
+ 0x1B80, 0x64000065,
+ 0x1B80, 0x64000067,
+ 0x1B80, 0x00020075,
+ 0x1B80, 0x00020077,
+ 0x1B80, 0x00080085,
+ 0x1B80, 0x00080087,
+ 0x1B80, 0x80000095,
+ 0x1B80, 0x80000097,
+ 0x1B80, 0x090100A5,
+ 0x1B80, 0x090100A7,
+ 0x1B80, 0x0F0200B5,
+ 0x1B80, 0x0F0200B7,
+ 0x1B80, 0x002400C5,
+ 0x1B80, 0x002400C7,
+ 0x1B80, 0x000400D5,
+ 0x1B80, 0x000400D7,
+ 0x1B80, 0x05C000E5,
+ 0x1B80, 0x05C000E7,
+ 0x1B80, 0x000700F5,
+ 0x1B80, 0x000700F7,
+ 0x1B80, 0x64020105,
+ 0x1B80, 0x64020107,
+ 0x1B80, 0x00020115,
+ 0x1B80, 0x00020117,
+ 0x1B80, 0x00040125,
+ 0x1B80, 0x00040127,
+ 0x1B80, 0x4A000135,
+ 0x1B80, 0x4A000137,
+ 0x1B80, 0x4B040145,
+ 0x1B80, 0x4B040147,
+ 0x1B80, 0x85030155,
+ 0x1B80, 0x85030157,
+ 0x1B80, 0x40010165,
+ 0x1B80, 0x40010167,
+ 0x1B80, 0xE0290175,
+ 0x1B80, 0xE0290177,
+ 0x1B80, 0x00040185,
+ 0x1B80, 0x00040187,
+ 0x1B80, 0x4B050195,
+ 0x1B80, 0x4B050197,
+ 0x1B80, 0x860301A5,
+ 0x1B80, 0x860301A7,
+ 0x1B80, 0x400301B5,
+ 0x1B80, 0x400301B7,
+ 0x1B80, 0xE02901C5,
+ 0x1B80, 0xE02901C7,
+ 0x1B80, 0x000401D5,
+ 0x1B80, 0x000401D7,
+ 0x1B80, 0x4B0601E5,
+ 0x1B80, 0x4B0601E7,
+ 0x1B80, 0x870301F5,
+ 0x1B80, 0x870301F7,
+ 0x1B80, 0x40050205,
+ 0x1B80, 0x40050207,
+ 0x1B80, 0xE0290215,
+ 0x1B80, 0xE0290217,
+ 0x1B80, 0x00040225,
+ 0x1B80, 0x00040227,
+ 0x1B80, 0x4B070235,
+ 0x1B80, 0x4B070237,
+ 0x1B80, 0x88030245,
+ 0x1B80, 0x88030247,
+ 0x1B80, 0x40070255,
+ 0x1B80, 0x40070257,
+ 0x1B80, 0xE0290265,
+ 0x1B80, 0xE0290267,
+ 0x1B80, 0x4B000275,
+ 0x1B80, 0x4B000277,
+ 0x1B80, 0x30000285,
+ 0x1B80, 0x30000287,
+ 0x1B80, 0xFE100295,
+ 0x1B80, 0xFE100297,
+ 0x1B80, 0xFF1002A5,
+ 0x1B80, 0xFF1002A7,
+ 0x1B80, 0xE18602B5,
+ 0x1B80, 0xE18602B7,
+ 0x1B80, 0xF00A02C5,
+ 0x1B80, 0xF00A02C7,
+ 0x1B80, 0xF10A02D5,
+ 0x1B80, 0xF10A02D7,
+ 0x1B80, 0xF20A02E5,
+ 0x1B80, 0xF20A02E7,
+ 0x1B80, 0xF30802F5,
+ 0x1B80, 0xF30802F7,
+ 0x1B80, 0xF4070305,
+ 0x1B80, 0xF4070307,
+ 0x1B80, 0xF5060315,
+ 0x1B80, 0xF5060317,
+ 0x1B80, 0xF7060325,
+ 0x1B80, 0xF7060327,
+ 0x1B80, 0xF8050335,
+ 0x1B80, 0xF8050337,
+ 0x1B80, 0xF9040345,
+ 0x1B80, 0xF9040347,
+ 0x1B80, 0x00010355,
+ 0x1B80, 0x00010357,
+ 0x1B80, 0x303B0365,
+ 0x1B80, 0x303B0367,
+ 0x1B80, 0x30500375,
+ 0x1B80, 0x30500377,
+ 0x1B80, 0x305C0385,
+ 0x1B80, 0x305C0387,
+ 0x1B80, 0x31D50395,
+ 0x1B80, 0x31D50397,
+ 0x1B80, 0x31C503A5,
+ 0x1B80, 0x31C503A7,
+ 0x1B80, 0x4D0403B5,
+ 0x1B80, 0x4D0403B7,
+ 0x1B80, 0x2EF003C5,
+ 0x1B80, 0x2EF003C7,
+ 0x1B80, 0x000203D5,
+ 0x1B80, 0x000203D7,
+ 0x1B80, 0x208003E5,
+ 0x1B80, 0x208003E7,
+ 0x1B80, 0x000003F5,
+ 0x1B80, 0x000003F7,
+ 0x1B80, 0x4D000405,
+ 0x1B80, 0x4D000407,
+ 0x1B80, 0x55070415,
+ 0x1B80, 0x55070417,
+ 0x1B80, 0xE1230425,
+ 0x1B80, 0xE1230427,
+ 0x1B80, 0xE1230435,
+ 0x1B80, 0xE1230437,
+ 0x1B80, 0x4D040445,
+ 0x1B80, 0x4D040447,
+ 0x1B80, 0x20800455,
+ 0x1B80, 0x20800457,
+ 0x1B80, 0x84000465,
+ 0x1B80, 0x84000467,
+ 0x1B80, 0x4D000475,
+ 0x1B80, 0x4D000477,
+ 0x1B80, 0x550F0485,
+ 0x1B80, 0x550F0487,
+ 0x1B80, 0xE1230495,
+ 0x1B80, 0xE1230497,
+ 0x1B80, 0x4F0204A5,
+ 0x1B80, 0x4F0204A7,
+ 0x1B80, 0x4E0004B5,
+ 0x1B80, 0x4E0004B7,
+ 0x1B80, 0x530204C5,
+ 0x1B80, 0x530204C7,
+ 0x1B80, 0x520104D5,
+ 0x1B80, 0x520104D7,
+ 0x1B80, 0xE12704E5,
+ 0x1B80, 0xE12704E7,
+ 0x1B80, 0x000104F5,
+ 0x1B80, 0x000104F7,
+ 0x1B80, 0x5C720505,
+ 0x1B80, 0x5C720507,
+ 0x1B80, 0xE1320515,
+ 0x1B80, 0xE1320517,
+ 0x1B80, 0x54E50525,
+ 0x1B80, 0x54E50527,
+ 0x1B80, 0x54BF0535,
+ 0x1B80, 0x54BF0537,
+ 0x1B80, 0x54C50545,
+ 0x1B80, 0x54C50547,
+ 0x1B80, 0x54BE0555,
+ 0x1B80, 0x54BE0557,
+ 0x1B80, 0x54DF0565,
+ 0x1B80, 0x54DF0567,
+ 0x1B80, 0x0BA60575,
+ 0x1B80, 0x0BA60577,
+ 0x1B80, 0xF3130585,
+ 0x1B80, 0xF3130587,
+ 0x1B80, 0xF41E0595,
+ 0x1B80, 0xF41E0597,
+ 0x1B80, 0xF53C05A5,
+ 0x1B80, 0xF53C05A7,
+ 0x1B80, 0x000105B5,
+ 0x1B80, 0x000105B7,
+ 0x1B80, 0x620605C5,
+ 0x1B80, 0x620605C7,
+ 0x1B80, 0x600605D5,
+ 0x1B80, 0x600605D7,
+ 0x1B80, 0xE1A905E5,
+ 0x1B80, 0xE1A905E7,
+ 0x1B80, 0x0C0005F5,
+ 0x1B80, 0x0C0005F7,
+ 0x1B80, 0x5C720605,
+ 0x1B80, 0x5C720607,
+ 0x1B80, 0xE1320615,
+ 0x1B80, 0xE1320617,
+ 0x1B80, 0x5CF10625,
+ 0x1B80, 0x5CF10627,
+ 0x1B80, 0x0C010635,
+ 0x1B80, 0x0C010637,
+ 0x1B80, 0xF2020645,
+ 0x1B80, 0xF2020647,
+ 0x1B80, 0x30D60655,
+ 0x1B80, 0x30D60657,
+ 0x1B80, 0x0AC60665,
+ 0x1B80, 0x0AC60667,
+ 0x1B80, 0xE1B60675,
+ 0x1B80, 0xE1B60677,
+ 0x1B80, 0xE1580685,
+ 0x1B80, 0xE1580687,
+ 0x1B80, 0x54E50695,
+ 0x1B80, 0x54E50697,
+ 0x1B80, 0x000106A5,
+ 0x1B80, 0x000106A7,
+ 0x1B80, 0x560106B5,
+ 0x1B80, 0x560106B7,
+ 0x1B80, 0x5CE206C5,
+ 0x1B80, 0x5CE206C7,
+ 0x1B80, 0x0AE106D5,
+ 0x1B80, 0x0AE106D7,
+ 0x1B80, 0x630C06E5,
+ 0x1B80, 0x630C06E7,
+ 0x1B80, 0xE13F06F5,
+ 0x1B80, 0xE13F06F7,
+ 0x1B80, 0x00270705,
+ 0x1B80, 0x00270707,
+ 0x1B80, 0xE16C0715,
+ 0x1B80, 0xE16C0717,
+ 0x1B80, 0x00020725,
+ 0x1B80, 0x00020727,
+ 0x1B80, 0x002A0735,
+ 0x1B80, 0x002A0737,
+ 0x1B80, 0x07140745,
+ 0x1B80, 0x07140747,
+ 0x1B80, 0x00020755,
+ 0x1B80, 0x00020757,
+ 0x1B80, 0x30C30765,
+ 0x1B80, 0x30C30767,
+ 0x1B80, 0x56010775,
+ 0x1B80, 0x56010777,
+ 0x1B80, 0x5CE20785,
+ 0x1B80, 0x5CE20787,
+ 0x1B80, 0x0AE10795,
+ 0x1B80, 0x0AE10797,
+ 0x1B80, 0x631707A5,
+ 0x1B80, 0x631707A7,
+ 0x1B80, 0xE13F07B5,
+ 0x1B80, 0xE13F07B7,
+ 0x1B80, 0x002507C5,
+ 0x1B80, 0x002507C7,
+ 0x1B80, 0xE16C07D5,
+ 0x1B80, 0xE16C07D7,
+ 0x1B80, 0x000207E5,
+ 0x1B80, 0x000207E7,
+ 0x1B80, 0x630F07F5,
+ 0x1B80, 0x630F07F7,
+ 0x1B80, 0xE13F0805,
+ 0x1B80, 0xE13F0807,
+ 0x1B80, 0x63070815,
+ 0x1B80, 0x63070817,
+ 0x1B80, 0xE13F0825,
+ 0x1B80, 0xE13F0827,
+ 0x1B80, 0x07140835,
+ 0x1B80, 0x07140837,
+ 0x1B80, 0x56000845,
+ 0x1B80, 0x56000847,
+ 0x1B80, 0x5CF20855,
+ 0x1B80, 0x5CF20857,
+ 0x1B80, 0x0AF10865,
+ 0x1B80, 0x0AF10867,
+ 0x1B80, 0x07140875,
+ 0x1B80, 0x07140877,
+ 0x1B80, 0x07140885,
+ 0x1B80, 0x07140887,
+ 0x1B80, 0x630F0895,
+ 0x1B80, 0x630F0897,
+ 0x1B80, 0xE13F08A5,
+ 0x1B80, 0xE13F08A7,
+ 0x1B80, 0x631708B5,
+ 0x1B80, 0x631708B7,
+ 0x1B80, 0xE13F08C5,
+ 0x1B80, 0xE13F08C7,
+ 0x1B80, 0x002508D5,
+ 0x1B80, 0x002508D7,
+ 0x1B80, 0xE16C08E5,
+ 0x1B80, 0xE16C08E7,
+ 0x1B80, 0x000208F5,
+ 0x1B80, 0x000208F7,
+ 0x1B80, 0x30C30905,
+ 0x1B80, 0x30C30907,
+ 0x1B80, 0xE1A90915,
+ 0x1B80, 0xE1A90917,
+ 0x1B80, 0x62060925,
+ 0x1B80, 0x62060927,
+ 0x1B80, 0x60060935,
+ 0x1B80, 0x60060937,
+ 0x1B80, 0xE1160945,
+ 0x1B80, 0xE1160947,
+ 0x1B80, 0x54BE0955,
+ 0x1B80, 0x54BE0957,
+ 0x1B80, 0x56010965,
+ 0x1B80, 0x56010967,
+ 0x1B80, 0x5CE20975,
+ 0x1B80, 0x5CE20977,
+ 0x1B80, 0x0AE10985,
+ 0x1B80, 0x0AE10987,
+ 0x1B80, 0x633A0995,
+ 0x1B80, 0x633A0997,
+ 0x1B80, 0xE13F09A5,
+ 0x1B80, 0xE13F09A7,
+ 0x1B80, 0x633709B5,
+ 0x1B80, 0x633709B7,
+ 0x1B80, 0xE13F09C5,
+ 0x1B80, 0xE13F09C7,
+ 0x1B80, 0x632F09D5,
+ 0x1B80, 0x632F09D7,
+ 0x1B80, 0xE13F09E5,
+ 0x1B80, 0xE13F09E7,
+ 0x1B80, 0x632709F5,
+ 0x1B80, 0x632709F7,
+ 0x1B80, 0xE13F0A05,
+ 0x1B80, 0xE13F0A07,
+ 0x1B80, 0x631F0A15,
+ 0x1B80, 0x631F0A17,
+ 0x1B80, 0xE13F0A25,
+ 0x1B80, 0xE13F0A27,
+ 0x1B80, 0x63170A35,
+ 0x1B80, 0x63170A37,
+ 0x1B80, 0xE13F0A45,
+ 0x1B80, 0xE13F0A47,
+ 0x1B80, 0x630F0A55,
+ 0x1B80, 0x630F0A57,
+ 0x1B80, 0xE13F0A65,
+ 0x1B80, 0xE13F0A67,
+ 0x1B80, 0x63070A75,
+ 0x1B80, 0x63070A77,
+ 0x1B80, 0xE13F0A85,
+ 0x1B80, 0xE13F0A87,
+ 0x1B80, 0xE16C0A95,
+ 0x1B80, 0xE16C0A97,
+ 0x1B80, 0x56000AA5,
+ 0x1B80, 0x56000AA7,
+ 0x1B80, 0x5CF20AB5,
+ 0x1B80, 0x5CF20AB7,
+ 0x1B80, 0x0AF10AC5,
+ 0x1B80, 0x0AF10AC7,
+ 0x1B80, 0xF5040AD5,
+ 0x1B80, 0xF5040AD7,
+ 0x1B80, 0xE13F0AE5,
+ 0x1B80, 0xE13F0AE7,
+ 0x1B80, 0xE16C0AF5,
+ 0x1B80, 0xE16C0AF7,
+ 0x1B80, 0x30B30B05,
+ 0x1B80, 0x30B30B07,
+ 0x1B80, 0x07140B15,
+ 0x1B80, 0x07140B17,
+ 0x1B80, 0x07140B25,
+ 0x1B80, 0x07140B27,
+ 0x1B80, 0x630F0B35,
+ 0x1B80, 0x630F0B37,
+ 0x1B80, 0xE13F0B45,
+ 0x1B80, 0xE13F0B47,
+ 0x1B80, 0x63170B55,
+ 0x1B80, 0x63170B57,
+ 0x1B80, 0xE13F0B65,
+ 0x1B80, 0xE13F0B67,
+ 0x1B80, 0x631F0B75,
+ 0x1B80, 0x631F0B77,
+ 0x1B80, 0xE13F0B85,
+ 0x1B80, 0xE13F0B87,
+ 0x1B80, 0x63270B95,
+ 0x1B80, 0x63270B97,
+ 0x1B80, 0xE13F0BA5,
+ 0x1B80, 0xE13F0BA7,
+ 0x1B80, 0x632F0BB5,
+ 0x1B80, 0x632F0BB7,
+ 0x1B80, 0xE13F0BC5,
+ 0x1B80, 0xE13F0BC7,
+ 0x1B80, 0x63370BD5,
+ 0x1B80, 0x63370BD7,
+ 0x1B80, 0xE13F0BE5,
+ 0x1B80, 0xE13F0BE7,
+ 0x1B80, 0x633A0BF5,
+ 0x1B80, 0x633A0BF7,
+ 0x1B80, 0xE13F0C05,
+ 0x1B80, 0xE13F0C07,
+ 0x1B80, 0xF60B0C15,
+ 0x1B80, 0xF60B0C17,
+ 0x1B80, 0xF7170C25,
+ 0x1B80, 0xF7170C27,
+ 0x1B80, 0x4D300C35,
+ 0x1B80, 0x4D300C37,
+ 0x1B80, 0x57040C45,
+ 0x1B80, 0x57040C47,
+ 0x1B80, 0x57000C55,
+ 0x1B80, 0x57000C57,
+ 0x1B80, 0x96000C65,
+ 0x1B80, 0x96000C67,
+ 0x1B80, 0x57080C75,
+ 0x1B80, 0x57080C77,
+ 0x1B80, 0x57000C85,
+ 0x1B80, 0x57000C87,
+ 0x1B80, 0x95000C95,
+ 0x1B80, 0x95000C97,
+ 0x1B80, 0x4D000CA5,
+ 0x1B80, 0x4D000CA7,
+ 0x1B80, 0x6C070CB5,
+ 0x1B80, 0x6C070CB7,
+ 0x1B80, 0x00010CC5,
+ 0x1B80, 0x00010CC7,
+ 0x1B80, 0x00220CD5,
+ 0x1B80, 0x00220CD7,
+ 0x1B80, 0x06140CE5,
+ 0x1B80, 0x06140CE7,
+ 0x1B80, 0xE16C0CF5,
+ 0x1B80, 0xE16C0CF7,
+ 0x1B80, 0x00020D05,
+ 0x1B80, 0x00020D07,
+ 0x1B80, 0x00250D15,
+ 0x1B80, 0x00250D17,
+ 0x1B80, 0x06140D25,
+ 0x1B80, 0x06140D27,
+ 0x1B80, 0xE16C0D35,
+ 0x1B80, 0xE16C0D37,
+ 0x1B80, 0x00020D45,
+ 0x1B80, 0x00020D47,
+ 0x1B80, 0x00010D55,
+ 0x1B80, 0x00010D57,
+ 0x1B80, 0x00320D65,
+ 0x1B80, 0x00320D67,
+ 0x1B80, 0xE16C0D75,
+ 0x1B80, 0xE16C0D77,
+ 0x1B80, 0x00020D85,
+ 0x1B80, 0x00020D87,
+ 0x1B80, 0xE1860D95,
+ 0x1B80, 0xE1860D97,
+ 0x1B80, 0xE1B60DA5,
+ 0x1B80, 0xE1B60DA7,
+ 0x1B80, 0x5CD10DB5,
+ 0x1B80, 0x5CD10DB7,
+ 0x1B80, 0x673A0DC5,
+ 0x1B80, 0x673A0DC7,
+ 0x1B80, 0xE1230DD5,
+ 0x1B80, 0xE1230DD7,
+ 0x1B80, 0xF80B0DE5,
+ 0x1B80, 0xF80B0DE7,
+ 0x1B80, 0xF9110DF5,
+ 0x1B80, 0xF9110DF7,
+ 0x1B80, 0xE1580E05,
+ 0x1B80, 0xE1580E07,
+ 0x1B80, 0x67370E15,
+ 0x1B80, 0x67370E17,
+ 0x1B80, 0xE1580E25,
+ 0x1B80, 0xE1580E27,
+ 0x1B80, 0x672F0E35,
+ 0x1B80, 0x672F0E37,
+ 0x1B80, 0xE1580E45,
+ 0x1B80, 0xE1580E47,
+ 0x1B80, 0x67270E55,
+ 0x1B80, 0x67270E57,
+ 0x1B80, 0xE1580E65,
+ 0x1B80, 0xE1580E67,
+ 0x1B80, 0x671F0E75,
+ 0x1B80, 0x671F0E77,
+ 0x1B80, 0xE1580E85,
+ 0x1B80, 0xE1580E87,
+ 0x1B80, 0x67170E95,
+ 0x1B80, 0x67170E97,
+ 0x1B80, 0xE1580EA5,
+ 0x1B80, 0xE1580EA7,
+ 0x1B80, 0xF8020EB5,
+ 0x1B80, 0xF8020EB7,
+ 0x1B80, 0x30EE0EC5,
+ 0x1B80, 0x30EE0EC7,
+ 0x1B80, 0xE0D10ED5,
+ 0x1B80, 0xE0D10ED7,
+ 0x1B80, 0x670F0EE5,
+ 0x1B80, 0x670F0EE7,
+ 0x1B80, 0xE1580EF5,
+ 0x1B80, 0xE1580EF7,
+ 0x1B80, 0x67070F05,
+ 0x1B80, 0x67070F07,
+ 0x1B80, 0xE1580F15,
+ 0x1B80, 0xE1580F17,
+ 0x1B80, 0xF9020F25,
+ 0x1B80, 0xF9020F27,
+ 0x1B80, 0x30F50F35,
+ 0x1B80, 0x30F50F37,
+ 0x1B80, 0xE0CD0F45,
+ 0x1B80, 0xE0CD0F47,
+ 0x1B80, 0x06140F55,
+ 0x1B80, 0x06140F57,
+ 0x1B80, 0xE16C0F65,
+ 0x1B80, 0xE16C0F67,
+ 0x1B80, 0x5CF10F75,
+ 0x1B80, 0x5CF10F77,
+ 0x1B80, 0xE1580F85,
+ 0x1B80, 0xE1580F87,
+ 0x1B80, 0x06140F95,
+ 0x1B80, 0x06140F97,
+ 0x1B80, 0xE16C0FA5,
+ 0x1B80, 0xE16C0FA7,
+ 0x1B80, 0xF9020FB5,
+ 0x1B80, 0xF9020FB7,
+ 0x1B80, 0x30FF0FC5,
+ 0x1B80, 0x30FF0FC7,
+ 0x1B80, 0xE0CD0FD5,
+ 0x1B80, 0xE0CD0FD7,
+ 0x1B80, 0x31130FE5,
+ 0x1B80, 0x31130FE7,
+ 0x1B80, 0x670F0FF5,
+ 0x1B80, 0x670F0FF7,
+ 0x1B80, 0xE1581005,
+ 0x1B80, 0xE1581007,
+ 0x1B80, 0x67171015,
+ 0x1B80, 0x67171017,
+ 0x1B80, 0xE1581025,
+ 0x1B80, 0xE1581027,
+ 0x1B80, 0xF8021035,
+ 0x1B80, 0xF8021037,
+ 0x1B80, 0x31071045,
+ 0x1B80, 0x31071047,
+ 0x1B80, 0xE0D11055,
+ 0x1B80, 0xE0D11057,
+ 0x1B80, 0x31131065,
+ 0x1B80, 0x31131067,
+ 0x1B80, 0x670F1075,
+ 0x1B80, 0x670F1077,
+ 0x1B80, 0xE1581085,
+ 0x1B80, 0xE1581087,
+ 0x1B80, 0x671F1095,
+ 0x1B80, 0x671F1097,
+ 0x1B80, 0xE15810A5,
+ 0x1B80, 0xE15810A7,
+ 0x1B80, 0x672710B5,
+ 0x1B80, 0x672710B7,
+ 0x1B80, 0xE15810C5,
+ 0x1B80, 0xE15810C7,
+ 0x1B80, 0x672F10D5,
+ 0x1B80, 0x672F10D7,
+ 0x1B80, 0xE15810E5,
+ 0x1B80, 0xE15810E7,
+ 0x1B80, 0x673710F5,
+ 0x1B80, 0x673710F7,
+ 0x1B80, 0xE1581105,
+ 0x1B80, 0xE1581107,
+ 0x1B80, 0x673A1115,
+ 0x1B80, 0x673A1117,
+ 0x1B80, 0xE1581125,
+ 0x1B80, 0xE1581127,
+ 0x1B80, 0x4D101135,
+ 0x1B80, 0x4D101137,
+ 0x1B80, 0x30C41145,
+ 0x1B80, 0x30C41147,
+ 0x1B80, 0x00011155,
+ 0x1B80, 0x00011157,
+ 0x1B80, 0x6F241165,
+ 0x1B80, 0x6F241167,
+ 0x1B80, 0x6E401175,
+ 0x1B80, 0x6E401177,
+ 0x1B80, 0x6D001185,
+ 0x1B80, 0x6D001187,
+ 0x1B80, 0x55031195,
+ 0x1B80, 0x55031197,
+ 0x1B80, 0x312311A5,
+ 0x1B80, 0x312311A7,
+ 0x1B80, 0x6F1C11B5,
+ 0x1B80, 0x6F1C11B7,
+ 0x1B80, 0x6E4011C5,
+ 0x1B80, 0x6E4011C7,
+ 0x1B80, 0x550B11D5,
+ 0x1B80, 0x550B11D7,
+ 0x1B80, 0x312311E5,
+ 0x1B80, 0x312311E7,
+ 0x1B80, 0x061C11F5,
+ 0x1B80, 0x061C11F7,
+ 0x1B80, 0x54DE1205,
+ 0x1B80, 0x54DE1207,
+ 0x1B80, 0x06DC1215,
+ 0x1B80, 0x06DC1217,
+ 0x1B80, 0x55131225,
+ 0x1B80, 0x55131227,
+ 0x1B80, 0x74011235,
+ 0x1B80, 0x74011237,
+ 0x1B80, 0x74001245,
+ 0x1B80, 0x74001247,
+ 0x1B80, 0x8E001255,
+ 0x1B80, 0x8E001257,
+ 0x1B80, 0x00011265,
+ 0x1B80, 0x00011267,
+ 0x1B80, 0x57021275,
+ 0x1B80, 0x57021277,
+ 0x1B80, 0x57001285,
+ 0x1B80, 0x57001287,
+ 0x1B80, 0x97001295,
+ 0x1B80, 0x97001297,
+ 0x1B80, 0x000112A5,
+ 0x1B80, 0x000112A7,
+ 0x1B80, 0x54BF12B5,
+ 0x1B80, 0x54BF12B7,
+ 0x1B80, 0x54C112C5,
+ 0x1B80, 0x54C112C7,
+ 0x1B80, 0x54A212D5,
+ 0x1B80, 0x54A212D7,
+ 0x1B80, 0x54C012E5,
+ 0x1B80, 0x54C012E7,
+ 0x1B80, 0x54A112F5,
+ 0x1B80, 0x54A112F7,
+ 0x1B80, 0x54DF1305,
+ 0x1B80, 0x54DF1307,
+ 0x1B80, 0x00011315,
+ 0x1B80, 0x00011317,
+ 0x1B80, 0x55001325,
+ 0x1B80, 0x55001327,
+ 0x1B80, 0xE1231335,
+ 0x1B80, 0xE1231337,
+ 0x1B80, 0x54811345,
+ 0x1B80, 0x54811347,
+ 0x1B80, 0xE1231355,
+ 0x1B80, 0xE1231357,
+ 0x1B80, 0x54801365,
+ 0x1B80, 0x54801367,
+ 0x1B80, 0x002A1375,
+ 0x1B80, 0x002A1377,
+ 0x1B80, 0xE12B1385,
+ 0x1B80, 0xE12B1387,
+ 0x1B80, 0xE1231395,
+ 0x1B80, 0xE1231397,
+ 0x1B80, 0x548013A5,
+ 0x1B80, 0x548013A7,
+ 0x1B80, 0xE17213B5,
+ 0x1B80, 0xE17213B7,
+ 0x1B80, 0xBF3013C5,
+ 0x1B80, 0xBF3013C7,
+ 0x1B80, 0x000213D5,
+ 0x1B80, 0x000213D7,
+ 0x1B80, 0x302813E5,
+ 0x1B80, 0x302813E7,
+ 0x1B80, 0x4F7813F5,
+ 0x1B80, 0x4F7813F7,
+ 0x1B80, 0x4E001405,
+ 0x1B80, 0x4E001407,
+ 0x1B80, 0x53871415,
+ 0x1B80, 0x53871417,
+ 0x1B80, 0x52F11425,
+ 0x1B80, 0x52F11427,
+ 0x1B80, 0xE1161435,
+ 0x1B80, 0xE1161437,
+ 0x1B80, 0xE11B1445,
+ 0x1B80, 0xE11B1447,
+ 0x1B80, 0xE11F1455,
+ 0x1B80, 0xE11F1457,
+ 0x1B80, 0xE1271465,
+ 0x1B80, 0xE1271467,
+ 0x1B80, 0x54811475,
+ 0x1B80, 0x54811477,
+ 0x1B80, 0xE1161485,
+ 0x1B80, 0xE1161487,
+ 0x1B80, 0xE11B1495,
+ 0x1B80, 0xE11B1497,
+ 0x1B80, 0xE11F14A5,
+ 0x1B80, 0xE11F14A7,
+ 0x1B80, 0xE12714B5,
+ 0x1B80, 0xE12714B7,
+ 0x1B80, 0x548014C5,
+ 0x1B80, 0x548014C7,
+ 0x1B80, 0x002A14D5,
+ 0x1B80, 0x002A14D7,
+ 0x1B80, 0xE12B14E5,
+ 0x1B80, 0xE12B14E7,
+ 0x1B80, 0xE11614F5,
+ 0x1B80, 0xE11614F7,
+ 0x1B80, 0xE11B1505,
+ 0x1B80, 0xE11B1507,
+ 0x1B80, 0xE11F1515,
+ 0x1B80, 0xE11F1517,
+ 0x1B80, 0xE1271525,
+ 0x1B80, 0xE1271527,
+ 0x1B80, 0x54801535,
+ 0x1B80, 0x54801537,
+ 0x1B80, 0xE1721545,
+ 0x1B80, 0xE1721547,
+ 0x1B80, 0xBF171555,
+ 0x1B80, 0xBF171557,
+ 0x1B80, 0x00021565,
+ 0x1B80, 0x00021567,
+ 0x1B80, 0x30281575,
+ 0x1B80, 0x30281577,
+ 0x1B80, 0x06141585,
+ 0x1B80, 0x06141587,
+ 0x1B80, 0x73201595,
+ 0x1B80, 0x73201597,
+ 0x1B80, 0x720015A5,
+ 0x1B80, 0x720015A7,
+ 0x1B80, 0x710015B5,
+ 0x1B80, 0x710015B7,
+ 0x1B80, 0x550115C5,
+ 0x1B80, 0x550115C7,
+ 0x1B80, 0xE12315D5,
+ 0x1B80, 0xE12315D7,
+ 0x1B80, 0xE12715E5,
+ 0x1B80, 0xE12715E7,
+ 0x1B80, 0x548115F5,
+ 0x1B80, 0x548115F7,
+ 0x1B80, 0xE1231605,
+ 0x1B80, 0xE1231607,
+ 0x1B80, 0xE1271615,
+ 0x1B80, 0xE1271617,
+ 0x1B80, 0x54801625,
+ 0x1B80, 0x54801627,
+ 0x1B80, 0x002A1635,
+ 0x1B80, 0x002A1637,
+ 0x1B80, 0xE12B1645,
+ 0x1B80, 0xE12B1647,
+ 0x1B80, 0xE1231655,
+ 0x1B80, 0xE1231657,
+ 0x1B80, 0xE1271665,
+ 0x1B80, 0xE1271667,
+ 0x1B80, 0x54801675,
+ 0x1B80, 0x54801677,
+ 0x1B80, 0xE1721685,
+ 0x1B80, 0xE1721687,
+ 0x1B80, 0xBF031695,
+ 0x1B80, 0xBF031697,
+ 0x1B80, 0x000216A5,
+ 0x1B80, 0x000216A7,
+ 0x1B80, 0x302816B5,
+ 0x1B80, 0x302816B7,
+ 0x1B80, 0x54BF16C5,
+ 0x1B80, 0x54BF16C7,
+ 0x1B80, 0x54C516D5,
+ 0x1B80, 0x54C516D7,
+ 0x1B80, 0x050A16E5,
+ 0x1B80, 0x050A16E7,
+ 0x1B80, 0x071416F5,
+ 0x1B80, 0x071416F7,
+ 0x1B80, 0x54DF1705,
+ 0x1B80, 0x54DF1707,
+ 0x1B80, 0x00011715,
+ 0x1B80, 0x00011717,
+ 0x1B80, 0x54BF1725,
+ 0x1B80, 0x54BF1727,
+ 0x1B80, 0x54C01735,
+ 0x1B80, 0x54C01737,
+ 0x1B80, 0x54A31745,
+ 0x1B80, 0x54A31747,
+ 0x1B80, 0x54C11755,
+ 0x1B80, 0x54C11757,
+ 0x1B80, 0x54A41765,
+ 0x1B80, 0x54A41767,
+ 0x1B80, 0x4C831775,
+ 0x1B80, 0x4C831777,
+ 0x1B80, 0x4C031785,
+ 0x1B80, 0x4C031787,
+ 0x1B80, 0xBF0B1795,
+ 0x1B80, 0xBF0B1797,
+ 0x1B80, 0x54C217A5,
+ 0x1B80, 0x54C217A7,
+ 0x1B80, 0x54A417B5,
+ 0x1B80, 0x54A417B7,
+ 0x1B80, 0x4C8517C5,
+ 0x1B80, 0x4C8517C7,
+ 0x1B80, 0x4C0517D5,
+ 0x1B80, 0x4C0517D7,
+ 0x1B80, 0xBF0617E5,
+ 0x1B80, 0xBF0617E7,
+ 0x1B80, 0x54C117F5,
+ 0x1B80, 0x54C117F7,
+ 0x1B80, 0x54A31805,
+ 0x1B80, 0x54A31807,
+ 0x1B80, 0x4C861815,
+ 0x1B80, 0x4C861817,
+ 0x1B80, 0x4C061825,
+ 0x1B80, 0x4C061827,
+ 0x1B80, 0xBF011835,
+ 0x1B80, 0xBF011837,
+ 0x1B80, 0x54DF1845,
+ 0x1B80, 0x54DF1847,
+ 0x1B80, 0x00011855,
+ 0x1B80, 0x00011857,
+ 0x1B80, 0x00071865,
+ 0x1B80, 0x00071867,
+ 0x1B80, 0x54011875,
+ 0x1B80, 0x54011877,
+ 0x1B80, 0x00041885,
+ 0x1B80, 0x00041887,
+ 0x1B80, 0x56001895,
+ 0x1B80, 0x56001897,
+ 0x1B80, 0x5CF218A5,
+ 0x1B80, 0x5CF218A7,
+ 0x1B80, 0x630718B5,
+ 0x1B80, 0x630718B7,
+ 0x1B80, 0x620418C5,
+ 0x1B80, 0x620418C7,
+ 0x1B80, 0x610018D5,
+ 0x1B80, 0x610018D7,
+ 0x1B80, 0x670718E5,
+ 0x1B80, 0x670718E7,
+ 0x1B80, 0x660618F5,
+ 0x1B80, 0x660618F7,
+ 0x1B80, 0x6F201905,
+ 0x1B80, 0x6F201907,
+ 0x1B80, 0x6E001915,
+ 0x1B80, 0x6E001917,
+ 0x1B80, 0x6D001925,
+ 0x1B80, 0x6D001927,
+ 0x1B80, 0x6C031935,
+ 0x1B80, 0x6C031937,
+ 0x1B80, 0x73201945,
+ 0x1B80, 0x73201947,
+ 0x1B80, 0x72001955,
+ 0x1B80, 0x72001957,
+ 0x1B80, 0x71001965,
+ 0x1B80, 0x71001967,
+ 0x1B80, 0x7B201975,
+ 0x1B80, 0x7B201977,
+ 0x1B80, 0x7A001985,
+ 0x1B80, 0x7A001987,
+ 0x1B80, 0x79001995,
+ 0x1B80, 0x79001997,
+ 0x1B80, 0x7F2019A5,
+ 0x1B80, 0x7F2019A7,
+ 0x1B80, 0x7E0019B5,
+ 0x1B80, 0x7E0019B7,
+ 0x1B80, 0x7D0019C5,
+ 0x1B80, 0x7D0019C7,
+ 0x1B80, 0x090119D5,
+ 0x1B80, 0x090119D7,
+ 0x1B80, 0x0AC619E5,
+ 0x1B80, 0x0AC619E7,
+ 0x1B80, 0x0BA619F5,
+ 0x1B80, 0x0BA619F7,
+ 0x1B80, 0x0C011A05,
+ 0x1B80, 0x0C011A07,
+ 0x1B80, 0x0D021A15,
+ 0x1B80, 0x0D021A17,
+ 0x1B80, 0x0E041A25,
+ 0x1B80, 0x0E041A27,
+ 0x1B80, 0x0FFF1A35,
+ 0x1B80, 0x0FFF1A37,
+ 0x1B80, 0x4D041A45,
+ 0x1B80, 0x4D041A47,
+ 0x1B80, 0x28F81A55,
+ 0x1B80, 0x28F81A57,
+ 0x1B80, 0xE0001A65,
+ 0x1B80, 0xE0001A67,
+ 0x1B80, 0x4D001A75,
+ 0x1B80, 0x4D001A77,
+ 0x1B80, 0x00011A85,
+ 0x1B80, 0x00011A87,
+ 0x1B80, 0x4D041A95,
+ 0x1B80, 0x4D041A97,
+ 0x1B80, 0x2EF81AA5,
+ 0x1B80, 0x2EF81AA7,
+ 0x1B80, 0x00021AB5,
+ 0x1B80, 0x00021AB7,
+ 0x1B80, 0x23031AC5,
+ 0x1B80, 0x23031AC7,
+ 0x1B80, 0x00001AD5,
+ 0x1B80, 0x00001AD7,
+ 0x1B80, 0x23131AE5,
+ 0x1B80, 0x23131AE7,
+ 0x1B80, 0xE77F1AF5,
+ 0x1B80, 0xE77F1AF7,
+ 0x1B80, 0x232F1B05,
+ 0x1B80, 0x232F1B07,
+ 0x1B80, 0xEFBF1B15,
+ 0x1B80, 0xEFBF1B17,
+ 0x1B80, 0x2EF01B25,
+ 0x1B80, 0x2EF01B27,
+ 0x1B80, 0x00021B35,
+ 0x1B80, 0x00021B37,
+ 0x1B80, 0x4D001B45,
+ 0x1B80, 0x4D001B47,
+ 0x1B80, 0x00011B55,
+ 0x1B80, 0x00011B57,
+ 0x1B80, 0x4D041B65,
+ 0x1B80, 0x4D041B67,
+ 0x1B80, 0x2EF81B75,
+ 0x1B80, 0x2EF81B77,
+ 0x1B80, 0x00021B85,
+ 0x1B80, 0x00021B87,
+ 0x1B80, 0x23031B95,
+ 0x1B80, 0x23031B97,
+ 0x1B80, 0x00001BA5,
+ 0x1B80, 0x00001BA7,
+ 0x1B80, 0x23131BB5,
+ 0x1B80, 0x23131BB7,
+ 0x1B80, 0xE77F1BC5,
+ 0x1B80, 0xE77F1BC7,
+ 0x1B80, 0x232F1BD5,
+ 0x1B80, 0x232F1BD7,
+ 0x1B80, 0xE79F1BE5,
+ 0x1B80, 0xE79F1BE7,
+ 0x1B80, 0x2EF01BF5,
+ 0x1B80, 0x2EF01BF7,
+ 0x1B80, 0x00021C05,
+ 0x1B80, 0x00021C07,
+ 0x1B80, 0x28F81C15,
+ 0x1B80, 0x28F81C17,
+ 0x1B80, 0x80001C25,
+ 0x1B80, 0x80001C27,
+ 0x1B80, 0x4D001C35,
+ 0x1B80, 0x4D001C37,
+ 0x1B80, 0x00011C45,
+ 0x1B80, 0x00011C47,
+ 0x1B80, 0x00041C55,
+ 0x1B80, 0x00041C57,
+ 0x1B80, 0x6BC01C65,
+ 0x1B80, 0x6BC01C67,
+ 0x1B80, 0x4D041C75,
+ 0x1B80, 0x4D041C77,
+ 0x80000001, 0x00000000, 0x40000000, 0x00000000,
+ 0x1B80, 0x68241C85,
+ 0x1B80, 0x68241C87,
+ 0x90000002, 0x00000000, 0x40000000, 0x00000000,
+ 0x1B80, 0x68241C85,
+ 0x1B80, 0x68241C87,
+ 0x90000004, 0x00000000, 0x40000000, 0x00000000,
+ 0x1B80, 0x68241C85,
+ 0x1B80, 0x68241C87,
+ 0x90000003, 0x00000000, 0x40000000, 0x00000000,
+ 0x1B80, 0x68241C85,
+ 0x1B80, 0x68241C87,
+ 0x90000005, 0x00000000, 0x40000000, 0x00000000,
+ 0x1B80, 0x68241C85,
+ 0x1B80, 0x68241C87,
+ 0x90000006, 0x00000000, 0x40000000, 0x00000000,
+ 0x1B80, 0x68241C85,
+ 0x1B80, 0x68241C87,
+ 0x9000000a, 0x00000000, 0x40000000, 0x00000000,
+ 0x1B80, 0x68241C85,
+ 0x1B80, 0x68241C87,
+ 0x9000000b, 0x00000000, 0x40000000, 0x00000000,
+ 0x1B80, 0x68241C85,
+ 0x1B80, 0x68241C87,
+ 0xA0000000, 0x00000000,
+ 0x1B80, 0x68481C85,
+ 0x1B80, 0x68481C87,
+ 0xB0000000, 0x00000000,
+ 0x1B80, 0x66061C95,
+ 0x1B80, 0x66061C97,
+ 0x80000001, 0x00000000, 0x40000000, 0x00000000,
+ 0x1B80, 0x650C1CA5,
+ 0x1B80, 0x650C1CA7,
+ 0x90000002, 0x00000000, 0x40000000, 0x00000000,
+ 0x1B80, 0x650C1CA5,
+ 0x1B80, 0x650C1CA7,
+ 0x90000004, 0x00000000, 0x40000000, 0x00000000,
+ 0x1B80, 0x650C1CA5,
+ 0x1B80, 0x650C1CA7,
+ 0x90000003, 0x00000000, 0x40000000, 0x00000000,
+ 0x1B80, 0x650C1CA5,
+ 0x1B80, 0x650C1CA7,
+ 0x90000005, 0x00000000, 0x40000000, 0x00000000,
+ 0x1B80, 0x650C1CA5,
+ 0x1B80, 0x650C1CA7,
+ 0x90000006, 0x00000000, 0x40000000, 0x00000000,
+ 0x1B80, 0x650C1CA5,
+ 0x1B80, 0x650C1CA7,
+ 0x9000000a, 0x00000000, 0x40000000, 0x00000000,
+ 0x1B80, 0x650C1CA5,
+ 0x1B80, 0x650C1CA7,
+ 0x9000000b, 0x00000000, 0x40000000, 0x00000000,
+ 0x1B80, 0x650C1CA5,
+ 0x1B80, 0x650C1CA7,
+ 0xA0000000, 0x00000000,
+ 0x1B80, 0x65041CA5,
+ 0x1B80, 0x65041CA7,
+ 0xB0000000, 0x00000000,
+ 0x1B80, 0x64471CB5,
+ 0x1B80, 0x64471CB7,
+ 0x1B80, 0x23411CC5,
+ 0x1B80, 0x23411CC7,
+ 0x1B80, 0x100E1CD5,
+ 0x1B80, 0x100E1CD7,
+ 0x80000001, 0x00000000, 0x40000000, 0x00000000,
+ 0x1B80, 0x60101CE5,
+ 0x1B80, 0x60101CE7,
+ 0x90000002, 0x00000000, 0x40000000, 0x00000000,
+ 0x1B80, 0x60101CE5,
+ 0x1B80, 0x60101CE7,
+ 0x90000004, 0x00000000, 0x40000000, 0x00000000,
+ 0x1B80, 0x60101CE5,
+ 0x1B80, 0x60101CE7,
+ 0x90000003, 0x00000000, 0x40000000, 0x00000000,
+ 0x1B80, 0x60101CE5,
+ 0x1B80, 0x60101CE7,
+ 0x90000005, 0x00000000, 0x40000000, 0x00000000,
+ 0x1B80, 0x60101CE5,
+ 0x1B80, 0x60101CE7,
+ 0x90000006, 0x00000000, 0x40000000, 0x00000000,
+ 0x1B80, 0x60101CE5,
+ 0x1B80, 0x60101CE7,
+ 0x9000000a, 0x00000000, 0x40000000, 0x00000000,
+ 0x1B80, 0x60101CE5,
+ 0x1B80, 0x60101CE7,
+ 0x9000000b, 0x00000000, 0x40000000, 0x00000000,
+ 0x1B80, 0x60101CE5,
+ 0x1B80, 0x60101CE7,
+ 0xA0000000, 0x00000000,
+ 0x1B80, 0x60011CE5,
+ 0x1B80, 0x60011CE7,
+ 0xB0000000, 0x00000000,
+ 0x1B80, 0x23411CF5,
+ 0x1B80, 0x23411CF7,
+ 0x80000001, 0x00000000, 0x40000000, 0x00000000,
+ 0x1B80, 0x60811D05,
+ 0x1B80, 0x60811D07,
+ 0x90000002, 0x00000000, 0x40000000, 0x00000000,
+ 0x1B80, 0x60811D05,
+ 0x1B80, 0x60811D07,
+ 0x90000004, 0x00000000, 0x40000000, 0x00000000,
+ 0x1B80, 0x60811D05,
+ 0x1B80, 0x60811D07,
+ 0x90000003, 0x00000000, 0x40000000, 0x00000000,
+ 0x1B80, 0x60811D05,
+ 0x1B80, 0x60811D07,
+ 0x90000005, 0x00000000, 0x40000000, 0x00000000,
+ 0x1B80, 0x60811D05,
+ 0x1B80, 0x60811D07,
+ 0x90000006, 0x00000000, 0x40000000, 0x00000000,
+ 0x1B80, 0x60811D05,
+ 0x1B80, 0x60811D07,
+ 0x9000000a, 0x00000000, 0x40000000, 0x00000000,
+ 0x1B80, 0x60811D05,
+ 0x1B80, 0x60811D07,
+ 0x9000000b, 0x00000000, 0x40000000, 0x00000000,
+ 0x1B80, 0x60811D05,
+ 0x1B80, 0x60811D07,
+ 0xA0000000, 0x00000000,
+ 0x1B80, 0x60611D05,
+ 0x1B80, 0x60611D07,
+ 0xB0000000, 0x00000000,
+ 0x1B80, 0x23411D15,
+ 0x1B80, 0x23411D17,
+ 0x1B80, 0x70E11D25,
+ 0x1B80, 0x70E11D27,
+ 0x1B80, 0x4D001D35,
+ 0x1B80, 0x4D001D37,
+ 0x1B80, 0x00011D45,
+ 0x1B80, 0x00011D47,
+ 0x1B80, 0x00041D55,
+ 0x1B80, 0x00041D57,
+ 0x1B80, 0x6B401D65,
+ 0x1B80, 0x6B401D67,
+ 0x1B80, 0x4D041D75,
+ 0x1B80, 0x4D041D77,
+ 0x80000002, 0x00000000, 0x40000000, 0x00000000,
+ 0x1B80, 0x68241D85,
+ 0x1B80, 0x68241D87,
+ 0x90000004, 0x00000000, 0x40000000, 0x00000000,
+ 0x1B80, 0x68241D85,
+ 0x1B80, 0x68241D87,
+ 0x90000003, 0x00000000, 0x40000000, 0x00000000,
+ 0x1B80, 0x68241D85,
+ 0x1B80, 0x68241D87,
+ 0x90000005, 0x00000000, 0x40000000, 0x00000000,
+ 0x1B80, 0x68241D85,
+ 0x1B80, 0x68241D87,
+ 0x9000000a, 0x00000000, 0x40000000, 0x00000000,
+ 0x1B80, 0x68241D85,
+ 0x1B80, 0x68241D87,
+ 0x9000000b, 0x00000000, 0x40000000, 0x00000000,
+ 0x1B80, 0x68241D85,
+ 0x1B80, 0x68241D87,
+ 0xA0000000, 0x00000000,
+ 0x1B80, 0x68481D85,
+ 0x1B80, 0x68481D87,
+ 0xB0000000, 0x00000000,
+ 0x1B80, 0x66061D95,
+ 0x1B80, 0x66061D97,
+ 0x80000001, 0x00000000, 0x40000000, 0x00000000,
+ 0x1B80, 0x65081DA5,
+ 0x1B80, 0x65081DA7,
+ 0x90000002, 0x00000000, 0x40000000, 0x00000000,
+ 0x1B80, 0x65181DA5,
+ 0x1B80, 0x65181DA7,
+ 0x90000004, 0x00000000, 0x40000000, 0x00000000,
+ 0x1B80, 0x65181DA5,
+ 0x1B80, 0x65181DA7,
+ 0x90000003, 0x00000000, 0x40000000, 0x00000000,
+ 0x1B80, 0x65181DA5,
+ 0x1B80, 0x65181DA7,
+ 0x90000005, 0x00000000, 0x40000000, 0x00000000,
+ 0x1B80, 0x65181DA5,
+ 0x1B80, 0x65181DA7,
+ 0x90000006, 0x00000000, 0x40000000, 0x00000000,
+ 0x1B80, 0x65081DA5,
+ 0x1B80, 0x65081DA7,
+ 0x9000000a, 0x00000000, 0x40000000, 0x00000000,
+ 0x1B80, 0x65181DA5,
+ 0x1B80, 0x65181DA7,
+ 0x9000000b, 0x00000000, 0x40000000, 0x00000000,
+ 0x1B80, 0x65181DA5,
+ 0x1B80, 0x65181DA7,
+ 0xA0000000, 0x00000000,
+ 0x1B80, 0x65081DA5,
+ 0x1B80, 0x65081DA7,
+ 0xB0000000, 0x00000000,
+ 0x80000002, 0x00000000, 0x40000000, 0x00000000,
+ 0x1B80, 0x64481DB5,
+ 0x1B80, 0x64481DB7,
+ 0x90000004, 0x00000000, 0x40000000, 0x00000000,
+ 0x1B80, 0x64481DB5,
+ 0x1B80, 0x64481DB7,
+ 0x90000003, 0x00000000, 0x40000000, 0x00000000,
+ 0x1B80, 0x64481DB5,
+ 0x1B80, 0x64481DB7,
+ 0x90000005, 0x00000000, 0x40000000, 0x00000000,
+ 0x1B80, 0x64481DB5,
+ 0x1B80, 0x64481DB7,
+ 0x9000000a, 0x00000000, 0x40000000, 0x00000000,
+ 0x1B80, 0x64481DB5,
+ 0x1B80, 0x64481DB7,
+ 0x9000000b, 0x00000000, 0x40000000, 0x00000000,
+ 0x1B80, 0x64481DB5,
+ 0x1B80, 0x64481DB7,
+ 0xA0000000, 0x00000000,
+ 0x1B80, 0x64471DB5,
+ 0x1B80, 0x64471DB7,
+ 0xB0000000, 0x00000000,
+ 0x1B80, 0x23411DC5,
+ 0x1B80, 0x23411DC7,
+ 0x80000001, 0x00000000, 0x40000000, 0x00000000,
+ 0x1B80, 0x11E41DD5,
+ 0x1B80, 0x11E41DD7,
+ 0x90000002, 0x00000000, 0x40000000, 0x00000000,
+ 0x1B80, 0x11E81DD5,
+ 0x1B80, 0x11E81DD7,
+ 0x90000004, 0x00000000, 0x40000000, 0x00000000,
+ 0x1B80, 0x11E81DD5,
+ 0x1B80, 0x11E81DD7,
+ 0x90000003, 0x00000000, 0x40000000, 0x00000000,
+ 0x1B80, 0x11E81DD5,
+ 0x1B80, 0x11E81DD7,
+ 0x90000005, 0x00000000, 0x40000000, 0x00000000,
+ 0x1B80, 0x11E81DD5,
+ 0x1B80, 0x11E81DD7,
+ 0x90000006, 0x00000000, 0x40000000, 0x00000000,
+ 0x1B80, 0x11E41DD5,
+ 0x1B80, 0x11E41DD7,
+ 0x9000000a, 0x00000000, 0x40000000, 0x00000000,
+ 0x1B80, 0x11E81DD5,
+ 0x1B80, 0x11E81DD7,
+ 0x9000000b, 0x00000000, 0x40000000, 0x00000000,
+ 0x1B80, 0x11E81DD5,
+ 0x1B80, 0x11E81DD7,
+ 0xA0000000, 0x00000000,
+ 0x1B80, 0x11E41DD5,
+ 0x1B80, 0x11E41DD7,
+ 0xB0000000, 0x00000000,
+ 0x1B80, 0x60011DE5,
+ 0x1B80, 0x60011DE7,
+ 0x1B80, 0x23411DF5,
+ 0x1B80, 0x23411DF7,
+ 0x80000001, 0x00000000, 0x40000000, 0x00000000,
+ 0x1B80, 0x60E11E05,
+ 0x1B80, 0x60E11E07,
+ 0x90000002, 0x00000000, 0x40000000, 0x00000000,
+ 0x1B80, 0x61E11E05,
+ 0x1B80, 0x61E11E07,
+ 0x90000004, 0x00000000, 0x40000000, 0x00000000,
+ 0x1B80, 0x61E11E05,
+ 0x1B80, 0x61E11E07,
+ 0x90000003, 0x00000000, 0x40000000, 0x00000000,
+ 0x1B80, 0x61E11E05,
+ 0x1B80, 0x61E11E07,
+ 0x90000005, 0x00000000, 0x40000000, 0x00000000,
+ 0x1B80, 0x61E11E05,
+ 0x1B80, 0x61E11E07,
+ 0x90000006, 0x00000000, 0x40000000, 0x00000000,
+ 0x1B80, 0x60E11E05,
+ 0x1B80, 0x60E11E07,
+ 0x9000000a, 0x00000000, 0x40000000, 0x00000000,
+ 0x1B80, 0x61E11E05,
+ 0x1B80, 0x61E11E07,
+ 0x9000000b, 0x00000000, 0x40000000, 0x00000000,
+ 0x1B80, 0x61E11E05,
+ 0x1B80, 0x61E11E07,
+ 0xA0000000, 0x00000000,
+ 0x1B80, 0x60E11E05,
+ 0x1B80, 0x60E11E07,
+ 0xB0000000, 0x00000000,
+ 0x1B80, 0x23411E15,
+ 0x1B80, 0x23411E17,
+ 0x1B80, 0x70611E25,
+ 0x1B80, 0x70611E27,
+ 0x1B80, 0x4D001E35,
+ 0x1B80, 0x4D001E37,
+ 0x1B80, 0x00011E45,
+ 0x1B80, 0x00011E47,
+ 0x1B80, 0x00001E55,
+ 0x1B80, 0x00001E57,
+ 0x1B80, 0x00001E65,
+ 0x1B80, 0x00001E67,
+ 0x1B80, 0x00001E75,
+ 0x1B80, 0x00001E77,
+ 0x1B80, 0x00001E85,
+ 0x1B80, 0x00001E87,
+ 0x1B80, 0x00001E95,
+ 0x1B80, 0x00001E97,
+ 0x1B80, 0x00001EA5,
+ 0x1B80, 0x00001EA7,
+ 0x1B80, 0x00001EB5,
+ 0x1B80, 0x00001EB7,
+ 0x1B80, 0x00001EC5,
+ 0x1B80, 0x00001EC7,
+ 0x1B80, 0x00001ED5,
+ 0x1B80, 0x00001ED7,
+ 0x1B80, 0x00001EE5,
+ 0x1B80, 0x00001EE7,
+ 0x1B80, 0x00001EF5,
+ 0x1B80, 0x00001EF7,
+ 0x1B80, 0x00001F05,
+ 0x1B80, 0x00001F07,
+ 0x1B80, 0x00001F15,
+ 0x1B80, 0x00001F17,
+ 0x1B80, 0x00001F25,
+ 0x1B80, 0x00001F27,
+ 0x1B80, 0x00001F35,
+ 0x1B80, 0x00001F37,
+ 0x1B80, 0x00001F45,
+ 0x1B80, 0x00001F47,
+ 0x1B80, 0x00001F55,
+ 0x1B80, 0x00001F57,
+ 0x1B80, 0x00001F65,
+ 0x1B80, 0x00001F67,
+ 0x1B80, 0x00001F75,
+ 0x1B80, 0x00001F77,
+ 0x1B80, 0x00001F85,
+ 0x1B80, 0x00001F87,
+ 0x1B80, 0x00001F95,
+ 0x1B80, 0x00001F97,
+ 0x1B80, 0x00001FA5,
+ 0x1B80, 0x00001FA7,
+ 0x1B80, 0x00001FB5,
+ 0x1B80, 0x00001FB7,
+ 0x1B80, 0x00001FC5,
+ 0x1B80, 0x00001FC7,
+ 0x1B80, 0x00001FD5,
+ 0x1B80, 0x00001FD7,
+ 0x1B80, 0x00001FE5,
+ 0x1B80, 0x00001FE7,
+ 0x1B80, 0x00001FF5,
+ 0x1B80, 0x00001FF7,
+ 0x1B80, 0x00000006,
+ 0x1B80, 0x00000002,
+};
+
+RTW_DECL_TABLE_PHY_COND(rtw8814a_bb, rtw_phy_cfg_bb);
+
+static const struct rtw_phy_pg_cfg_pair rtw8814a_bb_pg[] = {
+ { 0, 0, 0, 0x00000c20, 0xffffffff, 0x34343434, },
+ { 0, 0, 0, 0x00000c24, 0xffffffff, 0x34343434, },
+ { 0, 0, 0, 0x00000c28, 0xffffffff, 0x30323434, },
+ { 0, 0, 0, 0x00000c2c, 0xffffffff, 0x34343434, },
+ { 0, 0, 0, 0x00000c30, 0xffffffff, 0x28303234, },
+ { 0, 0, 1, 0x00000c34, 0xffffffff, 0x32323232, },
+ { 0, 0, 1, 0x00000c38, 0xffffffff, 0x26283032, },
+ { 0, 0, 2, 0x00000cd8, 0xffffffff, 0x30303030, },
+ { 0, 0, 2, 0x00000cdc, 0xffffffff, 0x24262830, },
+ { 0, 0, 0, 0x00000c3c, 0xffffffff, 0x34343434, },
+ { 0, 0, 0, 0x00000c40, 0xffffffff, 0x28303234, },
+ { 0, 0, 0, 0x00000c44, 0xffffffff, 0x32322426, },
+ { 0, 0, 1, 0x00000c48, 0xffffffff, 0x30323232, },
+ { 0, 0, 1, 0x00000c4c, 0xffffffff, 0x22242628, },
+ { 0, 0, 2, 0x00000ce0, 0xffffffff, 0x30303030, },
+ { 0, 0, 2, 0x00000ce4, 0xffffffff, 0x24262830, },
+ { 0, 0, 2, 0x00000ce8, 0x0000ffff, 0x20222222, },
+ { 0, 1, 0, 0x00000e20, 0xffffffff, 0x34343434, },
+ { 0, 1, 0, 0x00000e24, 0xffffffff, 0x34343434, },
+ { 0, 1, 0, 0x00000e28, 0xffffffff, 0x30323434, },
+ { 0, 1, 0, 0x00000e2c, 0xffffffff, 0x34343434, },
+ { 0, 1, 0, 0x00000e30, 0xffffffff, 0x28303234, },
+ { 0, 1, 1, 0x00000e34, 0xffffffff, 0x32323232, },
+ { 0, 1, 1, 0x00000e38, 0xffffffff, 0x26283032, },
+ { 0, 1, 2, 0x00000ed8, 0xffffffff, 0x30303030, },
+ { 0, 1, 2, 0x00000edc, 0xffffffff, 0x24262830, },
+ { 0, 1, 0, 0x00000e3c, 0xffffffff, 0x34343434, },
+ { 0, 1, 0, 0x00000e40, 0xffffffff, 0x28303234, },
+ { 0, 1, 0, 0x00000e44, 0xffffffff, 0x32322426, },
+ { 0, 1, 1, 0x00000e48, 0xffffffff, 0x30323232, },
+ { 0, 1, 1, 0x00000e4c, 0xffffffff, 0x22242628, },
+ { 0, 1, 2, 0x00000ee0, 0xffffffff, 0x30303030, },
+ { 0, 1, 2, 0x00000ee4, 0xffffffff, 0x24262830, },
+ { 0, 1, 2, 0x00000ee8, 0x0000ffff, 0x20222222, },
+ { 0, 2, 0, 0x00001820, 0xffffffff, 0x34343434, },
+ { 0, 2, 0, 0x00001824, 0xffffffff, 0x34343434, },
+ { 0, 2, 0, 0x00001828, 0xffffffff, 0x30323434, },
+ { 0, 2, 0, 0x0000182c, 0xffffffff, 0x34343434, },
+ { 0, 2, 0, 0x00001830, 0xffffffff, 0x28303234, },
+ { 0, 2, 1, 0x00001834, 0xffffffff, 0x32323232, },
+ { 0, 2, 1, 0x00001838, 0xffffffff, 0x26283032, },
+ { 0, 2, 2, 0x000018d8, 0xffffffff, 0x30303030, },
+ { 0, 2, 2, 0x000018dc, 0xffffffff, 0x24262830, },
+ { 0, 2, 0, 0x0000183c, 0xffffffff, 0x34343434, },
+ { 0, 2, 0, 0x00001840, 0xffffffff, 0x28303234, },
+ { 0, 2, 0, 0x00001844, 0xffffffff, 0x32322426, },
+ { 0, 2, 1, 0x00001848, 0xffffffff, 0x30323232, },
+ { 0, 2, 1, 0x0000184c, 0xffffffff, 0x22242628, },
+ { 0, 2, 2, 0x000018e0, 0xffffffff, 0x30303030, },
+ { 0, 2, 2, 0x000018e4, 0xffffffff, 0x24262830, },
+ { 0, 2, 2, 0x000018e8, 0x0000ffff, 0x20222222, },
+ { 0, 3, 0, 0x00001a20, 0xffffffff, 0x34343434, },
+ { 0, 3, 0, 0x00001a24, 0xffffffff, 0x34343434, },
+ { 0, 3, 0, 0x00001a28, 0xffffffff, 0x30323434, },
+ { 0, 3, 0, 0x00001a2c, 0xffffffff, 0x34343434, },
+ { 0, 3, 0, 0x00001a30, 0xffffffff, 0x28303234, },
+ { 0, 3, 1, 0x00001a34, 0xffffffff, 0x32323232, },
+ { 0, 3, 1, 0x00001a38, 0xffffffff, 0x26283032, },
+ { 0, 3, 2, 0x00001ad8, 0xffffffff, 0x30303030, },
+ { 0, 3, 2, 0x00001adc, 0xffffffff, 0x24262830, },
+ { 0, 3, 0, 0x00001a3c, 0xffffffff, 0x34343434, },
+ { 0, 3, 0, 0x00001a40, 0xffffffff, 0x28303234, },
+ { 0, 3, 0, 0x00001a44, 0xffffffff, 0x32322426, },
+ { 0, 3, 1, 0x00001a48, 0xffffffff, 0x30323232, },
+ { 0, 3, 1, 0x00001a4c, 0xffffffff, 0x22242628, },
+ { 0, 3, 2, 0x00001ae0, 0xffffffff, 0x30303030, },
+ { 0, 3, 2, 0x00001ae4, 0xffffffff, 0x24262830, },
+ { 0, 3, 2, 0x00001ae8, 0x0000ffff, 0x20222222, },
+ { 1, 0, 0, 0x00000c24, 0xffffffff, 0x34343434, },
+ { 1, 0, 0, 0x00000c28, 0xffffffff, 0x30323434, },
+ { 1, 0, 0, 0x00000c2c, 0xffffffff, 0x34343434, },
+ { 1, 0, 0, 0x00000c30, 0xffffffff, 0x28303234, },
+ { 1, 0, 1, 0x00000c34, 0xffffffff, 0x32323232, },
+ { 1, 0, 1, 0x00000c38, 0xffffffff, 0x26283032, },
+ { 1, 0, 2, 0x00000cd8, 0xffffffff, 0x30303030, },
+ { 1, 0, 2, 0x00000cdc, 0xffffffff, 0x24262830, },
+ { 1, 0, 0, 0x00000c3c, 0xffffffff, 0x34343434, },
+ { 1, 0, 0, 0x00000c40, 0xffffffff, 0x28303234, },
+ { 1, 0, 0, 0x00000c44, 0xffffffff, 0x32322426, },
+ { 1, 0, 1, 0x00000c48, 0xffffffff, 0x30323232, },
+ { 1, 0, 1, 0x00000c4c, 0xffffffff, 0x22242628, },
+ { 1, 0, 2, 0x00000ce0, 0xffffffff, 0x30303030, },
+ { 1, 0, 2, 0x00000ce4, 0xffffffff, 0x24262830, },
+ { 1, 0, 2, 0x00000ce8, 0x0000ffff, 0x20222222, },
+ { 1, 1, 0, 0x00000e24, 0xffffffff, 0x34343434, },
+ { 1, 1, 0, 0x00000e28, 0xffffffff, 0x30323434, },
+ { 1, 1, 0, 0x00000e2c, 0xffffffff, 0x34343434, },
+ { 1, 1, 0, 0x00000e30, 0xffffffff, 0x28303234, },
+ { 1, 1, 1, 0x00000e34, 0xffffffff, 0x32323232, },
+ { 1, 1, 1, 0x00000e38, 0xffffffff, 0x26283032, },
+ { 1, 1, 2, 0x00000ed8, 0xffffffff, 0x30303030, },
+ { 1, 1, 2, 0x00000edc, 0xffffffff, 0x24262830, },
+ { 1, 1, 0, 0x00000e3c, 0xffffffff, 0x34343434, },
+ { 1, 1, 0, 0x00000e40, 0xffffffff, 0x28303234, },
+ { 1, 1, 0, 0x00000e44, 0xffffffff, 0x32322426, },
+ { 1, 1, 1, 0x00000e48, 0xffffffff, 0x30323232, },
+ { 1, 1, 1, 0x00000e4c, 0xffffffff, 0x22242628, },
+ { 1, 1, 2, 0x00000ee0, 0xffffffff, 0x30303030, },
+ { 1, 1, 2, 0x00000ee4, 0xffffffff, 0x24262830, },
+ { 1, 1, 2, 0x00000ee8, 0x0000ffff, 0x20222222, },
+ { 1, 2, 0, 0x00001824, 0xffffffff, 0x34343434, },
+ { 1, 2, 0, 0x00001828, 0xffffffff, 0x30323434, },
+ { 1, 2, 0, 0x0000182c, 0xffffffff, 0x34343434, },
+ { 1, 2, 0, 0x00001830, 0xffffffff, 0x28303234, },
+ { 1, 2, 1, 0x00001834, 0xffffffff, 0x32323232, },
+ { 1, 2, 1, 0x00001838, 0xffffffff, 0x26283032, },
+ { 1, 2, 2, 0x000018d8, 0xffffffff, 0x30303030, },
+ { 1, 2, 2, 0x000018dc, 0xffffffff, 0x24262830, },
+ { 1, 2, 0, 0x0000183c, 0xffffffff, 0x34343434, },
+ { 1, 2, 0, 0x00001840, 0xffffffff, 0x28303234, },
+ { 1, 2, 0, 0x00001844, 0xffffffff, 0x32322426, },
+ { 1, 2, 1, 0x00001848, 0xffffffff, 0x30323232, },
+ { 1, 2, 1, 0x0000184c, 0xffffffff, 0x22242628, },
+ { 1, 2, 2, 0x000018e0, 0xffffffff, 0x30303030, },
+ { 1, 2, 2, 0x000018e4, 0xffffffff, 0x24262830, },
+ { 1, 2, 2, 0x000018e8, 0x0000ffff, 0x20222222, },
+ { 1, 3, 0, 0x00001a24, 0xffffffff, 0x34343434, },
+ { 1, 3, 0, 0x00001a28, 0xffffffff, 0x30323434, },
+ { 1, 3, 0, 0x00001a2c, 0xffffffff, 0x34343434, },
+ { 1, 3, 0, 0x00001a30, 0xffffffff, 0x28303234, },
+ { 1, 3, 1, 0x00001a34, 0xffffffff, 0x32323232, },
+ { 1, 3, 1, 0x00001a38, 0xffffffff, 0x26283032, },
+ { 1, 3, 2, 0x00001ad8, 0xffffffff, 0x30303030, },
+ { 1, 3, 2, 0x00001adc, 0xffffffff, 0x24262830, },
+ { 1, 3, 0, 0x00001a3c, 0xffffffff, 0x34343434, },
+ { 1, 3, 0, 0x00001a40, 0xffffffff, 0x28303234, },
+ { 1, 3, 0, 0x00001a44, 0xffffffff, 0x32322426, },
+ { 1, 3, 1, 0x00001a48, 0xffffffff, 0x30323232, },
+ { 1, 3, 1, 0x00001a4c, 0xffffffff, 0x22242628, },
+ { 1, 3, 2, 0x00001ae0, 0xffffffff, 0x30303030, },
+ { 1, 3, 2, 0x00001ae4, 0xffffffff, 0x24262830, },
+ { 1, 3, 2, 0x00001ae8, 0x0000ffff, 0x20222222, },
+};
+
+RTW_DECL_TABLE_BB_PG(rtw8814a_bb_pg);
+
+static const struct rtw_phy_pg_cfg_pair rtw8814a_bb_pg_type0[] = {
+ { 0, 0, 0, 0x00000c20, 0xffffffff, 0x32323232, },
+ { 0, 0, 0, 0x00000c24, 0xffffffff, 0x32323232, },
+ { 0, 0, 0, 0x00000c28, 0xffffffff, 0x28303232, },
+ { 0, 0, 0, 0x00000c2c, 0xffffffff, 0x32323232, },
+ { 0, 0, 0, 0x00000c30, 0xffffffff, 0x26283032, },
+ { 0, 0, 1, 0x00000c34, 0xffffffff, 0x30303030, },
+ { 0, 0, 1, 0x00000c38, 0xffffffff, 0x24262830, },
+ { 0, 0, 2, 0x00000cd8, 0xffffffff, 0x28282828, },
+ { 0, 0, 2, 0x00000cdc, 0xffffffff, 0x22242628, },
+ { 0, 0, 0, 0x00000c3c, 0xffffffff, 0x32323232, },
+ { 0, 0, 0, 0x00000c40, 0xffffffff, 0x26283032, },
+ { 0, 0, 0, 0x00000c44, 0xffffffff, 0x30302224, },
+ { 0, 0, 1, 0x00000c48, 0xffffffff, 0x28303030, },
+ { 0, 0, 1, 0x00000c4c, 0xffffffff, 0x20222426, },
+ { 0, 0, 2, 0x00000ce0, 0xffffffff, 0x28282828, },
+ { 0, 0, 2, 0x00000ce4, 0xffffffff, 0x22242628, },
+ { 0, 0, 2, 0x00000ce8, 0x0000ffff, 0x18202020, },
+ { 0, 1, 0, 0x00000e20, 0xffffffff, 0x32323232, },
+ { 0, 1, 0, 0x00000e24, 0xffffffff, 0x32323232, },
+ { 0, 1, 0, 0x00000e28, 0xffffffff, 0x28303232, },
+ { 0, 1, 0, 0x00000e2c, 0xffffffff, 0x32323232, },
+ { 0, 1, 0, 0x00000e30, 0xffffffff, 0x26283032, },
+ { 0, 1, 1, 0x00000e34, 0xffffffff, 0x30303030, },
+ { 0, 1, 1, 0x00000e38, 0xffffffff, 0x24262830, },
+ { 0, 1, 2, 0x00000ed8, 0xffffffff, 0x28282828, },
+ { 0, 1, 2, 0x00000edc, 0xffffffff, 0x22242628, },
+ { 0, 1, 0, 0x00000e3c, 0xffffffff, 0x32323232, },
+ { 0, 1, 0, 0x00000e40, 0xffffffff, 0x26283032, },
+ { 0, 1, 0, 0x00000e44, 0xffffffff, 0x30302224, },
+ { 0, 1, 1, 0x00000e48, 0xffffffff, 0x28303030, },
+ { 0, 1, 1, 0x00000e4c, 0xffffffff, 0x20222426, },
+ { 0, 1, 2, 0x00000ee0, 0xffffffff, 0x28282828, },
+ { 0, 1, 2, 0x00000ee4, 0xffffffff, 0x22242628, },
+ { 0, 1, 2, 0x00000ee8, 0x0000ffff, 0x18202020, },
+ { 0, 2, 0, 0x00001820, 0xffffffff, 0x32323232, },
+ { 0, 2, 0, 0x00001824, 0xffffffff, 0x32323232, },
+ { 0, 2, 0, 0x00001828, 0xffffffff, 0x28303232, },
+ { 0, 2, 0, 0x0000182c, 0xffffffff, 0x32323232, },
+ { 0, 2, 0, 0x00001830, 0xffffffff, 0x26283032, },
+ { 0, 2, 1, 0x00001834, 0xffffffff, 0x30303030, },
+ { 0, 2, 1, 0x00001838, 0xffffffff, 0x24262830, },
+ { 0, 2, 2, 0x000018d8, 0xffffffff, 0x28282828, },
+ { 0, 2, 2, 0x000018dc, 0xffffffff, 0x22242628, },
+ { 0, 2, 0, 0x0000183c, 0xffffffff, 0x32323232, },
+ { 0, 2, 0, 0x00001840, 0xffffffff, 0x26283032, },
+ { 0, 2, 0, 0x00001844, 0xffffffff, 0x30302224, },
+ { 0, 2, 1, 0x00001848, 0xffffffff, 0x28303030, },
+ { 0, 2, 1, 0x0000184c, 0xffffffff, 0x20222426, },
+ { 0, 2, 2, 0x000018e0, 0xffffffff, 0x28282828, },
+ { 0, 2, 2, 0x000018e4, 0xffffffff, 0x22242628, },
+ { 0, 2, 2, 0x000018e8, 0x0000ffff, 0x18202020, },
+ { 0, 3, 0, 0x00001a20, 0xffffffff, 0x32323232, },
+ { 0, 3, 0, 0x00001a24, 0xffffffff, 0x32323232, },
+ { 0, 3, 0, 0x00001a28, 0xffffffff, 0x28303232, },
+ { 0, 3, 0, 0x00001a2c, 0xffffffff, 0x32323232, },
+ { 0, 3, 0, 0x00001a30, 0xffffffff, 0x26283032, },
+ { 0, 3, 1, 0x00001a34, 0xffffffff, 0x30303030, },
+ { 0, 3, 1, 0x00001a38, 0xffffffff, 0x24262830, },
+ { 0, 3, 2, 0x00001ad8, 0xffffffff, 0x28282828, },
+ { 0, 3, 2, 0x00001adc, 0xffffffff, 0x22242628, },
+ { 0, 3, 0, 0x00001a3c, 0xffffffff, 0x32323232, },
+ { 0, 3, 0, 0x00001a40, 0xffffffff, 0x26283032, },
+ { 0, 3, 0, 0x00001a44, 0xffffffff, 0x30302224, },
+ { 0, 3, 1, 0x00001a48, 0xffffffff, 0x28303030, },
+ { 0, 3, 1, 0x00001a4c, 0xffffffff, 0x20222426, },
+ { 0, 3, 2, 0x00001ae0, 0xffffffff, 0x28282828, },
+ { 0, 3, 2, 0x00001ae4, 0xffffffff, 0x22242628, },
+ { 0, 3, 2, 0x00001ae8, 0x0000ffff, 0x18202020, },
+ { 1, 0, 0, 0x00000c24, 0xffffffff, 0x32323232, },
+ { 1, 0, 0, 0x00000c28, 0xffffffff, 0x28303232, },
+ { 1, 0, 0, 0x00000c2c, 0xffffffff, 0x32323232, },
+ { 1, 0, 0, 0x00000c30, 0xffffffff, 0x26283032, },
+ { 1, 0, 1, 0x00000c34, 0xffffffff, 0x30303030, },
+ { 1, 0, 1, 0x00000c38, 0xffffffff, 0x24262830, },
+ { 1, 0, 2, 0x00000cd8, 0xffffffff, 0x28282828, },
+ { 1, 0, 2, 0x00000cdc, 0xffffffff, 0x22242628, },
+ { 1, 0, 0, 0x00000c3c, 0xffffffff, 0x32323232, },
+ { 1, 0, 0, 0x00000c40, 0xffffffff, 0x26283032, },
+ { 1, 0, 0, 0x00000c44, 0xffffffff, 0x30302224, },
+ { 1, 0, 1, 0x00000c48, 0xffffffff, 0x28303030, },
+ { 1, 0, 1, 0x00000c4c, 0xffffffff, 0x20222426, },
+ { 1, 0, 2, 0x00000ce0, 0xffffffff, 0x28282828, },
+ { 1, 0, 2, 0x00000ce4, 0xffffffff, 0x22242628, },
+ { 1, 0, 2, 0x00000ce8, 0x0000ffff, 0x18202020, },
+ { 1, 1, 0, 0x00000e24, 0xffffffff, 0x32323232, },
+ { 1, 1, 0, 0x00000e28, 0xffffffff, 0x28303232, },
+ { 1, 1, 0, 0x00000e2c, 0xffffffff, 0x32323232, },
+ { 1, 1, 0, 0x00000e30, 0xffffffff, 0x26283032, },
+ { 1, 1, 1, 0x00000e34, 0xffffffff, 0x30303030, },
+ { 1, 1, 1, 0x00000e38, 0xffffffff, 0x24262830, },
+ { 1, 1, 2, 0x00000ed8, 0xffffffff, 0x28282828, },
+ { 1, 1, 2, 0x00000edc, 0xffffffff, 0x22242628, },
+ { 1, 1, 0, 0x00000e3c, 0xffffffff, 0x32323232, },
+ { 1, 1, 0, 0x00000e40, 0xffffffff, 0x26283032, },
+ { 1, 1, 0, 0x00000e44, 0xffffffff, 0x30302224, },
+ { 1, 1, 1, 0x00000e48, 0xffffffff, 0x28303030, },
+ { 1, 1, 1, 0x00000e4c, 0xffffffff, 0x20222426, },
+ { 1, 1, 2, 0x00000ee0, 0xffffffff, 0x28282828, },
+ { 1, 1, 2, 0x00000ee4, 0xffffffff, 0x22242628, },
+ { 1, 1, 2, 0x00000ee8, 0x0000ffff, 0x18202020, },
+ { 1, 2, 0, 0x00001824, 0xffffffff, 0x32323232, },
+ { 1, 2, 0, 0x00001828, 0xffffffff, 0x28303232, },
+ { 1, 2, 0, 0x0000182c, 0xffffffff, 0x32323232, },
+ { 1, 2, 0, 0x00001830, 0xffffffff, 0x26283032, },
+ { 1, 2, 1, 0x00001834, 0xffffffff, 0x30303030, },
+ { 1, 2, 1, 0x00001838, 0xffffffff, 0x24262830, },
+ { 1, 2, 2, 0x000018d8, 0xffffffff, 0x28282828, },
+ { 1, 2, 2, 0x000018dc, 0xffffffff, 0x22242628, },
+ { 1, 2, 0, 0x0000183c, 0xffffffff, 0x32323232, },
+ { 1, 2, 0, 0x00001840, 0xffffffff, 0x26283032, },
+ { 1, 2, 0, 0x00001844, 0xffffffff, 0x30302224, },
+ { 1, 2, 1, 0x00001848, 0xffffffff, 0x28303030, },
+ { 1, 2, 1, 0x0000184c, 0xffffffff, 0x20222426, },
+ { 1, 2, 2, 0x000018e0, 0xffffffff, 0x28282828, },
+ { 1, 2, 2, 0x000018e4, 0xffffffff, 0x22242628, },
+ { 1, 2, 2, 0x000018e8, 0x0000ffff, 0x18202020, },
+ { 1, 3, 0, 0x00001a24, 0xffffffff, 0x32323232, },
+ { 1, 3, 0, 0x00001a28, 0xffffffff, 0x28303232, },
+ { 1, 3, 0, 0x00001a2c, 0xffffffff, 0x32323232, },
+ { 1, 3, 0, 0x00001a30, 0xffffffff, 0x26283032, },
+ { 1, 3, 1, 0x00001a34, 0xffffffff, 0x30303030, },
+ { 1, 3, 1, 0x00001a38, 0xffffffff, 0x24262830, },
+ { 1, 3, 2, 0x00001ad8, 0xffffffff, 0x28282828, },
+ { 1, 3, 2, 0x00001adc, 0xffffffff, 0x22242628, },
+ { 1, 3, 0, 0x00001a3c, 0xffffffff, 0x32323232, },
+ { 1, 3, 0, 0x00001a40, 0xffffffff, 0x26283032, },
+ { 1, 3, 0, 0x00001a44, 0xffffffff, 0x30302224, },
+ { 1, 3, 1, 0x00001a48, 0xffffffff, 0x28303030, },
+ { 1, 3, 1, 0x00001a4c, 0xffffffff, 0x20222426, },
+ { 1, 3, 2, 0x00001ae0, 0xffffffff, 0x28282828, },
+ { 1, 3, 2, 0x00001ae4, 0xffffffff, 0x22242628, },
+ { 1, 3, 2, 0x00001ae8, 0x0000ffff, 0x18202020, },
+};
+
+RTW_DECL_TABLE_BB_PG(rtw8814a_bb_pg_type0);
+
+static const struct rtw_phy_pg_cfg_pair rtw8814a_bb_pg_type2[] = {
+ { 0, 0, 0, 0x00000c20, 0xffffffff, 0x34343434, },
+ { 0, 0, 0, 0x00000c24, 0xffffffff, 0x34343434, },
+ { 0, 0, 0, 0x00000c28, 0xffffffff, 0x30323434, },
+ { 0, 0, 0, 0x00000c2c, 0xffffffff, 0x34343434, },
+ { 0, 0, 0, 0x00000c30, 0xffffffff, 0x28303234, },
+ { 0, 0, 1, 0x00000c34, 0xffffffff, 0x32323232, },
+ { 0, 0, 1, 0x00000c38, 0xffffffff, 0x26283032, },
+ { 0, 0, 2, 0x00000cd8, 0xffffffff, 0x30303030, },
+ { 0, 0, 2, 0x00000cdc, 0xffffffff, 0x24262830, },
+ { 0, 0, 0, 0x00000c3c, 0xffffffff, 0x34343434, },
+ { 0, 0, 0, 0x00000c40, 0xffffffff, 0x28303234, },
+ { 0, 0, 0, 0x00000c44, 0xffffffff, 0x32322426, },
+ { 0, 0, 1, 0x00000c48, 0xffffffff, 0x30323232, },
+ { 0, 0, 1, 0x00000c4c, 0xffffffff, 0x22242628, },
+ { 0, 0, 2, 0x00000ce0, 0xffffffff, 0x30303030, },
+ { 0, 0, 2, 0x00000ce4, 0xffffffff, 0x24262830, },
+ { 0, 0, 2, 0x00000ce8, 0x0000ffff, 0x20222222, },
+ { 0, 1, 0, 0x00000e20, 0xffffffff, 0x34343434, },
+ { 0, 1, 0, 0x00000e24, 0xffffffff, 0x34343434, },
+ { 0, 1, 0, 0x00000e28, 0xffffffff, 0x30323434, },
+ { 0, 1, 0, 0x00000e2c, 0xffffffff, 0x34343434, },
+ { 0, 1, 0, 0x00000e30, 0xffffffff, 0x28303234, },
+ { 0, 1, 1, 0x00000e34, 0xffffffff, 0x32323232, },
+ { 0, 1, 1, 0x00000e38, 0xffffffff, 0x26283032, },
+ { 0, 1, 2, 0x00000ed8, 0xffffffff, 0x30303030, },
+ { 0, 1, 2, 0x00000edc, 0xffffffff, 0x24262830, },
+ { 0, 1, 0, 0x00000e3c, 0xffffffff, 0x34343434, },
+ { 0, 1, 0, 0x00000e40, 0xffffffff, 0x28303234, },
+ { 0, 1, 0, 0x00000e44, 0xffffffff, 0x32322426, },
+ { 0, 1, 1, 0x00000e48, 0xffffffff, 0x30323232, },
+ { 0, 1, 1, 0x00000e4c, 0xffffffff, 0x22242628, },
+ { 0, 1, 2, 0x00000ee0, 0xffffffff, 0x30303030, },
+ { 0, 1, 2, 0x00000ee4, 0xffffffff, 0x24262830, },
+ { 0, 1, 2, 0x00000ee8, 0x0000ffff, 0x20222222, },
+ { 0, 2, 0, 0x00001820, 0xffffffff, 0x34343434, },
+ { 0, 2, 0, 0x00001824, 0xffffffff, 0x34343434, },
+ { 0, 2, 0, 0x00001828, 0xffffffff, 0x30323434, },
+ { 0, 2, 0, 0x0000182c, 0xffffffff, 0x34343434, },
+ { 0, 2, 0, 0x00001830, 0xffffffff, 0x28303234, },
+ { 0, 2, 1, 0x00001834, 0xffffffff, 0x32323232, },
+ { 0, 2, 1, 0x00001838, 0xffffffff, 0x26283032, },
+ { 0, 2, 2, 0x000018d8, 0xffffffff, 0x30303030, },
+ { 0, 2, 2, 0x000018dc, 0xffffffff, 0x24262830, },
+ { 0, 2, 0, 0x0000183c, 0xffffffff, 0x34343434, },
+ { 0, 2, 0, 0x00001840, 0xffffffff, 0x28303234, },
+ { 0, 2, 0, 0x00001844, 0xffffffff, 0x32322426, },
+ { 0, 2, 1, 0x00001848, 0xffffffff, 0x30323232, },
+ { 0, 2, 1, 0x0000184c, 0xffffffff, 0x22242628, },
+ { 0, 2, 2, 0x000018e0, 0xffffffff, 0x30303030, },
+ { 0, 2, 2, 0x000018e4, 0xffffffff, 0x24262830, },
+ { 0, 2, 2, 0x000018e8, 0x0000ffff, 0x20222222, },
+ { 0, 3, 0, 0x00001a20, 0xffffffff, 0x34343434, },
+ { 0, 3, 0, 0x00001a24, 0xffffffff, 0x34343434, },
+ { 0, 3, 0, 0x00001a28, 0xffffffff, 0x30323434, },
+ { 0, 3, 0, 0x00001a2c, 0xffffffff, 0x34343434, },
+ { 0, 3, 0, 0x00001a30, 0xffffffff, 0x28303234, },
+ { 0, 3, 1, 0x00001a34, 0xffffffff, 0x32323232, },
+ { 0, 3, 1, 0x00001a38, 0xffffffff, 0x26283032, },
+ { 0, 3, 2, 0x00001ad8, 0xffffffff, 0x30303030, },
+ { 0, 3, 2, 0x00001adc, 0xffffffff, 0x24262830, },
+ { 0, 3, 0, 0x00001a3c, 0xffffffff, 0x34343434, },
+ { 0, 3, 0, 0x00001a40, 0xffffffff, 0x28303234, },
+ { 0, 3, 0, 0x00001a44, 0xffffffff, 0x32322426, },
+ { 0, 3, 1, 0x00001a48, 0xffffffff, 0x30323232, },
+ { 0, 3, 1, 0x00001a4c, 0xffffffff, 0x22242628, },
+ { 0, 3, 2, 0x00001ae0, 0xffffffff, 0x30303030, },
+ { 0, 3, 2, 0x00001ae4, 0xffffffff, 0x24262830, },
+ { 0, 3, 2, 0x00001ae8, 0x0000ffff, 0x20222222, },
+ { 1, 0, 0, 0x00000c24, 0xffffffff, 0x34343434, },
+ { 1, 0, 0, 0x00000c28, 0xffffffff, 0x30323434, },
+ { 1, 0, 0, 0x00000c2c, 0xffffffff, 0x34343434, },
+ { 1, 0, 0, 0x00000c30, 0xffffffff, 0x28303234, },
+ { 1, 0, 1, 0x00000c34, 0xffffffff, 0x32323232, },
+ { 1, 0, 1, 0x00000c38, 0xffffffff, 0x26283032, },
+ { 1, 0, 2, 0x00000cd8, 0xffffffff, 0x30303030, },
+ { 1, 0, 2, 0x00000cdc, 0xffffffff, 0x24262830, },
+ { 1, 0, 0, 0x00000c3c, 0xffffffff, 0x34343434, },
+ { 1, 0, 0, 0x00000c40, 0xffffffff, 0x28303234, },
+ { 1, 0, 0, 0x00000c44, 0xffffffff, 0x32322426, },
+ { 1, 0, 1, 0x00000c48, 0xffffffff, 0x30323232, },
+ { 1, 0, 1, 0x00000c4c, 0xffffffff, 0x22242628, },
+ { 1, 0, 2, 0x00000ce0, 0xffffffff, 0x30303030, },
+ { 1, 0, 2, 0x00000ce4, 0xffffffff, 0x24262830, },
+ { 1, 0, 2, 0x00000ce8, 0x0000ffff, 0x20222222, },
+ { 1, 1, 0, 0x00000e24, 0xffffffff, 0x34343434, },
+ { 1, 1, 0, 0x00000e28, 0xffffffff, 0x30323434, },
+ { 1, 1, 0, 0x00000e2c, 0xffffffff, 0x34343434, },
+ { 1, 1, 0, 0x00000e30, 0xffffffff, 0x28303234, },
+ { 1, 1, 1, 0x00000e34, 0xffffffff, 0x32323232, },
+ { 1, 1, 1, 0x00000e38, 0xffffffff, 0x26283032, },
+ { 1, 1, 2, 0x00000ed8, 0xffffffff, 0x30303030, },
+ { 1, 1, 2, 0x00000edc, 0xffffffff, 0x24262830, },
+ { 1, 1, 0, 0x00000e3c, 0xffffffff, 0x34343434, },
+ { 1, 1, 0, 0x00000e40, 0xffffffff, 0x28303234, },
+ { 1, 1, 0, 0x00000e44, 0xffffffff, 0x32322426, },
+ { 1, 1, 1, 0x00000e48, 0xffffffff, 0x30323232, },
+ { 1, 1, 1, 0x00000e4c, 0xffffffff, 0x22242628, },
+ { 1, 1, 2, 0x00000ee0, 0xffffffff, 0x30303030, },
+ { 1, 1, 2, 0x00000ee4, 0xffffffff, 0x24262830, },
+ { 1, 1, 2, 0x00000ee8, 0x0000ffff, 0x20222222, },
+ { 1, 2, 0, 0x00001824, 0xffffffff, 0x34343434, },
+ { 1, 2, 0, 0x00001828, 0xffffffff, 0x30323434, },
+ { 1, 2, 0, 0x0000182c, 0xffffffff, 0x34343434, },
+ { 1, 2, 0, 0x00001830, 0xffffffff, 0x28303234, },
+ { 1, 2, 1, 0x00001834, 0xffffffff, 0x32323232, },
+ { 1, 2, 1, 0x00001838, 0xffffffff, 0x26283032, },
+ { 1, 2, 2, 0x000018d8, 0xffffffff, 0x30303030, },
+ { 1, 2, 2, 0x000018dc, 0xffffffff, 0x24262830, },
+ { 1, 2, 0, 0x0000183c, 0xffffffff, 0x34343434, },
+ { 1, 2, 0, 0x00001840, 0xffffffff, 0x28303234, },
+ { 1, 2, 0, 0x00001844, 0xffffffff, 0x32322426, },
+ { 1, 2, 1, 0x00001848, 0xffffffff, 0x30323232, },
+ { 1, 2, 1, 0x0000184c, 0xffffffff, 0x22242628, },
+ { 1, 2, 2, 0x000018e0, 0xffffffff, 0x30303030, },
+ { 1, 2, 2, 0x000018e4, 0xffffffff, 0x24262830, },
+ { 1, 2, 2, 0x000018e8, 0x0000ffff, 0x20222222, },
+ { 1, 3, 0, 0x00001a24, 0xffffffff, 0x34343434, },
+ { 1, 3, 0, 0x00001a28, 0xffffffff, 0x30323434, },
+ { 1, 3, 0, 0x00001a2c, 0xffffffff, 0x34343434, },
+ { 1, 3, 0, 0x00001a30, 0xffffffff, 0x28303234, },
+ { 1, 3, 1, 0x00001a34, 0xffffffff, 0x32323232, },
+ { 1, 3, 1, 0x00001a38, 0xffffffff, 0x26283032, },
+ { 1, 3, 2, 0x00001ad8, 0xffffffff, 0x30303030, },
+ { 1, 3, 2, 0x00001adc, 0xffffffff, 0x24262830, },
+ { 1, 3, 0, 0x00001a3c, 0xffffffff, 0x34343434, },
+ { 1, 3, 0, 0x00001a40, 0xffffffff, 0x28303234, },
+ { 1, 3, 0, 0x00001a44, 0xffffffff, 0x32322426, },
+ { 1, 3, 1, 0x00001a48, 0xffffffff, 0x30323232, },
+ { 1, 3, 1, 0x00001a4c, 0xffffffff, 0x22242628, },
+ { 1, 3, 2, 0x00001ae0, 0xffffffff, 0x30303030, },
+ { 1, 3, 2, 0x00001ae4, 0xffffffff, 0x24262830, },
+ { 1, 3, 2, 0x00001ae8, 0x0000ffff, 0x20222222, },
+};
+
+RTW_DECL_TABLE_BB_PG(rtw8814a_bb_pg_type2);
+
+static const struct rtw_phy_pg_cfg_pair rtw8814a_bb_pg_type3[] = {
+ { 0, 0, 0, 0x00000c20, 0xffffffff, 0x48484848, },
+ { 0, 0, 0, 0x00000c24, 0xffffffff, 0x46464646, },
+ { 0, 0, 0, 0x00000c28, 0xffffffff, 0x44464646, },
+ { 0, 0, 0, 0x00000c2c, 0xffffffff, 0x46464646, },
+ { 0, 0, 0, 0x00000c30, 0xffffffff, 0x42444646, },
+ { 0, 0, 1, 0x00000c34, 0xffffffff, 0x46464646, },
+ { 0, 0, 1, 0x00000c38, 0xffffffff, 0x42444646, },
+ { 0, 0, 2, 0x00000cd8, 0xffffffff, 0x46464646, },
+ { 0, 0, 2, 0x00000cdc, 0xffffffff, 0x42444646, },
+ { 0, 0, 0, 0x00000c3c, 0xffffffff, 0x46464646, },
+ { 0, 0, 0, 0x00000c40, 0xffffffff, 0x42444646, },
+ { 0, 0, 0, 0x00000c44, 0xffffffff, 0x46463840, },
+ { 0, 0, 1, 0x00000c48, 0xffffffff, 0x46464646, },
+ { 0, 0, 1, 0x00000c4c, 0xffffffff, 0x38404244, },
+ { 0, 0, 2, 0x00000ce0, 0xffffffff, 0x46464646, },
+ { 0, 0, 2, 0x00000ce4, 0xffffffff, 0x42444646, },
+ { 0, 0, 2, 0x00000ce8, 0x0000ffff, 0x38383840, },
+ { 0, 1, 0, 0x00000e20, 0xffffffff, 0x48484848, },
+ { 0, 1, 0, 0x00000e24, 0xffffffff, 0x46464646, },
+ { 0, 1, 0, 0x00000e28, 0xffffffff, 0x44464646, },
+ { 0, 1, 0, 0x00000e2c, 0xffffffff, 0x46464646, },
+ { 0, 1, 0, 0x00000e30, 0xffffffff, 0x42444646, },
+ { 0, 1, 1, 0x00000e34, 0xffffffff, 0x46464646, },
+ { 0, 1, 1, 0x00000e38, 0xffffffff, 0x42444646, },
+ { 0, 1, 2, 0x00000ed8, 0xffffffff, 0x46464646, },
+ { 0, 1, 2, 0x00000edc, 0xffffffff, 0x42444646, },
+ { 0, 1, 0, 0x00000e3c, 0xffffffff, 0x46464646, },
+ { 0, 1, 0, 0x00000e40, 0xffffffff, 0x42444646, },
+ { 0, 1, 0, 0x00000e44, 0xffffffff, 0x46463840, },
+ { 0, 1, 1, 0x00000e48, 0xffffffff, 0x46464646, },
+ { 0, 1, 1, 0x00000e4c, 0xffffffff, 0x38404244, },
+ { 0, 1, 2, 0x00000ee0, 0xffffffff, 0x46464646, },
+ { 0, 1, 2, 0x00000ee4, 0xffffffff, 0x42444646, },
+ { 0, 1, 2, 0x00000ee8, 0x0000ffff, 0x38383840, },
+ { 0, 2, 0, 0x00001820, 0xffffffff, 0x48484848, },
+ { 0, 2, 0, 0x00001824, 0xffffffff, 0x46464646, },
+ { 0, 2, 0, 0x00001828, 0xffffffff, 0x44464646, },
+ { 0, 2, 0, 0x0000182c, 0xffffffff, 0x46464646, },
+ { 0, 2, 0, 0x00001830, 0xffffffff, 0x42444646, },
+ { 0, 2, 1, 0x00001834, 0xffffffff, 0x46464646, },
+ { 0, 2, 1, 0x00001838, 0xffffffff, 0x42444646, },
+ { 0, 2, 2, 0x000018d8, 0xffffffff, 0x46464646, },
+ { 0, 2, 2, 0x000018dc, 0xffffffff, 0x42444646, },
+ { 0, 2, 0, 0x0000183c, 0xffffffff, 0x46464646, },
+ { 0, 2, 0, 0x00001840, 0xffffffff, 0x42444646, },
+ { 0, 2, 0, 0x00001844, 0xffffffff, 0x46463840, },
+ { 0, 2, 1, 0x00001848, 0xffffffff, 0x46464646, },
+ { 0, 2, 1, 0x0000184c, 0xffffffff, 0x38404244, },
+ { 0, 2, 2, 0x000018e0, 0xffffffff, 0x46464646, },
+ { 0, 2, 2, 0x000018e4, 0xffffffff, 0x42444646, },
+ { 0, 2, 2, 0x000018e8, 0x0000ffff, 0x38383840, },
+ { 0, 3, 0, 0x00001a20, 0xffffffff, 0x48484848, },
+ { 0, 3, 0, 0x00001a24, 0xffffffff, 0x46464646, },
+ { 0, 3, 0, 0x00001a28, 0xffffffff, 0x44464646, },
+ { 0, 3, 0, 0x00001a2c, 0xffffffff, 0x46464646, },
+ { 0, 3, 0, 0x00001a30, 0xffffffff, 0x42444646, },
+ { 0, 3, 1, 0x00001a34, 0xffffffff, 0x46464646, },
+ { 0, 3, 1, 0x00001a38, 0xffffffff, 0x42444646, },
+ { 0, 3, 2, 0x00001ad8, 0xffffffff, 0x46464646, },
+ { 0, 3, 2, 0x00001adc, 0xffffffff, 0x42444646, },
+ { 0, 3, 0, 0x00001a3c, 0xffffffff, 0x46464646, },
+ { 0, 3, 0, 0x00001a40, 0xffffffff, 0x42444646, },
+ { 0, 3, 0, 0x00001a44, 0xffffffff, 0x46463840, },
+ { 0, 3, 1, 0x00001a48, 0xffffffff, 0x46464646, },
+ { 0, 3, 1, 0x00001a4c, 0xffffffff, 0x38404244, },
+ { 0, 3, 2, 0x00001ae0, 0xffffffff, 0x46464646, },
+ { 0, 3, 2, 0x00001ae4, 0xffffffff, 0x42444646, },
+ { 0, 3, 2, 0x00001ae8, 0x0000ffff, 0x38383840, },
+ { 1, 0, 0, 0x00000c24, 0xffffffff, 0x46464646, },
+ { 1, 0, 0, 0x00000c28, 0xffffffff, 0x44464646, },
+ { 1, 0, 0, 0x00000c2c, 0xffffffff, 0x46464646, },
+ { 1, 0, 0, 0x00000c30, 0xffffffff, 0x42444646, },
+ { 1, 0, 1, 0x00000c34, 0xffffffff, 0x46464646, },
+ { 1, 0, 1, 0x00000c38, 0xffffffff, 0x42444646, },
+ { 1, 0, 2, 0x00000cd8, 0xffffffff, 0x46464646, },
+ { 1, 0, 2, 0x00000cdc, 0xffffffff, 0x42444646, },
+ { 1, 0, 0, 0x00000c3c, 0xffffffff, 0x46464646, },
+ { 1, 0, 0, 0x00000c40, 0xffffffff, 0x42444646, },
+ { 1, 0, 0, 0x00000c44, 0xffffffff, 0x46463840, },
+ { 1, 0, 1, 0x00000c48, 0xffffffff, 0x46464646, },
+ { 1, 0, 1, 0x00000c4c, 0xffffffff, 0x38404244, },
+ { 1, 0, 2, 0x00000ce0, 0xffffffff, 0x46464646, },
+ { 1, 0, 2, 0x00000ce4, 0xffffffff, 0x42444646, },
+ { 1, 0, 2, 0x00000ce8, 0x0000ffff, 0x38383840, },
+ { 1, 1, 0, 0x00000e24, 0xffffffff, 0x46464646, },
+ { 1, 1, 0, 0x00000e28, 0xffffffff, 0x44464646, },
+ { 1, 1, 0, 0x00000e2c, 0xffffffff, 0x46464646, },
+ { 1, 1, 0, 0x00000e30, 0xffffffff, 0x42444646, },
+ { 1, 1, 1, 0x00000e34, 0xffffffff, 0x46464646, },
+ { 1, 1, 1, 0x00000e38, 0xffffffff, 0x42444646, },
+ { 1, 1, 2, 0x00000ed8, 0xffffffff, 0x46464646, },
+ { 1, 1, 2, 0x00000edc, 0xffffffff, 0x42444646, },
+ { 1, 1, 0, 0x00000e3c, 0xffffffff, 0x46464646, },
+ { 1, 1, 0, 0x00000e40, 0xffffffff, 0x42444646, },
+ { 1, 1, 0, 0x00000e44, 0xffffffff, 0x46463840, },
+ { 1, 1, 1, 0x00000e48, 0xffffffff, 0x46464646, },
+ { 1, 1, 1, 0x00000e4c, 0xffffffff, 0x38404244, },
+ { 1, 1, 2, 0x00000ee0, 0xffffffff, 0x46464646, },
+ { 1, 1, 2, 0x00000ee4, 0xffffffff, 0x42444646, },
+ { 1, 1, 2, 0x00000ee8, 0x0000ffff, 0x38383840, },
+ { 1, 2, 0, 0x00001824, 0xffffffff, 0x46464646, },
+ { 1, 2, 0, 0x00001828, 0xffffffff, 0x44464646, },
+ { 1, 2, 0, 0x0000182c, 0xffffffff, 0x46464646, },
+ { 1, 2, 0, 0x00001830, 0xffffffff, 0x42444646, },
+ { 1, 2, 1, 0x00001834, 0xffffffff, 0x46464646, },
+ { 1, 2, 1, 0x00001838, 0xffffffff, 0x42444646, },
+ { 1, 2, 2, 0x000018d8, 0xffffffff, 0x46464646, },
+ { 1, 2, 2, 0x000018dc, 0xffffffff, 0x42444646, },
+ { 1, 2, 0, 0x0000183c, 0xffffffff, 0x46464646, },
+ { 1, 2, 0, 0x00001840, 0xffffffff, 0x42444646, },
+ { 1, 2, 0, 0x00001844, 0xffffffff, 0x46463840, },
+ { 1, 2, 1, 0x00001848, 0xffffffff, 0x46464646, },
+ { 1, 2, 1, 0x0000184c, 0xffffffff, 0x38404244, },
+ { 1, 2, 2, 0x000018e0, 0xffffffff, 0x46464646, },
+ { 1, 2, 2, 0x000018e4, 0xffffffff, 0x42444646, },
+ { 1, 2, 2, 0x000018e8, 0x0000ffff, 0x38383840, },
+ { 1, 3, 0, 0x00001a24, 0xffffffff, 0x46464646, },
+ { 1, 3, 0, 0x00001a28, 0xffffffff, 0x44464646, },
+ { 1, 3, 0, 0x00001a2c, 0xffffffff, 0x46464646, },
+ { 1, 3, 0, 0x00001a30, 0xffffffff, 0x42444646, },
+ { 1, 3, 1, 0x00001a34, 0xffffffff, 0x46464646, },
+ { 1, 3, 1, 0x00001a38, 0xffffffff, 0x42444646, },
+ { 1, 3, 2, 0x00001ad8, 0xffffffff, 0x46464646, },
+ { 1, 3, 2, 0x00001adc, 0xffffffff, 0x42444646, },
+ { 1, 3, 0, 0x00001a3c, 0xffffffff, 0x46464646, },
+ { 1, 3, 0, 0x00001a40, 0xffffffff, 0x42444646, },
+ { 1, 3, 0, 0x00001a44, 0xffffffff, 0x46463840, },
+ { 1, 3, 1, 0x00001a48, 0xffffffff, 0x46464646, },
+ { 1, 3, 1, 0x00001a4c, 0xffffffff, 0x38404244, },
+ { 1, 3, 2, 0x00001ae0, 0xffffffff, 0x46464646, },
+ { 1, 3, 2, 0x00001ae4, 0xffffffff, 0x42444646, },
+ { 1, 3, 2, 0x00001ae8, 0x0000ffff, 0x38383840, },
+};
+
+RTW_DECL_TABLE_BB_PG(rtw8814a_bb_pg_type3);
+
+static const struct rtw_phy_pg_cfg_pair rtw8814a_bb_pg_type4[] = {
+ { 0, 0, 0, 0x00000c20, 0xffffffff, 0x42424242, },
+ { 0, 0, 0, 0x00000c24, 0xffffffff, 0x42424242, },
+ { 0, 0, 0, 0x00000c28, 0xffffffff, 0x36384042, },
+ { 0, 0, 0, 0x00000c2c, 0xffffffff, 0x42424242, },
+ { 0, 0, 0, 0x00000c30, 0xffffffff, 0x34363840, },
+ { 0, 0, 1, 0x00000c34, 0xffffffff, 0x42424242, },
+ { 0, 0, 1, 0x00000c38, 0xffffffff, 0x34363840, },
+ { 0, 0, 2, 0x00000cd8, 0xffffffff, 0x42424242, },
+ { 0, 0, 2, 0x00000cdc, 0xffffffff, 0x34363840, },
+ { 0, 0, 0, 0x00000c3c, 0xffffffff, 0x42424242, },
+ { 0, 0, 0, 0x00000c40, 0xffffffff, 0x34363840, },
+ { 0, 0, 0, 0x00000c44, 0xffffffff, 0x42423032, },
+ { 0, 0, 1, 0x00000c48, 0xffffffff, 0x38404242, },
+ { 0, 0, 1, 0x00000c4c, 0xffffffff, 0x30323436, },
+ { 0, 0, 2, 0x00000ce0, 0xffffffff, 0x42424242, },
+ { 0, 0, 2, 0x00000ce4, 0xffffffff, 0x34363840, },
+ { 0, 0, 2, 0x00000ce8, 0x0000ffff, 0x30303032, },
+ { 0, 1, 0, 0x00000e20, 0xffffffff, 0x42424242, },
+ { 0, 1, 0, 0x00000e24, 0xffffffff, 0x42424242, },
+ { 0, 1, 0, 0x00000e28, 0xffffffff, 0x36384042, },
+ { 0, 1, 0, 0x00000e2c, 0xffffffff, 0x42424242, },
+ { 0, 1, 0, 0x00000e30, 0xffffffff, 0x34363840, },
+ { 0, 1, 1, 0x00000e34, 0xffffffff, 0x42424242, },
+ { 0, 1, 1, 0x00000e38, 0xffffffff, 0x34363840, },
+ { 0, 1, 2, 0x00000ed8, 0xffffffff, 0x42424242, },
+ { 0, 1, 2, 0x00000edc, 0xffffffff, 0x34363840, },
+ { 0, 1, 0, 0x00000e3c, 0xffffffff, 0x42424242, },
+ { 0, 1, 0, 0x00000e40, 0xffffffff, 0x34363840, },
+ { 0, 1, 0, 0x00000e44, 0xffffffff, 0x42423032, },
+ { 0, 1, 1, 0x00000e48, 0xffffffff, 0x38404242, },
+ { 0, 1, 1, 0x00000e4c, 0xffffffff, 0x30323436, },
+ { 0, 1, 2, 0x00000ee0, 0xffffffff, 0x42424242, },
+ { 0, 1, 2, 0x00000ee4, 0xffffffff, 0x34363840, },
+ { 0, 1, 2, 0x00000ee8, 0x0000ffff, 0x30303032, },
+ { 0, 2, 0, 0x00001820, 0xffffffff, 0x42424242, },
+ { 0, 2, 0, 0x00001824, 0xffffffff, 0x42424242, },
+ { 0, 2, 0, 0x00001828, 0xffffffff, 0x36384042, },
+ { 0, 2, 0, 0x0000182c, 0xffffffff, 0x42424242, },
+ { 0, 2, 0, 0x00001830, 0xffffffff, 0x34363840, },
+ { 0, 2, 1, 0x00001834, 0xffffffff, 0x42424242, },
+ { 0, 2, 1, 0x00001838, 0xffffffff, 0x34363840, },
+ { 0, 2, 2, 0x000018d8, 0xffffffff, 0x42424242, },
+ { 0, 2, 2, 0x000018dc, 0xffffffff, 0x34363840, },
+ { 0, 2, 0, 0x0000183c, 0xffffffff, 0x42424242, },
+ { 0, 2, 0, 0x00001840, 0xffffffff, 0x34363840, },
+ { 0, 2, 0, 0x00001844, 0xffffffff, 0x42423032, },
+ { 0, 2, 1, 0x00001848, 0xffffffff, 0x38404242, },
+ { 0, 2, 1, 0x0000184c, 0xffffffff, 0x30323436, },
+ { 0, 2, 2, 0x000018e0, 0xffffffff, 0x42424242, },
+ { 0, 2, 2, 0x000018e4, 0xffffffff, 0x34363840, },
+ { 0, 2, 2, 0x000018e8, 0x0000ffff, 0x30303032, },
+ { 0, 3, 0, 0x00001a20, 0xffffffff, 0x42424242, },
+ { 0, 3, 0, 0x00001a24, 0xffffffff, 0x42424242, },
+ { 0, 3, 0, 0x00001a28, 0xffffffff, 0x36384042, },
+ { 0, 3, 0, 0x00001a2c, 0xffffffff, 0x42424242, },
+ { 0, 3, 0, 0x00001a30, 0xffffffff, 0x34363840, },
+ { 0, 3, 1, 0x00001a34, 0xffffffff, 0x42424242, },
+ { 0, 3, 1, 0x00001a38, 0xffffffff, 0x34363840, },
+ { 0, 3, 2, 0x00001ad8, 0xffffffff, 0x42424242, },
+ { 0, 3, 2, 0x00001adc, 0xffffffff, 0x34363840, },
+ { 0, 3, 0, 0x00001a3c, 0xffffffff, 0x42424242, },
+ { 0, 3, 0, 0x00001a40, 0xffffffff, 0x34363840, },
+ { 0, 3, 0, 0x00001a44, 0xffffffff, 0x42423032, },
+ { 0, 3, 1, 0x00001a48, 0xffffffff, 0x38404242, },
+ { 0, 3, 1, 0x00001a4c, 0xffffffff, 0x30323436, },
+ { 0, 3, 2, 0x00001ae0, 0xffffffff, 0x42424242, },
+ { 0, 3, 2, 0x00001ae4, 0xffffffff, 0x34363840, },
+ { 0, 3, 2, 0x00001ae8, 0x0000ffff, 0x30303032, },
+ { 1, 0, 0, 0x00000c24, 0xffffffff, 0x42424242, },
+ { 1, 0, 0, 0x00000c28, 0xffffffff, 0x36384042, },
+ { 1, 0, 0, 0x00000c2c, 0xffffffff, 0x42424242, },
+ { 1, 0, 0, 0x00000c30, 0xffffffff, 0x34363840, },
+ { 1, 0, 1, 0x00000c34, 0xffffffff, 0x42424242, },
+ { 1, 0, 1, 0x00000c38, 0xffffffff, 0x34363840, },
+ { 1, 0, 2, 0x00000cd8, 0xffffffff, 0x42424242, },
+ { 1, 0, 2, 0x00000cdc, 0xffffffff, 0x34363840, },
+ { 1, 0, 0, 0x00000c3c, 0xffffffff, 0x42424242, },
+ { 1, 0, 0, 0x00000c40, 0xffffffff, 0x34363840, },
+ { 1, 0, 0, 0x00000c44, 0xffffffff, 0x42423032, },
+ { 1, 0, 1, 0x00000c48, 0xffffffff, 0x38404242, },
+ { 1, 0, 1, 0x00000c4c, 0xffffffff, 0x30323436, },
+ { 1, 0, 2, 0x00000ce0, 0xffffffff, 0x42424242, },
+ { 1, 0, 2, 0x00000ce4, 0xffffffff, 0x34363840, },
+ { 1, 0, 2, 0x00000ce8, 0x0000ffff, 0x30303032, },
+ { 1, 1, 0, 0x00000e24, 0xffffffff, 0x42424242, },
+ { 1, 1, 0, 0x00000e28, 0xffffffff, 0x36384042, },
+ { 1, 1, 0, 0x00000e2c, 0xffffffff, 0x42424242, },
+ { 1, 1, 0, 0x00000e30, 0xffffffff, 0x34363840, },
+ { 1, 1, 1, 0x00000e34, 0xffffffff, 0x42424242, },
+ { 1, 1, 1, 0x00000e38, 0xffffffff, 0x34363840, },
+ { 1, 1, 2, 0x00000ed8, 0xffffffff, 0x42424242, },
+ { 1, 1, 2, 0x00000edc, 0xffffffff, 0x34363840, },
+ { 1, 1, 0, 0x00000e3c, 0xffffffff, 0x42424242, },
+ { 1, 1, 0, 0x00000e40, 0xffffffff, 0x34363840, },
+ { 1, 1, 0, 0x00000e44, 0xffffffff, 0x42423032, },
+ { 1, 1, 1, 0x00000e48, 0xffffffff, 0x38404242, },
+ { 1, 1, 1, 0x00000e4c, 0xffffffff, 0x30323436, },
+ { 1, 1, 2, 0x00000ee0, 0xffffffff, 0x42424242, },
+ { 1, 1, 2, 0x00000ee4, 0xffffffff, 0x34363840, },
+ { 1, 1, 2, 0x00000ee8, 0x0000ffff, 0x30303032, },
+ { 1, 2, 0, 0x00001824, 0xffffffff, 0x42424242, },
+ { 1, 2, 0, 0x00001828, 0xffffffff, 0x36384042, },
+ { 1, 2, 0, 0x0000182c, 0xffffffff, 0x42424242, },
+ { 1, 2, 0, 0x00001830, 0xffffffff, 0x34363840, },
+ { 1, 2, 1, 0x00001834, 0xffffffff, 0x42424242, },
+ { 1, 2, 1, 0x00001838, 0xffffffff, 0x34363840, },
+ { 1, 2, 2, 0x000018d8, 0xffffffff, 0x42424242, },
+ { 1, 2, 2, 0x000018dc, 0xffffffff, 0x34363840, },
+ { 1, 2, 0, 0x0000183c, 0xffffffff, 0x42424242, },
+ { 1, 2, 0, 0x00001840, 0xffffffff, 0x34363840, },
+ { 1, 2, 0, 0x00001844, 0xffffffff, 0x42423032, },
+ { 1, 2, 1, 0x00001848, 0xffffffff, 0x38404242, },
+ { 1, 2, 1, 0x0000184c, 0xffffffff, 0x30323436, },
+ { 1, 2, 2, 0x000018e0, 0xffffffff, 0x42424242, },
+ { 1, 2, 2, 0x000018e4, 0xffffffff, 0x34363840, },
+ { 1, 2, 2, 0x000018e8, 0x0000ffff, 0x30303032, },
+ { 1, 3, 0, 0x00001a24, 0xffffffff, 0x42424242, },
+ { 1, 3, 0, 0x00001a28, 0xffffffff, 0x36384042, },
+ { 1, 3, 0, 0x00001a2c, 0xffffffff, 0x42424242, },
+ { 1, 3, 0, 0x00001a30, 0xffffffff, 0x34363840, },
+ { 1, 3, 1, 0x00001a34, 0xffffffff, 0x42424242, },
+ { 1, 3, 1, 0x00001a38, 0xffffffff, 0x34363840, },
+ { 1, 3, 2, 0x00001ad8, 0xffffffff, 0x42424242, },
+ { 1, 3, 2, 0x00001adc, 0xffffffff, 0x34363840, },
+ { 1, 3, 0, 0x00001a3c, 0xffffffff, 0x42424242, },
+ { 1, 3, 0, 0x00001a40, 0xffffffff, 0x34363840, },
+ { 1, 3, 0, 0x00001a44, 0xffffffff, 0x42423032, },
+ { 1, 3, 1, 0x00001a48, 0xffffffff, 0x38404242, },
+ { 1, 3, 1, 0x00001a4c, 0xffffffff, 0x30323436, },
+ { 1, 3, 2, 0x00001ae0, 0xffffffff, 0x42424242, },
+ { 1, 3, 2, 0x00001ae4, 0xffffffff, 0x34363840, },
+ { 1, 3, 2, 0x00001ae8, 0x0000ffff, 0x30303032, },
+};
+
+RTW_DECL_TABLE_BB_PG(rtw8814a_bb_pg_type4);
+
+static const struct rtw_phy_pg_cfg_pair rtw8814a_bb_pg_type5[] = {
+ { 0, 0, 0, 0x00000c20, 0xffffffff, 0x48484848, },
+ { 0, 0, 0, 0x00000c24, 0xffffffff, 0x46464646, },
+ { 0, 0, 0, 0x00000c28, 0xffffffff, 0x44464646, },
+ { 0, 0, 0, 0x00000c2c, 0xffffffff, 0x46464646, },
+ { 0, 0, 0, 0x00000c30, 0xffffffff, 0x42444646, },
+ { 0, 0, 1, 0x00000c34, 0xffffffff, 0x44444444, },
+ { 0, 0, 1, 0x00000c38, 0xffffffff, 0x40424444, },
+ { 0, 0, 2, 0x00000cd8, 0xffffffff, 0x42424242, },
+ { 0, 0, 2, 0x00000cdc, 0xffffffff, 0x38404242, },
+ { 0, 0, 0, 0x00000c3c, 0xffffffff, 0x46464646, },
+ { 0, 0, 0, 0x00000c40, 0xffffffff, 0x42444646, },
+ { 0, 0, 0, 0x00000c44, 0xffffffff, 0x44444040, },
+ { 0, 0, 1, 0x00000c48, 0xffffffff, 0x44444444, },
+ { 0, 0, 1, 0x00000c4c, 0xffffffff, 0x38384042, },
+ { 0, 0, 2, 0x00000ce0, 0xffffffff, 0x42424242, },
+ { 0, 0, 2, 0x00000ce4, 0xffffffff, 0x38404242, },
+ { 0, 0, 2, 0x00000ce8, 0x0000ffff, 0x20203636, },
+ { 0, 1, 0, 0x00000e20, 0xffffffff, 0x48484848, },
+ { 0, 1, 0, 0x00000e24, 0xffffffff, 0x46464646, },
+ { 0, 1, 0, 0x00000e28, 0xffffffff, 0x44464646, },
+ { 0, 1, 0, 0x00000e2c, 0xffffffff, 0x46464646, },
+ { 0, 1, 0, 0x00000e30, 0xffffffff, 0x42444646, },
+ { 0, 1, 1, 0x00000e34, 0xffffffff, 0x44444444, },
+ { 0, 1, 1, 0x00000e38, 0xffffffff, 0x40424444, },
+ { 0, 1, 2, 0x00000ed8, 0xffffffff, 0x42424242, },
+ { 0, 1, 2, 0x00000edc, 0xffffffff, 0x38404242, },
+ { 0, 1, 0, 0x00000e3c, 0xffffffff, 0x46464646, },
+ { 0, 1, 0, 0x00000e40, 0xffffffff, 0x42444646, },
+ { 0, 1, 0, 0x00000e44, 0xffffffff, 0x44444040, },
+ { 0, 1, 1, 0x00000e48, 0xffffffff, 0x44444444, },
+ { 0, 1, 1, 0x00000e4c, 0xffffffff, 0x38384042, },
+ { 0, 1, 2, 0x00000ee0, 0xffffffff, 0x42424242, },
+ { 0, 1, 2, 0x00000ee4, 0xffffffff, 0x38404242, },
+ { 0, 1, 2, 0x00000ee8, 0x0000ffff, 0x20203636, },
+ { 0, 2, 0, 0x00001820, 0xffffffff, 0x48484848, },
+ { 0, 2, 0, 0x00001824, 0xffffffff, 0x46464646, },
+ { 0, 2, 0, 0x00001828, 0xffffffff, 0x44464646, },
+ { 0, 2, 0, 0x0000182c, 0xffffffff, 0x46464646, },
+ { 0, 2, 0, 0x00001830, 0xffffffff, 0x42444646, },
+ { 0, 2, 1, 0x00001834, 0xffffffff, 0x44444444, },
+ { 0, 2, 1, 0x00001838, 0xffffffff, 0x40424444, },
+ { 0, 2, 2, 0x000018d8, 0xffffffff, 0x42424242, },
+ { 0, 2, 2, 0x000018dc, 0xffffffff, 0x38404242, },
+ { 0, 2, 0, 0x0000183c, 0xffffffff, 0x46464646, },
+ { 0, 2, 0, 0x00001840, 0xffffffff, 0x42444646, },
+ { 0, 2, 0, 0x00001844, 0xffffffff, 0x44444040, },
+ { 0, 2, 1, 0x00001848, 0xffffffff, 0x44444444, },
+ { 0, 2, 1, 0x0000184c, 0xffffffff, 0x38384042, },
+ { 0, 2, 2, 0x000018e0, 0xffffffff, 0x42424242, },
+ { 0, 2, 2, 0x000018e4, 0xffffffff, 0x38404242, },
+ { 0, 2, 2, 0x000018e8, 0x0000ffff, 0x20203636, },
+ { 0, 3, 0, 0x00001a20, 0xffffffff, 0x48484848, },
+ { 0, 3, 0, 0x00001a24, 0xffffffff, 0x46464646, },
+ { 0, 3, 0, 0x00001a28, 0xffffffff, 0x44464646, },
+ { 0, 3, 0, 0x00001a2c, 0xffffffff, 0x46464646, },
+ { 0, 3, 0, 0x00001a30, 0xffffffff, 0x42444646, },
+ { 0, 3, 1, 0x00001a34, 0xffffffff, 0x44444444, },
+ { 0, 3, 1, 0x00001a38, 0xffffffff, 0x40424444, },
+ { 0, 3, 2, 0x00001ad8, 0xffffffff, 0x42424242, },
+ { 0, 3, 2, 0x00001adc, 0xffffffff, 0x38404242, },
+ { 0, 3, 0, 0x00001a3c, 0xffffffff, 0x46464646, },
+ { 0, 3, 0, 0x00001a40, 0xffffffff, 0x42444646, },
+ { 0, 3, 0, 0x00001a44, 0xffffffff, 0x44444040, },
+ { 0, 3, 1, 0x00001a48, 0xffffffff, 0x44444444, },
+ { 0, 3, 1, 0x00001a4c, 0xffffffff, 0x38384042, },
+ { 0, 3, 2, 0x00001ae0, 0xffffffff, 0x42424242, },
+ { 0, 3, 2, 0x00001ae4, 0xffffffff, 0x38404242, },
+ { 0, 3, 2, 0x00001ae8, 0x0000ffff, 0x20203636, },
+ { 1, 0, 0, 0x00000c24, 0xffffffff, 0x46464646, },
+ { 1, 0, 0, 0x00000c28, 0xffffffff, 0x44464646, },
+ { 1, 0, 0, 0x00000c2c, 0xffffffff, 0x46464646, },
+ { 1, 0, 0, 0x00000c30, 0xffffffff, 0x42444646, },
+ { 1, 0, 1, 0x00000c34, 0xffffffff, 0x44444444, },
+ { 1, 0, 1, 0x00000c38, 0xffffffff, 0x40424444, },
+ { 1, 0, 2, 0x00000cd8, 0xffffffff, 0x42424242, },
+ { 1, 0, 2, 0x00000cdc, 0xffffffff, 0x38404242, },
+ { 1, 0, 0, 0x00000c3c, 0xffffffff, 0x46464646, },
+ { 1, 0, 0, 0x00000c40, 0xffffffff, 0x42444646, },
+ { 1, 0, 0, 0x00000c44, 0xffffffff, 0x44443840, },
+ { 1, 0, 1, 0x00000c48, 0xffffffff, 0x44444444, },
+ { 1, 0, 1, 0x00000c4c, 0xffffffff, 0x36384042, },
+ { 1, 0, 2, 0x00000ce0, 0xffffffff, 0x42424242, },
+ { 1, 0, 2, 0x00000ce4, 0xffffffff, 0x38404242, },
+ { 1, 0, 2, 0x00000ce8, 0x0000ffff, 0x20203436, },
+ { 1, 1, 0, 0x00000e24, 0xffffffff, 0x46464646, },
+ { 1, 1, 0, 0x00000e28, 0xffffffff, 0x44464646, },
+ { 1, 1, 0, 0x00000e2c, 0xffffffff, 0x46464646, },
+ { 1, 1, 0, 0x00000e30, 0xffffffff, 0x42444646, },
+ { 1, 1, 1, 0x00000e34, 0xffffffff, 0x44444444, },
+ { 1, 1, 1, 0x00000e38, 0xffffffff, 0x40424444, },
+ { 1, 1, 2, 0x00000ed8, 0xffffffff, 0x42424242, },
+ { 1, 1, 2, 0x00000edc, 0xffffffff, 0x38404242, },
+ { 1, 1, 0, 0x00000e3c, 0xffffffff, 0x46464646, },
+ { 1, 1, 0, 0x00000e40, 0xffffffff, 0x42444646, },
+ { 1, 1, 0, 0x00000e44, 0xffffffff, 0x44443840, },
+ { 1, 1, 1, 0x00000e48, 0xffffffff, 0x44444444, },
+ { 1, 1, 1, 0x00000e4c, 0xffffffff, 0x36384042, },
+ { 1, 1, 2, 0x00000ee0, 0xffffffff, 0x42424242, },
+ { 1, 1, 2, 0x00000ee4, 0xffffffff, 0x38404242, },
+ { 1, 1, 2, 0x00000ee8, 0x0000ffff, 0x20203436, },
+ { 1, 2, 0, 0x00001824, 0xffffffff, 0x46464646, },
+ { 1, 2, 0, 0x00001828, 0xffffffff, 0x44464646, },
+ { 1, 2, 0, 0x0000182c, 0xffffffff, 0x46464646, },
+ { 1, 2, 0, 0x00001830, 0xffffffff, 0x42444646, },
+ { 1, 2, 1, 0x00001834, 0xffffffff, 0x44444444, },
+ { 1, 2, 1, 0x00001838, 0xffffffff, 0x40424444, },
+ { 1, 2, 2, 0x000018d8, 0xffffffff, 0x42424242, },
+ { 1, 2, 2, 0x000018dc, 0xffffffff, 0x38404242, },
+ { 1, 2, 0, 0x0000183c, 0xffffffff, 0x46464646, },
+ { 1, 2, 0, 0x00001840, 0xffffffff, 0x42444646, },
+ { 1, 2, 0, 0x00001844, 0xffffffff, 0x44443840, },
+ { 1, 2, 1, 0x00001848, 0xffffffff, 0x44444444, },
+ { 1, 2, 1, 0x0000184c, 0xffffffff, 0x36384042, },
+ { 1, 2, 2, 0x000018e0, 0xffffffff, 0x42424242, },
+ { 1, 2, 2, 0x000018e4, 0xffffffff, 0x38404242, },
+ { 1, 2, 2, 0x000018e8, 0x0000ffff, 0x20203436, },
+ { 1, 3, 0, 0x00001a24, 0xffffffff, 0x46464646, },
+ { 1, 3, 0, 0x00001a28, 0xffffffff, 0x44464646, },
+ { 1, 3, 0, 0x00001a2c, 0xffffffff, 0x46464646, },
+ { 1, 3, 0, 0x00001a30, 0xffffffff, 0x42444646, },
+ { 1, 3, 1, 0x00001a34, 0xffffffff, 0x44444444, },
+ { 1, 3, 1, 0x00001a38, 0xffffffff, 0x40424444, },
+ { 1, 3, 2, 0x00001ad8, 0xffffffff, 0x42424242, },
+ { 1, 3, 2, 0x00001adc, 0xffffffff, 0x38404242, },
+ { 1, 3, 0, 0x00001a3c, 0xffffffff, 0x46464646, },
+ { 1, 3, 0, 0x00001a40, 0xffffffff, 0x42444646, },
+ { 1, 3, 0, 0x00001a44, 0xffffffff, 0x44443840, },
+ { 1, 3, 1, 0x00001a48, 0xffffffff, 0x44444444, },
+ { 1, 3, 1, 0x00001a4c, 0xffffffff, 0x36384042, },
+ { 1, 3, 2, 0x00001ae0, 0xffffffff, 0x42424242, },
+ { 1, 3, 2, 0x00001ae4, 0xffffffff, 0x38404242, },
+ { 1, 3, 2, 0x00001ae8, 0x0000ffff, 0x20203436, },
+};
+
+RTW_DECL_TABLE_BB_PG(rtw8814a_bb_pg_type5);
+
+static const struct rtw_phy_pg_cfg_pair rtw8814a_bb_pg_type7[] = {
+ { 0, 0, 0, 0x00000c20, 0xffffffff, 0x34343434, },
+ { 0, 0, 0, 0x00000c24, 0xffffffff, 0x34343434, },
+ { 0, 0, 0, 0x00000c28, 0xffffffff, 0x30323434, },
+ { 0, 0, 0, 0x00000c2c, 0xffffffff, 0x34343434, },
+ { 0, 0, 0, 0x00000c30, 0xffffffff, 0x28303234, },
+ { 0, 0, 1, 0x00000c34, 0xffffffff, 0x34343434, },
+ { 0, 0, 1, 0x00000c38, 0xffffffff, 0x28303234, },
+ { 0, 0, 2, 0x00000cd8, 0xffffffff, 0x34343434, },
+ { 0, 0, 2, 0x00000cdc, 0xffffffff, 0x28303234, },
+ { 0, 0, 0, 0x00000c3c, 0xffffffff, 0x34343434, },
+ { 0, 0, 0, 0x00000c40, 0xffffffff, 0x28303234, },
+ { 0, 0, 0, 0x00000c44, 0xffffffff, 0x34342426, },
+ { 0, 0, 1, 0x00000c48, 0xffffffff, 0x32343434, },
+ { 0, 0, 1, 0x00000c4c, 0xffffffff, 0x24262830, },
+ { 0, 0, 2, 0x00000ce0, 0xffffffff, 0x34343434, },
+ { 0, 0, 2, 0x00000ce4, 0xffffffff, 0x28303234, },
+ { 0, 0, 2, 0x00000ce8, 0x0000ffff, 0x24263434, },
+ { 0, 1, 0, 0x00000e20, 0xffffffff, 0x34343434, },
+ { 0, 1, 0, 0x00000e24, 0xffffffff, 0x34343434, },
+ { 0, 1, 0, 0x00000e28, 0xffffffff, 0x30323434, },
+ { 0, 1, 0, 0x00000e2c, 0xffffffff, 0x34343434, },
+ { 0, 1, 0, 0x00000e30, 0xffffffff, 0x28303234, },
+ { 0, 1, 1, 0x00000e34, 0xffffffff, 0x34343434, },
+ { 0, 1, 1, 0x00000e38, 0xffffffff, 0x28303234, },
+ { 0, 1, 2, 0x00000ed8, 0xffffffff, 0x34343434, },
+ { 0, 1, 2, 0x00000edc, 0xffffffff, 0x28303234, },
+ { 0, 1, 0, 0x00000e3c, 0xffffffff, 0x34343434, },
+ { 0, 1, 0, 0x00000e40, 0xffffffff, 0x28303234, },
+ { 0, 1, 0, 0x00000e44, 0xffffffff, 0x34342426, },
+ { 0, 1, 1, 0x00000e48, 0xffffffff, 0x32343434, },
+ { 0, 1, 1, 0x00000e4c, 0xffffffff, 0x24262830, },
+ { 0, 1, 2, 0x00000ee0, 0xffffffff, 0x34343434, },
+ { 0, 1, 2, 0x00000ee4, 0xffffffff, 0x28303234, },
+ { 0, 1, 2, 0x00000ee8, 0x0000ffff, 0x24263434, },
+ { 0, 2, 0, 0x00001820, 0xffffffff, 0x34343434, },
+ { 0, 2, 0, 0x00001824, 0xffffffff, 0x34343434, },
+ { 0, 2, 0, 0x00001828, 0xffffffff, 0x30323434, },
+ { 0, 2, 0, 0x0000182c, 0xffffffff, 0x34343434, },
+ { 0, 2, 0, 0x00001830, 0xffffffff, 0x28303234, },
+ { 0, 2, 1, 0x00001834, 0xffffffff, 0x34343434, },
+ { 0, 2, 1, 0x00001838, 0xffffffff, 0x28303234, },
+ { 0, 2, 2, 0x000018d8, 0xffffffff, 0x34343434, },
+ { 0, 2, 2, 0x000018dc, 0xffffffff, 0x28303234, },
+ { 0, 2, 0, 0x0000183c, 0xffffffff, 0x34343434, },
+ { 0, 2, 0, 0x00001840, 0xffffffff, 0x28303234, },
+ { 0, 2, 0, 0x00001844, 0xffffffff, 0x34342426, },
+ { 0, 2, 1, 0x00001848, 0xffffffff, 0x32343434, },
+ { 0, 2, 1, 0x0000184c, 0xffffffff, 0x24262830, },
+ { 0, 2, 2, 0x000018e0, 0xffffffff, 0x34343434, },
+ { 0, 2, 2, 0x000018e4, 0xffffffff, 0x28303234, },
+ { 0, 2, 2, 0x000018e8, 0x0000ffff, 0x24263434, },
+ { 0, 3, 0, 0x00001a20, 0xffffffff, 0x34343434, },
+ { 0, 3, 0, 0x00001a24, 0xffffffff, 0x34343434, },
+ { 0, 3, 0, 0x00001a28, 0xffffffff, 0x30323434, },
+ { 0, 3, 0, 0x00001a2c, 0xffffffff, 0x34343434, },
+ { 0, 3, 0, 0x00001a30, 0xffffffff, 0x28303234, },
+ { 0, 3, 1, 0x00001a34, 0xffffffff, 0x34343434, },
+ { 0, 3, 1, 0x00001a38, 0xffffffff, 0x28303234, },
+ { 0, 3, 2, 0x00001ad8, 0xffffffff, 0x34343434, },
+ { 0, 3, 2, 0x00001adc, 0xffffffff, 0x28303234, },
+ { 0, 3, 0, 0x00001a3c, 0xffffffff, 0x34343434, },
+ { 0, 3, 0, 0x00001a40, 0xffffffff, 0x28303234, },
+ { 0, 3, 0, 0x00001a44, 0xffffffff, 0x34342426, },
+ { 0, 3, 1, 0x00001a48, 0xffffffff, 0x32343434, },
+ { 0, 3, 1, 0x00001a4c, 0xffffffff, 0x24262830, },
+ { 0, 3, 2, 0x00001ae0, 0xffffffff, 0x34343434, },
+ { 0, 3, 2, 0x00001ae4, 0xffffffff, 0x28303234, },
+ { 0, 3, 2, 0x00001ae8, 0x0000ffff, 0x24263434, },
+ { 1, 0, 0, 0x00000c24, 0xffffffff, 0x34343434, },
+ { 1, 0, 0, 0x00000c28, 0xffffffff, 0x30323434, },
+ { 1, 0, 0, 0x00000c2c, 0xffffffff, 0x34343434, },
+ { 1, 0, 0, 0x00000c30, 0xffffffff, 0x28303234, },
+ { 1, 0, 1, 0x00000c34, 0xffffffff, 0x34343434, },
+ { 1, 0, 1, 0x00000c38, 0xffffffff, 0x28303234, },
+ { 1, 0, 2, 0x00000cd8, 0xffffffff, 0x34343434, },
+ { 1, 0, 2, 0x00000cdc, 0xffffffff, 0x28303234, },
+ { 1, 0, 0, 0x00000c3c, 0xffffffff, 0x34343434, },
+ { 1, 0, 0, 0x00000c40, 0xffffffff, 0x28303234, },
+ { 1, 0, 0, 0x00000c44, 0xffffffff, 0x34342426, },
+ { 1, 0, 1, 0x00000c48, 0xffffffff, 0x32343434, },
+ { 1, 0, 1, 0x00000c4c, 0xffffffff, 0x24262830, },
+ { 1, 0, 2, 0x00000ce0, 0xffffffff, 0x34343434, },
+ { 1, 0, 2, 0x00000ce4, 0xffffffff, 0x28303234, },
+ { 1, 0, 2, 0x00000ce8, 0x0000ffff, 0x24263434, },
+ { 1, 1, 0, 0x00000e24, 0xffffffff, 0x34343434, },
+ { 1, 1, 0, 0x00000e28, 0xffffffff, 0x30323434, },
+ { 1, 1, 0, 0x00000e2c, 0xffffffff, 0x34343434, },
+ { 1, 1, 0, 0x00000e30, 0xffffffff, 0x28303234, },
+ { 1, 1, 1, 0x00000e34, 0xffffffff, 0x34343434, },
+ { 1, 1, 1, 0x00000e38, 0xffffffff, 0x28303234, },
+ { 1, 1, 2, 0x00000ed8, 0xffffffff, 0x34343434, },
+ { 1, 1, 2, 0x00000edc, 0xffffffff, 0x28303234, },
+ { 1, 1, 0, 0x00000e3c, 0xffffffff, 0x34343434, },
+ { 1, 1, 0, 0x00000e40, 0xffffffff, 0x28303234, },
+ { 1, 1, 0, 0x00000e44, 0xffffffff, 0x34342426, },
+ { 1, 1, 1, 0x00000e48, 0xffffffff, 0x32343434, },
+ { 1, 1, 1, 0x00000e4c, 0xffffffff, 0x24262830, },
+ { 1, 1, 2, 0x00000ee0, 0xffffffff, 0x34343434, },
+ { 1, 1, 2, 0x00000ee4, 0xffffffff, 0x28303234, },
+ { 1, 1, 2, 0x00000ee8, 0x0000ffff, 0x24263434, },
+ { 1, 2, 0, 0x00001824, 0xffffffff, 0x34343434, },
+ { 1, 2, 0, 0x00001828, 0xffffffff, 0x30323434, },
+ { 1, 2, 0, 0x0000182c, 0xffffffff, 0x34343434, },
+ { 1, 2, 0, 0x00001830, 0xffffffff, 0x28303234, },
+ { 1, 2, 1, 0x00001834, 0xffffffff, 0x34343434, },
+ { 1, 2, 1, 0x00001838, 0xffffffff, 0x28303234, },
+ { 1, 2, 2, 0x000018d8, 0xffffffff, 0x34343434, },
+ { 1, 2, 2, 0x000018dc, 0xffffffff, 0x28303234, },
+ { 1, 2, 0, 0x0000183c, 0xffffffff, 0x34343434, },
+ { 1, 2, 0, 0x00001840, 0xffffffff, 0x28303234, },
+ { 1, 2, 0, 0x00001844, 0xffffffff, 0x34342426, },
+ { 1, 2, 1, 0x00001848, 0xffffffff, 0x32343434, },
+ { 1, 2, 1, 0x0000184c, 0xffffffff, 0x24262830, },
+ { 1, 2, 2, 0x000018e0, 0xffffffff, 0x34343434, },
+ { 1, 2, 2, 0x000018e4, 0xffffffff, 0x28303234, },
+ { 1, 2, 2, 0x000018e8, 0x0000ffff, 0x24263434, },
+ { 1, 3, 0, 0x00001a24, 0xffffffff, 0x34343434, },
+ { 1, 3, 0, 0x00001a28, 0xffffffff, 0x30323434, },
+ { 1, 3, 0, 0x00001a2c, 0xffffffff, 0x34343434, },
+ { 1, 3, 0, 0x00001a30, 0xffffffff, 0x28303234, },
+ { 1, 3, 1, 0x00001a34, 0xffffffff, 0x34343434, },
+ { 1, 3, 1, 0x00001a38, 0xffffffff, 0x28303234, },
+ { 1, 3, 2, 0x00001ad8, 0xffffffff, 0x34343434, },
+ { 1, 3, 2, 0x00001adc, 0xffffffff, 0x28303234, },
+ { 1, 3, 0, 0x00001a3c, 0xffffffff, 0x34343434, },
+ { 1, 3, 0, 0x00001a40, 0xffffffff, 0x28303234, },
+ { 1, 3, 0, 0x00001a44, 0xffffffff, 0x34342426, },
+ { 1, 3, 1, 0x00001a48, 0xffffffff, 0x32343434, },
+ { 1, 3, 1, 0x00001a4c, 0xffffffff, 0x24262830, },
+ { 1, 3, 2, 0x00001ae0, 0xffffffff, 0x34343434, },
+ { 1, 3, 2, 0x00001ae4, 0xffffffff, 0x28303234, },
+ { 1, 3, 2, 0x00001ae8, 0x0000ffff, 0x24263434, },
+};
+
+RTW_DECL_TABLE_BB_PG(rtw8814a_bb_pg_type7);
+
+static const struct rtw_phy_pg_cfg_pair rtw8814a_bb_pg_type8[] = {
+ { 0, 0, 0, 0x00000c20, 0xffffffff, 0x43434343, },
+ { 0, 0, 0, 0x00000c24, 0xffffffff, 0x43434343, },
+ { 0, 0, 0, 0x00000c28, 0xffffffff, 0x35373941, },
+ { 0, 0, 0, 0x00000c2c, 0xffffffff, 0x43434343, },
+ { 0, 0, 0, 0x00000c30, 0xffffffff, 0x33353739, },
+ { 0, 0, 1, 0x00000c34, 0xffffffff, 0x43434343, },
+ { 0, 0, 1, 0x00000c38, 0xffffffff, 0x31333537, },
+ { 0, 0, 2, 0x00000cd8, 0xffffffff, 0x43434343, },
+ { 0, 0, 2, 0x00000cdc, 0xffffffff, 0x29313335, },
+ { 0, 0, 0, 0x00000c3c, 0xffffffff, 0x34343434, },
+ { 0, 0, 0, 0x00000c40, 0xffffffff, 0x28303234, },
+ { 0, 0, 0, 0x00000c44, 0xffffffff, 0x32322426, },
+ { 0, 0, 1, 0x00000c48, 0xffffffff, 0x30323232, },
+ { 0, 0, 1, 0x00000c4c, 0xffffffff, 0x22242628, },
+ { 0, 0, 2, 0x00000ce0, 0xffffffff, 0x30303030, },
+ { 0, 0, 2, 0x00000ce4, 0xffffffff, 0x24262830, },
+ { 0, 0, 2, 0x00000ce8, 0x0000ffff, 0x20222222, },
+ { 0, 1, 0, 0x00000e20, 0xffffffff, 0x43434343, },
+ { 0, 1, 0, 0x00000e24, 0xffffffff, 0x43434343, },
+ { 0, 1, 0, 0x00000e28, 0xffffffff, 0x35373941, },
+ { 0, 1, 0, 0x00000e2c, 0xffffffff, 0x41434343, },
+ { 0, 1, 0, 0x00000e30, 0xffffffff, 0x33353739, },
+ { 0, 1, 1, 0x00000e34, 0xffffffff, 0x39414141, },
+ { 0, 1, 1, 0x00000e38, 0xffffffff, 0x31333537, },
+ { 0, 1, 2, 0x00000ed8, 0xffffffff, 0x37393939, },
+ { 0, 1, 2, 0x00000edc, 0xffffffff, 0x29313335, },
+ { 0, 1, 0, 0x00000e3c, 0xffffffff, 0x34343434, },
+ { 0, 1, 0, 0x00000e40, 0xffffffff, 0x28303234, },
+ { 0, 1, 0, 0x00000e44, 0xffffffff, 0x32322426, },
+ { 0, 1, 1, 0x00000e48, 0xffffffff, 0x30323232, },
+ { 0, 1, 1, 0x00000e4c, 0xffffffff, 0x22242628, },
+ { 0, 1, 2, 0x00000ee0, 0xffffffff, 0x30303030, },
+ { 0, 1, 2, 0x00000ee4, 0xffffffff, 0x24262830, },
+ { 0, 1, 2, 0x00000ee8, 0x0000ffff, 0x20222222, },
+ { 0, 2, 0, 0x00001820, 0xffffffff, 0x43434343, },
+ { 0, 2, 0, 0x00001824, 0xffffffff, 0x43434343, },
+ { 0, 2, 0, 0x00001828, 0xffffffff, 0x35373941, },
+ { 0, 2, 0, 0x0000182c, 0xffffffff, 0x41434343, },
+ { 0, 2, 0, 0x00001830, 0xffffffff, 0x33353739, },
+ { 0, 2, 1, 0x00001834, 0xffffffff, 0x39414141, },
+ { 0, 2, 1, 0x00001838, 0xffffffff, 0x31333537, },
+ { 0, 2, 2, 0x000018d8, 0xffffffff, 0x37393939, },
+ { 0, 2, 2, 0x000018dc, 0xffffffff, 0x29313335, },
+ { 0, 2, 0, 0x0000183c, 0xffffffff, 0x34343434, },
+ { 0, 2, 0, 0x00001840, 0xffffffff, 0x28303234, },
+ { 0, 2, 0, 0x00001844, 0xffffffff, 0x32322426, },
+ { 0, 2, 1, 0x00001848, 0xffffffff, 0x30323232, },
+ { 0, 2, 1, 0x0000184c, 0xffffffff, 0x22242628, },
+ { 0, 2, 2, 0x000018e0, 0xffffffff, 0x30303030, },
+ { 0, 2, 2, 0x000018e4, 0xffffffff, 0x24262830, },
+ { 0, 2, 2, 0x000018e8, 0x0000ffff, 0x20222222, },
+ { 0, 3, 0, 0x00001a20, 0xffffffff, 0x43434343, },
+ { 0, 3, 0, 0x00001a24, 0xffffffff, 0x43434343, },
+ { 0, 3, 0, 0x00001a28, 0xffffffff, 0x35373941, },
+ { 0, 3, 0, 0x00001a2c, 0xffffffff, 0x41434343, },
+ { 0, 3, 0, 0x00001a30, 0xffffffff, 0x33353739, },
+ { 0, 3, 1, 0x00001a34, 0xffffffff, 0x39414141, },
+ { 0, 3, 1, 0x00001a38, 0xffffffff, 0x31333537, },
+ { 0, 3, 2, 0x00001ad8, 0xffffffff, 0x37393939, },
+ { 0, 3, 2, 0x00001adc, 0xffffffff, 0x29313335, },
+ { 0, 3, 0, 0x00001a3c, 0xffffffff, 0x34343434, },
+ { 0, 3, 0, 0x00001a40, 0xffffffff, 0x28303234, },
+ { 0, 3, 0, 0x00001a44, 0xffffffff, 0x32322426, },
+ { 0, 3, 1, 0x00001a48, 0xffffffff, 0x30323232, },
+ { 0, 3, 1, 0x00001a4c, 0xffffffff, 0x22242628, },
+ { 0, 3, 2, 0x00001ae0, 0xffffffff, 0x30303030, },
+ { 0, 3, 2, 0x00001ae4, 0xffffffff, 0x24262830, },
+ { 0, 3, 2, 0x00001ae8, 0x0000ffff, 0x20222222, },
+ { 1, 0, 0, 0x00000c24, 0xffffffff, 0x46464646, },
+ { 1, 0, 0, 0x00000c28, 0xffffffff, 0x39414345, },
+ { 1, 0, 0, 0x00000c2c, 0xffffffff, 0x46464646, },
+ { 1, 0, 0, 0x00000c30, 0xffffffff, 0x38404244, },
+ { 1, 0, 1, 0x00000c34, 0xffffffff, 0x46464646, },
+ { 1, 0, 1, 0x00000c38, 0xffffffff, 0x36384042, },
+ { 1, 0, 2, 0x00000cd8, 0xffffffff, 0x46464646, },
+ { 1, 0, 2, 0x00000cdc, 0xffffffff, 0x34363840, },
+ { 1, 0, 0, 0x00000c3c, 0xffffffff, 0x46464646, },
+ { 1, 0, 0, 0x00000c40, 0xffffffff, 0x38404244, },
+ { 1, 0, 0, 0x00000c44, 0xffffffff, 0x46463738, },
+ { 1, 0, 1, 0x00000c48, 0xffffffff, 0x42444646, },
+ { 1, 0, 1, 0x00000c4c, 0xffffffff, 0x35373840, },
+ { 1, 0, 2, 0x00000ce0, 0xffffffff, 0x46464646, },
+ { 1, 0, 2, 0x00000ce4, 0xffffffff, 0x37394143, },
+ { 1, 0, 2, 0x00000ce8, 0x0000ffff, 0x33333335, },
+ { 1, 1, 0, 0x00000e24, 0xffffffff, 0x46464646, },
+ { 1, 1, 0, 0x00000e28, 0xffffffff, 0x39414345, },
+ { 1, 1, 0, 0x00000e2c, 0xffffffff, 0x46464646, },
+ { 1, 1, 0, 0x00000e30, 0xffffffff, 0x38404244, },
+ { 1, 1, 1, 0x00000e34, 0xffffffff, 0x46464646, },
+ { 1, 1, 1, 0x00000e38, 0xffffffff, 0x36384042, },
+ { 1, 1, 2, 0x00000ed8, 0xffffffff, 0x46464646, },
+ { 1, 1, 2, 0x00000edc, 0xffffffff, 0x34363840, },
+ { 1, 1, 0, 0x00000e3c, 0xffffffff, 0x46464646, },
+ { 1, 1, 0, 0x00000e40, 0xffffffff, 0x38404244, },
+ { 1, 1, 0, 0x00000e44, 0xffffffff, 0x46463738, },
+ { 1, 1, 1, 0x00000e48, 0xffffffff, 0x42444646, },
+ { 1, 1, 1, 0x00000e4c, 0xffffffff, 0x35373840, },
+ { 1, 1, 2, 0x00000ee0, 0xffffffff, 0x46464646, },
+ { 1, 1, 2, 0x00000ee4, 0xffffffff, 0x37394143, },
+ { 1, 1, 2, 0x00000ee8, 0x0000ffff, 0x33333335, },
+ { 1, 2, 0, 0x00001824, 0xffffffff, 0x46464646, },
+ { 1, 2, 0, 0x00001828, 0xffffffff, 0x39414345, },
+ { 1, 2, 0, 0x0000182c, 0xffffffff, 0x46464646, },
+ { 1, 2, 0, 0x00001830, 0xffffffff, 0x38404244, },
+ { 1, 2, 1, 0x00001834, 0xffffffff, 0x46464646, },
+ { 1, 2, 1, 0x00001838, 0xffffffff, 0x36384042, },
+ { 1, 2, 2, 0x000018d8, 0xffffffff, 0x46464646, },
+ { 1, 2, 2, 0x000018dc, 0xffffffff, 0x34363840, },
+ { 1, 2, 0, 0x0000183c, 0xffffffff, 0x46464646, },
+ { 1, 2, 0, 0x00001840, 0xffffffff, 0x38404244, },
+ { 1, 2, 0, 0x00001844, 0xffffffff, 0x46463738, },
+ { 1, 2, 1, 0x00001848, 0xffffffff, 0x42444646, },
+ { 1, 2, 1, 0x0000184c, 0xffffffff, 0x35373840, },
+ { 1, 2, 2, 0x000018e0, 0xffffffff, 0x46464646, },
+ { 1, 2, 2, 0x000018e4, 0xffffffff, 0x37394143, },
+ { 1, 2, 2, 0x000018e8, 0x0000ffff, 0x33333335, },
+ { 1, 3, 0, 0x00001a24, 0xffffffff, 0x46464646, },
+ { 1, 3, 0, 0x00001a28, 0xffffffff, 0x39414345, },
+ { 1, 3, 0, 0x00001a2c, 0xffffffff, 0x46464646, },
+ { 1, 3, 0, 0x00001a30, 0xffffffff, 0x38404244, },
+ { 1, 3, 1, 0x00001a34, 0xffffffff, 0x46464646, },
+ { 1, 3, 1, 0x00001a38, 0xffffffff, 0x36384042, },
+ { 1, 3, 2, 0x00001ad8, 0xffffffff, 0x46464646, },
+ { 1, 3, 2, 0x00001adc, 0xffffffff, 0x34363840, },
+ { 1, 3, 0, 0x00001a3c, 0xffffffff, 0x46464646, },
+ { 1, 3, 0, 0x00001a40, 0xffffffff, 0x38404244, },
+ { 1, 3, 0, 0x00001a44, 0xffffffff, 0x46463738, },
+ { 1, 3, 1, 0x00001a48, 0xffffffff, 0x42444646, },
+ { 1, 3, 1, 0x00001a4c, 0xffffffff, 0x35373840, },
+ { 1, 3, 2, 0x00001ae0, 0xffffffff, 0x46464646, },
+ { 1, 3, 2, 0x00001ae4, 0xffffffff, 0x37394143, },
+ { 1, 3, 2, 0x00001ae8, 0x0000ffff, 0x33333335, },
+};
+
+RTW_DECL_TABLE_BB_PG(rtw8814a_bb_pg_type8);
+
+static const u32 rtw8814a_rf_a[] = {
+ 0x018, 0x00013124,
+ 0x040, 0x00000C00,
+ 0x058, 0x00000F98,
+ 0x07F, 0x00068004,
+ 0x0B0, 0x000FFFFE,
+ 0x0B1, 0x0003FF48,
+ 0x0B2, 0x0006AA3F,
+ 0x0B3, 0x000FFC9A,
+ 0x0B4, 0x0000A78F,
+ 0x0B5, 0x00000A3F,
+ 0x0B6, 0x0000C09C,
+ 0x80000001, 0x00000000, 0x40000000, 0x00000000,
+ 0x0B7, 0x00030008,
+ 0x90000002, 0x00000000, 0x40000000, 0x00000000,
+ 0x0B7, 0x00030008,
+ 0x90000003, 0x00000000, 0x40000000, 0x00000000,
+ 0x0B7, 0x00030008,
+ 0x90000004, 0x00000000, 0x40000000, 0x00000000,
+ 0x0B7, 0x00030008,
+ 0x90000005, 0x00000000, 0x40000000, 0x00000000,
+ 0x0B7, 0x00030008,
+ 0x90000006, 0x00000000, 0x40000000, 0x00000000,
+ 0x0B7, 0x00030008,
+ 0x90000008, 0x00000000, 0x40000000, 0x00000000,
+ 0x0B7, 0x00030008,
+ 0x9000000a, 0x00000000, 0x40000000, 0x00000000,
+ 0x0B7, 0x00030008,
+ 0x9000000b, 0x00000000, 0x40000000, 0x00000000,
+ 0x0B7, 0x00030008,
+ 0xA0000000, 0x00000000,
+ 0x0B7, 0x0003000C,
+ 0xB0000000, 0x00000000,
+ 0x0B8, 0x0007400E,
+ 0x80000001, 0x00000000, 0x40000000, 0x00000000,
+ 0x0B9, 0x000FBF50,
+ 0x90000002, 0x00000000, 0x40000000, 0x00000000,
+ 0x0B9, 0x000FBF50,
+ 0x90000003, 0x00000000, 0x40000000, 0x00000000,
+ 0x0B9, 0x000FBF50,
+ 0x90000004, 0x00000000, 0x40000000, 0x00000000,
+ 0x0B9, 0x000FBF50,
+ 0x90000005, 0x00000000, 0x40000000, 0x00000000,
+ 0x0B9, 0x000FBF50,
+ 0x90000006, 0x00000000, 0x40000000, 0x00000000,
+ 0x0B9, 0x000FBF50,
+ 0x90000008, 0x00000000, 0x40000000, 0x00000000,
+ 0x0B9, 0x000FBF50,
+ 0x9000000a, 0x00000000, 0x40000000, 0x00000000,
+ 0x0B9, 0x000FBF50,
+ 0x9000000b, 0x00000000, 0x40000000, 0x00000000,
+ 0x0B9, 0x000FBF50,
+ 0xA0000000, 0x00000000,
+ 0x0B9, 0x000FBF50,
+ 0xB0000000, 0x00000000,
+ 0x0BA, 0x00050780,
+ 0x0BB, 0x00000000,
+ 0x0BC, 0x00040009,
+ 0x0BD, 0x00000000,
+ 0x0BE, 0x00000000,
+ 0x0BF, 0x00000000,
+ 0x0EF, 0x00020000,
+ 0x03E, 0x00000000,
+ 0x80000001, 0x00000000, 0x40000000, 0x00000000,
+ 0x03F, 0x00030000,
+ 0x90000006, 0x00000000, 0x40000000, 0x00000000,
+ 0x03F, 0x00030000,
+ 0xA0000000, 0x00000000,
+ 0x03F, 0x00030000,
+ 0xB0000000, 0x00000000,
+ 0x03E, 0x00020000,
+ 0x80000001, 0x00000000, 0x40000000, 0x00000000,
+ 0x03F, 0x00040000,
+ 0x90000006, 0x00000000, 0x40000000, 0x00000000,
+ 0x03F, 0x00040000,
+ 0xA0000000, 0x00000000,
+ 0x03F, 0x00040000,
+ 0xB0000000, 0x00000000,
+ 0x03E, 0x00040000,
+ 0x80000001, 0x00000000, 0x40000000, 0x00000000,
+ 0x03F, 0x00030000,
+ 0x90000006, 0x00000000, 0x40000000, 0x00000000,
+ 0x03F, 0x00030000,
+ 0xA0000000, 0x00000000,
+ 0x03F, 0x00030000,
+ 0xB0000000, 0x00000000,
+ 0x03E, 0x00060000,
+ 0x80000001, 0x00000000, 0x40000000, 0x00000000,
+ 0x03F, 0x00030000,
+ 0x90000006, 0x00000000, 0x40000000, 0x00000000,
+ 0x03F, 0x00030000,
+ 0xA0000000, 0x00000000,
+ 0x03F, 0x00030000,
+ 0xB0000000, 0x00000000,
+ 0x0EF, 0x00000000,
+ 0x0EF, 0x00010000,
+ 0x03E, 0x00000000,
+ 0x03F, 0x00006800,
+ 0x03E, 0x00000080,
+ 0x03F, 0x00006000,
+ 0x03E, 0x00000100,
+ 0x03F, 0x00004800,
+ 0x03E, 0x00000180,
+ 0x03F, 0x00004000,
+ 0x03E, 0x00000200,
+ 0x03F, 0x00004000,
+ 0x03E, 0x00000280,
+ 0x03F, 0x00002800,
+ 0x03E, 0x00000300,
+ 0x03F, 0x00002800,
+ 0x03E, 0x00000380,
+ 0x03F, 0x00002000,
+ 0x0EF, 0x00000000,
+ 0x0EF, 0x00040000,
+ 0x03E, 0x00000000,
+ 0x03F, 0x000000BC,
+ 0x03E, 0x00000040,
+ 0x03F, 0x00000053,
+ 0x03E, 0x00000050,
+ 0x03F, 0x00000050,
+ 0x03E, 0x00000060,
+ 0x03F, 0x00000050,
+ 0x0EF, 0x00000000,
+ 0x0EF, 0x00000400,
+ 0x03E, 0x00000006,
+ 0x041, 0x000EE080,
+ 0x03E, 0x00000008,
+ 0x041, 0x000EE0C0,
+ 0x03E, 0x0000000A,
+ 0x041, 0x000EE100,
+ 0x03E, 0x0000000C,
+ 0x041, 0x000EE100,
+ 0x0EF, 0x00000000,
+ 0x018, 0x00000006,
+ 0x80000002, 0x00000000, 0x40000000, 0x00000000,
+ 0x086, 0x000E335A,
+ 0x087, 0x00079F80,
+ 0x90000003, 0x00000000, 0x40000000, 0x00000000,
+ 0x086, 0x000E335A,
+ 0x087, 0x00079F80,
+ 0x90000004, 0x00000000, 0x40000000, 0x00000000,
+ 0x086, 0x000E335A,
+ 0x087, 0x00079F80,
+ 0x90000005, 0x00000000, 0x40000000, 0x00000000,
+ 0x086, 0x000E335A,
+ 0x087, 0x00079F80,
+ 0x90000008, 0x00000000, 0x40000000, 0x00000000,
+ 0x086, 0x000E335A,
+ 0x087, 0x00079F80,
+ 0x9000000a, 0x00000000, 0x40000000, 0x00000000,
+ 0x086, 0x000E335A,
+ 0x087, 0x00079F80,
+ 0x9000000b, 0x00000000, 0x40000000, 0x00000000,
+ 0x086, 0x000E335A,
+ 0x087, 0x00079F80,
+ 0xA0000000, 0x00000000,
+ 0x086, 0x000E4B58,
+ 0x087, 0x00049F80,
+ 0xB0000000, 0x00000000,
+ 0x0DF, 0x00000008,
+ 0x0EF, 0x00002000,
+ 0x80000002, 0x00000000, 0x40000000, 0x00000000,
+ 0x03B, 0x0003F19B,
+ 0x03B, 0x00037A5B,
+ 0x03B, 0x0002A433,
+ 0x03B, 0x00027BD3,
+ 0x03B, 0x0001F80B,
+ 0x03B, 0x000179C3,
+ 0x90000003, 0x00000000, 0x40000000, 0x00000000,
+ 0x03B, 0x0003F19B,
+ 0x03B, 0x00037A5B,
+ 0x03B, 0x0002A433,
+ 0x03B, 0x00027BD3,
+ 0x03B, 0x0001F80B,
+ 0x03B, 0x000179C3,
+ 0x90000004, 0x00000000, 0x40000000, 0x00000000,
+ 0x03B, 0x0003F19B,
+ 0x03B, 0x00037A5B,
+ 0x03B, 0x0002A433,
+ 0x03B, 0x00027BD3,
+ 0x03B, 0x0001F80B,
+ 0x03B, 0x000179C3,
+ 0x90000005, 0x00000000, 0x40000000, 0x00000000,
+ 0x03B, 0x0003F19B,
+ 0x03B, 0x00037A5B,
+ 0x03B, 0x0002A433,
+ 0x03B, 0x00027BD3,
+ 0x03B, 0x0001F80B,
+ 0x03B, 0x000179C3,
+ 0x90000008, 0x00000000, 0x40000000, 0x00000000,
+ 0x03B, 0x0003F19B,
+ 0x03B, 0x00037A5B,
+ 0x03B, 0x0002A433,
+ 0x03B, 0x00027BD3,
+ 0x03B, 0x0001F80B,
+ 0x03B, 0x000179C3,
+ 0x9000000a, 0x00000000, 0x40000000, 0x00000000,
+ 0x03B, 0x0003F19B,
+ 0x03B, 0x00037A5B,
+ 0x03B, 0x0002A433,
+ 0x03B, 0x00027BD3,
+ 0x03B, 0x0001F80B,
+ 0x03B, 0x000179C3,
+ 0x9000000b, 0x00000000, 0x40000000, 0x00000000,
+ 0x03B, 0x0003F19B,
+ 0x03B, 0x00037A5B,
+ 0x03B, 0x0002A433,
+ 0x03B, 0x00027BD3,
+ 0x03B, 0x0001F80B,
+ 0x03B, 0x000179C3,
+ 0xA0000000, 0x00000000,
+ 0x03B, 0x0003F258,
+ 0x03B, 0x00030A58,
+ 0x03B, 0x0002FA58,
+ 0x03B, 0x00022590,
+ 0x03B, 0x0001FA50,
+ 0x03B, 0x00010248,
+ 0x03B, 0x00008240,
+ 0xB0000000, 0x00000000,
+ 0x0EF, 0x00000100,
+ 0x80000002, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0000A0D0,
+ 0x034, 0x000090CD,
+ 0x034, 0x000080CA,
+ 0x034, 0x0000704D,
+ 0x034, 0x0000604A,
+ 0x034, 0x00005047,
+ 0x034, 0x0000400A,
+ 0x034, 0x00003007,
+ 0x034, 0x00002004,
+ 0x034, 0x00001001,
+ 0x034, 0x00000001,
+ 0x90000003, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0000A0D0,
+ 0x034, 0x000090CD,
+ 0x034, 0x000080CA,
+ 0x034, 0x0000704D,
+ 0x034, 0x0000604A,
+ 0x034, 0x00005047,
+ 0x034, 0x0000400A,
+ 0x034, 0x00003007,
+ 0x034, 0x00002004,
+ 0x034, 0x00001001,
+ 0x034, 0x00000001,
+ 0x90000004, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0000A0D0,
+ 0x034, 0x000090CD,
+ 0x034, 0x000080CA,
+ 0x034, 0x0000704D,
+ 0x034, 0x0000604A,
+ 0x034, 0x00005047,
+ 0x034, 0x0000400A,
+ 0x034, 0x00003007,
+ 0x034, 0x00002004,
+ 0x034, 0x00001001,
+ 0x034, 0x00000001,
+ 0x90000005, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0000A0D0,
+ 0x034, 0x000090CD,
+ 0x034, 0x000080CA,
+ 0x034, 0x0000704D,
+ 0x034, 0x0000604A,
+ 0x034, 0x00005047,
+ 0x034, 0x0000400A,
+ 0x034, 0x00003007,
+ 0x034, 0x00002004,
+ 0x034, 0x00001001,
+ 0x034, 0x00000001,
+ 0x90000008, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0000A0D0,
+ 0x034, 0x000090CD,
+ 0x034, 0x000080CA,
+ 0x034, 0x0000704D,
+ 0x034, 0x0000604A,
+ 0x034, 0x00005047,
+ 0x034, 0x0000400A,
+ 0x034, 0x00003007,
+ 0x034, 0x00002004,
+ 0x034, 0x00001001,
+ 0x034, 0x00000001,
+ 0x9000000a, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0000A0D0,
+ 0x034, 0x000090CD,
+ 0x034, 0x000080CA,
+ 0x034, 0x0000704D,
+ 0x034, 0x0000604A,
+ 0x034, 0x00005047,
+ 0x034, 0x0000400A,
+ 0x034, 0x00003007,
+ 0x034, 0x00002004,
+ 0x034, 0x00001001,
+ 0x034, 0x00000001,
+ 0x9000000b, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0000A0D0,
+ 0x034, 0x000090CD,
+ 0x034, 0x000080CA,
+ 0x034, 0x0000704D,
+ 0x034, 0x0000604A,
+ 0x034, 0x00005047,
+ 0x034, 0x0000400A,
+ 0x034, 0x00003007,
+ 0x034, 0x00002004,
+ 0x034, 0x00001001,
+ 0x034, 0x00000001,
+ 0xA0000000, 0x00000000,
+ 0x034, 0x0000ADF6,
+ 0x034, 0x00009DF3,
+ 0x034, 0x00008DF0,
+ 0x034, 0x00007DED,
+ 0x034, 0x00006DEA,
+ 0x034, 0x00005CED,
+ 0x034, 0x00004CEA,
+ 0x034, 0x000034EA,
+ 0x034, 0x000024E7,
+ 0x034, 0x0000146A,
+ 0x034, 0x0000006B,
+ 0xB0000000, 0x00000000,
+ 0x80000002, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0000A0D0,
+ 0x034, 0x000090CD,
+ 0x034, 0x000080CA,
+ 0x034, 0x0000704D,
+ 0x034, 0x0000604A,
+ 0x034, 0x00005047,
+ 0x034, 0x0000400A,
+ 0x034, 0x00003007,
+ 0x034, 0x00002004,
+ 0x034, 0x00001001,
+ 0x034, 0x00000001,
+ 0x90000003, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0000A0D0,
+ 0x034, 0x000090CD,
+ 0x034, 0x000080CA,
+ 0x034, 0x0000704D,
+ 0x034, 0x0000604A,
+ 0x034, 0x00005047,
+ 0x034, 0x0000400A,
+ 0x034, 0x00003007,
+ 0x034, 0x00002004,
+ 0x034, 0x00001001,
+ 0x034, 0x00000001,
+ 0x90000004, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0000A0D0,
+ 0x034, 0x000090CD,
+ 0x034, 0x000080CA,
+ 0x034, 0x0000704D,
+ 0x034, 0x0000604A,
+ 0x034, 0x00005047,
+ 0x034, 0x0000400A,
+ 0x034, 0x00003007,
+ 0x034, 0x00002004,
+ 0x034, 0x00001001,
+ 0x034, 0x00000001,
+ 0x90000005, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0000A0D0,
+ 0x034, 0x000090CD,
+ 0x034, 0x000080CA,
+ 0x034, 0x0000704D,
+ 0x034, 0x0000604A,
+ 0x034, 0x00005047,
+ 0x034, 0x0000400A,
+ 0x034, 0x00003007,
+ 0x034, 0x00002004,
+ 0x034, 0x00001001,
+ 0x034, 0x00000001,
+ 0x90000008, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0000A0D0,
+ 0x034, 0x000090CD,
+ 0x034, 0x000080CA,
+ 0x034, 0x0000704D,
+ 0x034, 0x0000604A,
+ 0x034, 0x00005047,
+ 0x034, 0x0000400A,
+ 0x034, 0x00003007,
+ 0x034, 0x00002004,
+ 0x034, 0x00001001,
+ 0x034, 0x00000001,
+ 0x9000000a, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0000A0D0,
+ 0x034, 0x000090CD,
+ 0x034, 0x000080CA,
+ 0x034, 0x0000704D,
+ 0x034, 0x0000604A,
+ 0x034, 0x00005047,
+ 0x034, 0x0000400A,
+ 0x034, 0x00003007,
+ 0x034, 0x00002004,
+ 0x034, 0x00001001,
+ 0x034, 0x00000001,
+ 0x9000000b, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0000A0D0,
+ 0x034, 0x000090CD,
+ 0x034, 0x000080CA,
+ 0x034, 0x0000704D,
+ 0x034, 0x0000604A,
+ 0x034, 0x00005047,
+ 0x034, 0x0000400A,
+ 0x034, 0x00003007,
+ 0x034, 0x00002004,
+ 0x034, 0x00001001,
+ 0x034, 0x00000001,
+ 0xA0000000, 0x00000000,
+ 0x034, 0x0008ADF6,
+ 0x034, 0x00089DF3,
+ 0x034, 0x00088DF0,
+ 0x034, 0x00087DED,
+ 0x034, 0x00086DEA,
+ 0x034, 0x00085CED,
+ 0x034, 0x00084CEA,
+ 0x034, 0x000834EA,
+ 0x034, 0x000824E7,
+ 0x034, 0x0008146A,
+ 0x034, 0x0008006B,
+ 0xB0000000, 0x00000000,
+ 0x0EF, 0x00000000,
+ 0x0EF, 0x000020A2,
+ 0x0DF, 0x00000080,
+ 0x035, 0x00000192,
+ 0x035, 0x00008192,
+ 0x035, 0x00010192,
+ 0x036, 0x00000024,
+ 0x036, 0x00008024,
+ 0x036, 0x00010024,
+ 0x036, 0x00018024,
+ 0x0EF, 0x00000000,
+ 0x051, 0x00000C21,
+ 0x052, 0x000006D9,
+ 0x053, 0x000FC649,
+ 0x054, 0x0000017E,
+ 0x018, 0x0001012A,
+ 0x081, 0x0007FC00,
+ 0x089, 0x00050110,
+ 0x08A, 0x00043E50,
+ 0x08B, 0x0002E180,
+ 0x08C, 0x00093C3C,
+ 0x80000001, 0x00000000, 0x40000000, 0x00000000,
+ 0x085, 0x000F8000,
+ 0x90000006, 0x00000000, 0x40000000, 0x00000000,
+ 0x085, 0x000F8000,
+ 0xA0000000, 0x00000000,
+ 0x085, 0x000F8000,
+ 0xB0000000, 0x00000000,
+ 0x80000001, 0x00000000, 0x40000000, 0x00000000,
+ 0x08D, 0x000FFFF0,
+ 0x90000002, 0x00000000, 0x40000000, 0x00000000,
+ 0x08D, 0x000FFFF0,
+ 0x90000003, 0x00000000, 0x40000000, 0x00000000,
+ 0x08D, 0x000FFFF0,
+ 0x90000004, 0x00000000, 0x40000000, 0x00000000,
+ 0x08D, 0x000FFFF0,
+ 0x90000005, 0x00000000, 0x40000000, 0x00000000,
+ 0x08D, 0x000FFFF0,
+ 0x90000006, 0x00000000, 0x40000000, 0x00000000,
+ 0x08D, 0x000FFFF0,
+ 0x90000008, 0x00000000, 0x40000000, 0x00000000,
+ 0x08D, 0x000FFFF0,
+ 0x9000000a, 0x00000000, 0x40000000, 0x00000000,
+ 0x08D, 0x000FFFF0,
+ 0x9000000b, 0x00000000, 0x40000000, 0x00000000,
+ 0x08D, 0x000FFFF0,
+ 0xA0000000, 0x00000000,
+ 0x08D, 0x000FFFF0,
+ 0xB0000000, 0x00000000,
+ 0x0EF, 0x00001000,
+ 0x03A, 0x0000013C,
+ 0x03B, 0x00038023,
+ 0x80000001, 0x00000000, 0x40000000, 0x00000000,
+ 0x03C, 0x00024000,
+ 0x90000002, 0x00000000, 0x40000000, 0x00000000,
+ 0x03C, 0x00024000,
+ 0x90000003, 0x00000000, 0x40000000, 0x00000000,
+ 0x03C, 0x00000000,
+ 0x90000004, 0x00000000, 0x40000000, 0x00000000,
+ 0x03C, 0x00088000,
+ 0x90000005, 0x00000000, 0x40000000, 0x00000000,
+ 0x03C, 0x00000000,
+ 0x90000006, 0x00000000, 0x40000000, 0x00000000,
+ 0x03C, 0x00024000,
+ 0x90000007, 0x00000000, 0x40000000, 0x00000000,
+ 0x03C, 0x00000000,
+ 0x90000008, 0x00000000, 0x40000000, 0x00000000,
+ 0x03C, 0x00084000,
+ 0x90000009, 0x00000000, 0x40000000, 0x00000000,
+ 0x03C, 0x00000000,
+ 0x9000000a, 0x00000000, 0x40000000, 0x00000000,
+ 0x03C, 0x00088000,
+ 0x9000000b, 0x00000000, 0x40000000, 0x00000000,
+ 0x03C, 0x00088000,
+ 0xA0000000, 0x00000000,
+ 0x03C, 0x00028000,
+ 0xB0000000, 0x00000000,
+ 0x03A, 0x0000013C,
+ 0x03B, 0x00030023,
+ 0x03C, 0x00048000,
+ 0x03A, 0x0000013C,
+ 0x03B, 0x00028623,
+ 0x03C, 0x00000000,
+ 0x03A, 0x0000013C,
+ 0x03B, 0x00021633,
+ 0x03C, 0x00000000,
+ 0x03A, 0x0000013C,
+ 0x03B, 0x0001C633,
+ 0x03C, 0x00000000,
+ 0x03A, 0x0000013C,
+ 0x03B, 0x00010293,
+ 0x03C, 0x00000000,
+ 0x03A, 0x0000013C,
+ 0x03B, 0x00009593,
+ 0x03C, 0x00000000,
+ 0x03A, 0x00000148,
+ 0x80000008, 0x00000000, 0x40000000, 0x00000000,
+ 0x03B, 0x0000118B,
+ 0xA0000000, 0x00000000,
+ 0x03B, 0x0000078B,
+ 0xB0000000, 0x00000000,
+ 0x03C, 0x00000000,
+ 0x80000001, 0x00000000, 0x40000000, 0x00000000,
+ 0x03A, 0x0000013C,
+ 0x03B, 0x00078023,
+ 0x90000002, 0x00000000, 0x40000000, 0x00000000,
+ 0x03A, 0x0000013C,
+ 0x03B, 0x00078023,
+ 0x90000003, 0x00000000, 0x40000000, 0x00000000,
+ 0x03A, 0x0000013C,
+ 0x03B, 0x00078023,
+ 0x90000004, 0x00000000, 0x40000000, 0x00000000,
+ 0x03A, 0x0000013C,
+ 0x03B, 0x00078023,
+ 0x90000005, 0x00000000, 0x40000000, 0x00000000,
+ 0x03A, 0x0000013C,
+ 0x03B, 0x00078023,
+ 0x90000006, 0x00000000, 0x40000000, 0x00000000,
+ 0x03A, 0x0000013C,
+ 0x03B, 0x00078023,
+ 0x90000008, 0x00000000, 0x40000000, 0x00000000,
+ 0x03A, 0x0000013C,
+ 0x03B, 0x00078023,
+ 0x9000000a, 0x00000000, 0x40000000, 0x00000000,
+ 0x03A, 0x0000013C,
+ 0x03B, 0x00078023,
+ 0x9000000b, 0x00000000, 0x40000000, 0x00000000,
+ 0x03A, 0x0000013C,
+ 0x03B, 0x00078023,
+ 0xA0000000, 0x00000000,
+ 0x03A, 0x0000013C,
+ 0x03B, 0x00078023,
+ 0xB0000000, 0x00000000,
+ 0x80000001, 0x00000000, 0x40000000, 0x00000000,
+ 0x03C, 0x00024000,
+ 0x90000002, 0x00000000, 0x40000000, 0x00000000,
+ 0x03C, 0x000AC000,
+ 0x90000003, 0x00000000, 0x40000000, 0x00000000,
+ 0x03C, 0x00024000,
+ 0x90000004, 0x00000000, 0x40000000, 0x00000000,
+ 0x03C, 0x00088000,
+ 0x90000005, 0x00000000, 0x40000000, 0x00000000,
+ 0x03C, 0x00024000,
+ 0x90000006, 0x00000000, 0x40000000, 0x00000000,
+ 0x03C, 0x00024000,
+ 0x90000007, 0x00000000, 0x40000000, 0x00000000,
+ 0x03C, 0x00000000,
+ 0x90000008, 0x00000000, 0x40000000, 0x00000000,
+ 0x03C, 0x00040000,
+ 0x90000009, 0x00000000, 0x40000000, 0x00000000,
+ 0x03C, 0x00000000,
+ 0x9000000a, 0x00000000, 0x40000000, 0x00000000,
+ 0x03C, 0x00088000,
+ 0x9000000b, 0x00000000, 0x40000000, 0x00000000,
+ 0x03C, 0x00088000,
+ 0xA0000000, 0x00000000,
+ 0x03C, 0x0004C000,
+ 0xB0000000, 0x00000000,
+ 0x03A, 0x0000013C,
+ 0x03B, 0x00070023,
+ 0x03C, 0x00048000,
+ 0x03A, 0x0000013C,
+ 0x03B, 0x00068623,
+ 0x03C, 0x00000000,
+ 0x03A, 0x0000013C,
+ 0x03B, 0x00061633,
+ 0x03C, 0x00000000,
+ 0x03A, 0x0000013C,
+ 0x03B, 0x0005C633,
+ 0x03C, 0x00000000,
+ 0x03A, 0x0000013C,
+ 0x03B, 0x00050293,
+ 0x03C, 0x00000000,
+ 0x03A, 0x0000013C,
+ 0x03B, 0x00049593,
+ 0x03C, 0x00000000,
+ 0x03A, 0x00000148,
+ 0x80000008, 0x00000000, 0x40000000, 0x00000000,
+ 0x03B, 0x0004138B,
+ 0xA0000000, 0x00000000,
+ 0x03B, 0x0004078B,
+ 0xB0000000, 0x00000000,
+ 0x03C, 0x00000000,
+ 0x80000001, 0x00000000, 0x40000000, 0x00000000,
+ 0x03A, 0x0000013C,
+ 0x03B, 0x000B8023,
+ 0x90000002, 0x00000000, 0x40000000, 0x00000000,
+ 0x03A, 0x0000013C,
+ 0x03B, 0x000B8023,
+ 0x90000003, 0x00000000, 0x40000000, 0x00000000,
+ 0x03A, 0x0000013C,
+ 0x03B, 0x000B8023,
+ 0x90000004, 0x00000000, 0x40000000, 0x00000000,
+ 0x03A, 0x0000013C,
+ 0x03B, 0x000B8023,
+ 0x90000005, 0x00000000, 0x40000000, 0x00000000,
+ 0x03A, 0x0000013C,
+ 0x03B, 0x000B8023,
+ 0x90000006, 0x00000000, 0x40000000, 0x00000000,
+ 0x03A, 0x0000013C,
+ 0x03B, 0x000B8023,
+ 0x90000008, 0x00000000, 0x40000000, 0x00000000,
+ 0x03A, 0x0000013C,
+ 0x03B, 0x000B8023,
+ 0x9000000a, 0x00000000, 0x40000000, 0x00000000,
+ 0x03A, 0x0000013C,
+ 0x03B, 0x000B8023,
+ 0x9000000b, 0x00000000, 0x40000000, 0x00000000,
+ 0x03A, 0x0000013C,
+ 0x03B, 0x000B8023,
+ 0xA0000000, 0x00000000,
+ 0x03A, 0x0000013C,
+ 0x03B, 0x000B8023,
+ 0xB0000000, 0x00000000,
+ 0x80000001, 0x00000000, 0x40000000, 0x00000000,
+ 0x03C, 0x00084000,
+ 0x90000002, 0x00000000, 0x40000000, 0x00000000,
+ 0x03C, 0x0008C000,
+ 0x90000003, 0x00000000, 0x40000000, 0x00000000,
+ 0x03C, 0x00000000,
+ 0x90000004, 0x00000000, 0x40000000, 0x00000000,
+ 0x03C, 0x00084000,
+ 0x90000005, 0x00000000, 0x40000000, 0x00000000,
+ 0x03C, 0x00000000,
+ 0x90000006, 0x00000000, 0x40000000, 0x00000000,
+ 0x03C, 0x00084000,
+ 0x90000007, 0x00000000, 0x40000000, 0x00000000,
+ 0x03C, 0x00020000,
+ 0x90000008, 0x00000000, 0x40000000, 0x00000000,
+ 0x03C, 0x00060000,
+ 0x9000000a, 0x00000000, 0x40000000, 0x00000000,
+ 0x03C, 0x00084000,
+ 0x9000000b, 0x00000000, 0x40000000, 0x00000000,
+ 0x03C, 0x00084000,
+ 0xA0000000, 0x00000000,
+ 0x03C, 0x00004000,
+ 0xB0000000, 0x00000000,
+ 0x03A, 0x0000013C,
+ 0x03B, 0x000B0023,
+ 0x80000004, 0x00000000, 0x40000000, 0x00000000,
+ 0x03C, 0x00020000,
+ 0x9000000a, 0x00000000, 0x40000000, 0x00000000,
+ 0x03C, 0x00020000,
+ 0x9000000b, 0x00000000, 0x40000000, 0x00000000,
+ 0x03C, 0x00020000,
+ 0xA0000000, 0x00000000,
+ 0x03C, 0x00020000,
+ 0xB0000000, 0x00000000,
+ 0x03A, 0x0000013C,
+ 0x03B, 0x000A8623,
+ 0x03C, 0x00000000,
+ 0x03A, 0x0000013C,
+ 0x03B, 0x000A1633,
+ 0x03C, 0x00000000,
+ 0x03A, 0x0000013C,
+ 0x03B, 0x0009C633,
+ 0x03C, 0x00000000,
+ 0x03A, 0x0000013C,
+ 0x03B, 0x00090293,
+ 0x03C, 0x00000000,
+ 0x03A, 0x0000013C,
+ 0x03B, 0x00089593,
+ 0x03C, 0x00000000,
+ 0x03A, 0x00000148,
+ 0x80000008, 0x00000000, 0x40000000, 0x00000000,
+ 0x03B, 0x0008118B,
+ 0xA0000000, 0x00000000,
+ 0x03B, 0x0008078B,
+ 0xB0000000, 0x00000000,
+ 0x03C, 0x00000000,
+ 0x0EF, 0x00000000,
+ 0x0EF, 0x00000800,
+ 0x03B, 0x00000000,
+ 0x80000001, 0x00000000, 0x40000000, 0x00000000,
+ 0x03A, 0x00000803,
+ 0x90000002, 0x00000000, 0x40000000, 0x00000000,
+ 0x03A, 0x00000801,
+ 0x90000006, 0x00000000, 0x40000000, 0x00000000,
+ 0x03A, 0x00000803,
+ 0x90000007, 0x00000000, 0x40000000, 0x00000000,
+ 0x03A, 0x00001003,
+ 0x90000008, 0x00000000, 0x40000000, 0x00000000,
+ 0x03A, 0x00001003,
+ 0x90000009, 0x00000000, 0x40000000, 0x00000000,
+ 0x03A, 0x00001003,
+ 0xA0000000, 0x00000000,
+ 0x03A, 0x00000803,
+ 0xB0000000, 0x00000000,
+ 0x03B, 0x00040000,
+ 0x80000001, 0x00000000, 0x40000000, 0x00000000,
+ 0x03A, 0x00001000,
+ 0x90000002, 0x00000000, 0x40000000, 0x00000000,
+ 0x03A, 0x00001801,
+ 0x90000003, 0x00000000, 0x40000000, 0x00000000,
+ 0x03A, 0x00000003,
+ 0x90000005, 0x00000000, 0x40000000, 0x00000000,
+ 0x03A, 0x00000003,
+ 0x90000006, 0x00000000, 0x40000000, 0x00000000,
+ 0x03A, 0x00001000,
+ 0x90000007, 0x00000000, 0x40000000, 0x00000000,
+ 0x03A, 0x00000000,
+ 0x90000008, 0x00000000, 0x40000000, 0x00000000,
+ 0x03A, 0x00001001,
+ 0x90000009, 0x00000000, 0x40000000, 0x00000000,
+ 0x03A, 0x00000000,
+ 0xA0000000, 0x00000000,
+ 0x03A, 0x00001000,
+ 0xB0000000, 0x00000000,
+ 0x03B, 0x00080000,
+ 0x80000007, 0x00000000, 0x40000000, 0x00000000,
+ 0x03A, 0x00001802,
+ 0x90000009, 0x00000000, 0x40000000, 0x00000000,
+ 0x03A, 0x00000800,
+ 0xA0000000, 0x00000000,
+ 0x03A, 0x00001002,
+ 0xB0000000, 0x00000000,
+ 0x0EF, 0x00000000,
+ 0x80000001, 0x00000000, 0x40000000, 0x00000000,
+ 0x90000002, 0x00000000, 0x40000000, 0x00000000,
+ 0x90000003, 0x00000000, 0x40000000, 0x00000000,
+ 0x90000004, 0x00000000, 0x40000000, 0x00000000,
+ 0x90000005, 0x00000000, 0x40000000, 0x00000000,
+ 0x90000006, 0x00000000, 0x40000000, 0x00000000,
+ 0x90000008, 0x00000000, 0x40000000, 0x00000000,
+ 0x9000000a, 0x00000000, 0x40000000, 0x00000000,
+ 0x9000000b, 0x00000000, 0x40000000, 0x00000000,
+ 0xA0000000, 0x00000000,
+ 0xB0000000, 0x00000000,
+ 0x018, 0x00013124,
+ 0x0EF, 0x00000100,
+ 0x80000001, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0004A1AD,
+ 0x034, 0x000491AA,
+ 0x034, 0x000481A7,
+ 0x034, 0x000470AA,
+ 0x034, 0x000460A7,
+ 0x034, 0x00045049,
+ 0x034, 0x00044046,
+ 0x034, 0x00043026,
+ 0x034, 0x00042009,
+ 0x034, 0x00041006,
+ 0x034, 0x00040003,
+ 0x90000002, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0004A3EF,
+ 0x034, 0x000493AF,
+ 0x034, 0x000483AB,
+ 0x034, 0x0004718C,
+ 0x034, 0x00046189,
+ 0x034, 0x0004506D,
+ 0x034, 0x0004406A,
+ 0x034, 0x0004302C,
+ 0x034, 0x00042029,
+ 0x034, 0x00041026,
+ 0x034, 0x00040023,
+ 0x90000003, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0004A3EF,
+ 0x034, 0x000493AF,
+ 0x034, 0x000483AB,
+ 0x034, 0x0004718C,
+ 0x034, 0x00046189,
+ 0x034, 0x0004506D,
+ 0x034, 0x0004406A,
+ 0x034, 0x0004302C,
+ 0x034, 0x00042029,
+ 0x034, 0x00041026,
+ 0x034, 0x00040023,
+ 0x90000004, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0004A3EF,
+ 0x034, 0x000493AF,
+ 0x034, 0x000483AB,
+ 0x034, 0x0004718C,
+ 0x034, 0x00046189,
+ 0x034, 0x0004506D,
+ 0x034, 0x0004406A,
+ 0x034, 0x0004302C,
+ 0x034, 0x00042029,
+ 0x034, 0x00041026,
+ 0x034, 0x00040023,
+ 0x90000005, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0004A3EF,
+ 0x034, 0x000493AF,
+ 0x034, 0x000483AB,
+ 0x034, 0x0004718C,
+ 0x034, 0x00046189,
+ 0x034, 0x0004506D,
+ 0x034, 0x0004406A,
+ 0x034, 0x0004302C,
+ 0x034, 0x00042029,
+ 0x034, 0x00041026,
+ 0x034, 0x00040023,
+ 0x90000008, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0004A3F5,
+ 0x034, 0x000493F2,
+ 0x034, 0x000483B0,
+ 0x034, 0x00047370,
+ 0x034, 0x0004636D,
+ 0x034, 0x0004536A,
+ 0x034, 0x00044349,
+ 0x034, 0x0004316A,
+ 0x034, 0x00042167,
+ 0x034, 0x00041129,
+ 0x034, 0x00040049,
+ 0x9000000a, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0004A3EF,
+ 0x034, 0x000493AF,
+ 0x034, 0x000483AB,
+ 0x034, 0x0004718C,
+ 0x034, 0x00046189,
+ 0x034, 0x0004506D,
+ 0x034, 0x0004406A,
+ 0x034, 0x0004302C,
+ 0x034, 0x00042029,
+ 0x034, 0x00041026,
+ 0x034, 0x00040023,
+ 0x9000000b, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0004A3EF,
+ 0x034, 0x000493AF,
+ 0x034, 0x000483AB,
+ 0x034, 0x0004718C,
+ 0x034, 0x00046189,
+ 0x034, 0x0004506D,
+ 0x034, 0x0004406A,
+ 0x034, 0x0004302C,
+ 0x034, 0x00042029,
+ 0x034, 0x00041026,
+ 0x034, 0x00040023,
+ 0xA0000000, 0x00000000,
+ 0x034, 0x0004AFF1,
+ 0x034, 0x00049FEE,
+ 0x034, 0x00048FEB,
+ 0x034, 0x00047FE8,
+ 0x034, 0x00046DEA,
+ 0x034, 0x00045DE7,
+ 0x034, 0x00044CEA,
+ 0x034, 0x00043CE7,
+ 0x034, 0x00042C69,
+ 0x034, 0x00041C66,
+ 0x034, 0x00040C28,
+ 0xB0000000, 0x00000000,
+ 0x80000001, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0002A1AD,
+ 0x034, 0x000291AA,
+ 0x034, 0x000281A7,
+ 0x034, 0x000270AA,
+ 0x034, 0x000260A7,
+ 0x034, 0x00025049,
+ 0x034, 0x00024046,
+ 0x034, 0x00023026,
+ 0x034, 0x00022009,
+ 0x034, 0x00021006,
+ 0x034, 0x00020003,
+ 0x90000002, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0002A3EF,
+ 0x034, 0x000293AC,
+ 0x034, 0x0002838A,
+ 0x034, 0x0002718C,
+ 0x034, 0x00026189,
+ 0x034, 0x0002506D,
+ 0x034, 0x0002406A,
+ 0x034, 0x0002302C,
+ 0x034, 0x00022029,
+ 0x034, 0x00021026,
+ 0x034, 0x00020023,
+ 0x90000003, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0002A3EF,
+ 0x034, 0x000293AC,
+ 0x034, 0x0002838A,
+ 0x034, 0x0002718C,
+ 0x034, 0x00026189,
+ 0x034, 0x0002506D,
+ 0x034, 0x0002406A,
+ 0x034, 0x0002302C,
+ 0x034, 0x00022029,
+ 0x034, 0x00021026,
+ 0x034, 0x00020023,
+ 0x90000004, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0002A3EF,
+ 0x034, 0x000293AC,
+ 0x034, 0x0002838A,
+ 0x034, 0x0002718C,
+ 0x034, 0x00026189,
+ 0x034, 0x0002506D,
+ 0x034, 0x0002406A,
+ 0x034, 0x0002302C,
+ 0x034, 0x00022029,
+ 0x034, 0x00021026,
+ 0x034, 0x00020023,
+ 0x90000005, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0002A3EF,
+ 0x034, 0x000293AC,
+ 0x034, 0x0002838A,
+ 0x034, 0x0002718C,
+ 0x034, 0x00026189,
+ 0x034, 0x0002506D,
+ 0x034, 0x0002406A,
+ 0x034, 0x0002302C,
+ 0x034, 0x00022029,
+ 0x034, 0x00021026,
+ 0x034, 0x00020023,
+ 0x90000008, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0002A3F5,
+ 0x034, 0x000293F2,
+ 0x034, 0x000282F1,
+ 0x034, 0x000272B0,
+ 0x034, 0x000262AD,
+ 0x034, 0x000252AA,
+ 0x034, 0x000242A7,
+ 0x034, 0x000230EC,
+ 0x034, 0x000220E9,
+ 0x034, 0x0002106A,
+ 0x034, 0x00020067,
+ 0x9000000a, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0002A3EF,
+ 0x034, 0x000293AC,
+ 0x034, 0x0002838A,
+ 0x034, 0x0002718C,
+ 0x034, 0x00026189,
+ 0x034, 0x0002506D,
+ 0x034, 0x0002406A,
+ 0x034, 0x0002302C,
+ 0x034, 0x00022029,
+ 0x034, 0x00021026,
+ 0x034, 0x00020023,
+ 0x9000000b, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0002A3EF,
+ 0x034, 0x000293AC,
+ 0x034, 0x0002838A,
+ 0x034, 0x0002718C,
+ 0x034, 0x00026189,
+ 0x034, 0x0002506D,
+ 0x034, 0x0002406A,
+ 0x034, 0x0002302C,
+ 0x034, 0x00022029,
+ 0x034, 0x00021026,
+ 0x034, 0x00020023,
+ 0xA0000000, 0x00000000,
+ 0x034, 0x0002AFF1,
+ 0x034, 0x00029FEE,
+ 0x034, 0x00028FEB,
+ 0x034, 0x00027FE8,
+ 0x034, 0x00026DEA,
+ 0x034, 0x00025DE7,
+ 0x034, 0x00024CEA,
+ 0x034, 0x00023CE7,
+ 0x034, 0x00022C69,
+ 0x034, 0x00021C66,
+ 0x034, 0x00020C28,
+ 0xB0000000, 0x00000000,
+ 0x80000001, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0000A3EC,
+ 0x034, 0x0000938C,
+ 0x034, 0x000081AD,
+ 0x034, 0x000071AA,
+ 0x034, 0x000061A7,
+ 0x034, 0x000050AA,
+ 0x034, 0x000040A7,
+ 0x034, 0x0000302C,
+ 0x034, 0x00002029,
+ 0x034, 0x0000100C,
+ 0x034, 0x00000009,
+ 0x90000002, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0000A3EE,
+ 0x034, 0x000093AC,
+ 0x034, 0x0000838A,
+ 0x034, 0x0000718C,
+ 0x034, 0x00006189,
+ 0x034, 0x0000506D,
+ 0x034, 0x0000406A,
+ 0x034, 0x0000302C,
+ 0x034, 0x00002029,
+ 0x034, 0x00001026,
+ 0x034, 0x00000023,
+ 0x90000003, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0000A3EE,
+ 0x034, 0x000093AC,
+ 0x034, 0x0000838A,
+ 0x034, 0x0000718C,
+ 0x034, 0x00006189,
+ 0x034, 0x0000506D,
+ 0x034, 0x0000406A,
+ 0x034, 0x0000302C,
+ 0x034, 0x00002029,
+ 0x034, 0x00001026,
+ 0x034, 0x00000023,
+ 0x90000004, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0000A3EE,
+ 0x034, 0x000093AC,
+ 0x034, 0x0000838A,
+ 0x034, 0x0000718C,
+ 0x034, 0x00006189,
+ 0x034, 0x0000506D,
+ 0x034, 0x0000406A,
+ 0x034, 0x0000302C,
+ 0x034, 0x00002029,
+ 0x034, 0x00001026,
+ 0x034, 0x00000023,
+ 0x90000005, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0000A3EE,
+ 0x034, 0x000093AC,
+ 0x034, 0x0000838A,
+ 0x034, 0x0000718C,
+ 0x034, 0x00006189,
+ 0x034, 0x0000506D,
+ 0x034, 0x0000406A,
+ 0x034, 0x0000302C,
+ 0x034, 0x00002029,
+ 0x034, 0x00001026,
+ 0x034, 0x00000023,
+ 0x90000008, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0000A3F4,
+ 0x034, 0x000093F1,
+ 0x034, 0x000082B1,
+ 0x034, 0x000071D1,
+ 0x034, 0x000061CE,
+ 0x034, 0x000051CB,
+ 0x034, 0x000041C8,
+ 0x034, 0x000030CB,
+ 0x034, 0x000020C8,
+ 0x034, 0x00001087,
+ 0x034, 0x00000084,
+ 0x9000000a, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0000A3EE,
+ 0x034, 0x000093AC,
+ 0x034, 0x0000838A,
+ 0x034, 0x0000718C,
+ 0x034, 0x00006189,
+ 0x034, 0x0000506D,
+ 0x034, 0x0000406A,
+ 0x034, 0x0000302C,
+ 0x034, 0x00002029,
+ 0x034, 0x00001026,
+ 0x034, 0x00000023,
+ 0x9000000b, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0000A3EE,
+ 0x034, 0x000093AC,
+ 0x034, 0x0000838A,
+ 0x034, 0x0000718C,
+ 0x034, 0x00006189,
+ 0x034, 0x0000506D,
+ 0x034, 0x0000406A,
+ 0x034, 0x0000302C,
+ 0x034, 0x00002029,
+ 0x034, 0x00001026,
+ 0x034, 0x00000023,
+ 0xA0000000, 0x00000000,
+ 0x034, 0x0000AFF1,
+ 0x034, 0x00009FEE,
+ 0x034, 0x00008FEB,
+ 0x034, 0x00007FE8,
+ 0x034, 0x00006DEA,
+ 0x034, 0x00005DE7,
+ 0x034, 0x00004CEA,
+ 0x034, 0x00003CE7,
+ 0x034, 0x00002C69,
+ 0x034, 0x00001C66,
+ 0x034, 0x00000C28,
+ 0xB0000000, 0x00000000,
+ 0x80000001, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x000CA1AD,
+ 0x034, 0x000C91AA,
+ 0x034, 0x000C81A7,
+ 0x034, 0x000C70AA,
+ 0x034, 0x000C60A7,
+ 0x034, 0x000C5049,
+ 0x034, 0x000C4046,
+ 0x034, 0x000C3026,
+ 0x034, 0x000C2009,
+ 0x034, 0x000C1006,
+ 0x034, 0x000C0003,
+ 0x90000002, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x000CA3EF,
+ 0x034, 0x000C93AF,
+ 0x034, 0x000C83AB,
+ 0x034, 0x000C718C,
+ 0x034, 0x000C6189,
+ 0x034, 0x000C506D,
+ 0x034, 0x000C406A,
+ 0x034, 0x000C302C,
+ 0x034, 0x000C2029,
+ 0x034, 0x000C1026,
+ 0x034, 0x000C0023,
+ 0x90000003, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x000CA3EF,
+ 0x034, 0x000C93AF,
+ 0x034, 0x000C83AB,
+ 0x034, 0x000C718C,
+ 0x034, 0x000C6189,
+ 0x034, 0x000C506D,
+ 0x034, 0x000C406A,
+ 0x034, 0x000C302C,
+ 0x034, 0x000C2029,
+ 0x034, 0x000C1026,
+ 0x034, 0x000C0023,
+ 0x90000004, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x000CA3EF,
+ 0x034, 0x000C93AF,
+ 0x034, 0x000C83AB,
+ 0x034, 0x000C718C,
+ 0x034, 0x000C6189,
+ 0x034, 0x000C506D,
+ 0x034, 0x000C406A,
+ 0x034, 0x000C302C,
+ 0x034, 0x000C2029,
+ 0x034, 0x000C1026,
+ 0x034, 0x000C0023,
+ 0x90000005, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x000CA3EF,
+ 0x034, 0x000C93AF,
+ 0x034, 0x000C83AB,
+ 0x034, 0x000C718C,
+ 0x034, 0x000C6189,
+ 0x034, 0x000C506D,
+ 0x034, 0x000C406A,
+ 0x034, 0x000C302C,
+ 0x034, 0x000C2029,
+ 0x034, 0x000C1026,
+ 0x034, 0x000C0023,
+ 0x90000008, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x000CA3F5,
+ 0x034, 0x000C93F2,
+ 0x034, 0x000C83B0,
+ 0x034, 0x000C7370,
+ 0x034, 0x000C636D,
+ 0x034, 0x000C536A,
+ 0x034, 0x000C4349,
+ 0x034, 0x000C316A,
+ 0x034, 0x000C2167,
+ 0x034, 0x000C1129,
+ 0x034, 0x000C0049,
+ 0x9000000a, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x000CA3EF,
+ 0x034, 0x000C93AF,
+ 0x034, 0x000C83AB,
+ 0x034, 0x000C718C,
+ 0x034, 0x000C6189,
+ 0x034, 0x000C506D,
+ 0x034, 0x000C406A,
+ 0x034, 0x000C302C,
+ 0x034, 0x000C2029,
+ 0x034, 0x000C1026,
+ 0x034, 0x000C0023,
+ 0x9000000b, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x000CA3EF,
+ 0x034, 0x000C93AF,
+ 0x034, 0x000C83AB,
+ 0x034, 0x000C718C,
+ 0x034, 0x000C6189,
+ 0x034, 0x000C506D,
+ 0x034, 0x000C406A,
+ 0x034, 0x000C302C,
+ 0x034, 0x000C2029,
+ 0x034, 0x000C1026,
+ 0x034, 0x000C0023,
+ 0xA0000000, 0x00000000,
+ 0x034, 0x000CA794,
+ 0x034, 0x000C9791,
+ 0x034, 0x000C878E,
+ 0x034, 0x000C778B,
+ 0x034, 0x000C658D,
+ 0x034, 0x000C558A,
+ 0x034, 0x000C448D,
+ 0x034, 0x000C348A,
+ 0x034, 0x000C244C,
+ 0x034, 0x000C1449,
+ 0x034, 0x000C042B,
+ 0xB0000000, 0x00000000,
+ 0x80000001, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x000AA1AD,
+ 0x034, 0x000A91AA,
+ 0x034, 0x000A81A7,
+ 0x034, 0x000A70AA,
+ 0x034, 0x000A60A7,
+ 0x034, 0x000A5049,
+ 0x034, 0x000A4046,
+ 0x034, 0x000A3026,
+ 0x034, 0x000A2009,
+ 0x034, 0x000A1006,
+ 0x034, 0x000A0003,
+ 0x90000002, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x000AA3EF,
+ 0x034, 0x000A93AC,
+ 0x034, 0x000A838A,
+ 0x034, 0x000A718C,
+ 0x034, 0x000A6189,
+ 0x034, 0x000A506D,
+ 0x034, 0x000A406A,
+ 0x034, 0x000A302C,
+ 0x034, 0x000A2029,
+ 0x034, 0x000A1026,
+ 0x034, 0x000A0023,
+ 0x90000003, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x000AA3EF,
+ 0x034, 0x000A93AC,
+ 0x034, 0x000A838A,
+ 0x034, 0x000A718C,
+ 0x034, 0x000A6189,
+ 0x034, 0x000A506D,
+ 0x034, 0x000A406A,
+ 0x034, 0x000A302C,
+ 0x034, 0x000A2029,
+ 0x034, 0x000A1026,
+ 0x034, 0x000A0023,
+ 0x90000004, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x000AA3EF,
+ 0x034, 0x000A93AC,
+ 0x034, 0x000A838A,
+ 0x034, 0x000A718C,
+ 0x034, 0x000A6189,
+ 0x034, 0x000A506D,
+ 0x034, 0x000A406A,
+ 0x034, 0x000A302C,
+ 0x034, 0x000A2029,
+ 0x034, 0x000A1026,
+ 0x034, 0x000A0023,
+ 0x90000005, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x000AA3EF,
+ 0x034, 0x000A93AC,
+ 0x034, 0x000A838A,
+ 0x034, 0x000A718C,
+ 0x034, 0x000A6189,
+ 0x034, 0x000A506D,
+ 0x034, 0x000A406A,
+ 0x034, 0x000A302C,
+ 0x034, 0x000A2029,
+ 0x034, 0x000A1026,
+ 0x034, 0x000A0023,
+ 0x90000008, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x000AA3F5,
+ 0x034, 0x000A93F2,
+ 0x034, 0x000A82F1,
+ 0x034, 0x000A72B0,
+ 0x034, 0x000A62AD,
+ 0x034, 0x000A52AA,
+ 0x034, 0x000A42A7,
+ 0x034, 0x000A30EC,
+ 0x034, 0x000A20E9,
+ 0x034, 0x000A106A,
+ 0x034, 0x000A0067,
+ 0x9000000a, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x000AA3EF,
+ 0x034, 0x000A93AC,
+ 0x034, 0x000A838A,
+ 0x034, 0x000A718C,
+ 0x034, 0x000A6189,
+ 0x034, 0x000A506D,
+ 0x034, 0x000A406A,
+ 0x034, 0x000A302C,
+ 0x034, 0x000A2029,
+ 0x034, 0x000A1026,
+ 0x034, 0x000A0023,
+ 0x9000000b, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x000AA3EF,
+ 0x034, 0x000A93AC,
+ 0x034, 0x000A838A,
+ 0x034, 0x000A718C,
+ 0x034, 0x000A6189,
+ 0x034, 0x000A506D,
+ 0x034, 0x000A406A,
+ 0x034, 0x000A302C,
+ 0x034, 0x000A2029,
+ 0x034, 0x000A1026,
+ 0x034, 0x000A0023,
+ 0xA0000000, 0x00000000,
+ 0x034, 0x000AA794,
+ 0x034, 0x000A9791,
+ 0x034, 0x000A878E,
+ 0x034, 0x000A778B,
+ 0x034, 0x000A658D,
+ 0x034, 0x000A558A,
+ 0x034, 0x000A448D,
+ 0x034, 0x000A348A,
+ 0x034, 0x000A244C,
+ 0x034, 0x000A1449,
+ 0x034, 0x000A042B,
+ 0xB0000000, 0x00000000,
+ 0x80000001, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0008A3EC,
+ 0x034, 0x0008938C,
+ 0x034, 0x000881AD,
+ 0x034, 0x000871AA,
+ 0x034, 0x000861A7,
+ 0x034, 0x000850AA,
+ 0x034, 0x000840A7,
+ 0x034, 0x0008302C,
+ 0x034, 0x00082029,
+ 0x034, 0x0008100C,
+ 0x034, 0x00080009,
+ 0x90000002, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0008A3EE,
+ 0x034, 0x000893AC,
+ 0x034, 0x0008838A,
+ 0x034, 0x0008718C,
+ 0x034, 0x00086189,
+ 0x034, 0x0008506D,
+ 0x034, 0x0008406A,
+ 0x034, 0x0008302C,
+ 0x034, 0x00082029,
+ 0x034, 0x00081026,
+ 0x034, 0x00080023,
+ 0x90000003, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0008A3EE,
+ 0x034, 0x000893AC,
+ 0x034, 0x0008838A,
+ 0x034, 0x0008718C,
+ 0x034, 0x00086189,
+ 0x034, 0x0008506D,
+ 0x034, 0x0008406A,
+ 0x034, 0x0008302C,
+ 0x034, 0x00082029,
+ 0x034, 0x00081026,
+ 0x034, 0x00080023,
+ 0x90000004, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0008A3EE,
+ 0x034, 0x000893AC,
+ 0x034, 0x0008838A,
+ 0x034, 0x0008718C,
+ 0x034, 0x00086189,
+ 0x034, 0x0008506D,
+ 0x034, 0x0008406A,
+ 0x034, 0x0008302C,
+ 0x034, 0x00082029,
+ 0x034, 0x00081026,
+ 0x034, 0x00080023,
+ 0x90000005, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0008A3EE,
+ 0x034, 0x000893AC,
+ 0x034, 0x0008838A,
+ 0x034, 0x0008718C,
+ 0x034, 0x00086189,
+ 0x034, 0x0008506D,
+ 0x034, 0x0008406A,
+ 0x034, 0x0008302C,
+ 0x034, 0x00082029,
+ 0x034, 0x00081026,
+ 0x034, 0x00080023,
+ 0x90000008, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0008A3F4,
+ 0x034, 0x000893F1,
+ 0x034, 0x000882B1,
+ 0x034, 0x000871D1,
+ 0x034, 0x000861CE,
+ 0x034, 0x000851CB,
+ 0x034, 0x000841C8,
+ 0x034, 0x000830CB,
+ 0x034, 0x000820C8,
+ 0x034, 0x00081087,
+ 0x034, 0x00080084,
+ 0x9000000a, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0008A3EE,
+ 0x034, 0x000893AC,
+ 0x034, 0x0008838A,
+ 0x034, 0x0008718C,
+ 0x034, 0x00086189,
+ 0x034, 0x0008506D,
+ 0x034, 0x0008406A,
+ 0x034, 0x0008302C,
+ 0x034, 0x00082029,
+ 0x034, 0x00081026,
+ 0x034, 0x00080023,
+ 0x9000000b, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0008A3EE,
+ 0x034, 0x000893AC,
+ 0x034, 0x0008838A,
+ 0x034, 0x0008718C,
+ 0x034, 0x00086189,
+ 0x034, 0x0008506D,
+ 0x034, 0x0008406A,
+ 0x034, 0x0008302C,
+ 0x034, 0x00082029,
+ 0x034, 0x00081026,
+ 0x034, 0x00080023,
+ 0xA0000000, 0x00000000,
+ 0x034, 0x0008A794,
+ 0x034, 0x00089791,
+ 0x034, 0x0008878E,
+ 0x034, 0x0008778B,
+ 0x034, 0x0008658D,
+ 0x034, 0x0008558A,
+ 0x034, 0x0008448D,
+ 0x034, 0x0008348A,
+ 0x034, 0x0008244C,
+ 0x034, 0x00081449,
+ 0x034, 0x0008042B,
+ 0xB0000000, 0x00000000,
+ 0x0EF, 0x00000000,
+ 0x0DF, 0x00000001,
+ 0x018, 0x0001712A,
+ 0x0EF, 0x00000040,
+ 0x80000001, 0x00000000, 0x40000000, 0x00000000,
+ 0x035, 0x000006CC,
+ 0x035, 0x000086CC,
+ 0x035, 0x000106CC,
+ 0x035, 0x000206CC,
+ 0x035, 0x000286CC,
+ 0x035, 0x000306CC,
+ 0x035, 0x000406CC,
+ 0x035, 0x000486CC,
+ 0x035, 0x000506CC,
+ 0x035, 0x000806CC,
+ 0x035, 0x000886CC,
+ 0x035, 0x000906CC,
+ 0x035, 0x000A06CC,
+ 0x035, 0x000A86CC,
+ 0x035, 0x000B06CC,
+ 0x035, 0x000C06CC,
+ 0x035, 0x000C86CC,
+ 0x035, 0x000D06CC,
+ 0x90000002, 0x00000000, 0x40000000, 0x00000000,
+ 0x035, 0x000006CC,
+ 0x035, 0x000086CC,
+ 0x035, 0x000106CC,
+ 0x035, 0x000206CC,
+ 0x035, 0x000286CC,
+ 0x035, 0x000306CC,
+ 0x035, 0x000406CC,
+ 0x035, 0x000486CC,
+ 0x035, 0x000506CC,
+ 0x035, 0x000806CC,
+ 0x035, 0x000886CC,
+ 0x035, 0x000906CC,
+ 0x035, 0x000A06CC,
+ 0x035, 0x000A86CC,
+ 0x035, 0x000B06CC,
+ 0x035, 0x000C06CC,
+ 0x035, 0x000C86CC,
+ 0x035, 0x000D06CC,
+ 0x90000003, 0x00000000, 0x40000000, 0x00000000,
+ 0x035, 0x000006CC,
+ 0x035, 0x000086CC,
+ 0x035, 0x000106CC,
+ 0x035, 0x000206CC,
+ 0x035, 0x000286CC,
+ 0x035, 0x000306CC,
+ 0x035, 0x000406CC,
+ 0x035, 0x000486CC,
+ 0x035, 0x000506CC,
+ 0x035, 0x000806CC,
+ 0x035, 0x000886CC,
+ 0x035, 0x000906CC,
+ 0x035, 0x000A06CC,
+ 0x035, 0x000A86CC,
+ 0x035, 0x000B06CC,
+ 0x035, 0x000C06CC,
+ 0x035, 0x000C86CC,
+ 0x035, 0x000D06CC,
+ 0x90000004, 0x00000000, 0x40000000, 0x00000000,
+ 0x035, 0x000006CC,
+ 0x035, 0x000086CC,
+ 0x035, 0x000106CC,
+ 0x035, 0x000206CC,
+ 0x035, 0x000286CC,
+ 0x035, 0x000306CC,
+ 0x035, 0x000406CC,
+ 0x035, 0x000486CC,
+ 0x035, 0x000506CC,
+ 0x035, 0x000806CC,
+ 0x035, 0x000886CC,
+ 0x035, 0x000906CC,
+ 0x035, 0x000A06CC,
+ 0x035, 0x000A86CC,
+ 0x035, 0x000B06CC,
+ 0x035, 0x000C06CC,
+ 0x035, 0x000C86CC,
+ 0x035, 0x000D06CC,
+ 0x90000005, 0x00000000, 0x40000000, 0x00000000,
+ 0x035, 0x000006CC,
+ 0x035, 0x000086CC,
+ 0x035, 0x000106CC,
+ 0x035, 0x000206CC,
+ 0x035, 0x000286CC,
+ 0x035, 0x000306CC,
+ 0x035, 0x000406CC,
+ 0x035, 0x000486CC,
+ 0x035, 0x000506CC,
+ 0x035, 0x000806CC,
+ 0x035, 0x000886CC,
+ 0x035, 0x000906CC,
+ 0x035, 0x000A06CC,
+ 0x035, 0x000A86CC,
+ 0x035, 0x000B06CC,
+ 0x035, 0x000C06CC,
+ 0x035, 0x000C86CC,
+ 0x035, 0x000D06CC,
+ 0x90000008, 0x00000000, 0x40000000, 0x00000000,
+ 0x035, 0x000006CC,
+ 0x035, 0x000086CC,
+ 0x035, 0x000106CC,
+ 0x035, 0x000206CC,
+ 0x035, 0x000286CC,
+ 0x035, 0x000306CC,
+ 0x035, 0x000406CC,
+ 0x035, 0x000486CC,
+ 0x035, 0x000506CC,
+ 0x035, 0x000806CC,
+ 0x035, 0x000886CC,
+ 0x035, 0x000906CC,
+ 0x035, 0x000A06CC,
+ 0x035, 0x000A86CC,
+ 0x035, 0x000B06CC,
+ 0x035, 0x000C06CC,
+ 0x035, 0x000C86CC,
+ 0x035, 0x000D06CC,
+ 0x9000000a, 0x00000000, 0x40000000, 0x00000000,
+ 0x035, 0x000006CC,
+ 0x035, 0x000086CC,
+ 0x035, 0x000106CC,
+ 0x035, 0x000206CC,
+ 0x035, 0x000286CC,
+ 0x035, 0x000306CC,
+ 0x035, 0x000406CC,
+ 0x035, 0x000486CC,
+ 0x035, 0x000506CC,
+ 0x035, 0x000806CC,
+ 0x035, 0x000886CC,
+ 0x035, 0x000906CC,
+ 0x035, 0x000A06CC,
+ 0x035, 0x000A86CC,
+ 0x035, 0x000B06CC,
+ 0x035, 0x000C06CC,
+ 0x035, 0x000C86CC,
+ 0x035, 0x000D06CC,
+ 0x9000000b, 0x00000000, 0x40000000, 0x00000000,
+ 0x035, 0x000006CC,
+ 0x035, 0x000086CC,
+ 0x035, 0x000106CC,
+ 0x035, 0x000206CC,
+ 0x035, 0x000286CC,
+ 0x035, 0x000306CC,
+ 0x035, 0x000406CC,
+ 0x035, 0x000486CC,
+ 0x035, 0x000506CC,
+ 0x035, 0x000806CC,
+ 0x035, 0x000886CC,
+ 0x035, 0x000906CC,
+ 0x035, 0x000A06CC,
+ 0x035, 0x000A86CC,
+ 0x035, 0x000B06CC,
+ 0x035, 0x000C06CC,
+ 0x035, 0x000C86CC,
+ 0x035, 0x000D06CC,
+ 0xA0000000, 0x00000000,
+ 0x035, 0x00000747,
+ 0x035, 0x00008747,
+ 0x035, 0x00010747,
+ 0x035, 0x00020747,
+ 0x035, 0x00028747,
+ 0x035, 0x00030747,
+ 0x035, 0x00040747,
+ 0x035, 0x00048747,
+ 0x035, 0x00050747,
+ 0x035, 0x000805FB,
+ 0x035, 0x000885FB,
+ 0x035, 0x000905FB,
+ 0x035, 0x000A05FB,
+ 0x035, 0x000A85FB,
+ 0x035, 0x000B05FB,
+ 0x035, 0x000C05FB,
+ 0x035, 0x000C85FB,
+ 0x035, 0x000D05FB,
+ 0xB0000000, 0x00000000,
+ 0x0EF, 0x00000000,
+ 0x0DF, 0x00000001,
+ 0x018, 0x0001712A,
+ 0x0EF, 0x00000010,
+ 0x80000001, 0x00000000, 0x40000000, 0x00000000,
+ 0x036, 0x00000473,
+ 0x036, 0x00008473,
+ 0x036, 0x00010473,
+ 0x036, 0x00020473,
+ 0x036, 0x00028473,
+ 0x036, 0x00030473,
+ 0x036, 0x00040473,
+ 0x036, 0x00048473,
+ 0x036, 0x00050473,
+ 0x036, 0x00080473,
+ 0x036, 0x00088473,
+ 0x036, 0x00090473,
+ 0x036, 0x000A0473,
+ 0x036, 0x000A8473,
+ 0x036, 0x000B0473,
+ 0x036, 0x000C0473,
+ 0x036, 0x000C8473,
+ 0x036, 0x000D0473,
+ 0x90000002, 0x00000000, 0x40000000, 0x00000000,
+ 0x036, 0x00000475,
+ 0x036, 0x00008475,
+ 0x036, 0x00010475,
+ 0x036, 0x00020475,
+ 0x036, 0x00028475,
+ 0x036, 0x00030475,
+ 0x036, 0x00040475,
+ 0x036, 0x00048475,
+ 0x036, 0x00050475,
+ 0x036, 0x00080475,
+ 0x036, 0x00088475,
+ 0x036, 0x00090475,
+ 0x036, 0x000A0475,
+ 0x036, 0x000A8475,
+ 0x036, 0x000B0475,
+ 0x036, 0x000C0475,
+ 0x036, 0x000C8475,
+ 0x036, 0x000D0475,
+ 0x90000003, 0x00000000, 0x40000000, 0x00000000,
+ 0x036, 0x00000475,
+ 0x036, 0x00008475,
+ 0x036, 0x00010475,
+ 0x036, 0x00020475,
+ 0x036, 0x00028475,
+ 0x036, 0x00030475,
+ 0x036, 0x00040475,
+ 0x036, 0x00048475,
+ 0x036, 0x00050475,
+ 0x036, 0x00080475,
+ 0x036, 0x00088475,
+ 0x036, 0x00090475,
+ 0x036, 0x000A0475,
+ 0x036, 0x000A8475,
+ 0x036, 0x000B0475,
+ 0x036, 0x000C0475,
+ 0x036, 0x000C8475,
+ 0x036, 0x000D0475,
+ 0x90000004, 0x00000000, 0x40000000, 0x00000000,
+ 0x036, 0x00000475,
+ 0x036, 0x00008475,
+ 0x036, 0x00010475,
+ 0x036, 0x00020475,
+ 0x036, 0x00028475,
+ 0x036, 0x00030475,
+ 0x036, 0x00040475,
+ 0x036, 0x00048475,
+ 0x036, 0x00050475,
+ 0x036, 0x00080475,
+ 0x036, 0x00088475,
+ 0x036, 0x00090475,
+ 0x036, 0x000A0475,
+ 0x036, 0x000A8475,
+ 0x036, 0x000B0475,
+ 0x036, 0x000C0475,
+ 0x036, 0x000C8475,
+ 0x036, 0x000D0475,
+ 0x90000005, 0x00000000, 0x40000000, 0x00000000,
+ 0x036, 0x00000475,
+ 0x036, 0x00008475,
+ 0x036, 0x00010475,
+ 0x036, 0x00020475,
+ 0x036, 0x00028475,
+ 0x036, 0x00030475,
+ 0x036, 0x00040475,
+ 0x036, 0x00048475,
+ 0x036, 0x00050475,
+ 0x036, 0x00080475,
+ 0x036, 0x00088475,
+ 0x036, 0x00090475,
+ 0x036, 0x000A0475,
+ 0x036, 0x000A8475,
+ 0x036, 0x000B0475,
+ 0x036, 0x000C0475,
+ 0x036, 0x000C8475,
+ 0x036, 0x000D0475,
+ 0x90000008, 0x00000000, 0x40000000, 0x00000000,
+ 0x036, 0x00000475,
+ 0x036, 0x00008475,
+ 0x036, 0x00010475,
+ 0x036, 0x00020475,
+ 0x036, 0x00028475,
+ 0x036, 0x00030475,
+ 0x036, 0x00040475,
+ 0x036, 0x00048475,
+ 0x036, 0x00050475,
+ 0x036, 0x00080475,
+ 0x036, 0x00088475,
+ 0x036, 0x00090475,
+ 0x036, 0x000A0475,
+ 0x036, 0x000A8475,
+ 0x036, 0x000B0475,
+ 0x036, 0x000C0475,
+ 0x036, 0x000C8475,
+ 0x036, 0x000D0475,
+ 0x9000000a, 0x00000000, 0x40000000, 0x00000000,
+ 0x036, 0x00000475,
+ 0x036, 0x00008475,
+ 0x036, 0x00010475,
+ 0x036, 0x00020475,
+ 0x036, 0x00028475,
+ 0x036, 0x00030475,
+ 0x036, 0x00040475,
+ 0x036, 0x00048475,
+ 0x036, 0x00050475,
+ 0x036, 0x00080475,
+ 0x036, 0x00088475,
+ 0x036, 0x00090475,
+ 0x036, 0x000A0475,
+ 0x036, 0x000A8475,
+ 0x036, 0x000B0475,
+ 0x036, 0x000C0475,
+ 0x036, 0x000C8475,
+ 0x036, 0x000D0475,
+ 0x9000000b, 0x00000000, 0x40000000, 0x00000000,
+ 0x036, 0x00000475,
+ 0x036, 0x00008475,
+ 0x036, 0x00010475,
+ 0x036, 0x00020475,
+ 0x036, 0x00028475,
+ 0x036, 0x00030475,
+ 0x036, 0x00040475,
+ 0x036, 0x00048475,
+ 0x036, 0x00050475,
+ 0x036, 0x00080475,
+ 0x036, 0x00088475,
+ 0x036, 0x00090475,
+ 0x036, 0x000A0475,
+ 0x036, 0x000A8475,
+ 0x036, 0x000B0475,
+ 0x036, 0x000C0475,
+ 0x036, 0x000C8475,
+ 0x036, 0x000D0475,
+ 0xA0000000, 0x00000000,
+ 0x036, 0x00000473,
+ 0x036, 0x00008473,
+ 0x036, 0x00010473,
+ 0x036, 0x00020473,
+ 0x036, 0x00028473,
+ 0x036, 0x00030473,
+ 0x036, 0x00040473,
+ 0x036, 0x00048473,
+ 0x036, 0x00050473,
+ 0x036, 0x00080473,
+ 0x036, 0x00088473,
+ 0x036, 0x00090473,
+ 0x036, 0x000A0473,
+ 0x036, 0x000A8473,
+ 0x036, 0x000B0473,
+ 0x036, 0x000C0473,
+ 0x036, 0x000C8473,
+ 0x036, 0x000D0473,
+ 0xB0000000, 0x00000000,
+ 0x0EF, 0x00000000,
+ 0x80000001, 0x00000000, 0x40000000, 0x00000000,
+ 0x90000002, 0x00000000, 0x40000000, 0x00000000,
+ 0x90000003, 0x00000000, 0x40000000, 0x00000000,
+ 0x90000004, 0x00000000, 0x40000000, 0x00000000,
+ 0x90000005, 0x00000000, 0x40000000, 0x00000000,
+ 0x90000008, 0x00000000, 0x40000000, 0x00000000,
+ 0x9000000a, 0x00000000, 0x40000000, 0x00000000,
+ 0x9000000b, 0x00000000, 0x40000000, 0x00000000,
+ 0xA0000000, 0x00000000,
+ 0x0EF, 0x00000004,
+ 0x037, 0x00000000,
+ 0x038, 0x00005146,
+ 0x037, 0x00004000,
+ 0x038, 0x00005146,
+ 0x037, 0x00008000,
+ 0x038, 0x00005146,
+ 0x037, 0x00010000,
+ 0x038, 0x00005146,
+ 0x037, 0x00014000,
+ 0x038, 0x00005146,
+ 0x037, 0x00018000,
+ 0x038, 0x00004D4E,
+ 0x037, 0x0001C000,
+ 0x038, 0x00004D4E,
+ 0x037, 0x00020000,
+ 0x038, 0x00004D4E,
+ 0x037, 0x00024000,
+ 0x038, 0x000071C6,
+ 0x037, 0x00028000,
+ 0x038, 0x000071C6,
+ 0x037, 0x0002C000,
+ 0x038, 0x000071C6,
+ 0x037, 0x00030000,
+ 0x038, 0x000071CE,
+ 0x037, 0x00034000,
+ 0x038, 0x000071CE,
+ 0x037, 0x00038000,
+ 0x038, 0x00005126,
+ 0x037, 0x0003C000,
+ 0x038, 0x00005126,
+ 0x037, 0x00040000,
+ 0x038, 0x00005126,
+ 0x037, 0x00044000,
+ 0x038, 0x00005126,
+ 0x037, 0x00048000,
+ 0x038, 0x00005126,
+ 0x037, 0x00080000,
+ 0x038, 0x00005ECE,
+ 0x037, 0x00084000,
+ 0x038, 0x00005ECE,
+ 0x037, 0x00088000,
+ 0x038, 0x00005ECE,
+ 0x037, 0x00090000,
+ 0x038, 0x00005ECE,
+ 0x037, 0x00094000,
+ 0x038, 0x00005ECE,
+ 0x037, 0x00098000,
+ 0x038, 0x00005ECE,
+ 0x037, 0x0009C000,
+ 0x038, 0x00005ECE,
+ 0x037, 0x000A0000,
+ 0x038, 0x00005ECE,
+ 0x037, 0x000A4000,
+ 0x038, 0x00005ECE,
+ 0x037, 0x000A8000,
+ 0x038, 0x00005ECE,
+ 0x037, 0x000AC000,
+ 0x038, 0x00005ECE,
+ 0x037, 0x000B0000,
+ 0x038, 0x00005ECE,
+ 0x037, 0x000B4000,
+ 0x038, 0x00005ECE,
+ 0x037, 0x000B8000,
+ 0x038, 0x00005ECE,
+ 0x037, 0x000BC000,
+ 0x038, 0x00005ECE,
+ 0x037, 0x000C0000,
+ 0x038, 0x00005ECE,
+ 0x037, 0x000C4000,
+ 0x038, 0x00005ECE,
+ 0x037, 0x000C8000,
+ 0x038, 0x00005ECE,
+ 0x0EF, 0x00000000,
+ 0xB0000000, 0x00000000,
+ 0x0EF, 0x00000008,
+ 0x80000001, 0x00000000, 0x40000000, 0x00000000,
+ 0x90000002, 0x00000000, 0x40000000, 0x00000000,
+ 0x90000003, 0x00000000, 0x40000000, 0x00000000,
+ 0x90000004, 0x00000000, 0x40000000, 0x00000000,
+ 0x90000005, 0x00000000, 0x40000000, 0x00000000,
+ 0x90000008, 0x00000000, 0x40000000, 0x00000000,
+ 0x9000000a, 0x00000000, 0x40000000, 0x00000000,
+ 0x9000000b, 0x00000000, 0x40000000, 0x00000000,
+ 0xA0000000, 0x00000000,
+ 0x03C, 0x0000007D,
+ 0x03C, 0x0000047D,
+ 0x03C, 0x0000087D,
+ 0x03C, 0x0000107D,
+ 0x03C, 0x0000147D,
+ 0x03C, 0x0000187D,
+ 0xB0000000, 0x00000000,
+ 0x80000001, 0x00000000, 0x40000000, 0x00000000,
+ 0x03C, 0x0000027D,
+ 0x03C, 0x0000054A,
+ 0x03C, 0x00000821,
+ 0x03C, 0x0000127D,
+ 0x03C, 0x0000154A,
+ 0x03C, 0x00001821,
+ 0x03C, 0x0000227D,
+ 0x03C, 0x0000254A,
+ 0x03C, 0x00002821,
+ 0x90000002, 0x00000000, 0x40000000, 0x00000000,
+ 0x03C, 0x0000027D,
+ 0x03C, 0x0000054A,
+ 0x03C, 0x00000821,
+ 0x03C, 0x0000127D,
+ 0x03C, 0x0000154A,
+ 0x03C, 0x00001821,
+ 0x03C, 0x0000227D,
+ 0x03C, 0x0000254A,
+ 0x03C, 0x00002821,
+ 0x90000003, 0x00000000, 0x40000000, 0x00000000,
+ 0x03C, 0x0000027D,
+ 0x03C, 0x0000054A,
+ 0x03C, 0x00000821,
+ 0x03C, 0x0000127D,
+ 0x03C, 0x0000154A,
+ 0x03C, 0x00001821,
+ 0x03C, 0x0000227D,
+ 0x03C, 0x0000254A,
+ 0x03C, 0x00002821,
+ 0x90000004, 0x00000000, 0x40000000, 0x00000000,
+ 0x03C, 0x0000027D,
+ 0x03C, 0x0000054A,
+ 0x03C, 0x00000821,
+ 0x03C, 0x0000127D,
+ 0x03C, 0x0000154A,
+ 0x03C, 0x00001821,
+ 0x03C, 0x0000227D,
+ 0x03C, 0x0000254A,
+ 0x03C, 0x00002821,
+ 0x90000005, 0x00000000, 0x40000000, 0x00000000,
+ 0x03C, 0x0000027D,
+ 0x03C, 0x0000054A,
+ 0x03C, 0x00000821,
+ 0x03C, 0x0000127D,
+ 0x03C, 0x0000154A,
+ 0x03C, 0x00001821,
+ 0x03C, 0x0000227D,
+ 0x03C, 0x0000254A,
+ 0x03C, 0x00002821,
+ 0x90000008, 0x00000000, 0x40000000, 0x00000000,
+ 0x03C, 0x0000027D,
+ 0x03C, 0x0000054A,
+ 0x03C, 0x00000821,
+ 0x03C, 0x0000127D,
+ 0x03C, 0x0000154A,
+ 0x03C, 0x00001821,
+ 0x03C, 0x0000227D,
+ 0x03C, 0x0000254A,
+ 0x03C, 0x00002821,
+ 0x9000000a, 0x00000000, 0x40000000, 0x00000000,
+ 0x03C, 0x0000027D,
+ 0x03C, 0x0000054A,
+ 0x03C, 0x00000821,
+ 0x03C, 0x0000127D,
+ 0x03C, 0x0000154A,
+ 0x03C, 0x00001821,
+ 0x03C, 0x0000227D,
+ 0x03C, 0x0000254A,
+ 0x03C, 0x00002821,
+ 0x9000000b, 0x00000000, 0x40000000, 0x00000000,
+ 0x03C, 0x0000027D,
+ 0x03C, 0x0000054A,
+ 0x03C, 0x00000821,
+ 0x03C, 0x0000127D,
+ 0x03C, 0x0000154A,
+ 0x03C, 0x00001821,
+ 0x03C, 0x0000227D,
+ 0x03C, 0x0000254A,
+ 0x03C, 0x00002821,
+ 0xA0000000, 0x00000000,
+ 0x03C, 0x0000037E,
+ 0x03C, 0x00000575,
+ 0x03C, 0x00000971,
+ 0x03C, 0x0000127E,
+ 0x03C, 0x00001575,
+ 0x03C, 0x00001871,
+ 0x03C, 0x0000217E,
+ 0x03C, 0x00002575,
+ 0x03C, 0x00002871,
+ 0xB0000000, 0x00000000,
+ 0x0EF, 0x00000000,
+ 0x061, 0x000C0D47,
+ 0x062, 0x0000133C,
+ 0x80000001, 0x00000000, 0x40000000, 0x00000000,
+ 0x063, 0x000750E7,
+ 0x90000002, 0x00000000, 0x40000000, 0x00000000,
+ 0x063, 0x000750E7,
+ 0x90000003, 0x00000000, 0x40000000, 0x00000000,
+ 0x063, 0x000750E7,
+ 0x90000004, 0x00000000, 0x40000000, 0x00000000,
+ 0x063, 0x000750E7,
+ 0x90000005, 0x00000000, 0x40000000, 0x00000000,
+ 0x063, 0x000750E7,
+ 0x90000008, 0x00000000, 0x40000000, 0x00000000,
+ 0x063, 0x000750E7,
+ 0x9000000a, 0x00000000, 0x40000000, 0x00000000,
+ 0x063, 0x000750E7,
+ 0x9000000b, 0x00000000, 0x40000000, 0x00000000,
+ 0x063, 0x000750E7,
+ 0xA0000000, 0x00000000,
+ 0x063, 0x0007D0E7,
+ 0xB0000000, 0x00000000,
+ 0x064, 0x00014FEC,
+ 0x80000001, 0x00000000, 0x40000000, 0x00000000,
+ 0x065, 0x000920D0,
+ 0x90000002, 0x00000000, 0x40000000, 0x00000000,
+ 0x065, 0x000920D0,
+ 0x90000003, 0x00000000, 0x40000000, 0x00000000,
+ 0x065, 0x000920D0,
+ 0x90000004, 0x00000000, 0x40000000, 0x00000000,
+ 0x065, 0x000920D0,
+ 0x90000005, 0x00000000, 0x40000000, 0x00000000,
+ 0x065, 0x000920D0,
+ 0x90000008, 0x00000000, 0x40000000, 0x00000000,
+ 0x065, 0x000920D0,
+ 0x9000000a, 0x00000000, 0x40000000, 0x00000000,
+ 0x065, 0x000920D0,
+ 0x9000000b, 0x00000000, 0x40000000, 0x00000000,
+ 0x065, 0x000920D0,
+ 0xA0000000, 0x00000000,
+ 0x065, 0x000933FF,
+ 0xB0000000, 0x00000000,
+ 0x066, 0x00000040,
+ 0x057, 0x00050000,
+ 0x056, 0x00051DF0,
+ 0x80000001, 0x00000000, 0x40000000, 0x00000000,
+ 0x055, 0x00082061,
+ 0x90000002, 0x00000000, 0x40000000, 0x00000000,
+ 0x055, 0x00082061,
+ 0x90000003, 0x00000000, 0x40000000, 0x00000000,
+ 0x055, 0x00082061,
+ 0x90000004, 0x00000000, 0x40000000, 0x00000000,
+ 0x055, 0x00082061,
+ 0x90000005, 0x00000000, 0x40000000, 0x00000000,
+ 0x055, 0x00082061,
+ 0x90000008, 0x00000000, 0x40000000, 0x00000000,
+ 0x055, 0x00082061,
+ 0x9000000a, 0x00000000, 0x40000000, 0x00000000,
+ 0x055, 0x00082061,
+ 0x9000000b, 0x00000000, 0x40000000, 0x00000000,
+ 0x055, 0x00082061,
+ 0xA0000000, 0x00000000,
+ 0x055, 0x00082060,
+ 0xB0000000, 0x00000000,
+ 0x01C, 0x000739D2,
+ 0x80000001, 0x00000000, 0x40000000, 0x00000000,
+ 0x01F, 0x0002255C,
+ 0x90000004, 0x00000000, 0x40000000, 0x00000000,
+ 0x01F, 0x0002255C,
+ 0x9000000a, 0x00000000, 0x40000000, 0x00000000,
+ 0x01F, 0x0002255C,
+ 0x9000000b, 0x00000000, 0x40000000, 0x00000000,
+ 0x01F, 0x0002255C,
+ 0xA0000000, 0x00000000,
+ 0x01F, 0x0002255C,
+ 0xB0000000, 0x00000000,
+ 0x0B1, 0x0007FF48,
+ 0x80000001, 0x00000000, 0x40000000, 0x00000000,
+ 0x0C4, 0x00081700,
+ 0x90000002, 0x00000000, 0x40000000, 0x00000000,
+ 0x0C4, 0x00081700,
+ 0x90000003, 0x00000000, 0x40000000, 0x00000000,
+ 0x0C4, 0x00081700,
+ 0x90000004, 0x00000000, 0x40000000, 0x00000000,
+ 0x0C4, 0x00081700,
+ 0x90000005, 0x00000000, 0x40000000, 0x00000000,
+ 0x0C4, 0x00081700,
+ 0x90000006, 0x00000000, 0x40000000, 0x00000000,
+ 0x0C4, 0x00081700,
+ 0x90000008, 0x00000000, 0x40000000, 0x00000000,
+ 0x0C4, 0x00081700,
+ 0x9000000a, 0x00000000, 0x40000000, 0x00000000,
+ 0x0C4, 0x00081700,
+ 0x9000000b, 0x00000000, 0x40000000, 0x00000000,
+ 0x0C4, 0x00081700,
+ 0xA0000000, 0x00000000,
+ 0x0C4, 0x00083F00,
+ 0xB0000000, 0x00000000,
+ 0x018, 0x0001B126,
+ 0xFFE, 0x00000000,
+ 0xFFE, 0x00000000,
+ 0xFFE, 0x00000000,
+ 0x018, 0x00013126,
+ 0x018, 0x00013124,
+};
+
+RTW_DECL_TABLE_RF_RADIO(rtw8814a_rf_a, A);
+
+static const u32 rtw8814a_rf_b[] = {
+ 0x018, 0x00013124,
+ 0x040, 0x00000C00,
+ 0x058, 0x00000F98,
+ 0x07F, 0x00068004,
+ 0x018, 0x00000006,
+ 0x80000002, 0x00000000, 0x40000000, 0x00000000,
+ 0x086, 0x000E335A,
+ 0x087, 0x00079F80,
+ 0x90000003, 0x00000000, 0x40000000, 0x00000000,
+ 0x086, 0x000E335A,
+ 0x087, 0x00079F80,
+ 0x90000004, 0x00000000, 0x40000000, 0x00000000,
+ 0x086, 0x000E335A,
+ 0x087, 0x00079F80,
+ 0x90000005, 0x00000000, 0x40000000, 0x00000000,
+ 0x086, 0x000E335A,
+ 0x087, 0x00079F80,
+ 0x90000008, 0x00000000, 0x40000000, 0x00000000,
+ 0x086, 0x000E335A,
+ 0x087, 0x00079F80,
+ 0x9000000a, 0x00000000, 0x40000000, 0x00000000,
+ 0x086, 0x000E335A,
+ 0x087, 0x00079F80,
+ 0x9000000b, 0x00000000, 0x40000000, 0x00000000,
+ 0x086, 0x000E335A,
+ 0x087, 0x00079F80,
+ 0xA0000000, 0x00000000,
+ 0x086, 0x000E4B58,
+ 0x087, 0x00049F80,
+ 0xB0000000, 0x00000000,
+ 0x0DF, 0x00000008,
+ 0x0EF, 0x00002000,
+ 0x80000002, 0x00000000, 0x40000000, 0x00000000,
+ 0x03B, 0x0003F19B,
+ 0x03B, 0x00037A5B,
+ 0x03B, 0x0002A433,
+ 0x03B, 0x00027BD3,
+ 0x03B, 0x0001F80B,
+ 0x03B, 0x00017BC3,
+ 0x90000003, 0x00000000, 0x40000000, 0x00000000,
+ 0x03B, 0x0003F39B,
+ 0x03B, 0x00037A5B,
+ 0x03B, 0x0002A433,
+ 0x03B, 0x00027BD3,
+ 0x03B, 0x0001F80B,
+ 0x03B, 0x00017BC3,
+ 0x90000004, 0x00000000, 0x40000000, 0x00000000,
+ 0x03B, 0x0003F19B,
+ 0x03B, 0x00037A5B,
+ 0x03B, 0x0002A433,
+ 0x03B, 0x00027BD3,
+ 0x03B, 0x0001F80B,
+ 0x03B, 0x00017BC3,
+ 0x90000005, 0x00000000, 0x40000000, 0x00000000,
+ 0x03B, 0x0003F39B,
+ 0x03B, 0x00037A5B,
+ 0x03B, 0x0002A433,
+ 0x03B, 0x00027BD3,
+ 0x03B, 0x0001F80B,
+ 0x03B, 0x00017BC3,
+ 0x90000008, 0x00000000, 0x40000000, 0x00000000,
+ 0x03B, 0x0003F19B,
+ 0x03B, 0x00037A5B,
+ 0x03B, 0x0002A433,
+ 0x03B, 0x00027BD3,
+ 0x03B, 0x0001F80B,
+ 0x03B, 0x00017BC3,
+ 0x9000000a, 0x00000000, 0x40000000, 0x00000000,
+ 0x03B, 0x0003F19B,
+ 0x03B, 0x00037A5B,
+ 0x03B, 0x0002A433,
+ 0x03B, 0x00027BD3,
+ 0x03B, 0x0001F80B,
+ 0x03B, 0x00017BC3,
+ 0x9000000b, 0x00000000, 0x40000000, 0x00000000,
+ 0x03B, 0x0003F19B,
+ 0x03B, 0x00037A5B,
+ 0x03B, 0x0002A433,
+ 0x03B, 0x00027BD3,
+ 0x03B, 0x0001F80B,
+ 0x03B, 0x00017BC3,
+ 0xA0000000, 0x00000000,
+ 0x03B, 0x0003F258,
+ 0x03B, 0x00030A58,
+ 0x03B, 0x0002FA58,
+ 0x03B, 0x00022590,
+ 0x03B, 0x0001FA50,
+ 0x03B, 0x00010248,
+ 0x03B, 0x00008240,
+ 0xB0000000, 0x00000000,
+ 0x0EF, 0x00000100,
+ 0x80000002, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0000A0D0,
+ 0x034, 0x000090CD,
+ 0x034, 0x000080CA,
+ 0x034, 0x0000704D,
+ 0x034, 0x0000604A,
+ 0x034, 0x00005047,
+ 0x034, 0x0000400A,
+ 0x034, 0x00003007,
+ 0x034, 0x00002004,
+ 0x034, 0x00001001,
+ 0x034, 0x00000001,
+ 0x90000003, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0000A0D0,
+ 0x034, 0x000090CD,
+ 0x034, 0x000080CA,
+ 0x034, 0x0000704D,
+ 0x034, 0x0000604A,
+ 0x034, 0x00005047,
+ 0x034, 0x0000400A,
+ 0x034, 0x00003007,
+ 0x034, 0x00002004,
+ 0x034, 0x00001001,
+ 0x034, 0x00000001,
+ 0x90000004, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0000A0D0,
+ 0x034, 0x000090CD,
+ 0x034, 0x000080CA,
+ 0x034, 0x0000704D,
+ 0x034, 0x0000604A,
+ 0x034, 0x00005047,
+ 0x034, 0x0000400A,
+ 0x034, 0x00003007,
+ 0x034, 0x00002004,
+ 0x034, 0x00001001,
+ 0x034, 0x00000001,
+ 0x90000005, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0000A0D0,
+ 0x034, 0x000090CD,
+ 0x034, 0x000080CA,
+ 0x034, 0x0000704D,
+ 0x034, 0x0000604A,
+ 0x034, 0x00005047,
+ 0x034, 0x0000400A,
+ 0x034, 0x00003007,
+ 0x034, 0x00002004,
+ 0x034, 0x00001001,
+ 0x034, 0x00000001,
+ 0x90000008, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0000A0D0,
+ 0x034, 0x000090CD,
+ 0x034, 0x000080CA,
+ 0x034, 0x0000704D,
+ 0x034, 0x0000604A,
+ 0x034, 0x00005047,
+ 0x034, 0x0000400A,
+ 0x034, 0x00003007,
+ 0x034, 0x00002004,
+ 0x034, 0x00001001,
+ 0x034, 0x00000001,
+ 0x9000000a, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0000A0D0,
+ 0x034, 0x000090CD,
+ 0x034, 0x000080CA,
+ 0x034, 0x0000704D,
+ 0x034, 0x0000604A,
+ 0x034, 0x00005047,
+ 0x034, 0x0000400A,
+ 0x034, 0x00003007,
+ 0x034, 0x00002004,
+ 0x034, 0x00001001,
+ 0x034, 0x00000001,
+ 0x9000000b, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0000A0D0,
+ 0x034, 0x000090CD,
+ 0x034, 0x000080CA,
+ 0x034, 0x0000704D,
+ 0x034, 0x0000604A,
+ 0x034, 0x00005047,
+ 0x034, 0x0000400A,
+ 0x034, 0x00003007,
+ 0x034, 0x00002004,
+ 0x034, 0x00001001,
+ 0x034, 0x00000001,
+ 0xA0000000, 0x00000000,
+ 0x034, 0x0000ADF6,
+ 0x034, 0x00009DF3,
+ 0x034, 0x00008DF0,
+ 0x034, 0x00007DED,
+ 0x034, 0x00006DEA,
+ 0x034, 0x00005CED,
+ 0x034, 0x00004CEA,
+ 0x034, 0x000034EA,
+ 0x034, 0x000024E7,
+ 0x034, 0x0000146A,
+ 0x034, 0x0000006B,
+ 0xB0000000, 0x00000000,
+ 0x80000002, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0000A0D0,
+ 0x034, 0x000090CD,
+ 0x034, 0x000080CA,
+ 0x034, 0x0000704D,
+ 0x034, 0x0000604A,
+ 0x034, 0x00005047,
+ 0x034, 0x0000400A,
+ 0x034, 0x00003007,
+ 0x034, 0x00002004,
+ 0x034, 0x00001001,
+ 0x034, 0x00000001,
+ 0x90000003, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0000A0D0,
+ 0x034, 0x000090CD,
+ 0x034, 0x000080CA,
+ 0x034, 0x0000704D,
+ 0x034, 0x0000604A,
+ 0x034, 0x00005047,
+ 0x034, 0x0000400A,
+ 0x034, 0x00003007,
+ 0x034, 0x00002004,
+ 0x034, 0x00001001,
+ 0x034, 0x00000001,
+ 0x90000004, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0000A0D0,
+ 0x034, 0x000090CD,
+ 0x034, 0x000080CA,
+ 0x034, 0x0000704D,
+ 0x034, 0x0000604A,
+ 0x034, 0x00005047,
+ 0x034, 0x0000400A,
+ 0x034, 0x00003007,
+ 0x034, 0x00002004,
+ 0x034, 0x00001001,
+ 0x034, 0x00000001,
+ 0x90000005, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0000A0D0,
+ 0x034, 0x000090CD,
+ 0x034, 0x000080CA,
+ 0x034, 0x0000704D,
+ 0x034, 0x0000604A,
+ 0x034, 0x00005047,
+ 0x034, 0x0000400A,
+ 0x034, 0x00003007,
+ 0x034, 0x00002004,
+ 0x034, 0x00001001,
+ 0x034, 0x00000001,
+ 0x90000008, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0000A0D0,
+ 0x034, 0x000090CD,
+ 0x034, 0x000080CA,
+ 0x034, 0x0000704D,
+ 0x034, 0x0000604A,
+ 0x034, 0x00005047,
+ 0x034, 0x0000400A,
+ 0x034, 0x00003007,
+ 0x034, 0x00002004,
+ 0x034, 0x00001001,
+ 0x034, 0x00000001,
+ 0x9000000a, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0000A0D0,
+ 0x034, 0x000090CD,
+ 0x034, 0x000080CA,
+ 0x034, 0x0000704D,
+ 0x034, 0x0000604A,
+ 0x034, 0x00005047,
+ 0x034, 0x0000400A,
+ 0x034, 0x00003007,
+ 0x034, 0x00002004,
+ 0x034, 0x00001001,
+ 0x034, 0x00000001,
+ 0x9000000b, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0000A0D0,
+ 0x034, 0x000090CD,
+ 0x034, 0x000080CA,
+ 0x034, 0x0000704D,
+ 0x034, 0x0000604A,
+ 0x034, 0x00005047,
+ 0x034, 0x0000400A,
+ 0x034, 0x00003007,
+ 0x034, 0x00002004,
+ 0x034, 0x00001001,
+ 0x034, 0x00000001,
+ 0xA0000000, 0x00000000,
+ 0x034, 0x0008ADF6,
+ 0x034, 0x00089DF3,
+ 0x034, 0x00088DF0,
+ 0x034, 0x00087DED,
+ 0x034, 0x00086DEA,
+ 0x034, 0x00085CED,
+ 0x034, 0x00084CEA,
+ 0x034, 0x000834EA,
+ 0x034, 0x000824E7,
+ 0x034, 0x0008146A,
+ 0x034, 0x0008006B,
+ 0xB0000000, 0x00000000,
+ 0x0EF, 0x00000000,
+ 0x0EF, 0x000020A2,
+ 0x0DF, 0x00000080,
+ 0x035, 0x00000192,
+ 0x035, 0x00008192,
+ 0x035, 0x00010192,
+ 0x036, 0x00000024,
+ 0x036, 0x00008024,
+ 0x036, 0x00010024,
+ 0x036, 0x00018024,
+ 0x0EF, 0x00000000,
+ 0x051, 0x00000C21,
+ 0x052, 0x000006D9,
+ 0x053, 0x000FC649,
+ 0x054, 0x0000017E,
+ 0x018, 0x0001012A,
+ 0x081, 0x0007FC00,
+ 0x089, 0x00050110,
+ 0x08A, 0x00043E50,
+ 0x08B, 0x0002E180,
+ 0x08C, 0x00093C3C,
+ 0x80000001, 0x00000000, 0x40000000, 0x00000000,
+ 0x085, 0x000F8000,
+ 0x90000006, 0x00000000, 0x40000000, 0x00000000,
+ 0x085, 0x000F8000,
+ 0xA0000000, 0x00000000,
+ 0x085, 0x000F8000,
+ 0xB0000000, 0x00000000,
+ 0x08D, 0x000FFFF0,
+ 0x0EF, 0x00001000,
+ 0x03A, 0x0000013C,
+ 0x03B, 0x00038023,
+ 0x80000001, 0x00000000, 0x40000000, 0x00000000,
+ 0x03C, 0x00088000,
+ 0x90000002, 0x00000000, 0x40000000, 0x00000000,
+ 0x03C, 0x00084000,
+ 0x90000003, 0x00000000, 0x40000000, 0x00000000,
+ 0x03C, 0x00000000,
+ 0x90000004, 0x00000000, 0x40000000, 0x00000000,
+ 0x03C, 0x00088000,
+ 0x90000005, 0x00000000, 0x40000000, 0x00000000,
+ 0x03C, 0x00000000,
+ 0x90000006, 0x00000000, 0x40000000, 0x00000000,
+ 0x03C, 0x00088000,
+ 0x90000008, 0x00000000, 0x40000000, 0x00000000,
+ 0x03C, 0x00088000,
+ 0x9000000a, 0x00000000, 0x40000000, 0x00000000,
+ 0x03C, 0x00088000,
+ 0x9000000b, 0x00000000, 0x40000000, 0x00000000,
+ 0x03C, 0x00088000,
+ 0xA0000000, 0x00000000,
+ 0x03C, 0x00040000,
+ 0xB0000000, 0x00000000,
+ 0x03A, 0x0000013C,
+ 0x03B, 0x00030023,
+ 0x03C, 0x00048000,
+ 0x03A, 0x0000013C,
+ 0x03B, 0x00028623,
+ 0x03C, 0x00000000,
+ 0x03A, 0x0000013C,
+ 0x03B, 0x00021633,
+ 0x03C, 0x00000000,
+ 0x03A, 0x0000013C,
+ 0x03B, 0x0001C633,
+ 0x03C, 0x00000000,
+ 0x03A, 0x0000013C,
+ 0x03B, 0x00010293,
+ 0x03C, 0x00000000,
+ 0x03A, 0x0000013C,
+ 0x03B, 0x00009593,
+ 0x03C, 0x00000000,
+ 0x03A, 0x00000148,
+ 0x80000008, 0x00000000, 0x40000000, 0x00000000,
+ 0x03B, 0x00000F8B,
+ 0xA0000000, 0x00000000,
+ 0x03B, 0x0000078B,
+ 0xB0000000, 0x00000000,
+ 0x03C, 0x00000000,
+ 0x80000001, 0x00000000, 0x40000000, 0x00000000,
+ 0x03A, 0x0000013C,
+ 0x03B, 0x00078023,
+ 0x90000002, 0x00000000, 0x40000000, 0x00000000,
+ 0x03A, 0x0000013C,
+ 0x03B, 0x00078023,
+ 0x90000003, 0x00000000, 0x40000000, 0x00000000,
+ 0x03A, 0x0000013C,
+ 0x03B, 0x00078023,
+ 0x90000004, 0x00000000, 0x40000000, 0x00000000,
+ 0x03A, 0x0000013C,
+ 0x03B, 0x00078023,
+ 0x90000005, 0x00000000, 0x40000000, 0x00000000,
+ 0x03A, 0x0000013C,
+ 0x03B, 0x00078023,
+ 0x90000006, 0x00000000, 0x40000000, 0x00000000,
+ 0x03A, 0x0000013C,
+ 0x03B, 0x00078023,
+ 0x90000008, 0x00000000, 0x40000000, 0x00000000,
+ 0x03A, 0x0000013C,
+ 0x03B, 0x00078023,
+ 0x9000000a, 0x00000000, 0x40000000, 0x00000000,
+ 0x03A, 0x0000013C,
+ 0x03B, 0x00078023,
+ 0x9000000b, 0x00000000, 0x40000000, 0x00000000,
+ 0x03A, 0x0000013C,
+ 0x03B, 0x00078023,
+ 0xA0000000, 0x00000000,
+ 0x03A, 0x0000013C,
+ 0x03B, 0x00078023,
+ 0xB0000000, 0x00000000,
+ 0x80000001, 0x00000000, 0x40000000, 0x00000000,
+ 0x03C, 0x00020000,
+ 0x90000002, 0x00000000, 0x40000000, 0x00000000,
+ 0x03C, 0x00060000,
+ 0x90000003, 0x00000000, 0x40000000, 0x00000000,
+ 0x03C, 0x00000000,
+ 0x90000004, 0x00000000, 0x40000000, 0x00000000,
+ 0x03C, 0x00048000,
+ 0x90000005, 0x00000000, 0x40000000, 0x00000000,
+ 0x03C, 0x00048000,
+ 0x90000006, 0x00000000, 0x40000000, 0x00000000,
+ 0x03C, 0x00020000,
+ 0x90000007, 0x00000000, 0x40000000, 0x00000000,
+ 0x03C, 0x00000000,
+ 0x90000008, 0x00000000, 0x40000000, 0x00000000,
+ 0x03C, 0x00088000,
+ 0x90000009, 0x00000000, 0x40000000, 0x00000000,
+ 0x03C, 0x00000000,
+ 0x9000000a, 0x00000000, 0x40000000, 0x00000000,
+ 0x03C, 0x00048000,
+ 0x9000000b, 0x00000000, 0x40000000, 0x00000000,
+ 0x03C, 0x00048000,
+ 0xA0000000, 0x00000000,
+ 0x03C, 0x00020000,
+ 0xB0000000, 0x00000000,
+ 0x03A, 0x0000013C,
+ 0x03B, 0x00070023,
+ 0x03C, 0x00048000,
+ 0x03A, 0x0000013C,
+ 0x03B, 0x00068623,
+ 0x03C, 0x00000000,
+ 0x03A, 0x0000013C,
+ 0x03B, 0x00061633,
+ 0x03C, 0x00000000,
+ 0x03A, 0x0000013C,
+ 0x03B, 0x0005C633,
+ 0x03C, 0x00000000,
+ 0x03A, 0x0000013C,
+ 0x03B, 0x00050293,
+ 0x03C, 0x00000000,
+ 0x03A, 0x0000013C,
+ 0x03B, 0x00049593,
+ 0x03C, 0x00000000,
+ 0x03A, 0x00000148,
+ 0x03B, 0x0004078B,
+ 0x03C, 0x00000000,
+ 0x80000001, 0x00000000, 0x40000000, 0x00000000,
+ 0x03A, 0x0000013C,
+ 0x03B, 0x000B8023,
+ 0x90000002, 0x00000000, 0x40000000, 0x00000000,
+ 0x03A, 0x0000013C,
+ 0x03B, 0x000B8023,
+ 0x90000003, 0x00000000, 0x40000000, 0x00000000,
+ 0x03A, 0x0000013C,
+ 0x03B, 0x000B8023,
+ 0x90000004, 0x00000000, 0x40000000, 0x00000000,
+ 0x03A, 0x0000013C,
+ 0x03B, 0x000B8023,
+ 0x90000005, 0x00000000, 0x40000000, 0x00000000,
+ 0x03A, 0x0000013C,
+ 0x03B, 0x000B8023,
+ 0x90000006, 0x00000000, 0x40000000, 0x00000000,
+ 0x03A, 0x0000013C,
+ 0x03B, 0x000B8023,
+ 0x90000008, 0x00000000, 0x40000000, 0x00000000,
+ 0x03A, 0x0000013C,
+ 0x03B, 0x000B8023,
+ 0x9000000a, 0x00000000, 0x40000000, 0x00000000,
+ 0x03A, 0x0000013C,
+ 0x03B, 0x000B8023,
+ 0x9000000b, 0x00000000, 0x40000000, 0x00000000,
+ 0x03A, 0x0000013C,
+ 0x03B, 0x000B8023,
+ 0xA0000000, 0x00000000,
+ 0x03A, 0x0000013C,
+ 0x03B, 0x000B8023,
+ 0xB0000000, 0x00000000,
+ 0x80000001, 0x00000000, 0x40000000, 0x00000000,
+ 0x03C, 0x00048000,
+ 0x90000002, 0x00000000, 0x40000000, 0x00000000,
+ 0x03C, 0x00060000,
+ 0x90000003, 0x00000000, 0x40000000, 0x00000000,
+ 0x03C, 0x0004C000,
+ 0x90000004, 0x00000000, 0x40000000, 0x00000000,
+ 0x03C, 0x00044000,
+ 0x90000005, 0x00000000, 0x40000000, 0x00000000,
+ 0x03C, 0x0004C000,
+ 0x90000006, 0x00000000, 0x40000000, 0x00000000,
+ 0x03C, 0x00048000,
+ 0x90000007, 0x00000000, 0x40000000, 0x00000000,
+ 0x03C, 0x00000000,
+ 0x90000008, 0x00000000, 0x40000000, 0x00000000,
+ 0x03C, 0x00020000,
+ 0x90000009, 0x00000000, 0x40000000, 0x00000000,
+ 0x03C, 0x00004000,
+ 0x9000000a, 0x00000000, 0x40000000, 0x00000000,
+ 0x03C, 0x00044000,
+ 0x9000000b, 0x00000000, 0x40000000, 0x00000000,
+ 0x03C, 0x00044000,
+ 0xA0000000, 0x00000000,
+ 0x03C, 0x00020000,
+ 0xB0000000, 0x00000000,
+ 0x03A, 0x0000013C,
+ 0x03B, 0x000B0023,
+ 0x80000004, 0x00000000, 0x40000000, 0x00000000,
+ 0x03C, 0x00020000,
+ 0x9000000a, 0x00000000, 0x40000000, 0x00000000,
+ 0x03C, 0x00020000,
+ 0x9000000b, 0x00000000, 0x40000000, 0x00000000,
+ 0x03C, 0x00020000,
+ 0xA0000000, 0x00000000,
+ 0x03C, 0x00020000,
+ 0xB0000000, 0x00000000,
+ 0x03A, 0x0000013C,
+ 0x03B, 0x000A8623,
+ 0x03C, 0x00000000,
+ 0x03A, 0x0000013C,
+ 0x03B, 0x000A1633,
+ 0x03C, 0x00000000,
+ 0x03A, 0x0000013C,
+ 0x03B, 0x0009C633,
+ 0x03C, 0x00000000,
+ 0x03A, 0x0000013C,
+ 0x03B, 0x00090293,
+ 0x03C, 0x00000000,
+ 0x03A, 0x0000013C,
+ 0x03B, 0x00089593,
+ 0x03C, 0x00000000,
+ 0x03A, 0x00000148,
+ 0x80000008, 0x00000000, 0x40000000, 0x00000000,
+ 0x03B, 0x0008138B,
+ 0xA0000000, 0x00000000,
+ 0x03B, 0x0008078B,
+ 0xB0000000, 0x00000000,
+ 0x03C, 0x00000000,
+ 0x0EF, 0x00000000,
+ 0x0EF, 0x00000800,
+ 0x03B, 0x00000000,
+ 0x80000001, 0x00000000, 0x40000000, 0x00000000,
+ 0x03A, 0x00000803,
+ 0x90000006, 0x00000000, 0x40000000, 0x00000000,
+ 0x03A, 0x00000803,
+ 0x90000007, 0x00000000, 0x40000000, 0x00000000,
+ 0x03A, 0x00001003,
+ 0x90000008, 0x00000000, 0x40000000, 0x00000000,
+ 0x03A, 0x00001003,
+ 0x90000009, 0x00000000, 0x40000000, 0x00000000,
+ 0x03A, 0x00001003,
+ 0xA0000000, 0x00000000,
+ 0x03A, 0x00000803,
+ 0xB0000000, 0x00000000,
+ 0x03B, 0x00040000,
+ 0x80000001, 0x00000000, 0x40000000, 0x00000000,
+ 0x03A, 0x00001000,
+ 0x90000002, 0x00000000, 0x40000000, 0x00000000,
+ 0x03A, 0x00001001,
+ 0x90000003, 0x00000000, 0x40000000, 0x00000000,
+ 0x03A, 0x00000803,
+ 0x90000005, 0x00000000, 0x40000000, 0x00000000,
+ 0x03A, 0x00001003,
+ 0x90000006, 0x00000000, 0x40000000, 0x00000000,
+ 0x03A, 0x00001000,
+ 0x90000007, 0x00000000, 0x40000000, 0x00000000,
+ 0x03A, 0x00000000,
+ 0x90000008, 0x00000000, 0x40000000, 0x00000000,
+ 0x03A, 0x00000800,
+ 0x90000009, 0x00000000, 0x40000000, 0x00000000,
+ 0x03A, 0x00000800,
+ 0xA0000000, 0x00000000,
+ 0x03A, 0x00001000,
+ 0xB0000000, 0x00000000,
+ 0x03B, 0x00080000,
+ 0x80000003, 0x00000000, 0x40000000, 0x00000000,
+ 0x03A, 0x00000000,
+ 0x90000005, 0x00000000, 0x40000000, 0x00000000,
+ 0x03A, 0x00000000,
+ 0x90000007, 0x00000000, 0x40000000, 0x00000000,
+ 0x03A, 0x00001802,
+ 0x90000009, 0x00000000, 0x40000000, 0x00000000,
+ 0x03A, 0x00001802,
+ 0xA0000000, 0x00000000,
+ 0x03A, 0x00001002,
+ 0xB0000000, 0x00000000,
+ 0x0EF, 0x00000000,
+ 0x80000001, 0x00000000, 0x40000000, 0x00000000,
+ 0x90000002, 0x00000000, 0x40000000, 0x00000000,
+ 0x90000003, 0x00000000, 0x40000000, 0x00000000,
+ 0x90000004, 0x00000000, 0x40000000, 0x00000000,
+ 0x90000005, 0x00000000, 0x40000000, 0x00000000,
+ 0x90000006, 0x00000000, 0x40000000, 0x00000000,
+ 0x90000008, 0x00000000, 0x40000000, 0x00000000,
+ 0x9000000a, 0x00000000, 0x40000000, 0x00000000,
+ 0x9000000b, 0x00000000, 0x40000000, 0x00000000,
+ 0xA0000000, 0x00000000,
+ 0xB0000000, 0x00000000,
+ 0x018, 0x00013124,
+ 0x0EF, 0x00000100,
+ 0x80000001, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0004A38C,
+ 0x034, 0x000491AD,
+ 0x034, 0x000481AA,
+ 0x034, 0x000471A7,
+ 0x034, 0x000460AA,
+ 0x034, 0x000450A7,
+ 0x034, 0x0004402C,
+ 0x034, 0x00043029,
+ 0x034, 0x0004200C,
+ 0x034, 0x00041009,
+ 0x034, 0x00040006,
+ 0x90000002, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0004A38C,
+ 0x034, 0x00049389,
+ 0x034, 0x0004816D,
+ 0x034, 0x0004716A,
+ 0x034, 0x0004606D,
+ 0x034, 0x0004506A,
+ 0x034, 0x0004402C,
+ 0x034, 0x00043029,
+ 0x034, 0x00042026,
+ 0x034, 0x00041009,
+ 0x034, 0x00040006,
+ 0x90000003, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0004A38B,
+ 0x034, 0x00049388,
+ 0x034, 0x0004818B,
+ 0x034, 0x00047188,
+ 0x034, 0x0004606D,
+ 0x034, 0x0004506A,
+ 0x034, 0x0004402C,
+ 0x034, 0x00043029,
+ 0x034, 0x00042026,
+ 0x034, 0x00041009,
+ 0x034, 0x00040006,
+ 0x90000004, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0004A38C,
+ 0x034, 0x00049389,
+ 0x034, 0x0004816D,
+ 0x034, 0x0004716A,
+ 0x034, 0x0004606D,
+ 0x034, 0x0004506A,
+ 0x034, 0x0004402C,
+ 0x034, 0x00043029,
+ 0x034, 0x00042026,
+ 0x034, 0x00041009,
+ 0x034, 0x00040006,
+ 0x90000005, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0004A38B,
+ 0x034, 0x00049388,
+ 0x034, 0x0004818B,
+ 0x034, 0x00047188,
+ 0x034, 0x0004606D,
+ 0x034, 0x0004506A,
+ 0x034, 0x0004402C,
+ 0x034, 0x00043029,
+ 0x034, 0x00042026,
+ 0x034, 0x00041009,
+ 0x034, 0x00040006,
+ 0x90000008, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0004A3F5,
+ 0x034, 0x000493F3,
+ 0x034, 0x000483B2,
+ 0x034, 0x00047390,
+ 0x034, 0x0004638D,
+ 0x034, 0x0004538A,
+ 0x034, 0x00044387,
+ 0x034, 0x0004324A,
+ 0x034, 0x00042247,
+ 0x034, 0x0004104D,
+ 0x034, 0x0004004A,
+ 0x90000009, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0004AFF7,
+ 0x034, 0x00049FF6,
+ 0x034, 0x00048FF3,
+ 0x034, 0x00047FF0,
+ 0x034, 0x00046FED,
+ 0x034, 0x00045FEA,
+ 0x034, 0x00044FE7,
+ 0x034, 0x00043DEA,
+ 0x034, 0x00042DE7,
+ 0x034, 0x00041DE4,
+ 0x034, 0x00040CE7,
+ 0x9000000a, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0004A38C,
+ 0x034, 0x00049389,
+ 0x034, 0x0004816D,
+ 0x034, 0x0004716A,
+ 0x034, 0x0004606D,
+ 0x034, 0x0004506A,
+ 0x034, 0x0004402C,
+ 0x034, 0x00043029,
+ 0x034, 0x00042026,
+ 0x034, 0x00041009,
+ 0x034, 0x00040006,
+ 0x9000000b, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0004A38C,
+ 0x034, 0x00049389,
+ 0x034, 0x0004816D,
+ 0x034, 0x0004716A,
+ 0x034, 0x0004606D,
+ 0x034, 0x0004506A,
+ 0x034, 0x0004402C,
+ 0x034, 0x00043029,
+ 0x034, 0x00042026,
+ 0x034, 0x00041009,
+ 0x034, 0x00040006,
+ 0xA0000000, 0x00000000,
+ 0x034, 0x0004AFF4,
+ 0x034, 0x00049FF1,
+ 0x034, 0x00048FEE,
+ 0x034, 0x00047FEB,
+ 0x034, 0x00046FE8,
+ 0x034, 0x00045DEA,
+ 0x034, 0x00044CED,
+ 0x034, 0x00043CEA,
+ 0x034, 0x00042C6C,
+ 0x034, 0x00041C69,
+ 0x034, 0x00040C2B,
+ 0xB0000000, 0x00000000,
+ 0x80000001, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0002A38C,
+ 0x034, 0x000291AD,
+ 0x034, 0x000281AA,
+ 0x034, 0x000271A7,
+ 0x034, 0x000260AA,
+ 0x034, 0x000250A7,
+ 0x034, 0x0002402C,
+ 0x034, 0x00023029,
+ 0x034, 0x0002200C,
+ 0x034, 0x00021009,
+ 0x034, 0x00020006,
+ 0x90000002, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0002A3EE,
+ 0x034, 0x000293AC,
+ 0x034, 0x00028389,
+ 0x034, 0x0002716D,
+ 0x034, 0x0002616A,
+ 0x034, 0x0002506D,
+ 0x034, 0x0002406A,
+ 0x034, 0x0002302C,
+ 0x034, 0x00022029,
+ 0x034, 0x00021026,
+ 0x034, 0x00020023,
+ 0x90000003, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0002A3EF,
+ 0x034, 0x000293AD,
+ 0x034, 0x0002838A,
+ 0x034, 0x0002718C,
+ 0x034, 0x00026189,
+ 0x034, 0x0002506D,
+ 0x034, 0x0002406A,
+ 0x034, 0x0002302C,
+ 0x034, 0x00022029,
+ 0x034, 0x00021026,
+ 0x034, 0x00020023,
+ 0x90000004, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0002A3EE,
+ 0x034, 0x000293AC,
+ 0x034, 0x00028389,
+ 0x034, 0x0002716D,
+ 0x034, 0x0002616A,
+ 0x034, 0x0002506D,
+ 0x034, 0x0002406A,
+ 0x034, 0x0002302C,
+ 0x034, 0x00022029,
+ 0x034, 0x00021026,
+ 0x034, 0x00020023,
+ 0x90000005, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0002A3EF,
+ 0x034, 0x000293AD,
+ 0x034, 0x0002838A,
+ 0x034, 0x0002718C,
+ 0x034, 0x00026189,
+ 0x034, 0x0002506D,
+ 0x034, 0x0002406A,
+ 0x034, 0x0002302C,
+ 0x034, 0x00022029,
+ 0x034, 0x00021026,
+ 0x034, 0x00020023,
+ 0x90000008, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0002A3F5,
+ 0x034, 0x000293F3,
+ 0x034, 0x000283D0,
+ 0x034, 0x00027371,
+ 0x034, 0x0002636E,
+ 0x034, 0x0002536B,
+ 0x034, 0x00024368,
+ 0x034, 0x0002332A,
+ 0x034, 0x00022327,
+ 0x034, 0x0002104C,
+ 0x034, 0x00020049,
+ 0x90000009, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0002AFF7,
+ 0x034, 0x00029FF6,
+ 0x034, 0x00028FF3,
+ 0x034, 0x00027FF0,
+ 0x034, 0x00026FED,
+ 0x034, 0x00025FEA,
+ 0x034, 0x00024FE7,
+ 0x034, 0x00023DEA,
+ 0x034, 0x00022DE7,
+ 0x034, 0x00021DE4,
+ 0x034, 0x00020F25,
+ 0x9000000a, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0002A3EE,
+ 0x034, 0x000293AC,
+ 0x034, 0x00028389,
+ 0x034, 0x0002716D,
+ 0x034, 0x0002616A,
+ 0x034, 0x0002506D,
+ 0x034, 0x0002406A,
+ 0x034, 0x0002302C,
+ 0x034, 0x00022029,
+ 0x034, 0x00021026,
+ 0x034, 0x00020023,
+ 0x9000000b, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0002A3EE,
+ 0x034, 0x000293AC,
+ 0x034, 0x00028389,
+ 0x034, 0x0002716D,
+ 0x034, 0x0002616A,
+ 0x034, 0x0002506D,
+ 0x034, 0x0002406A,
+ 0x034, 0x0002302C,
+ 0x034, 0x00022029,
+ 0x034, 0x00021026,
+ 0x034, 0x00020023,
+ 0xA0000000, 0x00000000,
+ 0x034, 0x0002AFF4,
+ 0x034, 0x00029FF1,
+ 0x034, 0x00028FEE,
+ 0x034, 0x00027FEB,
+ 0x034, 0x00026FE8,
+ 0x034, 0x00025DEA,
+ 0x034, 0x00024CED,
+ 0x034, 0x00023CEA,
+ 0x034, 0x00022C6C,
+ 0x034, 0x00021C69,
+ 0x034, 0x00020C2B,
+ 0xB0000000, 0x00000000,
+ 0x80000001, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0000A38C,
+ 0x034, 0x000091AD,
+ 0x034, 0x000081AA,
+ 0x034, 0x000071A7,
+ 0x034, 0x000060AA,
+ 0x034, 0x000050A7,
+ 0x034, 0x0000402C,
+ 0x034, 0x00003029,
+ 0x034, 0x00002026,
+ 0x034, 0x00001009,
+ 0x034, 0x00000006,
+ 0x90000002, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0000A3EC,
+ 0x034, 0x000093AC,
+ 0x034, 0x000081EC,
+ 0x034, 0x0000716D,
+ 0x034, 0x0000616A,
+ 0x034, 0x0000506D,
+ 0x034, 0x0000404C,
+ 0x034, 0x0000302C,
+ 0x034, 0x00002029,
+ 0x034, 0x00001026,
+ 0x034, 0x00000023,
+ 0x90000003, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0000A3EF,
+ 0x034, 0x000093AD,
+ 0x034, 0x0000838A,
+ 0x034, 0x0000718C,
+ 0x034, 0x00006189,
+ 0x034, 0x0000506D,
+ 0x034, 0x0000406A,
+ 0x034, 0x0000302C,
+ 0x034, 0x00002029,
+ 0x034, 0x00001026,
+ 0x034, 0x00000023,
+ 0x90000004, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0000A3EC,
+ 0x034, 0x000093AC,
+ 0x034, 0x000081EC,
+ 0x034, 0x0000716D,
+ 0x034, 0x0000616A,
+ 0x034, 0x0000506D,
+ 0x034, 0x0000404C,
+ 0x034, 0x0000302C,
+ 0x034, 0x00002029,
+ 0x034, 0x00001026,
+ 0x034, 0x00000023,
+ 0x90000005, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0000A3EF,
+ 0x034, 0x000093AD,
+ 0x034, 0x0000838A,
+ 0x034, 0x0000718C,
+ 0x034, 0x00006189,
+ 0x034, 0x0000506D,
+ 0x034, 0x0000406A,
+ 0x034, 0x0000302C,
+ 0x034, 0x00002029,
+ 0x034, 0x00001026,
+ 0x034, 0x00000023,
+ 0x90000008, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0000A3F4,
+ 0x034, 0x000093F0,
+ 0x034, 0x000083AE,
+ 0x034, 0x00007350,
+ 0x034, 0x0000634D,
+ 0x034, 0x0000534A,
+ 0x034, 0x00004347,
+ 0x034, 0x0000312D,
+ 0x034, 0x0000212A,
+ 0x034, 0x00001127,
+ 0x034, 0x0000002A,
+ 0x90000009, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0000AFF7,
+ 0x034, 0x00009FF4,
+ 0x034, 0x00008FF1,
+ 0x034, 0x00007FEE,
+ 0x034, 0x00006FEB,
+ 0x034, 0x00005FE8,
+ 0x034, 0x00004DEB,
+ 0x034, 0x00003DE8,
+ 0x034, 0x00002DE5,
+ 0x034, 0x00001C8B,
+ 0x034, 0x00000C88,
+ 0x9000000a, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0000A3EC,
+ 0x034, 0x000093AC,
+ 0x034, 0x000081EC,
+ 0x034, 0x0000716D,
+ 0x034, 0x0000616A,
+ 0x034, 0x0000506D,
+ 0x034, 0x0000404C,
+ 0x034, 0x0000302C,
+ 0x034, 0x00002029,
+ 0x034, 0x00001026,
+ 0x034, 0x00000023,
+ 0x9000000b, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0000A3EC,
+ 0x034, 0x000093AC,
+ 0x034, 0x000081EC,
+ 0x034, 0x0000716D,
+ 0x034, 0x0000616A,
+ 0x034, 0x0000506D,
+ 0x034, 0x0000404C,
+ 0x034, 0x0000302C,
+ 0x034, 0x00002029,
+ 0x034, 0x00001026,
+ 0x034, 0x00000023,
+ 0xA0000000, 0x00000000,
+ 0x034, 0x0000AFF4,
+ 0x034, 0x00009FF1,
+ 0x034, 0x00008FEE,
+ 0x034, 0x00007FEB,
+ 0x034, 0x00006FE8,
+ 0x034, 0x00005DEA,
+ 0x034, 0x00004CED,
+ 0x034, 0x00003CEA,
+ 0x034, 0x00002C6C,
+ 0x034, 0x00001C69,
+ 0x034, 0x00000C2B,
+ 0xB0000000, 0x00000000,
+ 0x80000001, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x000CA38C,
+ 0x034, 0x000C91AD,
+ 0x034, 0x000C81AA,
+ 0x034, 0x000C71A7,
+ 0x034, 0x000C60AA,
+ 0x034, 0x000C50A7,
+ 0x034, 0x000C402C,
+ 0x034, 0x000C3029,
+ 0x034, 0x000C200C,
+ 0x034, 0x000C1009,
+ 0x034, 0x000C0006,
+ 0x90000002, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x000CA38C,
+ 0x034, 0x000C9389,
+ 0x034, 0x000C816D,
+ 0x034, 0x000C716A,
+ 0x034, 0x000C606D,
+ 0x034, 0x000C506A,
+ 0x034, 0x000C402C,
+ 0x034, 0x000C3029,
+ 0x034, 0x000C2026,
+ 0x034, 0x000C1009,
+ 0x034, 0x000C0006,
+ 0x90000003, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x000CA38B,
+ 0x034, 0x000C9388,
+ 0x034, 0x000C818B,
+ 0x034, 0x000C7188,
+ 0x034, 0x000C606D,
+ 0x034, 0x000C506A,
+ 0x034, 0x000C402C,
+ 0x034, 0x000C3029,
+ 0x034, 0x000C2026,
+ 0x034, 0x000C1009,
+ 0x034, 0x000C0006,
+ 0x90000004, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x000CA38C,
+ 0x034, 0x000C9389,
+ 0x034, 0x000C816D,
+ 0x034, 0x000C716A,
+ 0x034, 0x000C606D,
+ 0x034, 0x000C506A,
+ 0x034, 0x000C402C,
+ 0x034, 0x000C3029,
+ 0x034, 0x000C2026,
+ 0x034, 0x000C1009,
+ 0x034, 0x000C0006,
+ 0x90000005, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x000CA38B,
+ 0x034, 0x000C9388,
+ 0x034, 0x000C818B,
+ 0x034, 0x000C7188,
+ 0x034, 0x000C606D,
+ 0x034, 0x000C506A,
+ 0x034, 0x000C402C,
+ 0x034, 0x000C3029,
+ 0x034, 0x000C2026,
+ 0x034, 0x000C1009,
+ 0x034, 0x000C0006,
+ 0x90000008, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x000CA3F5,
+ 0x034, 0x000C93F3,
+ 0x034, 0x000C83B2,
+ 0x034, 0x000C7390,
+ 0x034, 0x000C638D,
+ 0x034, 0x000C538A,
+ 0x034, 0x000C4387,
+ 0x034, 0x000C324A,
+ 0x034, 0x000C2247,
+ 0x034, 0x000C104D,
+ 0x034, 0x000C004A,
+ 0x90000009, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x000CAFF7,
+ 0x034, 0x000C9FF6,
+ 0x034, 0x000C8FF3,
+ 0x034, 0x000C7FF0,
+ 0x034, 0x000C6FED,
+ 0x034, 0x000C5FEA,
+ 0x034, 0x000C4FE7,
+ 0x034, 0x000C3DEA,
+ 0x034, 0x000C2DE7,
+ 0x034, 0x000C1DE4,
+ 0x034, 0x000C0CE7,
+ 0x9000000a, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x000CA38C,
+ 0x034, 0x000C9389,
+ 0x034, 0x000C816D,
+ 0x034, 0x000C716A,
+ 0x034, 0x000C606D,
+ 0x034, 0x000C506A,
+ 0x034, 0x000C402C,
+ 0x034, 0x000C3029,
+ 0x034, 0x000C2026,
+ 0x034, 0x000C1009,
+ 0x034, 0x000C0006,
+ 0x9000000b, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x000CA38C,
+ 0x034, 0x000C9389,
+ 0x034, 0x000C816D,
+ 0x034, 0x000C716A,
+ 0x034, 0x000C606D,
+ 0x034, 0x000C506A,
+ 0x034, 0x000C402C,
+ 0x034, 0x000C3029,
+ 0x034, 0x000C2026,
+ 0x034, 0x000C1009,
+ 0x034, 0x000C0006,
+ 0xA0000000, 0x00000000,
+ 0x034, 0x000CA794,
+ 0x034, 0x000C9791,
+ 0x034, 0x000C878E,
+ 0x034, 0x000C778B,
+ 0x034, 0x000C658D,
+ 0x034, 0x000C558A,
+ 0x034, 0x000C448D,
+ 0x034, 0x000C348A,
+ 0x034, 0x000C244C,
+ 0x034, 0x000C1449,
+ 0x034, 0x000C042B,
+ 0xB0000000, 0x00000000,
+ 0x80000001, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x000AA38C,
+ 0x034, 0x000A91AD,
+ 0x034, 0x000A81AA,
+ 0x034, 0x000A71A7,
+ 0x034, 0x000A60AA,
+ 0x034, 0x000A50A7,
+ 0x034, 0x000A402C,
+ 0x034, 0x000A3029,
+ 0x034, 0x000A200C,
+ 0x034, 0x000A1009,
+ 0x034, 0x000A0006,
+ 0x90000002, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x000AA3EE,
+ 0x034, 0x000A93AC,
+ 0x034, 0x000A8389,
+ 0x034, 0x000A716D,
+ 0x034, 0x000A616A,
+ 0x034, 0x000A506D,
+ 0x034, 0x000A406A,
+ 0x034, 0x000A302C,
+ 0x034, 0x000A2029,
+ 0x034, 0x000A1026,
+ 0x034, 0x000A0023,
+ 0x90000003, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x000AA3EF,
+ 0x034, 0x000A93AD,
+ 0x034, 0x000A838A,
+ 0x034, 0x000A718C,
+ 0x034, 0x000A6189,
+ 0x034, 0x000A506D,
+ 0x034, 0x000A406A,
+ 0x034, 0x000A302C,
+ 0x034, 0x000A2029,
+ 0x034, 0x000A1026,
+ 0x034, 0x000A0023,
+ 0x90000004, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x000AA3EE,
+ 0x034, 0x000A93AC,
+ 0x034, 0x000A8389,
+ 0x034, 0x000A716D,
+ 0x034, 0x000A616A,
+ 0x034, 0x000A506D,
+ 0x034, 0x000A406A,
+ 0x034, 0x000A302C,
+ 0x034, 0x000A2029,
+ 0x034, 0x000A1026,
+ 0x034, 0x000A0023,
+ 0x90000005, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x000AA3EF,
+ 0x034, 0x000A93AD,
+ 0x034, 0x000A838A,
+ 0x034, 0x000A718C,
+ 0x034, 0x000A6189,
+ 0x034, 0x000A506D,
+ 0x034, 0x000A406A,
+ 0x034, 0x000A302C,
+ 0x034, 0x000A2029,
+ 0x034, 0x000A1026,
+ 0x034, 0x000A0023,
+ 0x90000008, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x000AA3F5,
+ 0x034, 0x000A93F3,
+ 0x034, 0x000A83D0,
+ 0x034, 0x000A7371,
+ 0x034, 0x000A636E,
+ 0x034, 0x000A536B,
+ 0x034, 0x000A4368,
+ 0x034, 0x000A332A,
+ 0x034, 0x000A2327,
+ 0x034, 0x000A104C,
+ 0x034, 0x000A0049,
+ 0x90000009, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x000AAFF7,
+ 0x034, 0x000A9FF6,
+ 0x034, 0x000A8FF3,
+ 0x034, 0x000A7FF0,
+ 0x034, 0x000A6FED,
+ 0x034, 0x000A5FEA,
+ 0x034, 0x000A4FE7,
+ 0x034, 0x000A3DEA,
+ 0x034, 0x000A2DE7,
+ 0x034, 0x000A1DE4,
+ 0x034, 0x000A0F25,
+ 0x9000000a, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x000AA3EE,
+ 0x034, 0x000A93AC,
+ 0x034, 0x000A8389,
+ 0x034, 0x000A716D,
+ 0x034, 0x000A616A,
+ 0x034, 0x000A506D,
+ 0x034, 0x000A406A,
+ 0x034, 0x000A302C,
+ 0x034, 0x000A2029,
+ 0x034, 0x000A1026,
+ 0x034, 0x000A0023,
+ 0x9000000b, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x000AA3EE,
+ 0x034, 0x000A93AC,
+ 0x034, 0x000A8389,
+ 0x034, 0x000A716D,
+ 0x034, 0x000A616A,
+ 0x034, 0x000A506D,
+ 0x034, 0x000A406A,
+ 0x034, 0x000A302C,
+ 0x034, 0x000A2029,
+ 0x034, 0x000A1026,
+ 0x034, 0x000A0023,
+ 0xA0000000, 0x00000000,
+ 0x034, 0x000AA794,
+ 0x034, 0x000A9791,
+ 0x034, 0x000A878E,
+ 0x034, 0x000A778B,
+ 0x034, 0x000A658D,
+ 0x034, 0x000A558A,
+ 0x034, 0x000A448D,
+ 0x034, 0x000A348A,
+ 0x034, 0x000A244C,
+ 0x034, 0x000A1449,
+ 0x034, 0x000A042B,
+ 0xB0000000, 0x00000000,
+ 0x80000001, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0008A38C,
+ 0x034, 0x000891AD,
+ 0x034, 0x000881AA,
+ 0x034, 0x000871A7,
+ 0x034, 0x000860AA,
+ 0x034, 0x000850A7,
+ 0x034, 0x0008402C,
+ 0x034, 0x00083029,
+ 0x034, 0x00082026,
+ 0x034, 0x00081009,
+ 0x034, 0x00080006,
+ 0x90000002, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0008A3EC,
+ 0x034, 0x000893AC,
+ 0x034, 0x000881EC,
+ 0x034, 0x0008716D,
+ 0x034, 0x0008616A,
+ 0x034, 0x0008506D,
+ 0x034, 0x0008404C,
+ 0x034, 0x0008302C,
+ 0x034, 0x00082029,
+ 0x034, 0x00081026,
+ 0x034, 0x00080023,
+ 0x90000003, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0008A3EF,
+ 0x034, 0x000893AD,
+ 0x034, 0x0008838A,
+ 0x034, 0x0008718C,
+ 0x034, 0x00086189,
+ 0x034, 0x0008506D,
+ 0x034, 0x0008406A,
+ 0x034, 0x0008302C,
+ 0x034, 0x00082029,
+ 0x034, 0x00081026,
+ 0x034, 0x00080023,
+ 0x90000004, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0008A3EC,
+ 0x034, 0x000893AC,
+ 0x034, 0x000881EC,
+ 0x034, 0x0008716D,
+ 0x034, 0x0008616A,
+ 0x034, 0x0008506D,
+ 0x034, 0x0008404C,
+ 0x034, 0x0008302C,
+ 0x034, 0x00082029,
+ 0x034, 0x00081026,
+ 0x034, 0x00080023,
+ 0x90000005, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0008A3EF,
+ 0x034, 0x000893AD,
+ 0x034, 0x0008838A,
+ 0x034, 0x0008718C,
+ 0x034, 0x00086189,
+ 0x034, 0x0008506D,
+ 0x034, 0x0008406A,
+ 0x034, 0x0008302C,
+ 0x034, 0x00082029,
+ 0x034, 0x00081026,
+ 0x034, 0x00080023,
+ 0x90000008, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0008A3F4,
+ 0x034, 0x000893F0,
+ 0x034, 0x000883AE,
+ 0x034, 0x00087350,
+ 0x034, 0x0008634D,
+ 0x034, 0x0008534A,
+ 0x034, 0x00084347,
+ 0x034, 0x0008312D,
+ 0x034, 0x0008212A,
+ 0x034, 0x00081127,
+ 0x034, 0x0008002A,
+ 0x90000009, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0008AFF7,
+ 0x034, 0x00089FF4,
+ 0x034, 0x00088FF1,
+ 0x034, 0x00087FEE,
+ 0x034, 0x00086FEB,
+ 0x034, 0x00085FE8,
+ 0x034, 0x00084DEB,
+ 0x034, 0x00083DE8,
+ 0x034, 0x00082DE5,
+ 0x034, 0x00081C8B,
+ 0x034, 0x00080C88,
+ 0x9000000a, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0008A3EC,
+ 0x034, 0x000893AC,
+ 0x034, 0x000881EC,
+ 0x034, 0x0008716D,
+ 0x034, 0x0008616A,
+ 0x034, 0x0008506D,
+ 0x034, 0x0008404C,
+ 0x034, 0x0008302C,
+ 0x034, 0x00082029,
+ 0x034, 0x00081026,
+ 0x034, 0x00080023,
+ 0x9000000b, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0008A3EC,
+ 0x034, 0x000893AC,
+ 0x034, 0x000881EC,
+ 0x034, 0x0008716D,
+ 0x034, 0x0008616A,
+ 0x034, 0x0008506D,
+ 0x034, 0x0008404C,
+ 0x034, 0x0008302C,
+ 0x034, 0x00082029,
+ 0x034, 0x00081026,
+ 0x034, 0x00080023,
+ 0xA0000000, 0x00000000,
+ 0x034, 0x0008A794,
+ 0x034, 0x00089791,
+ 0x034, 0x0008878E,
+ 0x034, 0x0008778B,
+ 0x034, 0x0008658D,
+ 0x034, 0x0008558A,
+ 0x034, 0x0008448D,
+ 0x034, 0x0008348A,
+ 0x034, 0x0008244C,
+ 0x034, 0x00081449,
+ 0x034, 0x0008042B,
+ 0xB0000000, 0x00000000,
+ 0x0EF, 0x00000000,
+ 0x80000001, 0x00000000, 0x40000000, 0x00000000,
+ 0x0DF, 0x00000001,
+ 0x90000002, 0x00000000, 0x40000000, 0x00000000,
+ 0x0DF, 0x00000001,
+ 0x90000003, 0x00000000, 0x40000000, 0x00000000,
+ 0x0DF, 0x00000001,
+ 0x90000004, 0x00000000, 0x40000000, 0x00000000,
+ 0x0DF, 0x00000001,
+ 0x90000005, 0x00000000, 0x40000000, 0x00000000,
+ 0x0DF, 0x00000001,
+ 0x90000008, 0x00000000, 0x40000000, 0x00000000,
+ 0x0DF, 0x00000001,
+ 0x9000000a, 0x00000000, 0x40000000, 0x00000000,
+ 0x0DF, 0x00000001,
+ 0x9000000b, 0x00000000, 0x40000000, 0x00000000,
+ 0x0DF, 0x00000001,
+ 0xA0000000, 0x00000000,
+ 0x0DF, 0x00000000,
+ 0xB0000000, 0x00000000,
+ 0x018, 0x0001712A,
+ 0x0EF, 0x00000040,
+ 0x80000001, 0x00000000, 0x40000000, 0x00000000,
+ 0x035, 0x000006CC,
+ 0x035, 0x000086CC,
+ 0x035, 0x000106CC,
+ 0x035, 0x000206CC,
+ 0x035, 0x000286CC,
+ 0x035, 0x000306CC,
+ 0x035, 0x000406CC,
+ 0x035, 0x000486CC,
+ 0x035, 0x000506CC,
+ 0x035, 0x000806CC,
+ 0x035, 0x000886CC,
+ 0x035, 0x000906CC,
+ 0x035, 0x000A06CC,
+ 0x035, 0x000A86CC,
+ 0x035, 0x000B06CC,
+ 0x035, 0x000C06CC,
+ 0x035, 0x000C86CC,
+ 0x035, 0x000D06CC,
+ 0x90000002, 0x00000000, 0x40000000, 0x00000000,
+ 0x035, 0x000006CC,
+ 0x035, 0x000086CC,
+ 0x035, 0x000106CC,
+ 0x035, 0x000206CC,
+ 0x035, 0x000286CC,
+ 0x035, 0x000306CC,
+ 0x035, 0x000406CC,
+ 0x035, 0x000486CC,
+ 0x035, 0x000506CC,
+ 0x035, 0x000806CC,
+ 0x035, 0x000886CC,
+ 0x035, 0x000906CC,
+ 0x035, 0x000A06CC,
+ 0x035, 0x000A86CC,
+ 0x035, 0x000B06CC,
+ 0x035, 0x000C06CC,
+ 0x035, 0x000C86CC,
+ 0x035, 0x000D06CC,
+ 0x90000003, 0x00000000, 0x40000000, 0x00000000,
+ 0x035, 0x000006CC,
+ 0x035, 0x000086CC,
+ 0x035, 0x000106CC,
+ 0x035, 0x000206CC,
+ 0x035, 0x000286CC,
+ 0x035, 0x000306CC,
+ 0x035, 0x000406CC,
+ 0x035, 0x000486CC,
+ 0x035, 0x000506CC,
+ 0x035, 0x000806CC,
+ 0x035, 0x000886CC,
+ 0x035, 0x000906CC,
+ 0x035, 0x000A06CC,
+ 0x035, 0x000A86CC,
+ 0x035, 0x000B06CC,
+ 0x035, 0x000C06CC,
+ 0x035, 0x000C86CC,
+ 0x035, 0x000D06CC,
+ 0x90000004, 0x00000000, 0x40000000, 0x00000000,
+ 0x035, 0x000006CC,
+ 0x035, 0x000086CC,
+ 0x035, 0x000106CC,
+ 0x035, 0x000206CC,
+ 0x035, 0x000286CC,
+ 0x035, 0x000306CC,
+ 0x035, 0x000406CC,
+ 0x035, 0x000486CC,
+ 0x035, 0x000506CC,
+ 0x035, 0x000806CC,
+ 0x035, 0x000886CC,
+ 0x035, 0x000906CC,
+ 0x035, 0x000A06CC,
+ 0x035, 0x000A86CC,
+ 0x035, 0x000B06CC,
+ 0x035, 0x000C06CC,
+ 0x035, 0x000C86CC,
+ 0x035, 0x000D06CC,
+ 0x90000005, 0x00000000, 0x40000000, 0x00000000,
+ 0x035, 0x000006CC,
+ 0x035, 0x000086CC,
+ 0x035, 0x000106CC,
+ 0x035, 0x000206CC,
+ 0x035, 0x000286CC,
+ 0x035, 0x000306CC,
+ 0x035, 0x000406CC,
+ 0x035, 0x000486CC,
+ 0x035, 0x000506CC,
+ 0x035, 0x000806CC,
+ 0x035, 0x000886CC,
+ 0x035, 0x000906CC,
+ 0x035, 0x000A06CC,
+ 0x035, 0x000A86CC,
+ 0x035, 0x000B06CC,
+ 0x035, 0x000C06CC,
+ 0x035, 0x000C86CC,
+ 0x035, 0x000D06CC,
+ 0x90000008, 0x00000000, 0x40000000, 0x00000000,
+ 0x035, 0x000006CC,
+ 0x035, 0x000086CC,
+ 0x035, 0x000106CC,
+ 0x035, 0x000206CC,
+ 0x035, 0x000286CC,
+ 0x035, 0x000306CC,
+ 0x035, 0x000406CC,
+ 0x035, 0x000486CC,
+ 0x035, 0x000506CC,
+ 0x035, 0x000806CC,
+ 0x035, 0x000886CC,
+ 0x035, 0x000906CC,
+ 0x035, 0x000A06CC,
+ 0x035, 0x000A86CC,
+ 0x035, 0x000B06CC,
+ 0x035, 0x000C06CC,
+ 0x035, 0x000C86CC,
+ 0x035, 0x000D06CC,
+ 0x9000000a, 0x00000000, 0x40000000, 0x00000000,
+ 0x035, 0x000006CC,
+ 0x035, 0x000086CC,
+ 0x035, 0x000106CC,
+ 0x035, 0x000206CC,
+ 0x035, 0x000286CC,
+ 0x035, 0x000306CC,
+ 0x035, 0x000406CC,
+ 0x035, 0x000486CC,
+ 0x035, 0x000506CC,
+ 0x035, 0x000806CC,
+ 0x035, 0x000886CC,
+ 0x035, 0x000906CC,
+ 0x035, 0x000A06CC,
+ 0x035, 0x000A86CC,
+ 0x035, 0x000B06CC,
+ 0x035, 0x000C06CC,
+ 0x035, 0x000C86CC,
+ 0x035, 0x000D06CC,
+ 0x9000000b, 0x00000000, 0x40000000, 0x00000000,
+ 0x035, 0x000006CC,
+ 0x035, 0x000086CC,
+ 0x035, 0x000106CC,
+ 0x035, 0x000206CC,
+ 0x035, 0x000286CC,
+ 0x035, 0x000306CC,
+ 0x035, 0x000406CC,
+ 0x035, 0x000486CC,
+ 0x035, 0x000506CC,
+ 0x035, 0x000806CC,
+ 0x035, 0x000886CC,
+ 0x035, 0x000906CC,
+ 0x035, 0x000A06CC,
+ 0x035, 0x000A86CC,
+ 0x035, 0x000B06CC,
+ 0x035, 0x000C06CC,
+ 0x035, 0x000C86CC,
+ 0x035, 0x000D06CC,
+ 0xA0000000, 0x00000000,
+ 0x035, 0x00000484,
+ 0x035, 0x00008484,
+ 0x035, 0x00010484,
+ 0x035, 0x00020584,
+ 0x035, 0x00028584,
+ 0x035, 0x00030584,
+ 0x035, 0x00040584,
+ 0x035, 0x00048584,
+ 0x035, 0x00050584,
+ 0x035, 0x000805FB,
+ 0x035, 0x000885FB,
+ 0x035, 0x000905FB,
+ 0x035, 0x000A05FB,
+ 0x035, 0x000A85FB,
+ 0x035, 0x000B05FB,
+ 0x035, 0x000C05FB,
+ 0x035, 0x000C85FB,
+ 0x035, 0x000D05FB,
+ 0xB0000000, 0x00000000,
+ 0x0EF, 0x00000000,
+ 0x80000001, 0x00000000, 0x40000000, 0x00000000,
+ 0x0DF, 0x00000001,
+ 0x90000002, 0x00000000, 0x40000000, 0x00000000,
+ 0x0DF, 0x00000001,
+ 0x90000003, 0x00000000, 0x40000000, 0x00000000,
+ 0x0DF, 0x00000001,
+ 0x90000004, 0x00000000, 0x40000000, 0x00000000,
+ 0x0DF, 0x00000001,
+ 0x90000005, 0x00000000, 0x40000000, 0x00000000,
+ 0x0DF, 0x00000001,
+ 0x90000008, 0x00000000, 0x40000000, 0x00000000,
+ 0x0DF, 0x00000001,
+ 0x9000000a, 0x00000000, 0x40000000, 0x00000000,
+ 0x0DF, 0x00000001,
+ 0x9000000b, 0x00000000, 0x40000000, 0x00000000,
+ 0x0DF, 0x00000001,
+ 0xA0000000, 0x00000000,
+ 0x0DF, 0x00000000,
+ 0xB0000000, 0x00000000,
+ 0x018, 0x0001712A,
+ 0x0EF, 0x00000010,
+ 0x80000001, 0x00000000, 0x40000000, 0x00000000,
+ 0x036, 0x00000473,
+ 0x036, 0x00008473,
+ 0x036, 0x00010473,
+ 0x036, 0x00020473,
+ 0x036, 0x00028473,
+ 0x036, 0x00030473,
+ 0x036, 0x00040473,
+ 0x036, 0x00048473,
+ 0x036, 0x00050473,
+ 0x036, 0x00080473,
+ 0x036, 0x00088473,
+ 0x036, 0x00090473,
+ 0x036, 0x000A0473,
+ 0x036, 0x000A8473,
+ 0x036, 0x000B0473,
+ 0x036, 0x000C0473,
+ 0x036, 0x000C8473,
+ 0x036, 0x000D0473,
+ 0x90000002, 0x00000000, 0x40000000, 0x00000000,
+ 0x036, 0x00000475,
+ 0x036, 0x00008475,
+ 0x036, 0x00010475,
+ 0x036, 0x00020475,
+ 0x036, 0x00028475,
+ 0x036, 0x00030475,
+ 0x036, 0x00040475,
+ 0x036, 0x00048475,
+ 0x036, 0x00050475,
+ 0x036, 0x00080475,
+ 0x036, 0x00088475,
+ 0x036, 0x00090475,
+ 0x036, 0x000A0475,
+ 0x036, 0x000A8475,
+ 0x036, 0x000B0475,
+ 0x036, 0x000C0475,
+ 0x036, 0x000C8475,
+ 0x036, 0x000D0475,
+ 0x90000003, 0x00000000, 0x40000000, 0x00000000,
+ 0x036, 0x00000475,
+ 0x036, 0x00008475,
+ 0x036, 0x00010475,
+ 0x036, 0x00020475,
+ 0x036, 0x00028475,
+ 0x036, 0x00030475,
+ 0x036, 0x00040475,
+ 0x036, 0x00048475,
+ 0x036, 0x00050475,
+ 0x036, 0x00080475,
+ 0x036, 0x00088475,
+ 0x036, 0x00090475,
+ 0x036, 0x000A0475,
+ 0x036, 0x000A8475,
+ 0x036, 0x000B0475,
+ 0x036, 0x000C0475,
+ 0x036, 0x000C8475,
+ 0x036, 0x000D0475,
+ 0x90000004, 0x00000000, 0x40000000, 0x00000000,
+ 0x036, 0x00000475,
+ 0x036, 0x00008475,
+ 0x036, 0x00010475,
+ 0x036, 0x00020475,
+ 0x036, 0x00028475,
+ 0x036, 0x00030475,
+ 0x036, 0x00040475,
+ 0x036, 0x00048475,
+ 0x036, 0x00050475,
+ 0x036, 0x00080475,
+ 0x036, 0x00088475,
+ 0x036, 0x00090475,
+ 0x036, 0x000A0475,
+ 0x036, 0x000A8475,
+ 0x036, 0x000B0475,
+ 0x036, 0x000C0475,
+ 0x036, 0x000C8475,
+ 0x036, 0x000D0475,
+ 0x90000005, 0x00000000, 0x40000000, 0x00000000,
+ 0x036, 0x00000475,
+ 0x036, 0x00008475,
+ 0x036, 0x00010475,
+ 0x036, 0x00020475,
+ 0x036, 0x00028475,
+ 0x036, 0x00030475,
+ 0x036, 0x00040475,
+ 0x036, 0x00048475,
+ 0x036, 0x00050475,
+ 0x036, 0x00080475,
+ 0x036, 0x00088475,
+ 0x036, 0x00090475,
+ 0x036, 0x000A0475,
+ 0x036, 0x000A8475,
+ 0x036, 0x000B0475,
+ 0x036, 0x000C0475,
+ 0x036, 0x000C8475,
+ 0x036, 0x000D0475,
+ 0x90000008, 0x00000000, 0x40000000, 0x00000000,
+ 0x036, 0x00000475,
+ 0x036, 0x00008475,
+ 0x036, 0x00010475,
+ 0x036, 0x00020475,
+ 0x036, 0x00028475,
+ 0x036, 0x00030475,
+ 0x036, 0x00040475,
+ 0x036, 0x00048475,
+ 0x036, 0x00050475,
+ 0x036, 0x00080475,
+ 0x036, 0x00088475,
+ 0x036, 0x00090475,
+ 0x036, 0x000A0475,
+ 0x036, 0x000A8475,
+ 0x036, 0x000B0475,
+ 0x036, 0x000C0475,
+ 0x036, 0x000C8475,
+ 0x036, 0x000D0475,
+ 0x9000000a, 0x00000000, 0x40000000, 0x00000000,
+ 0x036, 0x00000475,
+ 0x036, 0x00008475,
+ 0x036, 0x00010475,
+ 0x036, 0x00020475,
+ 0x036, 0x00028475,
+ 0x036, 0x00030475,
+ 0x036, 0x00040475,
+ 0x036, 0x00048475,
+ 0x036, 0x00050475,
+ 0x036, 0x00080475,
+ 0x036, 0x00088475,
+ 0x036, 0x00090475,
+ 0x036, 0x000A0475,
+ 0x036, 0x000A8475,
+ 0x036, 0x000B0475,
+ 0x036, 0x000C0475,
+ 0x036, 0x000C8475,
+ 0x036, 0x000D0475,
+ 0x9000000b, 0x00000000, 0x40000000, 0x00000000,
+ 0x036, 0x00000475,
+ 0x036, 0x00008475,
+ 0x036, 0x00010475,
+ 0x036, 0x00020475,
+ 0x036, 0x00028475,
+ 0x036, 0x00030475,
+ 0x036, 0x00040475,
+ 0x036, 0x00048475,
+ 0x036, 0x00050475,
+ 0x036, 0x00080475,
+ 0x036, 0x00088475,
+ 0x036, 0x00090475,
+ 0x036, 0x000A0475,
+ 0x036, 0x000A8475,
+ 0x036, 0x000B0475,
+ 0x036, 0x000C0475,
+ 0x036, 0x000C8475,
+ 0x036, 0x000D0475,
+ 0xA0000000, 0x00000000,
+ 0x036, 0x00000474,
+ 0x036, 0x00008474,
+ 0x036, 0x00010474,
+ 0x036, 0x00020474,
+ 0x036, 0x00028474,
+ 0x036, 0x00030474,
+ 0x036, 0x00040474,
+ 0x036, 0x00048474,
+ 0x036, 0x00050474,
+ 0x036, 0x00080474,
+ 0x036, 0x00088474,
+ 0x036, 0x00090474,
+ 0x036, 0x000A0474,
+ 0x036, 0x000A8474,
+ 0x036, 0x000B0474,
+ 0x036, 0x000C0474,
+ 0x036, 0x000C8474,
+ 0x036, 0x000D0474,
+ 0xB0000000, 0x00000000,
+ 0x0EF, 0x00000000,
+ 0x80000001, 0x00000000, 0x40000000, 0x00000000,
+ 0x90000002, 0x00000000, 0x40000000, 0x00000000,
+ 0x90000003, 0x00000000, 0x40000000, 0x00000000,
+ 0x90000004, 0x00000000, 0x40000000, 0x00000000,
+ 0x90000005, 0x00000000, 0x40000000, 0x00000000,
+ 0x90000008, 0x00000000, 0x40000000, 0x00000000,
+ 0x9000000a, 0x00000000, 0x40000000, 0x00000000,
+ 0x9000000b, 0x00000000, 0x40000000, 0x00000000,
+ 0xA0000000, 0x00000000,
+ 0x0EF, 0x00000004,
+ 0x037, 0x00000000,
+ 0x038, 0x0000514E,
+ 0x037, 0x00004000,
+ 0x038, 0x0000514E,
+ 0x037, 0x00008000,
+ 0x038, 0x0000514E,
+ 0x037, 0x00010000,
+ 0x038, 0x0000514E,
+ 0x037, 0x00014000,
+ 0x038, 0x0000514E,
+ 0x037, 0x00018000,
+ 0x038, 0x0000514E,
+ 0x037, 0x0001C000,
+ 0x038, 0x0000514E,
+ 0x037, 0x00020000,
+ 0x038, 0x0000514E,
+ 0x037, 0x00024000,
+ 0x038, 0x0000514E,
+ 0x037, 0x00028000,
+ 0x038, 0x0000514E,
+ 0x037, 0x0002C000,
+ 0x038, 0x0000714E,
+ 0x037, 0x00030000,
+ 0x038, 0x0000514E,
+ 0x037, 0x00034000,
+ 0x038, 0x0000514E,
+ 0x037, 0x00038000,
+ 0x038, 0x0000514E,
+ 0x037, 0x0003C000,
+ 0x038, 0x0000514E,
+ 0x037, 0x00040000,
+ 0x038, 0x0000514E,
+ 0x037, 0x00044000,
+ 0x038, 0x0000514E,
+ 0x037, 0x00048000,
+ 0x038, 0x0000514E,
+ 0x037, 0x00080000,
+ 0x038, 0x00005ECE,
+ 0x037, 0x00084000,
+ 0x038, 0x00005ECE,
+ 0x037, 0x00088000,
+ 0x038, 0x00005ECE,
+ 0x037, 0x00090000,
+ 0x038, 0x00005ECE,
+ 0x037, 0x00094000,
+ 0x038, 0x00005ECE,
+ 0x037, 0x00098000,
+ 0x038, 0x00005ECE,
+ 0x037, 0x0009C000,
+ 0x038, 0x00005ECE,
+ 0x037, 0x000A0000,
+ 0x038, 0x00005ECE,
+ 0x037, 0x000A4000,
+ 0x038, 0x00005ECE,
+ 0x037, 0x000A8000,
+ 0x038, 0x00005ECE,
+ 0x037, 0x000AC000,
+ 0x038, 0x00005ECE,
+ 0x037, 0x000B0000,
+ 0x038, 0x00005ECE,
+ 0x037, 0x000B4000,
+ 0x038, 0x00005ECE,
+ 0x037, 0x000B8000,
+ 0x038, 0x00005ECE,
+ 0x037, 0x000BC000,
+ 0x038, 0x00005ECE,
+ 0x037, 0x000C0000,
+ 0x038, 0x00005ECE,
+ 0x037, 0x000C4000,
+ 0x038, 0x00005ECE,
+ 0x037, 0x000C8000,
+ 0x038, 0x00005ECE,
+ 0x0EF, 0x00000000,
+ 0xB0000000, 0x00000000,
+ 0x0EF, 0x00000008,
+ 0x80000001, 0x00000000, 0x40000000, 0x00000000,
+ 0x90000002, 0x00000000, 0x40000000, 0x00000000,
+ 0x90000003, 0x00000000, 0x40000000, 0x00000000,
+ 0x90000004, 0x00000000, 0x40000000, 0x00000000,
+ 0x90000005, 0x00000000, 0x40000000, 0x00000000,
+ 0x90000008, 0x00000000, 0x40000000, 0x00000000,
+ 0x9000000a, 0x00000000, 0x40000000, 0x00000000,
+ 0x9000000b, 0x00000000, 0x40000000, 0x00000000,
+ 0xA0000000, 0x00000000,
+ 0x03C, 0x0000007D,
+ 0x03C, 0x0000047D,
+ 0x03C, 0x0000087D,
+ 0x03C, 0x0000107D,
+ 0x03C, 0x0000147D,
+ 0x03C, 0x0000187D,
+ 0xB0000000, 0x00000000,
+ 0x80000001, 0x00000000, 0x40000000, 0x00000000,
+ 0x03C, 0x0000027E,
+ 0x03C, 0x00000546,
+ 0x03C, 0x00000821,
+ 0x03C, 0x0000127E,
+ 0x03C, 0x00001546,
+ 0x03C, 0x00001821,
+ 0x03C, 0x0000227E,
+ 0x03C, 0x00002546,
+ 0x03C, 0x00002821,
+ 0x90000002, 0x00000000, 0x40000000, 0x00000000,
+ 0x03C, 0x0000027E,
+ 0x03C, 0x00000546,
+ 0x03C, 0x00000821,
+ 0x03C, 0x0000127E,
+ 0x03C, 0x00001546,
+ 0x03C, 0x00001821,
+ 0x03C, 0x0000227E,
+ 0x03C, 0x00002546,
+ 0x03C, 0x00002821,
+ 0x90000003, 0x00000000, 0x40000000, 0x00000000,
+ 0x03C, 0x0000027E,
+ 0x03C, 0x00000546,
+ 0x03C, 0x00000821,
+ 0x03C, 0x0000127E,
+ 0x03C, 0x00001546,
+ 0x03C, 0x00001821,
+ 0x03C, 0x0000227E,
+ 0x03C, 0x00002546,
+ 0x03C, 0x00002821,
+ 0x90000004, 0x00000000, 0x40000000, 0x00000000,
+ 0x03C, 0x0000027E,
+ 0x03C, 0x00000546,
+ 0x03C, 0x00000821,
+ 0x03C, 0x0000127E,
+ 0x03C, 0x00001546,
+ 0x03C, 0x00001821,
+ 0x03C, 0x0000227E,
+ 0x03C, 0x00002546,
+ 0x03C, 0x00002821,
+ 0x90000005, 0x00000000, 0x40000000, 0x00000000,
+ 0x03C, 0x0000027E,
+ 0x03C, 0x00000546,
+ 0x03C, 0x00000821,
+ 0x03C, 0x0000127E,
+ 0x03C, 0x00001546,
+ 0x03C, 0x00001821,
+ 0x03C, 0x0000227E,
+ 0x03C, 0x00002546,
+ 0x03C, 0x00002821,
+ 0x90000008, 0x00000000, 0x40000000, 0x00000000,
+ 0x03C, 0x0000027E,
+ 0x03C, 0x00000546,
+ 0x03C, 0x00000821,
+ 0x03C, 0x0000127E,
+ 0x03C, 0x00001546,
+ 0x03C, 0x00001821,
+ 0x03C, 0x0000227E,
+ 0x03C, 0x00002546,
+ 0x03C, 0x00002821,
+ 0x9000000a, 0x00000000, 0x40000000, 0x00000000,
+ 0x03C, 0x0000027E,
+ 0x03C, 0x00000546,
+ 0x03C, 0x00000821,
+ 0x03C, 0x0000127E,
+ 0x03C, 0x00001546,
+ 0x03C, 0x00001821,
+ 0x03C, 0x0000227E,
+ 0x03C, 0x00002546,
+ 0x03C, 0x00002821,
+ 0x9000000b, 0x00000000, 0x40000000, 0x00000000,
+ 0x03C, 0x0000027E,
+ 0x03C, 0x00000546,
+ 0x03C, 0x00000821,
+ 0x03C, 0x0000127E,
+ 0x03C, 0x00001546,
+ 0x03C, 0x00001821,
+ 0x03C, 0x0000227E,
+ 0x03C, 0x00002546,
+ 0x03C, 0x00002821,
+ 0xA0000000, 0x00000000,
+ 0x03C, 0x0000037E,
+ 0x03C, 0x00000575,
+ 0x03C, 0x00000971,
+ 0x03C, 0x0000127E,
+ 0x03C, 0x00001575,
+ 0x03C, 0x00001871,
+ 0x03C, 0x0000217E,
+ 0x03C, 0x00002575,
+ 0x03C, 0x00002871,
+ 0xB0000000, 0x00000000,
+ 0x0EF, 0x00000000,
+ 0x061, 0x000C0D47,
+ 0x062, 0x0000133C,
+ 0x80000001, 0x00000000, 0x40000000, 0x00000000,
+ 0x063, 0x000750E7,
+ 0x90000002, 0x00000000, 0x40000000, 0x00000000,
+ 0x063, 0x000750E7,
+ 0x90000003, 0x00000000, 0x40000000, 0x00000000,
+ 0x063, 0x000750E7,
+ 0x90000004, 0x00000000, 0x40000000, 0x00000000,
+ 0x063, 0x000750E7,
+ 0x90000005, 0x00000000, 0x40000000, 0x00000000,
+ 0x063, 0x000750E7,
+ 0x90000008, 0x00000000, 0x40000000, 0x00000000,
+ 0x063, 0x000750E7,
+ 0x9000000a, 0x00000000, 0x40000000, 0x00000000,
+ 0x063, 0x000750E7,
+ 0x9000000b, 0x00000000, 0x40000000, 0x00000000,
+ 0x063, 0x000750E7,
+ 0xA0000000, 0x00000000,
+ 0x063, 0x0007D0E7,
+ 0xB0000000, 0x00000000,
+ 0x064, 0x00014FEC,
+ 0x80000001, 0x00000000, 0x40000000, 0x00000000,
+ 0x065, 0x000920D0,
+ 0x90000002, 0x00000000, 0x40000000, 0x00000000,
+ 0x065, 0x000920D0,
+ 0x90000003, 0x00000000, 0x40000000, 0x00000000,
+ 0x065, 0x000920D0,
+ 0x90000004, 0x00000000, 0x40000000, 0x00000000,
+ 0x065, 0x000920D0,
+ 0x90000005, 0x00000000, 0x40000000, 0x00000000,
+ 0x065, 0x000920D0,
+ 0x90000008, 0x00000000, 0x40000000, 0x00000000,
+ 0x065, 0x000920D0,
+ 0x9000000a, 0x00000000, 0x40000000, 0x00000000,
+ 0x065, 0x000920D0,
+ 0x9000000b, 0x00000000, 0x40000000, 0x00000000,
+ 0x065, 0x000920D0,
+ 0xA0000000, 0x00000000,
+ 0x065, 0x000923FF,
+ 0xB0000000, 0x00000000,
+ 0x066, 0x00000040,
+ 0x057, 0x00050000,
+ 0x056, 0x00051DF0,
+ 0x80000001, 0x00000000, 0x40000000, 0x00000000,
+ 0x90000002, 0x00000000, 0x40000000, 0x00000000,
+ 0x90000003, 0x00000000, 0x40000000, 0x00000000,
+ 0x90000004, 0x00000000, 0x40000000, 0x00000000,
+ 0x90000005, 0x00000000, 0x40000000, 0x00000000,
+ 0x90000008, 0x00000000, 0x40000000, 0x00000000,
+ 0x9000000a, 0x00000000, 0x40000000, 0x00000000,
+ 0x9000000b, 0x00000000, 0x40000000, 0x00000000,
+ 0xA0000000, 0x00000000,
+ 0x055, 0x00082060,
+ 0xB0000000, 0x00000000,
+};
+
+RTW_DECL_TABLE_RF_RADIO(rtw8814a_rf_b, B);
+
+static const u32 rtw8814a_rf_c[] = {
+ 0x018, 0x00013124,
+ 0x040, 0x00000C00,
+ 0x058, 0x00000F98,
+ 0x07F, 0x00068004,
+ 0x018, 0x00000006,
+ 0x80000002, 0x00000000, 0x40000000, 0x00000000,
+ 0x086, 0x000E335A,
+ 0x087, 0x00079F80,
+ 0x90000003, 0x00000000, 0x40000000, 0x00000000,
+ 0x086, 0x000E335A,
+ 0x087, 0x00079F80,
+ 0x90000004, 0x00000000, 0x40000000, 0x00000000,
+ 0x086, 0x000E335A,
+ 0x087, 0x00079F80,
+ 0x90000005, 0x00000000, 0x40000000, 0x00000000,
+ 0x086, 0x000E335A,
+ 0x087, 0x00079F80,
+ 0x90000008, 0x00000000, 0x40000000, 0x00000000,
+ 0x086, 0x000E335A,
+ 0x087, 0x00079F80,
+ 0x9000000a, 0x00000000, 0x40000000, 0x00000000,
+ 0x086, 0x000E335A,
+ 0x087, 0x00079F80,
+ 0x9000000b, 0x00000000, 0x40000000, 0x00000000,
+ 0x086, 0x000E335A,
+ 0x087, 0x00079F80,
+ 0xA0000000, 0x00000000,
+ 0x086, 0x000E4B58,
+ 0x087, 0x00049F80,
+ 0xB0000000, 0x00000000,
+ 0x0DF, 0x00000008,
+ 0x0EF, 0x00002000,
+ 0x80000002, 0x00000000, 0x40000000, 0x00000000,
+ 0x03B, 0x0003F19B,
+ 0x03B, 0x00037A5B,
+ 0x03B, 0x0002A433,
+ 0x03B, 0x00027BD3,
+ 0x03B, 0x0001F80B,
+ 0x03B, 0x00017823,
+ 0x90000003, 0x00000000, 0x40000000, 0x00000000,
+ 0x03B, 0x0003F19B,
+ 0x03B, 0x00037A5B,
+ 0x03B, 0x0002A433,
+ 0x03B, 0x00027BD3,
+ 0x03B, 0x0001F80B,
+ 0x03B, 0x00017823,
+ 0x90000004, 0x00000000, 0x40000000, 0x00000000,
+ 0x03B, 0x0003F19B,
+ 0x03B, 0x00037A5B,
+ 0x03B, 0x0002A433,
+ 0x03B, 0x00027BD3,
+ 0x03B, 0x0001F80B,
+ 0x03B, 0x00017823,
+ 0x90000005, 0x00000000, 0x40000000, 0x00000000,
+ 0x03B, 0x0003F19B,
+ 0x03B, 0x00037A5B,
+ 0x03B, 0x0002A433,
+ 0x03B, 0x00027BD3,
+ 0x03B, 0x0001F80B,
+ 0x03B, 0x00017823,
+ 0x90000008, 0x00000000, 0x40000000, 0x00000000,
+ 0x03B, 0x0003F19B,
+ 0x03B, 0x00037A5B,
+ 0x03B, 0x0002A433,
+ 0x03B, 0x00027BD3,
+ 0x03B, 0x0001F80B,
+ 0x03B, 0x00017823,
+ 0x9000000a, 0x00000000, 0x40000000, 0x00000000,
+ 0x03B, 0x0003F19B,
+ 0x03B, 0x00037A5B,
+ 0x03B, 0x0002A433,
+ 0x03B, 0x00027BD3,
+ 0x03B, 0x0001F80B,
+ 0x03B, 0x00017823,
+ 0x9000000b, 0x00000000, 0x40000000, 0x00000000,
+ 0x03B, 0x0003F19B,
+ 0x03B, 0x00037A5B,
+ 0x03B, 0x0002A433,
+ 0x03B, 0x00027BD3,
+ 0x03B, 0x0001F80B,
+ 0x03B, 0x00017823,
+ 0xA0000000, 0x00000000,
+ 0x03B, 0x0003F258,
+ 0x03B, 0x00030A58,
+ 0x03B, 0x0002FA58,
+ 0x03B, 0x00022590,
+ 0x03B, 0x0001FA50,
+ 0x03B, 0x00010248,
+ 0x03B, 0x00008240,
+ 0xB0000000, 0x00000000,
+ 0x0EF, 0x00000100,
+ 0x80000002, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0000A0D0,
+ 0x034, 0x000090CD,
+ 0x034, 0x000080CA,
+ 0x034, 0x0000704D,
+ 0x034, 0x0000604A,
+ 0x034, 0x00005047,
+ 0x034, 0x0000400A,
+ 0x034, 0x00003007,
+ 0x034, 0x00002004,
+ 0x034, 0x00001001,
+ 0x034, 0x00000001,
+ 0x90000003, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0000A0D0,
+ 0x034, 0x000090CD,
+ 0x034, 0x000080CA,
+ 0x034, 0x0000704D,
+ 0x034, 0x0000604A,
+ 0x034, 0x00005047,
+ 0x034, 0x0000400A,
+ 0x034, 0x00003007,
+ 0x034, 0x00002004,
+ 0x034, 0x00001001,
+ 0x034, 0x00000001,
+ 0x90000004, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0000A0D0,
+ 0x034, 0x000090CD,
+ 0x034, 0x000080CA,
+ 0x034, 0x0000704D,
+ 0x034, 0x0000604A,
+ 0x034, 0x00005047,
+ 0x034, 0x0000400A,
+ 0x034, 0x00003007,
+ 0x034, 0x00002004,
+ 0x034, 0x00001001,
+ 0x034, 0x00000001,
+ 0x90000005, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0000A0D0,
+ 0x034, 0x000090CD,
+ 0x034, 0x000080CA,
+ 0x034, 0x0000704D,
+ 0x034, 0x0000604A,
+ 0x034, 0x00005047,
+ 0x034, 0x0000400A,
+ 0x034, 0x00003007,
+ 0x034, 0x00002004,
+ 0x034, 0x00001001,
+ 0x034, 0x00000001,
+ 0x90000008, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0000A0D0,
+ 0x034, 0x000090CD,
+ 0x034, 0x000080CA,
+ 0x034, 0x0000704D,
+ 0x034, 0x0000604A,
+ 0x034, 0x00005047,
+ 0x034, 0x0000400A,
+ 0x034, 0x00003007,
+ 0x034, 0x00002004,
+ 0x034, 0x00001001,
+ 0x034, 0x00000001,
+ 0x9000000a, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0000A0D0,
+ 0x034, 0x000090CD,
+ 0x034, 0x000080CA,
+ 0x034, 0x0000704D,
+ 0x034, 0x0000604A,
+ 0x034, 0x00005047,
+ 0x034, 0x0000400A,
+ 0x034, 0x00003007,
+ 0x034, 0x00002004,
+ 0x034, 0x00001001,
+ 0x034, 0x00000001,
+ 0x9000000b, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0000A0D0,
+ 0x034, 0x000090CD,
+ 0x034, 0x000080CA,
+ 0x034, 0x0000704D,
+ 0x034, 0x0000604A,
+ 0x034, 0x00005047,
+ 0x034, 0x0000400A,
+ 0x034, 0x00003007,
+ 0x034, 0x00002004,
+ 0x034, 0x00001001,
+ 0x034, 0x00000001,
+ 0xA0000000, 0x00000000,
+ 0x034, 0x0000ADF6,
+ 0x034, 0x00009DF3,
+ 0x034, 0x00008DF0,
+ 0x034, 0x00007DED,
+ 0x034, 0x00006DEA,
+ 0x034, 0x00005CED,
+ 0x034, 0x00004CEA,
+ 0x034, 0x000034EA,
+ 0x034, 0x000024E7,
+ 0x034, 0x0000146A,
+ 0x034, 0x0000006B,
+ 0xB0000000, 0x00000000,
+ 0x80000002, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0000A0D0,
+ 0x034, 0x000090CD,
+ 0x034, 0x000080CA,
+ 0x034, 0x0000704D,
+ 0x034, 0x0000604A,
+ 0x034, 0x00005047,
+ 0x034, 0x0000400A,
+ 0x034, 0x00003007,
+ 0x034, 0x00002004,
+ 0x034, 0x00001001,
+ 0x034, 0x00000001,
+ 0x90000003, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0000A0D0,
+ 0x034, 0x000090CD,
+ 0x034, 0x000080CA,
+ 0x034, 0x0000704D,
+ 0x034, 0x0000604A,
+ 0x034, 0x00005047,
+ 0x034, 0x0000400A,
+ 0x034, 0x00003007,
+ 0x034, 0x00002004,
+ 0x034, 0x00001001,
+ 0x034, 0x00000001,
+ 0x90000004, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0000A0D0,
+ 0x034, 0x000090CD,
+ 0x034, 0x000080CA,
+ 0x034, 0x0000704D,
+ 0x034, 0x0000604A,
+ 0x034, 0x00005047,
+ 0x034, 0x0000400A,
+ 0x034, 0x00003007,
+ 0x034, 0x00002004,
+ 0x034, 0x00001001,
+ 0x034, 0x00000001,
+ 0x90000005, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0000A0D0,
+ 0x034, 0x000090CD,
+ 0x034, 0x000080CA,
+ 0x034, 0x0000704D,
+ 0x034, 0x0000604A,
+ 0x034, 0x00005047,
+ 0x034, 0x0000400A,
+ 0x034, 0x00003007,
+ 0x034, 0x00002004,
+ 0x034, 0x00001001,
+ 0x034, 0x00000001,
+ 0x90000008, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0000A0D0,
+ 0x034, 0x000090CD,
+ 0x034, 0x000080CA,
+ 0x034, 0x0000704D,
+ 0x034, 0x0000604A,
+ 0x034, 0x00005047,
+ 0x034, 0x0000400A,
+ 0x034, 0x00003007,
+ 0x034, 0x00002004,
+ 0x034, 0x00001001,
+ 0x034, 0x00000001,
+ 0x9000000a, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0000A0D0,
+ 0x034, 0x000090CD,
+ 0x034, 0x000080CA,
+ 0x034, 0x0000704D,
+ 0x034, 0x0000604A,
+ 0x034, 0x00005047,
+ 0x034, 0x0000400A,
+ 0x034, 0x00003007,
+ 0x034, 0x00002004,
+ 0x034, 0x00001001,
+ 0x034, 0x00000001,
+ 0x9000000b, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0000A0D0,
+ 0x034, 0x000090CD,
+ 0x034, 0x000080CA,
+ 0x034, 0x0000704D,
+ 0x034, 0x0000604A,
+ 0x034, 0x00005047,
+ 0x034, 0x0000400A,
+ 0x034, 0x00003007,
+ 0x034, 0x00002004,
+ 0x034, 0x00001001,
+ 0x034, 0x00000001,
+ 0xA0000000, 0x00000000,
+ 0x034, 0x0008ADF6,
+ 0x034, 0x00089DF3,
+ 0x034, 0x00088DF0,
+ 0x034, 0x00087DED,
+ 0x034, 0x00086DEA,
+ 0x034, 0x00085CED,
+ 0x034, 0x00084CEA,
+ 0x034, 0x000834EA,
+ 0x034, 0x000824E7,
+ 0x034, 0x0008146A,
+ 0x034, 0x0008006B,
+ 0xB0000000, 0x00000000,
+ 0x0EF, 0x00000000,
+ 0x0EF, 0x000020A2,
+ 0x0DF, 0x00000080,
+ 0x035, 0x00000192,
+ 0x035, 0x00008192,
+ 0x035, 0x00010192,
+ 0x036, 0x00000024,
+ 0x036, 0x00008024,
+ 0x036, 0x00010024,
+ 0x036, 0x00018024,
+ 0x0EF, 0x00000000,
+ 0x051, 0x00000C21,
+ 0x052, 0x000006D9,
+ 0x053, 0x000FC649,
+ 0x054, 0x0000017E,
+ 0x018, 0x0001012A,
+ 0x081, 0x0007FC00,
+ 0x089, 0x00050110,
+ 0x08A, 0x00043E50,
+ 0x08B, 0x0002E180,
+ 0x08C, 0x00093C3C,
+ 0x80000001, 0x00000000, 0x40000000, 0x00000000,
+ 0x085, 0x000F8000,
+ 0x90000006, 0x00000000, 0x40000000, 0x00000000,
+ 0x085, 0x000F8000,
+ 0xA0000000, 0x00000000,
+ 0x085, 0x000F8000,
+ 0xB0000000, 0x00000000,
+ 0x80000001, 0x00000000, 0x40000000, 0x00000000,
+ 0x08D, 0x000FFFF0,
+ 0x90000002, 0x00000000, 0x40000000, 0x00000000,
+ 0x08D, 0x000FFFF0,
+ 0x90000003, 0x00000000, 0x40000000, 0x00000000,
+ 0x08D, 0x000FFFF0,
+ 0x90000004, 0x00000000, 0x40000000, 0x00000000,
+ 0x08D, 0x000FFFF0,
+ 0x90000005, 0x00000000, 0x40000000, 0x00000000,
+ 0x08D, 0x000FFFF0,
+ 0x90000006, 0x00000000, 0x40000000, 0x00000000,
+ 0x08D, 0x000FFFF0,
+ 0x90000008, 0x00000000, 0x40000000, 0x00000000,
+ 0x08D, 0x000FFFF0,
+ 0x9000000a, 0x00000000, 0x40000000, 0x00000000,
+ 0x08D, 0x000FFFF0,
+ 0x9000000b, 0x00000000, 0x40000000, 0x00000000,
+ 0x08D, 0x000FFFF0,
+ 0xA0000000, 0x00000000,
+ 0x08D, 0x000FFFF0,
+ 0xB0000000, 0x00000000,
+ 0x0EF, 0x00001000,
+ 0x03A, 0x0000013C,
+ 0x03B, 0x00038023,
+ 0x80000001, 0x00000000, 0x40000000, 0x00000000,
+ 0x03C, 0x0006C000,
+ 0x90000002, 0x00000000, 0x40000000, 0x00000000,
+ 0x03C, 0x000D4000,
+ 0x90000003, 0x00000000, 0x40000000, 0x00000000,
+ 0x03C, 0x00080000,
+ 0x90000004, 0x00000000, 0x40000000, 0x00000000,
+ 0x03C, 0x00088000,
+ 0x90000005, 0x00000000, 0x40000000, 0x00000000,
+ 0x03C, 0x00000000,
+ 0x90000006, 0x00000000, 0x40000000, 0x00000000,
+ 0x03C, 0x0006C000,
+ 0x90000007, 0x00000000, 0x40000000, 0x00000000,
+ 0x03C, 0x00000000,
+ 0x90000008, 0x00000000, 0x40000000, 0x00000000,
+ 0x03C, 0x0008C000,
+ 0x90000009, 0x00000000, 0x40000000, 0x00000000,
+ 0x03C, 0x00004000,
+ 0x9000000a, 0x00000000, 0x40000000, 0x00000000,
+ 0x03C, 0x00088000,
+ 0x9000000b, 0x00000000, 0x40000000, 0x00000000,
+ 0x03C, 0x00088000,
+ 0xA0000000, 0x00000000,
+ 0x03C, 0x000A0000,
+ 0xB0000000, 0x00000000,
+ 0x03A, 0x0000013C,
+ 0x03B, 0x00030023,
+ 0x03C, 0x00048000,
+ 0x03A, 0x0000013C,
+ 0x03B, 0x00028623,
+ 0x03C, 0x00000000,
+ 0x03A, 0x0000013C,
+ 0x03B, 0x00021633,
+ 0x03C, 0x00000000,
+ 0x03A, 0x0000013C,
+ 0x03B, 0x0001C633,
+ 0x03C, 0x00000000,
+ 0x03A, 0x0000013C,
+ 0x03B, 0x00010293,
+ 0x03C, 0x00000000,
+ 0x03A, 0x0000013C,
+ 0x03B, 0x00009593,
+ 0x03C, 0x00000000,
+ 0x03A, 0x00000148,
+ 0x80000008, 0x00000000, 0x40000000, 0x00000000,
+ 0x03B, 0x0000118B,
+ 0xA0000000, 0x00000000,
+ 0x03B, 0x0000078B,
+ 0xB0000000, 0x00000000,
+ 0x03C, 0x00000000,
+ 0x80000001, 0x00000000, 0x40000000, 0x00000000,
+ 0x03A, 0x0000013C,
+ 0x03B, 0x00078023,
+ 0x90000002, 0x00000000, 0x40000000, 0x00000000,
+ 0x03A, 0x0000013C,
+ 0x03B, 0x00078023,
+ 0x90000003, 0x00000000, 0x40000000, 0x00000000,
+ 0x03A, 0x0000013C,
+ 0x03B, 0x00078023,
+ 0x90000004, 0x00000000, 0x40000000, 0x00000000,
+ 0x03A, 0x0000013C,
+ 0x03B, 0x00078023,
+ 0x90000005, 0x00000000, 0x40000000, 0x00000000,
+ 0x03A, 0x0000013C,
+ 0x03B, 0x00078023,
+ 0x90000006, 0x00000000, 0x40000000, 0x00000000,
+ 0x03A, 0x0000013C,
+ 0x03B, 0x00078023,
+ 0x90000008, 0x00000000, 0x40000000, 0x00000000,
+ 0x03A, 0x0000013C,
+ 0x03B, 0x00078023,
+ 0x9000000a, 0x00000000, 0x40000000, 0x00000000,
+ 0x03A, 0x0000013C,
+ 0x03B, 0x00078023,
+ 0x9000000b, 0x00000000, 0x40000000, 0x00000000,
+ 0x03A, 0x0000013C,
+ 0x03B, 0x00078023,
+ 0xA0000000, 0x00000000,
+ 0x03A, 0x0000013C,
+ 0x03B, 0x00078023,
+ 0xB0000000, 0x00000000,
+ 0x80000001, 0x00000000, 0x40000000, 0x00000000,
+ 0x03C, 0x0004C000,
+ 0x90000002, 0x00000000, 0x40000000, 0x00000000,
+ 0x03C, 0x00084000,
+ 0x90000003, 0x00000000, 0x40000000, 0x00000000,
+ 0x03C, 0x00000000,
+ 0x90000004, 0x00000000, 0x40000000, 0x00000000,
+ 0x03C, 0x00080000,
+ 0x90000005, 0x00000000, 0x40000000, 0x00000000,
+ 0x03C, 0x00000000,
+ 0x90000006, 0x00000000, 0x40000000, 0x00000000,
+ 0x03C, 0x0004C000,
+ 0x90000007, 0x00000000, 0x40000000, 0x00000000,
+ 0x03C, 0x00000000,
+ 0x90000008, 0x00000000, 0x40000000, 0x00000000,
+ 0x03C, 0x000D0000,
+ 0x90000009, 0x00000000, 0x40000000, 0x00000000,
+ 0x03C, 0x00000000,
+ 0x9000000a, 0x00000000, 0x40000000, 0x00000000,
+ 0x03C, 0x00080000,
+ 0x9000000b, 0x00000000, 0x40000000, 0x00000000,
+ 0x03C, 0x00080000,
+ 0xA0000000, 0x00000000,
+ 0x03C, 0x00028000,
+ 0xB0000000, 0x00000000,
+ 0x03A, 0x0000013C,
+ 0x03B, 0x00070023,
+ 0x03C, 0x00048000,
+ 0x03A, 0x0000013C,
+ 0x03B, 0x00068623,
+ 0x03C, 0x00000000,
+ 0x03A, 0x0000013C,
+ 0x03B, 0x00061633,
+ 0x03C, 0x00000000,
+ 0x03A, 0x0000013C,
+ 0x03B, 0x0005C633,
+ 0x03C, 0x00000000,
+ 0x03A, 0x0000013C,
+ 0x03B, 0x00050293,
+ 0x03C, 0x00000000,
+ 0x03A, 0x0000013C,
+ 0x03B, 0x00049593,
+ 0x03C, 0x00000000,
+ 0x03A, 0x00000148,
+ 0x03B, 0x0004078B,
+ 0x03C, 0x00000000,
+ 0x80000001, 0x00000000, 0x40000000, 0x00000000,
+ 0x03A, 0x0000013C,
+ 0x03B, 0x000B8023,
+ 0x90000002, 0x00000000, 0x40000000, 0x00000000,
+ 0x03A, 0x0000013C,
+ 0x03B, 0x000B8023,
+ 0x90000003, 0x00000000, 0x40000000, 0x00000000,
+ 0x03A, 0x0000013C,
+ 0x03B, 0x000B8023,
+ 0x90000004, 0x00000000, 0x40000000, 0x00000000,
+ 0x03A, 0x0000013C,
+ 0x03B, 0x000B8023,
+ 0x90000005, 0x00000000, 0x40000000, 0x00000000,
+ 0x03A, 0x0000013C,
+ 0x03B, 0x000B8023,
+ 0x90000006, 0x00000000, 0x40000000, 0x00000000,
+ 0x03A, 0x0000013C,
+ 0x03B, 0x000B8023,
+ 0x90000008, 0x00000000, 0x40000000, 0x00000000,
+ 0x03A, 0x0000013C,
+ 0x03B, 0x000B8023,
+ 0x9000000a, 0x00000000, 0x40000000, 0x00000000,
+ 0x03A, 0x0000013C,
+ 0x03B, 0x000B8023,
+ 0x9000000b, 0x00000000, 0x40000000, 0x00000000,
+ 0x03A, 0x0000013C,
+ 0x03B, 0x000B8023,
+ 0xA0000000, 0x00000000,
+ 0x03A, 0x0000013C,
+ 0x03B, 0x000B8023,
+ 0xB0000000, 0x00000000,
+ 0x80000001, 0x00000000, 0x40000000, 0x00000000,
+ 0x03C, 0x00024000,
+ 0x90000002, 0x00000000, 0x40000000, 0x00000000,
+ 0x03C, 0x00060000,
+ 0x90000003, 0x00000000, 0x40000000, 0x00000000,
+ 0x03C, 0x00080000,
+ 0x90000004, 0x00000000, 0x40000000, 0x00000000,
+ 0x03C, 0x00024000,
+ 0x90000005, 0x00000000, 0x40000000, 0x00000000,
+ 0x03C, 0x00000000,
+ 0x90000006, 0x00000000, 0x40000000, 0x00000000,
+ 0x03C, 0x00024000,
+ 0x90000008, 0x00000000, 0x40000000, 0x00000000,
+ 0x03C, 0x00020000,
+ 0x90000009, 0x00000000, 0x40000000, 0x00000000,
+ 0x03C, 0x00000000,
+ 0x9000000a, 0x00000000, 0x40000000, 0x00000000,
+ 0x03C, 0x00024000,
+ 0x9000000b, 0x00000000, 0x40000000, 0x00000000,
+ 0x03C, 0x00024000,
+ 0xA0000000, 0x00000000,
+ 0x03C, 0x00020000,
+ 0xB0000000, 0x00000000,
+ 0x03A, 0x0000013C,
+ 0x03B, 0x000B0023,
+ 0x80000004, 0x00000000, 0x40000000, 0x00000000,
+ 0x03C, 0x00020000,
+ 0x9000000a, 0x00000000, 0x40000000, 0x00000000,
+ 0x03C, 0x00020000,
+ 0x9000000b, 0x00000000, 0x40000000, 0x00000000,
+ 0x03C, 0x00020000,
+ 0xA0000000, 0x00000000,
+ 0x03C, 0x00020000,
+ 0xB0000000, 0x00000000,
+ 0x03A, 0x0000013C,
+ 0x03B, 0x000A8623,
+ 0x03C, 0x00000000,
+ 0x03A, 0x0000013C,
+ 0x03B, 0x000A1633,
+ 0x03C, 0x00000000,
+ 0x03A, 0x0000013C,
+ 0x03B, 0x0009C633,
+ 0x03C, 0x00000000,
+ 0x03A, 0x0000013C,
+ 0x03B, 0x00090293,
+ 0x03C, 0x00000000,
+ 0x03A, 0x0000013C,
+ 0x03B, 0x00089593,
+ 0x03C, 0x00000000,
+ 0x03A, 0x00000148,
+ 0x80000008, 0x00000000, 0x40000000, 0x00000000,
+ 0x03B, 0x0008128B,
+ 0xA0000000, 0x00000000,
+ 0x03B, 0x0008078B,
+ 0xB0000000, 0x00000000,
+ 0x03C, 0x00000000,
+ 0x0EF, 0x00000000,
+ 0x0EF, 0x00000800,
+ 0x03B, 0x00000000,
+ 0x80000001, 0x00000000, 0x40000000, 0x00000000,
+ 0x03A, 0x00000803,
+ 0x90000002, 0x00000000, 0x40000000, 0x00000000,
+ 0x03A, 0x00000000,
+ 0x90000003, 0x00000000, 0x40000000, 0x00000000,
+ 0x03A, 0x00001803,
+ 0x90000005, 0x00000000, 0x40000000, 0x00000000,
+ 0x03A, 0x00001803,
+ 0x90000006, 0x00000000, 0x40000000, 0x00000000,
+ 0x03A, 0x00000803,
+ 0x90000007, 0x00000000, 0x40000000, 0x00000000,
+ 0x03A, 0x00001003,
+ 0x90000008, 0x00000000, 0x40000000, 0x00000000,
+ 0x03A, 0x00001003,
+ 0x90000009, 0x00000000, 0x40000000, 0x00000000,
+ 0x03A, 0x00001003,
+ 0xA0000000, 0x00000000,
+ 0x03A, 0x00000803,
+ 0xB0000000, 0x00000000,
+ 0x03B, 0x00040000,
+ 0x80000001, 0x00000000, 0x40000000, 0x00000000,
+ 0x03A, 0x00001000,
+ 0x90000002, 0x00000000, 0x40000000, 0x00000000,
+ 0x03A, 0x00000800,
+ 0x90000003, 0x00000000, 0x40000000, 0x00000000,
+ 0x03A, 0x00000803,
+ 0x90000005, 0x00000000, 0x40000000, 0x00000000,
+ 0x03A, 0x00000803,
+ 0x90000006, 0x00000000, 0x40000000, 0x00000000,
+ 0x03A, 0x00001000,
+ 0x90000007, 0x00000000, 0x40000000, 0x00000000,
+ 0x03A, 0x00000000,
+ 0x90000008, 0x00000000, 0x40000000, 0x00000000,
+ 0x03A, 0x00000000,
+ 0x90000009, 0x00000000, 0x40000000, 0x00000000,
+ 0x03A, 0x00000000,
+ 0xA0000000, 0x00000000,
+ 0x03A, 0x00001000,
+ 0xB0000000, 0x00000000,
+ 0x03B, 0x00080000,
+ 0x80000003, 0x00000000, 0x40000000, 0x00000000,
+ 0x03A, 0x00000000,
+ 0x90000005, 0x00000000, 0x40000000, 0x00000000,
+ 0x03A, 0x00000000,
+ 0x90000007, 0x00000000, 0x40000000, 0x00000000,
+ 0x03A, 0x00001802,
+ 0x90000009, 0x00000000, 0x40000000, 0x00000000,
+ 0x03A, 0x00001802,
+ 0xA0000000, 0x00000000,
+ 0x03A, 0x00001002,
+ 0xB0000000, 0x00000000,
+ 0x0EF, 0x00000000,
+ 0x80000001, 0x00000000, 0x40000000, 0x00000000,
+ 0x90000002, 0x00000000, 0x40000000, 0x00000000,
+ 0x90000003, 0x00000000, 0x40000000, 0x00000000,
+ 0x90000004, 0x00000000, 0x40000000, 0x00000000,
+ 0x90000005, 0x00000000, 0x40000000, 0x00000000,
+ 0x90000006, 0x00000000, 0x40000000, 0x00000000,
+ 0x90000008, 0x00000000, 0x40000000, 0x00000000,
+ 0x9000000a, 0x00000000, 0x40000000, 0x00000000,
+ 0x9000000b, 0x00000000, 0x40000000, 0x00000000,
+ 0xA0000000, 0x00000000,
+ 0xB0000000, 0x00000000,
+ 0x80000002, 0x00000000, 0x40000000, 0x00000000,
+ 0x018, 0x00013124,
+ 0x90000003, 0x00000000, 0x40000000, 0x00000000,
+ 0x018, 0x00013124,
+ 0x90000004, 0x00000000, 0x40000000, 0x00000000,
+ 0x018, 0x00013124,
+ 0x90000005, 0x00000000, 0x40000000, 0x00000000,
+ 0x018, 0x00013124,
+ 0x90000008, 0x00000000, 0x40000000, 0x00000000,
+ 0x018, 0x00013124,
+ 0x9000000a, 0x00000000, 0x40000000, 0x00000000,
+ 0x018, 0x00013124,
+ 0x9000000b, 0x00000000, 0x40000000, 0x00000000,
+ 0x018, 0x00013124,
+ 0xA0000000, 0x00000000,
+ 0x018, 0x00013124,
+ 0xB0000000, 0x00000000,
+ 0x0EF, 0x00000100,
+ 0x80000001, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0004A38C,
+ 0x034, 0x000491AD,
+ 0x034, 0x000481AA,
+ 0x034, 0x000471A7,
+ 0x034, 0x000460AA,
+ 0x034, 0x000450A7,
+ 0x034, 0x0004402C,
+ 0x034, 0x00043029,
+ 0x034, 0x0004200C,
+ 0x034, 0x00041009,
+ 0x034, 0x00040006,
+ 0x90000002, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0004A3EF,
+ 0x034, 0x000493AD,
+ 0x034, 0x0004838A,
+ 0x034, 0x0004718C,
+ 0x034, 0x00046189,
+ 0x034, 0x0004506D,
+ 0x034, 0x0004404C,
+ 0x034, 0x0004302C,
+ 0x034, 0x00042029,
+ 0x034, 0x00041026,
+ 0x034, 0x00040023,
+ 0x90000003, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0004A3EF,
+ 0x034, 0x000493AD,
+ 0x034, 0x0004838A,
+ 0x034, 0x0004718C,
+ 0x034, 0x00046189,
+ 0x034, 0x0004506D,
+ 0x034, 0x0004404C,
+ 0x034, 0x0004302C,
+ 0x034, 0x00042029,
+ 0x034, 0x00041026,
+ 0x034, 0x00040023,
+ 0x90000004, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0004A3EF,
+ 0x034, 0x000493AD,
+ 0x034, 0x0004838A,
+ 0x034, 0x0004718C,
+ 0x034, 0x00046189,
+ 0x034, 0x0004506D,
+ 0x034, 0x0004404C,
+ 0x034, 0x0004302C,
+ 0x034, 0x00042029,
+ 0x034, 0x00041026,
+ 0x034, 0x00040023,
+ 0x90000005, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0004A3EF,
+ 0x034, 0x000493AD,
+ 0x034, 0x0004838A,
+ 0x034, 0x0004718C,
+ 0x034, 0x00046189,
+ 0x034, 0x0004506D,
+ 0x034, 0x0004404C,
+ 0x034, 0x0004302C,
+ 0x034, 0x00042029,
+ 0x034, 0x00041026,
+ 0x034, 0x00040023,
+ 0x90000008, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0004A3F5,
+ 0x034, 0x000493F3,
+ 0x034, 0x00048393,
+ 0x034, 0x00047390,
+ 0x034, 0x0004638D,
+ 0x034, 0x0004538A,
+ 0x034, 0x00044387,
+ 0x034, 0x000430ED,
+ 0x034, 0x000420EA,
+ 0x034, 0x000410E7,
+ 0x034, 0x0004002D,
+ 0x90000009, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0004AFF7,
+ 0x034, 0x00049FF6,
+ 0x034, 0x00048FF3,
+ 0x034, 0x00047FF0,
+ 0x034, 0x00046FED,
+ 0x034, 0x00045FEA,
+ 0x034, 0x00044FE7,
+ 0x034, 0x00043CD0,
+ 0x034, 0x00042CCD,
+ 0x034, 0x00041CCA,
+ 0x034, 0x00040CC7,
+ 0x9000000a, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0004A3EF,
+ 0x034, 0x000493AD,
+ 0x034, 0x0004838A,
+ 0x034, 0x0004718C,
+ 0x034, 0x00046189,
+ 0x034, 0x0004506D,
+ 0x034, 0x0004404C,
+ 0x034, 0x0004302C,
+ 0x034, 0x00042029,
+ 0x034, 0x00041026,
+ 0x034, 0x00040023,
+ 0x9000000b, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0004A3EF,
+ 0x034, 0x000493AD,
+ 0x034, 0x0004838A,
+ 0x034, 0x0004718C,
+ 0x034, 0x00046189,
+ 0x034, 0x0004506D,
+ 0x034, 0x0004404C,
+ 0x034, 0x0004302C,
+ 0x034, 0x00042029,
+ 0x034, 0x00041026,
+ 0x034, 0x00040023,
+ 0xA0000000, 0x00000000,
+ 0x034, 0x0004AFF4,
+ 0x034, 0x00049FF1,
+ 0x034, 0x00048FEE,
+ 0x034, 0x00047FEB,
+ 0x034, 0x00046FE8,
+ 0x034, 0x00045DEA,
+ 0x034, 0x00044CED,
+ 0x034, 0x00043CEA,
+ 0x034, 0x00042C6C,
+ 0x034, 0x00041C69,
+ 0x034, 0x00040C2B,
+ 0xB0000000, 0x00000000,
+ 0x80000001, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0002A3EC,
+ 0x034, 0x0002938C,
+ 0x034, 0x000281AD,
+ 0x034, 0x000271AA,
+ 0x034, 0x000261A7,
+ 0x034, 0x000250AA,
+ 0x034, 0x000240A7,
+ 0x034, 0x0002302C,
+ 0x034, 0x00022029,
+ 0x034, 0x0002100C,
+ 0x034, 0x00020009,
+ 0x90000002, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0002A3EC,
+ 0x034, 0x0002936D,
+ 0x034, 0x0002836A,
+ 0x034, 0x0002716D,
+ 0x034, 0x0002616A,
+ 0x034, 0x0002506D,
+ 0x034, 0x0002406A,
+ 0x034, 0x0002302C,
+ 0x034, 0x00022029,
+ 0x034, 0x00021026,
+ 0x034, 0x00020023,
+ 0x90000003, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0002A3EC,
+ 0x034, 0x000293AC,
+ 0x034, 0x0002838A,
+ 0x034, 0x0002718C,
+ 0x034, 0x00026189,
+ 0x034, 0x0002506D,
+ 0x034, 0x0002406A,
+ 0x034, 0x0002302C,
+ 0x034, 0x00022029,
+ 0x034, 0x00021026,
+ 0x034, 0x00020023,
+ 0x90000004, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0002A3EC,
+ 0x034, 0x0002936D,
+ 0x034, 0x0002836A,
+ 0x034, 0x0002716D,
+ 0x034, 0x0002616A,
+ 0x034, 0x0002506D,
+ 0x034, 0x0002406A,
+ 0x034, 0x0002302C,
+ 0x034, 0x00022029,
+ 0x034, 0x00021026,
+ 0x034, 0x00020023,
+ 0x90000005, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0002A3EC,
+ 0x034, 0x000293AC,
+ 0x034, 0x0002838A,
+ 0x034, 0x0002718C,
+ 0x034, 0x00026189,
+ 0x034, 0x0002506D,
+ 0x034, 0x0002406A,
+ 0x034, 0x0002302C,
+ 0x034, 0x00022029,
+ 0x034, 0x00021026,
+ 0x034, 0x00020023,
+ 0x90000008, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0002A3F5,
+ 0x034, 0x000293F3,
+ 0x034, 0x000282F2,
+ 0x034, 0x000272D0,
+ 0x034, 0x000262CD,
+ 0x034, 0x000252CA,
+ 0x034, 0x000242C7,
+ 0x034, 0x000230CD,
+ 0x034, 0x000220CA,
+ 0x034, 0x000210C7,
+ 0x034, 0x00020086,
+ 0x90000009, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0002AFF7,
+ 0x034, 0x00029FF6,
+ 0x034, 0x00028FF3,
+ 0x034, 0x00027FF0,
+ 0x034, 0x00026FED,
+ 0x034, 0x00025FEA,
+ 0x034, 0x00024FE7,
+ 0x034, 0x00023DEA,
+ 0x034, 0x00022DE7,
+ 0x034, 0x00021DE4,
+ 0x034, 0x00020E44,
+ 0x9000000a, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0002A3EC,
+ 0x034, 0x0002936D,
+ 0x034, 0x0002836A,
+ 0x034, 0x0002716D,
+ 0x034, 0x0002616A,
+ 0x034, 0x0002506D,
+ 0x034, 0x0002406A,
+ 0x034, 0x0002302C,
+ 0x034, 0x00022029,
+ 0x034, 0x00021026,
+ 0x034, 0x00020023,
+ 0x9000000b, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0002A3EC,
+ 0x034, 0x0002936D,
+ 0x034, 0x0002836A,
+ 0x034, 0x0002716D,
+ 0x034, 0x0002616A,
+ 0x034, 0x0002506D,
+ 0x034, 0x0002406A,
+ 0x034, 0x0002302C,
+ 0x034, 0x00022029,
+ 0x034, 0x00021026,
+ 0x034, 0x00020023,
+ 0xA0000000, 0x00000000,
+ 0x034, 0x0002AFF4,
+ 0x034, 0x00029FF1,
+ 0x034, 0x00028FEE,
+ 0x034, 0x00027FEB,
+ 0x034, 0x00026FE8,
+ 0x034, 0x00025DEA,
+ 0x034, 0x00024CED,
+ 0x034, 0x00023CEA,
+ 0x034, 0x00022C6C,
+ 0x034, 0x00021C69,
+ 0x034, 0x00020C2B,
+ 0xB0000000, 0x00000000,
+ 0x80000001, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0000A38C,
+ 0x034, 0x000091AD,
+ 0x034, 0x000081AA,
+ 0x034, 0x000071A7,
+ 0x034, 0x000060AA,
+ 0x034, 0x000050A7,
+ 0x034, 0x0000402C,
+ 0x034, 0x00003029,
+ 0x034, 0x0000200C,
+ 0x034, 0x00001009,
+ 0x034, 0x00000006,
+ 0x90000002, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0000A3EE,
+ 0x034, 0x000093AB,
+ 0x034, 0x00008389,
+ 0x034, 0x0000718C,
+ 0x034, 0x00006189,
+ 0x034, 0x0000506D,
+ 0x034, 0x0000406A,
+ 0x034, 0x0000302C,
+ 0x034, 0x00002029,
+ 0x034, 0x00001026,
+ 0x034, 0x00000023,
+ 0x90000003, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0000A3EE,
+ 0x034, 0x000093AB,
+ 0x034, 0x00008389,
+ 0x034, 0x0000718C,
+ 0x034, 0x00006189,
+ 0x034, 0x0000506D,
+ 0x034, 0x0000406A,
+ 0x034, 0x0000302C,
+ 0x034, 0x00002029,
+ 0x034, 0x00001026,
+ 0x034, 0x00000023,
+ 0x90000004, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0000A3EE,
+ 0x034, 0x000093AB,
+ 0x034, 0x00008389,
+ 0x034, 0x0000718C,
+ 0x034, 0x00006189,
+ 0x034, 0x0000506D,
+ 0x034, 0x0000406A,
+ 0x034, 0x0000302C,
+ 0x034, 0x00002029,
+ 0x034, 0x00001026,
+ 0x034, 0x00000023,
+ 0x90000005, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0000A3EE,
+ 0x034, 0x000093AB,
+ 0x034, 0x00008389,
+ 0x034, 0x0000718C,
+ 0x034, 0x00006189,
+ 0x034, 0x0000506D,
+ 0x034, 0x0000406A,
+ 0x034, 0x0000302C,
+ 0x034, 0x00002029,
+ 0x034, 0x00001026,
+ 0x034, 0x00000023,
+ 0x90000008, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0000A3F5,
+ 0x034, 0x000093F1,
+ 0x034, 0x000083B0,
+ 0x034, 0x00007370,
+ 0x034, 0x0000636D,
+ 0x034, 0x0000536A,
+ 0x034, 0x00004367,
+ 0x034, 0x0000308E,
+ 0x034, 0x0000208B,
+ 0x034, 0x00001088,
+ 0x034, 0x00000085,
+ 0x90000009, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0000AFF7,
+ 0x034, 0x00009FF5,
+ 0x034, 0x00008FF2,
+ 0x034, 0x00007FEF,
+ 0x034, 0x00006FEC,
+ 0x034, 0x00005FE9,
+ 0x034, 0x00004EAA,
+ 0x034, 0x00003EA7,
+ 0x034, 0x00002C70,
+ 0x034, 0x00001C6D,
+ 0x034, 0x00000C6A,
+ 0x9000000a, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0000A3EE,
+ 0x034, 0x000093AB,
+ 0x034, 0x00008389,
+ 0x034, 0x0000718C,
+ 0x034, 0x00006189,
+ 0x034, 0x0000506D,
+ 0x034, 0x0000406A,
+ 0x034, 0x0000302C,
+ 0x034, 0x00002029,
+ 0x034, 0x00001026,
+ 0x034, 0x00000023,
+ 0x9000000b, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0000A3EE,
+ 0x034, 0x000093AB,
+ 0x034, 0x00008389,
+ 0x034, 0x0000718C,
+ 0x034, 0x00006189,
+ 0x034, 0x0000506D,
+ 0x034, 0x0000406A,
+ 0x034, 0x0000302C,
+ 0x034, 0x00002029,
+ 0x034, 0x00001026,
+ 0x034, 0x00000023,
+ 0xA0000000, 0x00000000,
+ 0x034, 0x0000AFF4,
+ 0x034, 0x00009FF1,
+ 0x034, 0x00008FEE,
+ 0x034, 0x00007FEB,
+ 0x034, 0x00006FE8,
+ 0x034, 0x00005DEA,
+ 0x034, 0x00004CED,
+ 0x034, 0x00003CEA,
+ 0x034, 0x00002C6C,
+ 0x034, 0x00001C69,
+ 0x034, 0x00000C2B,
+ 0xB0000000, 0x00000000,
+ 0x80000001, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x000CA38C,
+ 0x034, 0x000C91AD,
+ 0x034, 0x000C81AA,
+ 0x034, 0x000C71A7,
+ 0x034, 0x000C60AA,
+ 0x034, 0x000C50A7,
+ 0x034, 0x000C402C,
+ 0x034, 0x000C3029,
+ 0x034, 0x000C200C,
+ 0x034, 0x000C1009,
+ 0x034, 0x000C0006,
+ 0x90000002, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x000CA3EF,
+ 0x034, 0x000C93AD,
+ 0x034, 0x000C838A,
+ 0x034, 0x000C718C,
+ 0x034, 0x000C6189,
+ 0x034, 0x000C506D,
+ 0x034, 0x000C404C,
+ 0x034, 0x000C302C,
+ 0x034, 0x000C2029,
+ 0x034, 0x000C1026,
+ 0x034, 0x000C0023,
+ 0x90000003, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x000CA3EF,
+ 0x034, 0x000C93AD,
+ 0x034, 0x000C838A,
+ 0x034, 0x000C718C,
+ 0x034, 0x000C6189,
+ 0x034, 0x000C506D,
+ 0x034, 0x000C404C,
+ 0x034, 0x000C302C,
+ 0x034, 0x000C2029,
+ 0x034, 0x000C1026,
+ 0x034, 0x000C0023,
+ 0x90000004, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x000CA3EF,
+ 0x034, 0x000C93AD,
+ 0x034, 0x000C838A,
+ 0x034, 0x000C718C,
+ 0x034, 0x000C6189,
+ 0x034, 0x000C506D,
+ 0x034, 0x000C404C,
+ 0x034, 0x000C302C,
+ 0x034, 0x000C2029,
+ 0x034, 0x000C1026,
+ 0x034, 0x000C0023,
+ 0x90000005, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x000CA3EF,
+ 0x034, 0x000C93AD,
+ 0x034, 0x000C838A,
+ 0x034, 0x000C718C,
+ 0x034, 0x000C6189,
+ 0x034, 0x000C506D,
+ 0x034, 0x000C404C,
+ 0x034, 0x000C302C,
+ 0x034, 0x000C2029,
+ 0x034, 0x000C1026,
+ 0x034, 0x000C0023,
+ 0x90000008, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x000CA3F5,
+ 0x034, 0x000C93F3,
+ 0x034, 0x000C8393,
+ 0x034, 0x000C7390,
+ 0x034, 0x000C638D,
+ 0x034, 0x000C538A,
+ 0x034, 0x000C4387,
+ 0x034, 0x000C30ED,
+ 0x034, 0x000C20EA,
+ 0x034, 0x000C10E7,
+ 0x034, 0x000C002D,
+ 0x90000009, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x000CAFF7,
+ 0x034, 0x000C9FF6,
+ 0x034, 0x000C8FF3,
+ 0x034, 0x000C7FF0,
+ 0x034, 0x000C6FED,
+ 0x034, 0x000C5FEA,
+ 0x034, 0x000C4FE7,
+ 0x034, 0x000C3CD0,
+ 0x034, 0x000C2CCD,
+ 0x034, 0x000C1CCA,
+ 0x034, 0x000C0CC7,
+ 0x9000000a, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x000CA3EF,
+ 0x034, 0x000C93AD,
+ 0x034, 0x000C838A,
+ 0x034, 0x000C718C,
+ 0x034, 0x000C6189,
+ 0x034, 0x000C506D,
+ 0x034, 0x000C404C,
+ 0x034, 0x000C302C,
+ 0x034, 0x000C2029,
+ 0x034, 0x000C1026,
+ 0x034, 0x000C0023,
+ 0x9000000b, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x000CA3EF,
+ 0x034, 0x000C93AD,
+ 0x034, 0x000C838A,
+ 0x034, 0x000C718C,
+ 0x034, 0x000C6189,
+ 0x034, 0x000C506D,
+ 0x034, 0x000C404C,
+ 0x034, 0x000C302C,
+ 0x034, 0x000C2029,
+ 0x034, 0x000C1026,
+ 0x034, 0x000C0023,
+ 0xA0000000, 0x00000000,
+ 0x034, 0x000CA794,
+ 0x034, 0x000C9791,
+ 0x034, 0x000C878E,
+ 0x034, 0x000C778B,
+ 0x034, 0x000C658D,
+ 0x034, 0x000C558A,
+ 0x034, 0x000C448D,
+ 0x034, 0x000C348A,
+ 0x034, 0x000C244C,
+ 0x034, 0x000C1449,
+ 0x034, 0x000C042B,
+ 0xB0000000, 0x00000000,
+ 0x80000001, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x000AA3EC,
+ 0x034, 0x000A938C,
+ 0x034, 0x000A81AD,
+ 0x034, 0x000A71AA,
+ 0x034, 0x000A61A7,
+ 0x034, 0x000A50AA,
+ 0x034, 0x000A40A7,
+ 0x034, 0x000A302C,
+ 0x034, 0x000A2029,
+ 0x034, 0x000A100C,
+ 0x034, 0x000A0009,
+ 0x90000002, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x000AA3EC,
+ 0x034, 0x000A936D,
+ 0x034, 0x000A836A,
+ 0x034, 0x000A716D,
+ 0x034, 0x000A616A,
+ 0x034, 0x000A506D,
+ 0x034, 0x000A406A,
+ 0x034, 0x000A302C,
+ 0x034, 0x000A2029,
+ 0x034, 0x000A1026,
+ 0x034, 0x000A0023,
+ 0x90000003, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x000AA3EC,
+ 0x034, 0x000A93AC,
+ 0x034, 0x000A838A,
+ 0x034, 0x000A718C,
+ 0x034, 0x000A6189,
+ 0x034, 0x000A506D,
+ 0x034, 0x000A406A,
+ 0x034, 0x000A302C,
+ 0x034, 0x000A2029,
+ 0x034, 0x000A1026,
+ 0x034, 0x000A0023,
+ 0x90000004, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x000AA3EC,
+ 0x034, 0x000A936D,
+ 0x034, 0x000A836A,
+ 0x034, 0x000A716D,
+ 0x034, 0x000A616A,
+ 0x034, 0x000A506D,
+ 0x034, 0x000A406A,
+ 0x034, 0x000A302C,
+ 0x034, 0x000A2029,
+ 0x034, 0x000A1026,
+ 0x034, 0x000A0023,
+ 0x90000005, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x000AA3EC,
+ 0x034, 0x000A93AC,
+ 0x034, 0x000A838A,
+ 0x034, 0x000A718C,
+ 0x034, 0x000A6189,
+ 0x034, 0x000A506D,
+ 0x034, 0x000A406A,
+ 0x034, 0x000A302C,
+ 0x034, 0x000A2029,
+ 0x034, 0x000A1026,
+ 0x034, 0x000A0023,
+ 0x90000008, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x000AA3F5,
+ 0x034, 0x000A93F3,
+ 0x034, 0x000A82F2,
+ 0x034, 0x000A72D0,
+ 0x034, 0x000A62CD,
+ 0x034, 0x000A52CA,
+ 0x034, 0x000A42C7,
+ 0x034, 0x000A30CD,
+ 0x034, 0x000A20CA,
+ 0x034, 0x000A10C7,
+ 0x034, 0x000A0086,
+ 0x90000009, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x000AAFF7,
+ 0x034, 0x000A9FF6,
+ 0x034, 0x000A8FF3,
+ 0x034, 0x000A7FF0,
+ 0x034, 0x000A6FED,
+ 0x034, 0x000A5FEA,
+ 0x034, 0x000A4FE7,
+ 0x034, 0x000A3DEA,
+ 0x034, 0x000A2DE7,
+ 0x034, 0x000A1DE4,
+ 0x034, 0x000A0E44,
+ 0x9000000a, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x000AA3EC,
+ 0x034, 0x000A936D,
+ 0x034, 0x000A836A,
+ 0x034, 0x000A716D,
+ 0x034, 0x000A616A,
+ 0x034, 0x000A506D,
+ 0x034, 0x000A406A,
+ 0x034, 0x000A302C,
+ 0x034, 0x000A2029,
+ 0x034, 0x000A1026,
+ 0x034, 0x000A0023,
+ 0x9000000b, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x000AA3EC,
+ 0x034, 0x000A936D,
+ 0x034, 0x000A836A,
+ 0x034, 0x000A716D,
+ 0x034, 0x000A616A,
+ 0x034, 0x000A506D,
+ 0x034, 0x000A406A,
+ 0x034, 0x000A302C,
+ 0x034, 0x000A2029,
+ 0x034, 0x000A1026,
+ 0x034, 0x000A0023,
+ 0xA0000000, 0x00000000,
+ 0x034, 0x000AA794,
+ 0x034, 0x000A9791,
+ 0x034, 0x000A878E,
+ 0x034, 0x000A778B,
+ 0x034, 0x000A658D,
+ 0x034, 0x000A558A,
+ 0x034, 0x000A448D,
+ 0x034, 0x000A348A,
+ 0x034, 0x000A244C,
+ 0x034, 0x000A1449,
+ 0x034, 0x000A042B,
+ 0xB0000000, 0x00000000,
+ 0x80000001, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0008A38C,
+ 0x034, 0x000891AD,
+ 0x034, 0x000881AA,
+ 0x034, 0x000871A7,
+ 0x034, 0x000860AA,
+ 0x034, 0x000850A7,
+ 0x034, 0x0008402C,
+ 0x034, 0x00083029,
+ 0x034, 0x0008200C,
+ 0x034, 0x00081009,
+ 0x034, 0x00000006,
+ 0x90000002, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0008A3EE,
+ 0x034, 0x000893AB,
+ 0x034, 0x00088389,
+ 0x034, 0x0008718C,
+ 0x034, 0x00086189,
+ 0x034, 0x0008506D,
+ 0x034, 0x0008406A,
+ 0x034, 0x0008302C,
+ 0x034, 0x00082029,
+ 0x034, 0x00081026,
+ 0x034, 0x00080023,
+ 0x90000003, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0008A3EE,
+ 0x034, 0x000893AB,
+ 0x034, 0x00088389,
+ 0x034, 0x0008718C,
+ 0x034, 0x00086189,
+ 0x034, 0x0008506D,
+ 0x034, 0x0008406A,
+ 0x034, 0x0008302C,
+ 0x034, 0x00082029,
+ 0x034, 0x00081026,
+ 0x034, 0x00080023,
+ 0x90000004, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0008A3EE,
+ 0x034, 0x000893AB,
+ 0x034, 0x00088389,
+ 0x034, 0x0008718C,
+ 0x034, 0x00086189,
+ 0x034, 0x0008506D,
+ 0x034, 0x0008406A,
+ 0x034, 0x0008302C,
+ 0x034, 0x00082029,
+ 0x034, 0x00081026,
+ 0x034, 0x00080023,
+ 0x90000005, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0008A3EE,
+ 0x034, 0x000893AB,
+ 0x034, 0x00088389,
+ 0x034, 0x0008718C,
+ 0x034, 0x00086189,
+ 0x034, 0x0008506D,
+ 0x034, 0x0008406A,
+ 0x034, 0x0008302C,
+ 0x034, 0x00082029,
+ 0x034, 0x00081026,
+ 0x034, 0x00080023,
+ 0x90000008, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0008A3F5,
+ 0x034, 0x000893F1,
+ 0x034, 0x000883B0,
+ 0x034, 0x00087370,
+ 0x034, 0x0008636D,
+ 0x034, 0x0008536A,
+ 0x034, 0x00084367,
+ 0x034, 0x0008308E,
+ 0x034, 0x0008208B,
+ 0x034, 0x00081088,
+ 0x034, 0x00080085,
+ 0x90000009, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0008AFF7,
+ 0x034, 0x00089FF5,
+ 0x034, 0x00088FF2,
+ 0x034, 0x00087FEF,
+ 0x034, 0x00086FEC,
+ 0x034, 0x00085FE9,
+ 0x034, 0x00084EAA,
+ 0x034, 0x00083EA7,
+ 0x034, 0x00082C70,
+ 0x034, 0x00081C6D,
+ 0x034, 0x00080C6A,
+ 0x9000000a, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0008A3EE,
+ 0x034, 0x000893AB,
+ 0x034, 0x00088389,
+ 0x034, 0x0008718C,
+ 0x034, 0x00086189,
+ 0x034, 0x0008506D,
+ 0x034, 0x0008406A,
+ 0x034, 0x0008302C,
+ 0x034, 0x00082029,
+ 0x034, 0x00081026,
+ 0x034, 0x00080023,
+ 0x9000000b, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0008A3EE,
+ 0x034, 0x000893AB,
+ 0x034, 0x00088389,
+ 0x034, 0x0008718C,
+ 0x034, 0x00086189,
+ 0x034, 0x0008506D,
+ 0x034, 0x0008406A,
+ 0x034, 0x0008302C,
+ 0x034, 0x00082029,
+ 0x034, 0x00081026,
+ 0x034, 0x00080023,
+ 0xA0000000, 0x00000000,
+ 0x034, 0x0008A794,
+ 0x034, 0x00089791,
+ 0x034, 0x0008878E,
+ 0x034, 0x0008778B,
+ 0x034, 0x0008658D,
+ 0x034, 0x0008558A,
+ 0x034, 0x0008448D,
+ 0x034, 0x0008348A,
+ 0x034, 0x0008244C,
+ 0x034, 0x00081449,
+ 0x034, 0x0008042B,
+ 0xB0000000, 0x00000000,
+ 0x0EF, 0x00000000,
+ 0x80000001, 0x00000000, 0x40000000, 0x00000000,
+ 0x0DF, 0x00000001,
+ 0x90000002, 0x00000000, 0x40000000, 0x00000000,
+ 0x0DF, 0x00000001,
+ 0x90000003, 0x00000000, 0x40000000, 0x00000000,
+ 0x0DF, 0x00000001,
+ 0x90000004, 0x00000000, 0x40000000, 0x00000000,
+ 0x0DF, 0x00000001,
+ 0x90000005, 0x00000000, 0x40000000, 0x00000000,
+ 0x0DF, 0x00000001,
+ 0x90000008, 0x00000000, 0x40000000, 0x00000000,
+ 0x0DF, 0x00000001,
+ 0x9000000a, 0x00000000, 0x40000000, 0x00000000,
+ 0x0DF, 0x00000001,
+ 0x9000000b, 0x00000000, 0x40000000, 0x00000000,
+ 0x0DF, 0x00000001,
+ 0xA0000000, 0x00000000,
+ 0x0DF, 0x00000000,
+ 0xB0000000, 0x00000000,
+ 0x018, 0x0001712A,
+ 0x0EF, 0x00000040,
+ 0x80000001, 0x00000000, 0x40000000, 0x00000000,
+ 0x035, 0x000006CC,
+ 0x035, 0x000086CC,
+ 0x035, 0x000106CC,
+ 0x035, 0x000206CC,
+ 0x035, 0x000286CC,
+ 0x035, 0x000306CC,
+ 0x035, 0x000406CC,
+ 0x035, 0x000486CC,
+ 0x035, 0x000506CC,
+ 0x035, 0x000806CC,
+ 0x035, 0x000886CC,
+ 0x035, 0x000906CC,
+ 0x035, 0x000A06CC,
+ 0x035, 0x000A86CC,
+ 0x035, 0x000B06CC,
+ 0x035, 0x000C06CC,
+ 0x035, 0x000C86CC,
+ 0x035, 0x000D06CC,
+ 0x90000002, 0x00000000, 0x40000000, 0x00000000,
+ 0x035, 0x000006CC,
+ 0x035, 0x000086CC,
+ 0x035, 0x000106CC,
+ 0x035, 0x000206CC,
+ 0x035, 0x000286CC,
+ 0x035, 0x000306CC,
+ 0x035, 0x000406CC,
+ 0x035, 0x000486CC,
+ 0x035, 0x000506CC,
+ 0x035, 0x000806CC,
+ 0x035, 0x000886CC,
+ 0x035, 0x000906CC,
+ 0x035, 0x000A06CC,
+ 0x035, 0x000A86CC,
+ 0x035, 0x000B06CC,
+ 0x035, 0x000C06CC,
+ 0x035, 0x000C86CC,
+ 0x035, 0x000D06CC,
+ 0x90000003, 0x00000000, 0x40000000, 0x00000000,
+ 0x035, 0x000006CC,
+ 0x035, 0x000086CC,
+ 0x035, 0x000106CC,
+ 0x035, 0x000206CC,
+ 0x035, 0x000286CC,
+ 0x035, 0x000306CC,
+ 0x035, 0x000406CC,
+ 0x035, 0x000486CC,
+ 0x035, 0x000506CC,
+ 0x035, 0x000806CC,
+ 0x035, 0x000886CC,
+ 0x035, 0x000906CC,
+ 0x035, 0x000A06CC,
+ 0x035, 0x000A86CC,
+ 0x035, 0x000B06CC,
+ 0x035, 0x000C06CC,
+ 0x035, 0x000C86CC,
+ 0x035, 0x000D06CC,
+ 0x90000004, 0x00000000, 0x40000000, 0x00000000,
+ 0x035, 0x000006CC,
+ 0x035, 0x000086CC,
+ 0x035, 0x000106CC,
+ 0x035, 0x000206CC,
+ 0x035, 0x000286CC,
+ 0x035, 0x000306CC,
+ 0x035, 0x000406CC,
+ 0x035, 0x000486CC,
+ 0x035, 0x000506CC,
+ 0x035, 0x000806CC,
+ 0x035, 0x000886CC,
+ 0x035, 0x000906CC,
+ 0x035, 0x000A06CC,
+ 0x035, 0x000A86CC,
+ 0x035, 0x000B06CC,
+ 0x035, 0x000C06CC,
+ 0x035, 0x000C86CC,
+ 0x035, 0x000D06CC,
+ 0x90000005, 0x00000000, 0x40000000, 0x00000000,
+ 0x035, 0x000006CC,
+ 0x035, 0x000086CC,
+ 0x035, 0x000106CC,
+ 0x035, 0x000206CC,
+ 0x035, 0x000286CC,
+ 0x035, 0x000306CC,
+ 0x035, 0x000406CC,
+ 0x035, 0x000486CC,
+ 0x035, 0x000506CC,
+ 0x035, 0x000806CC,
+ 0x035, 0x000886CC,
+ 0x035, 0x000906CC,
+ 0x035, 0x000A06CC,
+ 0x035, 0x000A86CC,
+ 0x035, 0x000B06CC,
+ 0x035, 0x000C06CC,
+ 0x035, 0x000C86CC,
+ 0x035, 0x000D06CC,
+ 0x90000008, 0x00000000, 0x40000000, 0x00000000,
+ 0x035, 0x000006CC,
+ 0x035, 0x000086CC,
+ 0x035, 0x000106CC,
+ 0x035, 0x000206CC,
+ 0x035, 0x000286CC,
+ 0x035, 0x000306CC,
+ 0x035, 0x000406CC,
+ 0x035, 0x000486CC,
+ 0x035, 0x000506CC,
+ 0x035, 0x000806CC,
+ 0x035, 0x000886CC,
+ 0x035, 0x000906CC,
+ 0x035, 0x000A06CC,
+ 0x035, 0x000A86CC,
+ 0x035, 0x000B06CC,
+ 0x035, 0x000C06CC,
+ 0x035, 0x000C86CC,
+ 0x035, 0x000D06CC,
+ 0x9000000a, 0x00000000, 0x40000000, 0x00000000,
+ 0x035, 0x000006CC,
+ 0x035, 0x000086CC,
+ 0x035, 0x000106CC,
+ 0x035, 0x000206CC,
+ 0x035, 0x000286CC,
+ 0x035, 0x000306CC,
+ 0x035, 0x000406CC,
+ 0x035, 0x000486CC,
+ 0x035, 0x000506CC,
+ 0x035, 0x000806CC,
+ 0x035, 0x000886CC,
+ 0x035, 0x000906CC,
+ 0x035, 0x000A06CC,
+ 0x035, 0x000A86CC,
+ 0x035, 0x000B06CC,
+ 0x035, 0x000C06CC,
+ 0x035, 0x000C86CC,
+ 0x035, 0x000D06CC,
+ 0x9000000b, 0x00000000, 0x40000000, 0x00000000,
+ 0x035, 0x000006CC,
+ 0x035, 0x000086CC,
+ 0x035, 0x000106CC,
+ 0x035, 0x000206CC,
+ 0x035, 0x000286CC,
+ 0x035, 0x000306CC,
+ 0x035, 0x000406CC,
+ 0x035, 0x000486CC,
+ 0x035, 0x000506CC,
+ 0x035, 0x000806CC,
+ 0x035, 0x000886CC,
+ 0x035, 0x000906CC,
+ 0x035, 0x000A06CC,
+ 0x035, 0x000A86CC,
+ 0x035, 0x000B06CC,
+ 0x035, 0x000C06CC,
+ 0x035, 0x000C86CC,
+ 0x035, 0x000D06CC,
+ 0xA0000000, 0x00000000,
+ 0x035, 0x00000484,
+ 0x035, 0x00008484,
+ 0x035, 0x00010484,
+ 0x035, 0x00020584,
+ 0x035, 0x00028584,
+ 0x035, 0x00030584,
+ 0x035, 0x00040584,
+ 0x035, 0x00048584,
+ 0x035, 0x00050584,
+ 0x035, 0x000805FB,
+ 0x035, 0x000885FB,
+ 0x035, 0x000905FB,
+ 0x035, 0x000A05FB,
+ 0x035, 0x000A85FB,
+ 0x035, 0x000B05FB,
+ 0x035, 0x000C05FB,
+ 0x035, 0x000C85FB,
+ 0x035, 0x000D05FB,
+ 0xB0000000, 0x00000000,
+ 0x0EF, 0x00000000,
+ 0x80000001, 0x00000000, 0x40000000, 0x00000000,
+ 0x0DF, 0x00000001,
+ 0x90000002, 0x00000000, 0x40000000, 0x00000000,
+ 0x0DF, 0x00000001,
+ 0x90000003, 0x00000000, 0x40000000, 0x00000000,
+ 0x0DF, 0x00000001,
+ 0x90000004, 0x00000000, 0x40000000, 0x00000000,
+ 0x0DF, 0x00000001,
+ 0x90000005, 0x00000000, 0x40000000, 0x00000000,
+ 0x0DF, 0x00000001,
+ 0x90000008, 0x00000000, 0x40000000, 0x00000000,
+ 0x0DF, 0x00000001,
+ 0x9000000a, 0x00000000, 0x40000000, 0x00000000,
+ 0x0DF, 0x00000001,
+ 0x9000000b, 0x00000000, 0x40000000, 0x00000000,
+ 0x0DF, 0x00000001,
+ 0xA0000000, 0x00000000,
+ 0x0DF, 0x00000000,
+ 0xB0000000, 0x00000000,
+ 0x018, 0x0001712A,
+ 0x0EF, 0x00000010,
+ 0x80000001, 0x00000000, 0x40000000, 0x00000000,
+ 0x036, 0x00000473,
+ 0x036, 0x00008473,
+ 0x036, 0x00010473,
+ 0x036, 0x00020473,
+ 0x036, 0x00028473,
+ 0x036, 0x00030473,
+ 0x036, 0x00040473,
+ 0x036, 0x00048473,
+ 0x036, 0x00050473,
+ 0x036, 0x00080473,
+ 0x036, 0x00088473,
+ 0x036, 0x00090473,
+ 0x036, 0x000A0473,
+ 0x036, 0x000A8473,
+ 0x036, 0x000B0473,
+ 0x036, 0x000C0473,
+ 0x036, 0x000C8473,
+ 0x036, 0x000D0473,
+ 0x90000002, 0x00000000, 0x40000000, 0x00000000,
+ 0x036, 0x00000475,
+ 0x036, 0x00008475,
+ 0x036, 0x00010475,
+ 0x036, 0x00020475,
+ 0x036, 0x00028475,
+ 0x036, 0x00030475,
+ 0x036, 0x00040475,
+ 0x036, 0x00048475,
+ 0x036, 0x00050475,
+ 0x036, 0x00080475,
+ 0x036, 0x00088475,
+ 0x036, 0x00090475,
+ 0x036, 0x000A0475,
+ 0x036, 0x000A8475,
+ 0x036, 0x000B0475,
+ 0x036, 0x000C0475,
+ 0x036, 0x000C8475,
+ 0x036, 0x000D0475,
+ 0x90000003, 0x00000000, 0x40000000, 0x00000000,
+ 0x036, 0x00000475,
+ 0x036, 0x00008475,
+ 0x036, 0x00010475,
+ 0x036, 0x00020475,
+ 0x036, 0x00028475,
+ 0x036, 0x00030475,
+ 0x036, 0x00040475,
+ 0x036, 0x00048475,
+ 0x036, 0x00050475,
+ 0x036, 0x00080475,
+ 0x036, 0x00088475,
+ 0x036, 0x00090475,
+ 0x036, 0x000A0475,
+ 0x036, 0x000A8475,
+ 0x036, 0x000B0475,
+ 0x036, 0x000C0475,
+ 0x036, 0x000C8475,
+ 0x036, 0x000D0475,
+ 0x90000004, 0x00000000, 0x40000000, 0x00000000,
+ 0x036, 0x00000475,
+ 0x036, 0x00008475,
+ 0x036, 0x00010475,
+ 0x036, 0x00020475,
+ 0x036, 0x00028475,
+ 0x036, 0x00030475,
+ 0x036, 0x00040475,
+ 0x036, 0x00048475,
+ 0x036, 0x00050475,
+ 0x036, 0x00080475,
+ 0x036, 0x00088475,
+ 0x036, 0x00090475,
+ 0x036, 0x000A0475,
+ 0x036, 0x000A8475,
+ 0x036, 0x000B0475,
+ 0x036, 0x000C0475,
+ 0x036, 0x000C8475,
+ 0x036, 0x000D0475,
+ 0x90000005, 0x00000000, 0x40000000, 0x00000000,
+ 0x036, 0x00000475,
+ 0x036, 0x00008475,
+ 0x036, 0x00010475,
+ 0x036, 0x00020475,
+ 0x036, 0x00028475,
+ 0x036, 0x00030475,
+ 0x036, 0x00040475,
+ 0x036, 0x00048475,
+ 0x036, 0x00050475,
+ 0x036, 0x00080475,
+ 0x036, 0x00088475,
+ 0x036, 0x00090475,
+ 0x036, 0x000A0475,
+ 0x036, 0x000A8475,
+ 0x036, 0x000B0475,
+ 0x036, 0x000C0475,
+ 0x036, 0x000C8475,
+ 0x036, 0x000D0475,
+ 0x90000008, 0x00000000, 0x40000000, 0x00000000,
+ 0x036, 0x00000475,
+ 0x036, 0x00008475,
+ 0x036, 0x00010475,
+ 0x036, 0x00020475,
+ 0x036, 0x00028475,
+ 0x036, 0x00030475,
+ 0x036, 0x00040475,
+ 0x036, 0x00048475,
+ 0x036, 0x00050475,
+ 0x036, 0x00080475,
+ 0x036, 0x00088475,
+ 0x036, 0x00090475,
+ 0x036, 0x000A0475,
+ 0x036, 0x000A8475,
+ 0x036, 0x000B0475,
+ 0x036, 0x000C0475,
+ 0x036, 0x000C8475,
+ 0x036, 0x000D0475,
+ 0x9000000a, 0x00000000, 0x40000000, 0x00000000,
+ 0x036, 0x00000475,
+ 0x036, 0x00008475,
+ 0x036, 0x00010475,
+ 0x036, 0x00020475,
+ 0x036, 0x00028475,
+ 0x036, 0x00030475,
+ 0x036, 0x00040475,
+ 0x036, 0x00048475,
+ 0x036, 0x00050475,
+ 0x036, 0x00080475,
+ 0x036, 0x00088475,
+ 0x036, 0x00090475,
+ 0x036, 0x000A0475,
+ 0x036, 0x000A8475,
+ 0x036, 0x000B0475,
+ 0x036, 0x000C0475,
+ 0x036, 0x000C8475,
+ 0x036, 0x000D0475,
+ 0x9000000b, 0x00000000, 0x40000000, 0x00000000,
+ 0x036, 0x00000475,
+ 0x036, 0x00008475,
+ 0x036, 0x00010475,
+ 0x036, 0x00020475,
+ 0x036, 0x00028475,
+ 0x036, 0x00030475,
+ 0x036, 0x00040475,
+ 0x036, 0x00048475,
+ 0x036, 0x00050475,
+ 0x036, 0x00080475,
+ 0x036, 0x00088475,
+ 0x036, 0x00090475,
+ 0x036, 0x000A0475,
+ 0x036, 0x000A8475,
+ 0x036, 0x000B0475,
+ 0x036, 0x000C0475,
+ 0x036, 0x000C8475,
+ 0x036, 0x000D0475,
+ 0xA0000000, 0x00000000,
+ 0x036, 0x00000474,
+ 0x036, 0x00008474,
+ 0x036, 0x00010474,
+ 0x036, 0x00020474,
+ 0x036, 0x00028474,
+ 0x036, 0x00030474,
+ 0x036, 0x00040474,
+ 0x036, 0x00048474,
+ 0x036, 0x00050474,
+ 0x036, 0x00080474,
+ 0x036, 0x00088474,
+ 0x036, 0x00090474,
+ 0x036, 0x000A0474,
+ 0x036, 0x000A8474,
+ 0x036, 0x000B0474,
+ 0x036, 0x000C0474,
+ 0x036, 0x000C8474,
+ 0x036, 0x000D0474,
+ 0xB0000000, 0x00000000,
+ 0x0EF, 0x00000000,
+ 0x80000001, 0x00000000, 0x40000000, 0x00000000,
+ 0x90000002, 0x00000000, 0x40000000, 0x00000000,
+ 0x90000003, 0x00000000, 0x40000000, 0x00000000,
+ 0x90000004, 0x00000000, 0x40000000, 0x00000000,
+ 0x90000005, 0x00000000, 0x40000000, 0x00000000,
+ 0x90000008, 0x00000000, 0x40000000, 0x00000000,
+ 0x9000000a, 0x00000000, 0x40000000, 0x00000000,
+ 0x9000000b, 0x00000000, 0x40000000, 0x00000000,
+ 0xA0000000, 0x00000000,
+ 0x0EF, 0x00000004,
+ 0x037, 0x00000000,
+ 0x038, 0x0000514E,
+ 0x037, 0x00004000,
+ 0x038, 0x0000514E,
+ 0x037, 0x00008000,
+ 0x038, 0x0000514E,
+ 0x037, 0x00010000,
+ 0x038, 0x0000514E,
+ 0x037, 0x00014000,
+ 0x038, 0x0000514E,
+ 0x037, 0x00018000,
+ 0x038, 0x0000514E,
+ 0x037, 0x0001C000,
+ 0x038, 0x0000514E,
+ 0x037, 0x00020000,
+ 0x038, 0x0000514E,
+ 0x037, 0x00024000,
+ 0x038, 0x0000514E,
+ 0x037, 0x00028000,
+ 0x038, 0x0000514E,
+ 0x037, 0x0002C000,
+ 0x038, 0x0000714E,
+ 0x037, 0x00030000,
+ 0x038, 0x0000514E,
+ 0x037, 0x00034000,
+ 0x038, 0x0000514E,
+ 0x037, 0x00038000,
+ 0x038, 0x0000514E,
+ 0x037, 0x0003C000,
+ 0x038, 0x0000514E,
+ 0x037, 0x00040000,
+ 0x038, 0x0000514E,
+ 0x037, 0x00044000,
+ 0x038, 0x0000514E,
+ 0x037, 0x00048000,
+ 0x038, 0x0000514E,
+ 0x037, 0x00080000,
+ 0x038, 0x00005ECE,
+ 0x037, 0x00084000,
+ 0x038, 0x00005ECE,
+ 0x037, 0x00088000,
+ 0x038, 0x00005ECE,
+ 0x037, 0x00090000,
+ 0x038, 0x00005ECE,
+ 0x037, 0x00094000,
+ 0x038, 0x00005ECE,
+ 0x037, 0x00098000,
+ 0x038, 0x00005ECE,
+ 0x037, 0x0009C000,
+ 0x038, 0x00005ECE,
+ 0x037, 0x000A0000,
+ 0x038, 0x00005ECE,
+ 0x037, 0x000A4000,
+ 0x038, 0x00005ECE,
+ 0x037, 0x000A8000,
+ 0x038, 0x00005ECE,
+ 0x037, 0x000AC000,
+ 0x038, 0x00005ECE,
+ 0x037, 0x000B0000,
+ 0x038, 0x00005ECE,
+ 0x037, 0x000B4000,
+ 0x038, 0x00005ECE,
+ 0x037, 0x000B8000,
+ 0x038, 0x00005ECE,
+ 0x037, 0x000BC000,
+ 0x038, 0x00005ECE,
+ 0x037, 0x000C0000,
+ 0x038, 0x00005ECE,
+ 0x037, 0x000C4000,
+ 0x038, 0x00005ECE,
+ 0x037, 0x000C8000,
+ 0x038, 0x00005ECE,
+ 0x0EF, 0x00000000,
+ 0xB0000000, 0x00000000,
+ 0x0EF, 0x00000008,
+ 0x80000001, 0x00000000, 0x40000000, 0x00000000,
+ 0x90000002, 0x00000000, 0x40000000, 0x00000000,
+ 0x90000003, 0x00000000, 0x40000000, 0x00000000,
+ 0x90000004, 0x00000000, 0x40000000, 0x00000000,
+ 0x90000005, 0x00000000, 0x40000000, 0x00000000,
+ 0x90000008, 0x00000000, 0x40000000, 0x00000000,
+ 0x9000000a, 0x00000000, 0x40000000, 0x00000000,
+ 0x9000000b, 0x00000000, 0x40000000, 0x00000000,
+ 0xA0000000, 0x00000000,
+ 0x03C, 0x0000007D,
+ 0x03C, 0x0000047D,
+ 0x03C, 0x0000087D,
+ 0x03C, 0x0000107D,
+ 0x03C, 0x0000147D,
+ 0x03C, 0x0000187D,
+ 0xB0000000, 0x00000000,
+ 0x80000001, 0x00000000, 0x40000000, 0x00000000,
+ 0x03C, 0x0000027D,
+ 0x03C, 0x00000541,
+ 0x03C, 0x00000821,
+ 0x03C, 0x0000127D,
+ 0x03C, 0x00001541,
+ 0x03C, 0x00001821,
+ 0x03C, 0x0000227D,
+ 0x03C, 0x00002541,
+ 0x03C, 0x00002821,
+ 0x90000002, 0x00000000, 0x40000000, 0x00000000,
+ 0x03C, 0x0000027D,
+ 0x03C, 0x00000546,
+ 0x03C, 0x00000821,
+ 0x03C, 0x0000127D,
+ 0x03C, 0x00001546,
+ 0x03C, 0x00001821,
+ 0x03C, 0x0000227D,
+ 0x03C, 0x00002546,
+ 0x03C, 0x00002821,
+ 0x90000003, 0x00000000, 0x40000000, 0x00000000,
+ 0x03C, 0x0000027D,
+ 0x03C, 0x00000546,
+ 0x03C, 0x00000821,
+ 0x03C, 0x0000127D,
+ 0x03C, 0x00001546,
+ 0x03C, 0x00001821,
+ 0x03C, 0x0000227D,
+ 0x03C, 0x00002546,
+ 0x03C, 0x00002821,
+ 0x90000004, 0x00000000, 0x40000000, 0x00000000,
+ 0x03C, 0x0000027D,
+ 0x03C, 0x00000546,
+ 0x03C, 0x00000821,
+ 0x03C, 0x0000127D,
+ 0x03C, 0x00001546,
+ 0x03C, 0x00001821,
+ 0x03C, 0x0000227D,
+ 0x03C, 0x00002546,
+ 0x03C, 0x00002821,
+ 0x90000005, 0x00000000, 0x40000000, 0x00000000,
+ 0x03C, 0x0000027D,
+ 0x03C, 0x00000546,
+ 0x03C, 0x00000821,
+ 0x03C, 0x0000127D,
+ 0x03C, 0x00001546,
+ 0x03C, 0x00001821,
+ 0x03C, 0x0000227D,
+ 0x03C, 0x00002546,
+ 0x03C, 0x00002821,
+ 0x90000008, 0x00000000, 0x40000000, 0x00000000,
+ 0x03C, 0x0000027D,
+ 0x03C, 0x00000546,
+ 0x03C, 0x00000821,
+ 0x03C, 0x0000127D,
+ 0x03C, 0x00001546,
+ 0x03C, 0x00001821,
+ 0x03C, 0x0000227D,
+ 0x03C, 0x00002546,
+ 0x03C, 0x00002821,
+ 0x9000000a, 0x00000000, 0x40000000, 0x00000000,
+ 0x03C, 0x0000027D,
+ 0x03C, 0x00000546,
+ 0x03C, 0x00000821,
+ 0x03C, 0x0000127D,
+ 0x03C, 0x00001546,
+ 0x03C, 0x00001821,
+ 0x03C, 0x0000227D,
+ 0x03C, 0x00002546,
+ 0x03C, 0x00002821,
+ 0x9000000b, 0x00000000, 0x40000000, 0x00000000,
+ 0x03C, 0x0000027D,
+ 0x03C, 0x00000546,
+ 0x03C, 0x00000821,
+ 0x03C, 0x0000127D,
+ 0x03C, 0x00001546,
+ 0x03C, 0x00001821,
+ 0x03C, 0x0000227D,
+ 0x03C, 0x00002546,
+ 0x03C, 0x00002821,
+ 0xA0000000, 0x00000000,
+ 0x03C, 0x0000037E,
+ 0x03C, 0x00000575,
+ 0x03C, 0x00000971,
+ 0x03C, 0x0000127E,
+ 0x03C, 0x00001575,
+ 0x03C, 0x00001871,
+ 0x03C, 0x0000217E,
+ 0x03C, 0x00002575,
+ 0x03C, 0x00002871,
+ 0xB0000000, 0x00000000,
+ 0x0EF, 0x00000000,
+ 0x061, 0x000C0D47,
+ 0x062, 0x0000133C,
+ 0x80000001, 0x00000000, 0x40000000, 0x00000000,
+ 0x063, 0x000750E7,
+ 0x90000002, 0x00000000, 0x40000000, 0x00000000,
+ 0x063, 0x000750E7,
+ 0x90000003, 0x00000000, 0x40000000, 0x00000000,
+ 0x063, 0x000750E7,
+ 0x90000004, 0x00000000, 0x40000000, 0x00000000,
+ 0x063, 0x000750E7,
+ 0x90000005, 0x00000000, 0x40000000, 0x00000000,
+ 0x063, 0x000750E7,
+ 0x90000008, 0x00000000, 0x40000000, 0x00000000,
+ 0x063, 0x000750E7,
+ 0x9000000a, 0x00000000, 0x40000000, 0x00000000,
+ 0x063, 0x000750E7,
+ 0x9000000b, 0x00000000, 0x40000000, 0x00000000,
+ 0x063, 0x000750E7,
+ 0xA0000000, 0x00000000,
+ 0x063, 0x0007D0E7,
+ 0xB0000000, 0x00000000,
+ 0x064, 0x00014FEC,
+ 0x80000001, 0x00000000, 0x40000000, 0x00000000,
+ 0x065, 0x000920D0,
+ 0x90000002, 0x00000000, 0x40000000, 0x00000000,
+ 0x065, 0x000920D0,
+ 0x90000003, 0x00000000, 0x40000000, 0x00000000,
+ 0x065, 0x000920D0,
+ 0x90000004, 0x00000000, 0x40000000, 0x00000000,
+ 0x065, 0x000920D0,
+ 0x90000005, 0x00000000, 0x40000000, 0x00000000,
+ 0x065, 0x000920D0,
+ 0x90000008, 0x00000000, 0x40000000, 0x00000000,
+ 0x065, 0x000920D0,
+ 0x9000000a, 0x00000000, 0x40000000, 0x00000000,
+ 0x065, 0x000920D0,
+ 0x9000000b, 0x00000000, 0x40000000, 0x00000000,
+ 0x065, 0x000920D0,
+ 0xA0000000, 0x00000000,
+ 0x065, 0x000923FF,
+ 0xB0000000, 0x00000000,
+ 0x066, 0x00000040,
+ 0x057, 0x00050000,
+ 0x056, 0x00051DF0,
+ 0x80000001, 0x00000000, 0x40000000, 0x00000000,
+ 0x90000002, 0x00000000, 0x40000000, 0x00000000,
+ 0x90000003, 0x00000000, 0x40000000, 0x00000000,
+ 0x90000004, 0x00000000, 0x40000000, 0x00000000,
+ 0x90000005, 0x00000000, 0x40000000, 0x00000000,
+ 0x90000008, 0x00000000, 0x40000000, 0x00000000,
+ 0x9000000a, 0x00000000, 0x40000000, 0x00000000,
+ 0x9000000b, 0x00000000, 0x40000000, 0x00000000,
+ 0xA0000000, 0x00000000,
+ 0x055, 0x00082060,
+ 0xB0000000, 0x00000000,
+};
+
+RTW_DECL_TABLE_RF_RADIO(rtw8814a_rf_c, C);
+
+static const u32 rtw8814a_rf_d[] = {
+ 0x018, 0x00013124,
+ 0x040, 0x00000C00,
+ 0x058, 0x00000F98,
+ 0x07F, 0x00068004,
+ 0x018, 0x00000006,
+ 0x80000002, 0x00000000, 0x40000000, 0x00000000,
+ 0x086, 0x000E335A,
+ 0x087, 0x00079F80,
+ 0x90000003, 0x00000000, 0x40000000, 0x00000000,
+ 0x086, 0x000E335A,
+ 0x087, 0x00079F80,
+ 0x90000004, 0x00000000, 0x40000000, 0x00000000,
+ 0x086, 0x000E335A,
+ 0x087, 0x00079F80,
+ 0x90000005, 0x00000000, 0x40000000, 0x00000000,
+ 0x086, 0x000E335A,
+ 0x087, 0x00079F80,
+ 0x90000008, 0x00000000, 0x40000000, 0x00000000,
+ 0x086, 0x000E335A,
+ 0x087, 0x00079F80,
+ 0x9000000a, 0x00000000, 0x40000000, 0x00000000,
+ 0x086, 0x000E335A,
+ 0x087, 0x00079F80,
+ 0x9000000b, 0x00000000, 0x40000000, 0x00000000,
+ 0x086, 0x000E335A,
+ 0x087, 0x00079F80,
+ 0xA0000000, 0x00000000,
+ 0x086, 0x000E4B58,
+ 0x087, 0x00049F80,
+ 0xB0000000, 0x00000000,
+ 0x0DF, 0x00000008,
+ 0x0EF, 0x00002000,
+ 0x80000002, 0x00000000, 0x40000000, 0x00000000,
+ 0x03B, 0x0003F19B,
+ 0x03B, 0x00037A5B,
+ 0x03B, 0x0002A433,
+ 0x03B, 0x00027BD3,
+ 0x03B, 0x0001F80B,
+ 0x03B, 0x00017803,
+ 0x90000003, 0x00000000, 0x40000000, 0x00000000,
+ 0x03B, 0x0003F09B,
+ 0x03B, 0x00037A5B,
+ 0x03B, 0x0002A433,
+ 0x03B, 0x00027BD3,
+ 0x03B, 0x0001F80B,
+ 0x03B, 0x00017803,
+ 0x90000004, 0x00000000, 0x40000000, 0x00000000,
+ 0x03B, 0x0003F19B,
+ 0x03B, 0x00037A5B,
+ 0x03B, 0x0002A433,
+ 0x03B, 0x00027BD3,
+ 0x03B, 0x0001F80B,
+ 0x03B, 0x00017803,
+ 0x90000005, 0x00000000, 0x40000000, 0x00000000,
+ 0x03B, 0x0003F09B,
+ 0x03B, 0x00037A5B,
+ 0x03B, 0x0002A433,
+ 0x03B, 0x00027BD3,
+ 0x03B, 0x0001F80B,
+ 0x03B, 0x00017803,
+ 0x90000008, 0x00000000, 0x40000000, 0x00000000,
+ 0x03B, 0x0003F19B,
+ 0x03B, 0x00037A5B,
+ 0x03B, 0x0002A433,
+ 0x03B, 0x00027BD3,
+ 0x03B, 0x0001F80B,
+ 0x03B, 0x00017803,
+ 0x9000000a, 0x00000000, 0x40000000, 0x00000000,
+ 0x03B, 0x0003F19B,
+ 0x03B, 0x00037A5B,
+ 0x03B, 0x0002A433,
+ 0x03B, 0x00027BD3,
+ 0x03B, 0x0001F80B,
+ 0x03B, 0x00017803,
+ 0x9000000b, 0x00000000, 0x40000000, 0x00000000,
+ 0x03B, 0x0003F19B,
+ 0x03B, 0x00037A5B,
+ 0x03B, 0x0002A433,
+ 0x03B, 0x00027BD3,
+ 0x03B, 0x0001F80B,
+ 0x03B, 0x00017803,
+ 0xA0000000, 0x00000000,
+ 0x03B, 0x0003F258,
+ 0x03B, 0x00030A58,
+ 0x03B, 0x0002FA58,
+ 0x03B, 0x00022590,
+ 0x03B, 0x0001FA50,
+ 0x03B, 0x00010248,
+ 0x03B, 0x00008240,
+ 0xB0000000, 0x00000000,
+ 0x0EF, 0x00000100,
+ 0x80000002, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0000A0D0,
+ 0x034, 0x000090CD,
+ 0x034, 0x000080CA,
+ 0x034, 0x0000704D,
+ 0x034, 0x0000604A,
+ 0x034, 0x00005047,
+ 0x034, 0x0000400A,
+ 0x034, 0x00003007,
+ 0x034, 0x00002004,
+ 0x034, 0x00001001,
+ 0x034, 0x00000001,
+ 0x90000003, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0000A0D0,
+ 0x034, 0x000090CD,
+ 0x034, 0x000080CA,
+ 0x034, 0x0000704D,
+ 0x034, 0x0000604A,
+ 0x034, 0x00005047,
+ 0x034, 0x0000400A,
+ 0x034, 0x00003007,
+ 0x034, 0x00002004,
+ 0x034, 0x00001001,
+ 0x034, 0x00000001,
+ 0x90000004, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0000A0D0,
+ 0x034, 0x000090CD,
+ 0x034, 0x000080CA,
+ 0x034, 0x0000704D,
+ 0x034, 0x0000604A,
+ 0x034, 0x00005047,
+ 0x034, 0x0000400A,
+ 0x034, 0x00003007,
+ 0x034, 0x00002004,
+ 0x034, 0x00001001,
+ 0x034, 0x00000001,
+ 0x90000005, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0000A0D0,
+ 0x034, 0x000090CD,
+ 0x034, 0x000080CA,
+ 0x034, 0x0000704D,
+ 0x034, 0x0000604A,
+ 0x034, 0x00005047,
+ 0x034, 0x0000400A,
+ 0x034, 0x00003007,
+ 0x034, 0x00002004,
+ 0x034, 0x00001001,
+ 0x034, 0x00000001,
+ 0x90000008, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0000A0D0,
+ 0x034, 0x000090CD,
+ 0x034, 0x000080CA,
+ 0x034, 0x0000704D,
+ 0x034, 0x0000604A,
+ 0x034, 0x00005047,
+ 0x034, 0x0000400A,
+ 0x034, 0x00003007,
+ 0x034, 0x00002004,
+ 0x034, 0x00001001,
+ 0x034, 0x00000001,
+ 0x9000000a, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0000A0D0,
+ 0x034, 0x000090CD,
+ 0x034, 0x000080CA,
+ 0x034, 0x0000704D,
+ 0x034, 0x0000604A,
+ 0x034, 0x00005047,
+ 0x034, 0x0000400A,
+ 0x034, 0x00003007,
+ 0x034, 0x00002004,
+ 0x034, 0x00001001,
+ 0x034, 0x00000001,
+ 0x9000000b, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0000A0D0,
+ 0x034, 0x000090CD,
+ 0x034, 0x000080CA,
+ 0x034, 0x0000704D,
+ 0x034, 0x0000604A,
+ 0x034, 0x00005047,
+ 0x034, 0x0000400A,
+ 0x034, 0x00003007,
+ 0x034, 0x00002004,
+ 0x034, 0x00001001,
+ 0x034, 0x00000001,
+ 0xA0000000, 0x00000000,
+ 0x034, 0x0000ADF6,
+ 0x034, 0x00009DF3,
+ 0x034, 0x00008DF0,
+ 0x034, 0x00007DED,
+ 0x034, 0x00006DEA,
+ 0x034, 0x00005CED,
+ 0x034, 0x00004CEA,
+ 0x034, 0x000034EA,
+ 0x034, 0x000024E7,
+ 0x034, 0x0000146A,
+ 0x034, 0x0000006B,
+ 0xB0000000, 0x00000000,
+ 0x80000002, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0000A0D0,
+ 0x034, 0x000090CD,
+ 0x034, 0x000080CA,
+ 0x034, 0x0000704D,
+ 0x034, 0x0000604A,
+ 0x034, 0x00005047,
+ 0x034, 0x0000400A,
+ 0x034, 0x00003007,
+ 0x034, 0x00002004,
+ 0x034, 0x00001001,
+ 0x034, 0x00000001,
+ 0x90000003, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0000A0D0,
+ 0x034, 0x000090CD,
+ 0x034, 0x000080CA,
+ 0x034, 0x0000704D,
+ 0x034, 0x0000604A,
+ 0x034, 0x00005047,
+ 0x034, 0x0000400A,
+ 0x034, 0x00003007,
+ 0x034, 0x00002004,
+ 0x034, 0x00001001,
+ 0x034, 0x00000001,
+ 0x90000004, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0000A0D0,
+ 0x034, 0x000090CD,
+ 0x034, 0x000080CA,
+ 0x034, 0x0000704D,
+ 0x034, 0x0000604A,
+ 0x034, 0x00005047,
+ 0x034, 0x0000400A,
+ 0x034, 0x00003007,
+ 0x034, 0x00002004,
+ 0x034, 0x00001001,
+ 0x034, 0x00000001,
+ 0x90000005, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0000A0D0,
+ 0x034, 0x000090CD,
+ 0x034, 0x000080CA,
+ 0x034, 0x0000704D,
+ 0x034, 0x0000604A,
+ 0x034, 0x00005047,
+ 0x034, 0x0000400A,
+ 0x034, 0x00003007,
+ 0x034, 0x00002004,
+ 0x034, 0x00001001,
+ 0x034, 0x00000001,
+ 0x90000008, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0000A0D0,
+ 0x034, 0x000090CD,
+ 0x034, 0x000080CA,
+ 0x034, 0x0000704D,
+ 0x034, 0x0000604A,
+ 0x034, 0x00005047,
+ 0x034, 0x0000400A,
+ 0x034, 0x00003007,
+ 0x034, 0x00002004,
+ 0x034, 0x00001001,
+ 0x034, 0x00000001,
+ 0x9000000a, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0000A0D0,
+ 0x034, 0x000090CD,
+ 0x034, 0x000080CA,
+ 0x034, 0x0000704D,
+ 0x034, 0x0000604A,
+ 0x034, 0x00005047,
+ 0x034, 0x0000400A,
+ 0x034, 0x00003007,
+ 0x034, 0x00002004,
+ 0x034, 0x00001001,
+ 0x034, 0x00000001,
+ 0x9000000b, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0000A0D0,
+ 0x034, 0x000090CD,
+ 0x034, 0x000080CA,
+ 0x034, 0x0000704D,
+ 0x034, 0x0000604A,
+ 0x034, 0x00005047,
+ 0x034, 0x0000400A,
+ 0x034, 0x00003007,
+ 0x034, 0x00002004,
+ 0x034, 0x00001001,
+ 0x034, 0x00000001,
+ 0xA0000000, 0x00000000,
+ 0x034, 0x0008ADF6,
+ 0x034, 0x00089DF3,
+ 0x034, 0x00088DF0,
+ 0x034, 0x00087DED,
+ 0x034, 0x00086DEA,
+ 0x034, 0x00085CED,
+ 0x034, 0x00084CEA,
+ 0x034, 0x000834EA,
+ 0x034, 0x000824E7,
+ 0x034, 0x0008146A,
+ 0x034, 0x0008006B,
+ 0xB0000000, 0x00000000,
+ 0x0EF, 0x00000000,
+ 0x0EF, 0x000020A2,
+ 0x0DF, 0x00000080,
+ 0x035, 0x00000192,
+ 0x035, 0x00008192,
+ 0x035, 0x00010192,
+ 0x036, 0x00000024,
+ 0x036, 0x00008024,
+ 0x036, 0x00010024,
+ 0x036, 0x00018024,
+ 0x0EF, 0x00000000,
+ 0x051, 0x00000C21,
+ 0x052, 0x000006D9,
+ 0x053, 0x000FC649,
+ 0x054, 0x0000017E,
+ 0x018, 0x0001012A,
+ 0x081, 0x0007FC00,
+ 0x089, 0x00050110,
+ 0x08A, 0x00043E50,
+ 0x08B, 0x0002E180,
+ 0x08C, 0x00093C3C,
+ 0x80000001, 0x00000000, 0x40000000, 0x00000000,
+ 0x085, 0x000F8000,
+ 0x90000006, 0x00000000, 0x40000000, 0x00000000,
+ 0x085, 0x000F8000,
+ 0xA0000000, 0x00000000,
+ 0x085, 0x000F8000,
+ 0xB0000000, 0x00000000,
+ 0x80000001, 0x00000000, 0x40000000, 0x00000000,
+ 0x08D, 0x000FFFF0,
+ 0x90000002, 0x00000000, 0x40000000, 0x00000000,
+ 0x08D, 0x000FFFF0,
+ 0x90000003, 0x00000000, 0x40000000, 0x00000000,
+ 0x08D, 0x000FFFF0,
+ 0x90000004, 0x00000000, 0x40000000, 0x00000000,
+ 0x08D, 0x000FFFF0,
+ 0x90000005, 0x00000000, 0x40000000, 0x00000000,
+ 0x08D, 0x000FFFF0,
+ 0x90000006, 0x00000000, 0x40000000, 0x00000000,
+ 0x08D, 0x000FFFF0,
+ 0x90000008, 0x00000000, 0x40000000, 0x00000000,
+ 0x08D, 0x000FFFF0,
+ 0x9000000a, 0x00000000, 0x40000000, 0x00000000,
+ 0x08D, 0x000FFFF0,
+ 0x9000000b, 0x00000000, 0x40000000, 0x00000000,
+ 0x08D, 0x000FFFF0,
+ 0xA0000000, 0x00000000,
+ 0x08D, 0x000FFFF0,
+ 0xB0000000, 0x00000000,
+ 0x0EF, 0x00001000,
+ 0x80000001, 0x00000000, 0x40000000, 0x00000000,
+ 0x03A, 0x0000013C,
+ 0x03B, 0x00038023,
+ 0x90000002, 0x00000000, 0x40000000, 0x00000000,
+ 0x03A, 0x0000013C,
+ 0x03B, 0x00038023,
+ 0x90000003, 0x00000000, 0x40000000, 0x00000000,
+ 0x03A, 0x0000013C,
+ 0x03B, 0x00038023,
+ 0x90000004, 0x00000000, 0x40000000, 0x00000000,
+ 0x03A, 0x0000013C,
+ 0x03B, 0x00038023,
+ 0x90000005, 0x00000000, 0x40000000, 0x00000000,
+ 0x03A, 0x0000013C,
+ 0x03B, 0x00038023,
+ 0x90000006, 0x00000000, 0x40000000, 0x00000000,
+ 0x03A, 0x0000013C,
+ 0x03B, 0x00038023,
+ 0x90000008, 0x00000000, 0x40000000, 0x00000000,
+ 0x03A, 0x0000013C,
+ 0x03B, 0x00038023,
+ 0x9000000a, 0x00000000, 0x40000000, 0x00000000,
+ 0x03A, 0x0000013C,
+ 0x03B, 0x00038023,
+ 0x9000000b, 0x00000000, 0x40000000, 0x00000000,
+ 0x03A, 0x0000013C,
+ 0x03B, 0x00038023,
+ 0xA0000000, 0x00000000,
+ 0x03A, 0x0000013C,
+ 0x03B, 0x00038023,
+ 0xB0000000, 0x00000000,
+ 0x80000001, 0x00000000, 0x40000000, 0x00000000,
+ 0x03C, 0x00044000,
+ 0x90000002, 0x00000000, 0x40000000, 0x00000000,
+ 0x03C, 0x00048000,
+ 0x90000003, 0x00000000, 0x40000000, 0x00000000,
+ 0x03C, 0x00000000,
+ 0x90000004, 0x00000000, 0x40000000, 0x00000000,
+ 0x03C, 0x00088000,
+ 0x90000005, 0x00000000, 0x40000000, 0x00000000,
+ 0x03C, 0x00000000,
+ 0x90000006, 0x00000000, 0x40000000, 0x00000000,
+ 0x03C, 0x00044000,
+ 0x90000007, 0x00000000, 0x40000000, 0x00000000,
+ 0x03C, 0x00000000,
+ 0x90000008, 0x00000000, 0x40000000, 0x00000000,
+ 0x03C, 0x00088000,
+ 0x90000009, 0x00000000, 0x40000000, 0x00000000,
+ 0x03C, 0x00040000,
+ 0x9000000a, 0x00000000, 0x40000000, 0x00000000,
+ 0x03C, 0x00088000,
+ 0x9000000b, 0x00000000, 0x40000000, 0x00000000,
+ 0x03C, 0x00088000,
+ 0xA0000000, 0x00000000,
+ 0x03C, 0x00048000,
+ 0xB0000000, 0x00000000,
+ 0x03A, 0x0000013C,
+ 0x03B, 0x00030023,
+ 0x03C, 0x00048000,
+ 0x03A, 0x0000013C,
+ 0x03B, 0x00028623,
+ 0x03C, 0x00000000,
+ 0x03A, 0x0000013C,
+ 0x03B, 0x00021633,
+ 0x03C, 0x00000000,
+ 0x03A, 0x0000013C,
+ 0x03B, 0x0001C633,
+ 0x03C, 0x00000000,
+ 0x03A, 0x0000013C,
+ 0x03B, 0x00010293,
+ 0x03C, 0x00000000,
+ 0x03A, 0x0000013C,
+ 0x03B, 0x00009593,
+ 0x03C, 0x00000000,
+ 0x03A, 0x00000148,
+ 0x80000008, 0x00000000, 0x40000000, 0x00000000,
+ 0x03B, 0x00000F8B,
+ 0xA0000000, 0x00000000,
+ 0x03B, 0x0000078B,
+ 0xB0000000, 0x00000000,
+ 0x03C, 0x00000000,
+ 0x80000001, 0x00000000, 0x40000000, 0x00000000,
+ 0x03A, 0x0000013C,
+ 0x03B, 0x00078023,
+ 0x90000002, 0x00000000, 0x40000000, 0x00000000,
+ 0x03A, 0x0000013C,
+ 0x03B, 0x00078023,
+ 0x90000003, 0x00000000, 0x40000000, 0x00000000,
+ 0x03A, 0x0000013C,
+ 0x03B, 0x00078023,
+ 0x90000004, 0x00000000, 0x40000000, 0x00000000,
+ 0x03A, 0x0000013C,
+ 0x03B, 0x00078023,
+ 0x90000005, 0x00000000, 0x40000000, 0x00000000,
+ 0x03A, 0x0000013C,
+ 0x03B, 0x00078023,
+ 0x90000006, 0x00000000, 0x40000000, 0x00000000,
+ 0x03A, 0x0000013C,
+ 0x03B, 0x00078023,
+ 0x90000008, 0x00000000, 0x40000000, 0x00000000,
+ 0x03A, 0x0000013C,
+ 0x03B, 0x00078023,
+ 0x9000000a, 0x00000000, 0x40000000, 0x00000000,
+ 0x03A, 0x0000013C,
+ 0x03B, 0x00078023,
+ 0x9000000b, 0x00000000, 0x40000000, 0x00000000,
+ 0x03A, 0x0000013C,
+ 0x03B, 0x00078023,
+ 0xA0000000, 0x00000000,
+ 0x03A, 0x0000013C,
+ 0x03B, 0x00078023,
+ 0xB0000000, 0x00000000,
+ 0x80000001, 0x00000000, 0x40000000, 0x00000000,
+ 0x03C, 0x00020000,
+ 0x90000002, 0x00000000, 0x40000000, 0x00000000,
+ 0x03C, 0x00020000,
+ 0x90000003, 0x00000000, 0x40000000, 0x00000000,
+ 0x03C, 0x00000000,
+ 0x90000004, 0x00000000, 0x40000000, 0x00000000,
+ 0x03C, 0x00044000,
+ 0x90000005, 0x00000000, 0x40000000, 0x00000000,
+ 0x03C, 0x00000000,
+ 0x90000006, 0x00000000, 0x40000000, 0x00000000,
+ 0x03C, 0x00020000,
+ 0x90000007, 0x00000000, 0x40000000, 0x00000000,
+ 0x03C, 0x00000000,
+ 0x90000008, 0x00000000, 0x40000000, 0x00000000,
+ 0x03C, 0x00020000,
+ 0x90000009, 0x00000000, 0x40000000, 0x00000000,
+ 0x03C, 0x00000000,
+ 0x9000000a, 0x00000000, 0x40000000, 0x00000000,
+ 0x03C, 0x00044000,
+ 0x9000000b, 0x00000000, 0x40000000, 0x00000000,
+ 0x03C, 0x00044000,
+ 0xA0000000, 0x00000000,
+ 0x03C, 0x00024000,
+ 0xB0000000, 0x00000000,
+ 0x03A, 0x0000013C,
+ 0x03B, 0x00070023,
+ 0x03C, 0x00048000,
+ 0x03A, 0x0000013C,
+ 0x03B, 0x00068623,
+ 0x03C, 0x00000000,
+ 0x03A, 0x0000013C,
+ 0x03B, 0x00061633,
+ 0x03C, 0x00000000,
+ 0x03A, 0x0000013C,
+ 0x03B, 0x0005C633,
+ 0x03C, 0x00000000,
+ 0x03A, 0x0000013C,
+ 0x03B, 0x00050293,
+ 0x03C, 0x00000000,
+ 0x03A, 0x0000013C,
+ 0x03B, 0x00049593,
+ 0x03C, 0x00000000,
+ 0x03A, 0x00000148,
+ 0x80000008, 0x00000000, 0x40000000, 0x00000000,
+ 0x03B, 0x00040F8B,
+ 0xA0000000, 0x00000000,
+ 0x03B, 0x0004078B,
+ 0xB0000000, 0x00000000,
+ 0x03C, 0x00000000,
+ 0x80000001, 0x00000000, 0x40000000, 0x00000000,
+ 0x03A, 0x0000013C,
+ 0x03B, 0x000B8023,
+ 0x90000002, 0x00000000, 0x40000000, 0x00000000,
+ 0x03A, 0x0000013C,
+ 0x03B, 0x000B8023,
+ 0x90000003, 0x00000000, 0x40000000, 0x00000000,
+ 0x03A, 0x0000013C,
+ 0x03B, 0x000B8023,
+ 0x90000004, 0x00000000, 0x40000000, 0x00000000,
+ 0x03A, 0x0000013C,
+ 0x03B, 0x000B8023,
+ 0x90000005, 0x00000000, 0x40000000, 0x00000000,
+ 0x03A, 0x0000013C,
+ 0x03B, 0x000B8023,
+ 0x90000006, 0x00000000, 0x40000000, 0x00000000,
+ 0x03A, 0x0000013C,
+ 0x03B, 0x000B8023,
+ 0x90000008, 0x00000000, 0x40000000, 0x00000000,
+ 0x03A, 0x0000013C,
+ 0x03B, 0x000B8023,
+ 0x9000000a, 0x00000000, 0x40000000, 0x00000000,
+ 0x03A, 0x0000013C,
+ 0x03B, 0x000B8023,
+ 0x9000000b, 0x00000000, 0x40000000, 0x00000000,
+ 0x03A, 0x0000013C,
+ 0x03B, 0x000B8023,
+ 0xA0000000, 0x00000000,
+ 0x03A, 0x0000013C,
+ 0x03B, 0x000B8023,
+ 0xB0000000, 0x00000000,
+ 0x80000001, 0x00000000, 0x40000000, 0x00000000,
+ 0x03C, 0x00004000,
+ 0x90000002, 0x00000000, 0x40000000, 0x00000000,
+ 0x03C, 0x00060000,
+ 0x90000003, 0x00000000, 0x40000000, 0x00000000,
+ 0x03C, 0x00000000,
+ 0x90000004, 0x00000000, 0x40000000, 0x00000000,
+ 0x03C, 0x00024000,
+ 0x90000005, 0x00000000, 0x40000000, 0x00000000,
+ 0x03C, 0x00000000,
+ 0x90000006, 0x00000000, 0x40000000, 0x00000000,
+ 0x03C, 0x00004000,
+ 0x90000007, 0x00000000, 0x40000000, 0x00000000,
+ 0x03C, 0x00000000,
+ 0x90000008, 0x00000000, 0x40000000, 0x00000000,
+ 0x03C, 0x00060000,
+ 0x90000009, 0x00000000, 0x40000000, 0x00000000,
+ 0x03C, 0x00020000,
+ 0x9000000a, 0x00000000, 0x40000000, 0x00000000,
+ 0x03C, 0x00024000,
+ 0x9000000b, 0x00000000, 0x40000000, 0x00000000,
+ 0x03C, 0x00024000,
+ 0xA0000000, 0x00000000,
+ 0x03C, 0x00004000,
+ 0xB0000000, 0x00000000,
+ 0x03A, 0x0000013C,
+ 0x03B, 0x000B0023,
+ 0x80000004, 0x00000000, 0x40000000, 0x00000000,
+ 0x03C, 0x00020000,
+ 0x9000000a, 0x00000000, 0x40000000, 0x00000000,
+ 0x03C, 0x00020000,
+ 0x9000000b, 0x00000000, 0x40000000, 0x00000000,
+ 0x03C, 0x00020000,
+ 0xA0000000, 0x00000000,
+ 0x03C, 0x00020000,
+ 0xB0000000, 0x00000000,
+ 0x03A, 0x0000013C,
+ 0x03B, 0x000A8623,
+ 0x03C, 0x00000000,
+ 0x03A, 0x0000013C,
+ 0x03B, 0x000A1633,
+ 0x03C, 0x00000000,
+ 0x03A, 0x0000013C,
+ 0x03B, 0x0009C633,
+ 0x03C, 0x00000000,
+ 0x03A, 0x0000013C,
+ 0x03B, 0x00090293,
+ 0x03C, 0x00000000,
+ 0x03A, 0x0000013C,
+ 0x03B, 0x00089593,
+ 0x03C, 0x00000000,
+ 0x03A, 0x00000148,
+ 0x80000008, 0x00000000, 0x40000000, 0x00000000,
+ 0x03B, 0x0008138B,
+ 0xA0000000, 0x00000000,
+ 0x03B, 0x0008078B,
+ 0xB0000000, 0x00000000,
+ 0x03C, 0x00000000,
+ 0x0EF, 0x00000000,
+ 0x0EF, 0x00000800,
+ 0x03B, 0x00000000,
+ 0x80000001, 0x00000000, 0x40000000, 0x00000000,
+ 0x03A, 0x00000803,
+ 0x90000002, 0x00000000, 0x40000000, 0x00000000,
+ 0x03A, 0x00000803,
+ 0x90000003, 0x00000000, 0x40000000, 0x00000000,
+ 0x03A, 0x00000803,
+ 0x90000004, 0x00000000, 0x40000000, 0x00000000,
+ 0x03A, 0x00000803,
+ 0x90000005, 0x00000000, 0x40000000, 0x00000000,
+ 0x03A, 0x00000803,
+ 0x90000006, 0x00000000, 0x40000000, 0x00000000,
+ 0x03A, 0x00000803,
+ 0x90000007, 0x00000000, 0x40000000, 0x00000000,
+ 0x03A, 0x00001003,
+ 0x90000008, 0x00000000, 0x40000000, 0x00000000,
+ 0x03A, 0x00001003,
+ 0x90000009, 0x00000000, 0x40000000, 0x00000000,
+ 0x03A, 0x00001803,
+ 0x9000000a, 0x00000000, 0x40000000, 0x00000000,
+ 0x03A, 0x00000803,
+ 0x9000000b, 0x00000000, 0x40000000, 0x00000000,
+ 0x03A, 0x00000803,
+ 0xA0000000, 0x00000000,
+ 0x03A, 0x00000803,
+ 0xB0000000, 0x00000000,
+ 0x03B, 0x00040000,
+ 0x80000001, 0x00000000, 0x40000000, 0x00000000,
+ 0x03A, 0x00001000,
+ 0x90000002, 0x00000000, 0x40000000, 0x00000000,
+ 0x03A, 0x00001002,
+ 0x90000003, 0x00000000, 0x40000000, 0x00000000,
+ 0x03A, 0x00000000,
+ 0x90000004, 0x00000000, 0x40000000, 0x00000000,
+ 0x03A, 0x00001000,
+ 0x90000005, 0x00000000, 0x40000000, 0x00000000,
+ 0x03A, 0x00000001,
+ 0x90000006, 0x00000000, 0x40000000, 0x00000000,
+ 0x03A, 0x00001000,
+ 0x90000007, 0x00000000, 0x40000000, 0x00000000,
+ 0x03A, 0x00000000,
+ 0x90000008, 0x00000000, 0x40000000, 0x00000000,
+ 0x03A, 0x00000802,
+ 0x90000009, 0x00000000, 0x40000000, 0x00000000,
+ 0x03A, 0x00001803,
+ 0x9000000a, 0x00000000, 0x40000000, 0x00000000,
+ 0x03A, 0x00001000,
+ 0x9000000b, 0x00000000, 0x40000000, 0x00000000,
+ 0x03A, 0x00001000,
+ 0xA0000000, 0x00000000,
+ 0x03A, 0x00001000,
+ 0xB0000000, 0x00000000,
+ 0x03B, 0x00080000,
+ 0x80000007, 0x00000000, 0x40000000, 0x00000000,
+ 0x03A, 0x00001802,
+ 0x90000008, 0x00000000, 0x40000000, 0x00000000,
+ 0x03A, 0x00001000,
+ 0x90000009, 0x00000000, 0x40000000, 0x00000000,
+ 0x03A, 0x00000802,
+ 0xA0000000, 0x00000000,
+ 0x03A, 0x00001002,
+ 0xB0000000, 0x00000000,
+ 0x0EF, 0x00000000,
+ 0x80000001, 0x00000000, 0x40000000, 0x00000000,
+ 0x90000002, 0x00000000, 0x40000000, 0x00000000,
+ 0x90000003, 0x00000000, 0x40000000, 0x00000000,
+ 0x90000004, 0x00000000, 0x40000000, 0x00000000,
+ 0x90000005, 0x00000000, 0x40000000, 0x00000000,
+ 0x90000006, 0x00000000, 0x40000000, 0x00000000,
+ 0x90000008, 0x00000000, 0x40000000, 0x00000000,
+ 0x9000000a, 0x00000000, 0x40000000, 0x00000000,
+ 0x9000000b, 0x00000000, 0x40000000, 0x00000000,
+ 0xA0000000, 0x00000000,
+ 0xB0000000, 0x00000000,
+ 0x018, 0x00013124,
+ 0x0EF, 0x00000100,
+ 0x80000001, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0004A3EB,
+ 0x034, 0x0004938B,
+ 0x034, 0x000481AC,
+ 0x034, 0x000471A9,
+ 0x034, 0x000460AC,
+ 0x034, 0x000450A9,
+ 0x034, 0x0004402E,
+ 0x034, 0x0004302B,
+ 0x034, 0x00042028,
+ 0x034, 0x0004100B,
+ 0x034, 0x00040008,
+ 0x90000002, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0004A3AD,
+ 0x034, 0x0004938A,
+ 0x034, 0x0004818C,
+ 0x034, 0x00047189,
+ 0x034, 0x0004606D,
+ 0x034, 0x0004506A,
+ 0x034, 0x0004402C,
+ 0x034, 0x00043029,
+ 0x034, 0x00042026,
+ 0x034, 0x00041009,
+ 0x034, 0x00040006,
+ 0x90000003, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0004A3AD,
+ 0x034, 0x0004938A,
+ 0x034, 0x0004818C,
+ 0x034, 0x00047189,
+ 0x034, 0x0004606D,
+ 0x034, 0x0004506A,
+ 0x034, 0x0004402C,
+ 0x034, 0x00043029,
+ 0x034, 0x00042026,
+ 0x034, 0x00041009,
+ 0x034, 0x00040006,
+ 0x90000004, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0004A3AD,
+ 0x034, 0x0004938A,
+ 0x034, 0x0004818C,
+ 0x034, 0x00047189,
+ 0x034, 0x0004606D,
+ 0x034, 0x0004506A,
+ 0x034, 0x0004402C,
+ 0x034, 0x00043029,
+ 0x034, 0x00042026,
+ 0x034, 0x00041009,
+ 0x034, 0x00040006,
+ 0x90000005, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0004A3AD,
+ 0x034, 0x0004938A,
+ 0x034, 0x0004818C,
+ 0x034, 0x00047189,
+ 0x034, 0x0004606D,
+ 0x034, 0x0004506A,
+ 0x034, 0x0004402C,
+ 0x034, 0x00043029,
+ 0x034, 0x00042026,
+ 0x034, 0x00041009,
+ 0x034, 0x00040006,
+ 0x90000008, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0004A3F4,
+ 0x034, 0x000493D2,
+ 0x034, 0x000482D1,
+ 0x034, 0x000471F1,
+ 0x034, 0x000461EE,
+ 0x034, 0x000451EB,
+ 0x034, 0x000441E8,
+ 0x034, 0x0004314B,
+ 0x034, 0x00042148,
+ 0x034, 0x0004104B,
+ 0x034, 0x00040048,
+ 0x90000009, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0004AFF7,
+ 0x034, 0x00049FF6,
+ 0x034, 0x00048FF3,
+ 0x034, 0x00047FF0,
+ 0x034, 0x00046FED,
+ 0x034, 0x00045FEA,
+ 0x034, 0x00044FE7,
+ 0x034, 0x00043CB1,
+ 0x034, 0x00042CAE,
+ 0x034, 0x00041CAB,
+ 0x034, 0x00040CA8,
+ 0x9000000a, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0004A3AD,
+ 0x034, 0x0004938A,
+ 0x034, 0x0004818C,
+ 0x034, 0x00047189,
+ 0x034, 0x0004606D,
+ 0x034, 0x0004506A,
+ 0x034, 0x0004402C,
+ 0x034, 0x00043029,
+ 0x034, 0x00042026,
+ 0x034, 0x00041009,
+ 0x034, 0x00040006,
+ 0x9000000b, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0004A3AD,
+ 0x034, 0x0004938A,
+ 0x034, 0x0004818C,
+ 0x034, 0x00047189,
+ 0x034, 0x0004606D,
+ 0x034, 0x0004506A,
+ 0x034, 0x0004402C,
+ 0x034, 0x00043029,
+ 0x034, 0x00042026,
+ 0x034, 0x00041009,
+ 0x034, 0x00040006,
+ 0xA0000000, 0x00000000,
+ 0x034, 0x0004AFF4,
+ 0x034, 0x00049FF1,
+ 0x034, 0x00048FEE,
+ 0x034, 0x00047FEB,
+ 0x034, 0x00046FE8,
+ 0x034, 0x00045DEA,
+ 0x034, 0x00044CED,
+ 0x034, 0x00043CEA,
+ 0x034, 0x00042C6C,
+ 0x034, 0x00041C69,
+ 0x034, 0x00040C2B,
+ 0xB0000000, 0x00000000,
+ 0x80000001, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0002A3EE,
+ 0x034, 0x000293EB,
+ 0x034, 0x0002838B,
+ 0x034, 0x000271AC,
+ 0x034, 0x000261A9,
+ 0x034, 0x000250AC,
+ 0x034, 0x000240A9,
+ 0x034, 0x000230A6,
+ 0x034, 0x0002202C,
+ 0x034, 0x00021029,
+ 0x034, 0x00020026,
+ 0x90000002, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0002A3AD,
+ 0x034, 0x0002938A,
+ 0x034, 0x0002818C,
+ 0x034, 0x00027189,
+ 0x034, 0x0002606D,
+ 0x034, 0x0002504C,
+ 0x034, 0x0002402C,
+ 0x034, 0x00023029,
+ 0x034, 0x00022026,
+ 0x034, 0x00021023,
+ 0x034, 0x00020006,
+ 0x90000003, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0002A3AD,
+ 0x034, 0x0002938A,
+ 0x034, 0x0002818C,
+ 0x034, 0x00027189,
+ 0x034, 0x0002606D,
+ 0x034, 0x0002504C,
+ 0x034, 0x0002402C,
+ 0x034, 0x00023029,
+ 0x034, 0x00022026,
+ 0x034, 0x00021023,
+ 0x034, 0x00020006,
+ 0x90000004, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0002A3AD,
+ 0x034, 0x0002938A,
+ 0x034, 0x0002818C,
+ 0x034, 0x00027189,
+ 0x034, 0x0002606D,
+ 0x034, 0x0002504C,
+ 0x034, 0x0002402C,
+ 0x034, 0x00023029,
+ 0x034, 0x00022026,
+ 0x034, 0x00021023,
+ 0x034, 0x00020006,
+ 0x90000005, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0002A3AD,
+ 0x034, 0x0002938A,
+ 0x034, 0x0002818C,
+ 0x034, 0x00027189,
+ 0x034, 0x0002606D,
+ 0x034, 0x0002504C,
+ 0x034, 0x0002402C,
+ 0x034, 0x00023029,
+ 0x034, 0x00022026,
+ 0x034, 0x00021023,
+ 0x034, 0x00020006,
+ 0x90000008, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0002A3F5,
+ 0x034, 0x000293D2,
+ 0x034, 0x000283CE,
+ 0x034, 0x00027290,
+ 0x034, 0x0002628D,
+ 0x034, 0x0002528A,
+ 0x034, 0x00024287,
+ 0x034, 0x0002308D,
+ 0x034, 0x0002208A,
+ 0x034, 0x00021087,
+ 0x034, 0x00020048,
+ 0x90000009, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0002AFF7,
+ 0x034, 0x00029FF6,
+ 0x034, 0x00028FF3,
+ 0x034, 0x00027FF0,
+ 0x034, 0x00026FED,
+ 0x034, 0x00025FEA,
+ 0x034, 0x00024FE7,
+ 0x034, 0x00023DEA,
+ 0x034, 0x00022DE7,
+ 0x034, 0x00021DE4,
+ 0x034, 0x00020D48,
+ 0x9000000a, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0002A3AD,
+ 0x034, 0x0002938A,
+ 0x034, 0x0002818C,
+ 0x034, 0x00027189,
+ 0x034, 0x0002606D,
+ 0x034, 0x0002504C,
+ 0x034, 0x0002402C,
+ 0x034, 0x00023029,
+ 0x034, 0x00022026,
+ 0x034, 0x00021023,
+ 0x034, 0x00020006,
+ 0x9000000b, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0002A3AD,
+ 0x034, 0x0002938A,
+ 0x034, 0x0002818C,
+ 0x034, 0x00027189,
+ 0x034, 0x0002606D,
+ 0x034, 0x0002504C,
+ 0x034, 0x0002402C,
+ 0x034, 0x00023029,
+ 0x034, 0x00022026,
+ 0x034, 0x00021023,
+ 0x034, 0x00020006,
+ 0xA0000000, 0x00000000,
+ 0x034, 0x0002AFF4,
+ 0x034, 0x00029FF1,
+ 0x034, 0x00028FEE,
+ 0x034, 0x00027FEB,
+ 0x034, 0x00026FE8,
+ 0x034, 0x00025DEA,
+ 0x034, 0x00024CED,
+ 0x034, 0x00023CEA,
+ 0x034, 0x00022C6C,
+ 0x034, 0x00021C69,
+ 0x034, 0x00020C2B,
+ 0xB0000000, 0x00000000,
+ 0x80000001, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0000A3EF,
+ 0x034, 0x000093EC,
+ 0x034, 0x0000838C,
+ 0x034, 0x000071AD,
+ 0x034, 0x000061AA,
+ 0x034, 0x000050AD,
+ 0x034, 0x000040AA,
+ 0x034, 0x0000306A,
+ 0x034, 0x0000202D,
+ 0x034, 0x0000102A,
+ 0x034, 0x00000027,
+ 0x90000002, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0000A3EE,
+ 0x034, 0x000093AC,
+ 0x034, 0x0000838A,
+ 0x034, 0x0000718C,
+ 0x034, 0x00006189,
+ 0x034, 0x0000506D,
+ 0x034, 0x0000406A,
+ 0x034, 0x0000302C,
+ 0x034, 0x00002029,
+ 0x034, 0x00001026,
+ 0x034, 0x00000023,
+ 0x90000003, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0000A3EE,
+ 0x034, 0x000093AC,
+ 0x034, 0x0000838A,
+ 0x034, 0x0000718C,
+ 0x034, 0x00006189,
+ 0x034, 0x0000506D,
+ 0x034, 0x0000406A,
+ 0x034, 0x0000302C,
+ 0x034, 0x00002029,
+ 0x034, 0x00001026,
+ 0x034, 0x00000023,
+ 0x90000004, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0000A3EE,
+ 0x034, 0x000093AC,
+ 0x034, 0x0000838A,
+ 0x034, 0x0000718C,
+ 0x034, 0x00006189,
+ 0x034, 0x0000506D,
+ 0x034, 0x0000406A,
+ 0x034, 0x0000302C,
+ 0x034, 0x00002029,
+ 0x034, 0x00001026,
+ 0x034, 0x00000023,
+ 0x90000005, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0000A3EE,
+ 0x034, 0x000093AC,
+ 0x034, 0x0000838A,
+ 0x034, 0x0000718C,
+ 0x034, 0x00006189,
+ 0x034, 0x0000506D,
+ 0x034, 0x0000406A,
+ 0x034, 0x0000302C,
+ 0x034, 0x00002029,
+ 0x034, 0x00001026,
+ 0x034, 0x00000023,
+ 0x90000008, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0000A3F1,
+ 0x034, 0x000092B1,
+ 0x034, 0x000081CF,
+ 0x034, 0x00007170,
+ 0x034, 0x0000616D,
+ 0x034, 0x0000516A,
+ 0x034, 0x00004167,
+ 0x034, 0x0000302F,
+ 0x034, 0x0000202C,
+ 0x034, 0x00001029,
+ 0x034, 0x00000026,
+ 0x90000009, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0000AFF7,
+ 0x034, 0x00009FF6,
+ 0x034, 0x00008FF3,
+ 0x034, 0x00007FF0,
+ 0x034, 0x00006FED,
+ 0x034, 0x00005FEA,
+ 0x034, 0x00004FE7,
+ 0x034, 0x00003EC7,
+ 0x034, 0x00002EC4,
+ 0x034, 0x00001D4B,
+ 0x034, 0x00000D48,
+ 0x9000000a, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0000A3EE,
+ 0x034, 0x000093AC,
+ 0x034, 0x0000838A,
+ 0x034, 0x0000718C,
+ 0x034, 0x00006189,
+ 0x034, 0x0000506D,
+ 0x034, 0x0000406A,
+ 0x034, 0x0000302C,
+ 0x034, 0x00002029,
+ 0x034, 0x00001026,
+ 0x034, 0x00000023,
+ 0x9000000b, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0000A3EE,
+ 0x034, 0x000093AC,
+ 0x034, 0x0000838A,
+ 0x034, 0x0000718C,
+ 0x034, 0x00006189,
+ 0x034, 0x0000506D,
+ 0x034, 0x0000406A,
+ 0x034, 0x0000302C,
+ 0x034, 0x00002029,
+ 0x034, 0x00001026,
+ 0x034, 0x00000023,
+ 0xA0000000, 0x00000000,
+ 0x034, 0x0000AFF4,
+ 0x034, 0x00009FF1,
+ 0x034, 0x00008FEE,
+ 0x034, 0x00007FEB,
+ 0x034, 0x00006FE8,
+ 0x034, 0x00005DEA,
+ 0x034, 0x00004CED,
+ 0x034, 0x00003CEA,
+ 0x034, 0x00002C6C,
+ 0x034, 0x00001C69,
+ 0x034, 0x00000C2B,
+ 0xB0000000, 0x00000000,
+ 0x80000001, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x000CA3EB,
+ 0x034, 0x000C938B,
+ 0x034, 0x000C81AC,
+ 0x034, 0x000C71A9,
+ 0x034, 0x000C60AC,
+ 0x034, 0x000C50A9,
+ 0x034, 0x000C402E,
+ 0x034, 0x000C302B,
+ 0x034, 0x000C2028,
+ 0x034, 0x000C100B,
+ 0x034, 0x000C0008,
+ 0x90000002, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x000CA3AD,
+ 0x034, 0x000C938A,
+ 0x034, 0x000C818C,
+ 0x034, 0x000C7189,
+ 0x034, 0x000C606D,
+ 0x034, 0x000C506A,
+ 0x034, 0x000C402C,
+ 0x034, 0x000C3029,
+ 0x034, 0x000C2026,
+ 0x034, 0x000C1009,
+ 0x034, 0x000C0006,
+ 0x90000003, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x000CA3AD,
+ 0x034, 0x000C938A,
+ 0x034, 0x000C818C,
+ 0x034, 0x000C7189,
+ 0x034, 0x000C606D,
+ 0x034, 0x000C506A,
+ 0x034, 0x000C402C,
+ 0x034, 0x000C3029,
+ 0x034, 0x000C2026,
+ 0x034, 0x000C1009,
+ 0x034, 0x000C0006,
+ 0x90000004, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x000CA3AD,
+ 0x034, 0x000C938A,
+ 0x034, 0x000C818C,
+ 0x034, 0x000C7189,
+ 0x034, 0x000C606D,
+ 0x034, 0x000C506A,
+ 0x034, 0x000C402C,
+ 0x034, 0x000C3029,
+ 0x034, 0x000C2026,
+ 0x034, 0x000C1009,
+ 0x034, 0x000C0006,
+ 0x90000005, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x000CA3AD,
+ 0x034, 0x000C938A,
+ 0x034, 0x000C818C,
+ 0x034, 0x000C7189,
+ 0x034, 0x000C606D,
+ 0x034, 0x000C506A,
+ 0x034, 0x000C402C,
+ 0x034, 0x000C3029,
+ 0x034, 0x000C2026,
+ 0x034, 0x000C1009,
+ 0x034, 0x000C0006,
+ 0x90000008, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x000CA3F4,
+ 0x034, 0x000C93D2,
+ 0x034, 0x000C82D1,
+ 0x034, 0x000C71F1,
+ 0x034, 0x000C61EE,
+ 0x034, 0x000C51EB,
+ 0x034, 0x000C41E8,
+ 0x034, 0x000C314B,
+ 0x034, 0x000C2148,
+ 0x034, 0x000C104B,
+ 0x034, 0x000C0048,
+ 0x90000009, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x000CAFF7,
+ 0x034, 0x000C9FF6,
+ 0x034, 0x000C8FF3,
+ 0x034, 0x000C7FF0,
+ 0x034, 0x000C6FED,
+ 0x034, 0x000C5FEA,
+ 0x034, 0x000C4FE7,
+ 0x034, 0x000C3CB1,
+ 0x034, 0x000C2CAE,
+ 0x034, 0x000C1CAB,
+ 0x034, 0x000C0CA8,
+ 0x9000000a, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x000CA3AD,
+ 0x034, 0x000C938A,
+ 0x034, 0x000C818C,
+ 0x034, 0x000C7189,
+ 0x034, 0x000C606D,
+ 0x034, 0x000C506A,
+ 0x034, 0x000C402C,
+ 0x034, 0x000C3029,
+ 0x034, 0x000C2026,
+ 0x034, 0x000C1009,
+ 0x034, 0x000C0006,
+ 0x9000000b, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x000CA3AD,
+ 0x034, 0x000C938A,
+ 0x034, 0x000C818C,
+ 0x034, 0x000C7189,
+ 0x034, 0x000C606D,
+ 0x034, 0x000C506A,
+ 0x034, 0x000C402C,
+ 0x034, 0x000C3029,
+ 0x034, 0x000C2026,
+ 0x034, 0x000C1009,
+ 0x034, 0x000C0006,
+ 0xA0000000, 0x00000000,
+ 0x034, 0x000CA794,
+ 0x034, 0x000C9791,
+ 0x034, 0x000C878E,
+ 0x034, 0x000C778B,
+ 0x034, 0x000C658D,
+ 0x034, 0x000C558A,
+ 0x034, 0x000C448D,
+ 0x034, 0x000C348A,
+ 0x034, 0x000C244C,
+ 0x034, 0x000C1449,
+ 0x034, 0x000C042B,
+ 0xB0000000, 0x00000000,
+ 0x80000001, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x000AA3EE,
+ 0x034, 0x000A93EB,
+ 0x034, 0x000A838B,
+ 0x034, 0x000A71AC,
+ 0x034, 0x000A61A9,
+ 0x034, 0x000A50AC,
+ 0x034, 0x000A40A9,
+ 0x034, 0x000A30A6,
+ 0x034, 0x000A202C,
+ 0x034, 0x000A1029,
+ 0x034, 0x000A0026,
+ 0x90000002, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x000AA3AD,
+ 0x034, 0x000A938A,
+ 0x034, 0x000A818C,
+ 0x034, 0x000A7189,
+ 0x034, 0x000A606D,
+ 0x034, 0x000A504C,
+ 0x034, 0x000A402C,
+ 0x034, 0x000A3029,
+ 0x034, 0x000A2026,
+ 0x034, 0x000A1023,
+ 0x034, 0x000A0006,
+ 0x90000003, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x000AA3AD,
+ 0x034, 0x000A938A,
+ 0x034, 0x000A818C,
+ 0x034, 0x000A7189,
+ 0x034, 0x000A606D,
+ 0x034, 0x000A504C,
+ 0x034, 0x000A402C,
+ 0x034, 0x000A3029,
+ 0x034, 0x000A2026,
+ 0x034, 0x000A1023,
+ 0x034, 0x000A0006,
+ 0x90000004, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x000AA3AD,
+ 0x034, 0x000A938A,
+ 0x034, 0x000A818C,
+ 0x034, 0x000A7189,
+ 0x034, 0x000A606D,
+ 0x034, 0x000A504C,
+ 0x034, 0x000A402C,
+ 0x034, 0x000A3029,
+ 0x034, 0x000A2026,
+ 0x034, 0x000A1023,
+ 0x034, 0x000A0006,
+ 0x90000005, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x000AA3AD,
+ 0x034, 0x000A938A,
+ 0x034, 0x000A818C,
+ 0x034, 0x000A7189,
+ 0x034, 0x000A606D,
+ 0x034, 0x000A504C,
+ 0x034, 0x000A402C,
+ 0x034, 0x000A3029,
+ 0x034, 0x000A2026,
+ 0x034, 0x000A1023,
+ 0x034, 0x000A0006,
+ 0x90000008, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x000AA3F5,
+ 0x034, 0x000A93D2,
+ 0x034, 0x000A83CE,
+ 0x034, 0x000A7290,
+ 0x034, 0x000A628D,
+ 0x034, 0x000A528A,
+ 0x034, 0x000A4287,
+ 0x034, 0x000A308D,
+ 0x034, 0x000A208A,
+ 0x034, 0x000A1087,
+ 0x034, 0x000A0048,
+ 0x90000009, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x000AAFF7,
+ 0x034, 0x000A9FF6,
+ 0x034, 0x000A8FF3,
+ 0x034, 0x000A7FF0,
+ 0x034, 0x000A6FED,
+ 0x034, 0x000A5FEA,
+ 0x034, 0x000A4FE7,
+ 0x034, 0x000A3DEA,
+ 0x034, 0x000A2DE7,
+ 0x034, 0x000A1DE4,
+ 0x034, 0x000A0D48,
+ 0x9000000a, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x000AA3AD,
+ 0x034, 0x000A938A,
+ 0x034, 0x000A818C,
+ 0x034, 0x000A7189,
+ 0x034, 0x000A606D,
+ 0x034, 0x000A504C,
+ 0x034, 0x000A402C,
+ 0x034, 0x000A3029,
+ 0x034, 0x000A2026,
+ 0x034, 0x000A1023,
+ 0x034, 0x000A0006,
+ 0x9000000b, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x000AA3AD,
+ 0x034, 0x000A938A,
+ 0x034, 0x000A818C,
+ 0x034, 0x000A7189,
+ 0x034, 0x000A606D,
+ 0x034, 0x000A504C,
+ 0x034, 0x000A402C,
+ 0x034, 0x000A3029,
+ 0x034, 0x000A2026,
+ 0x034, 0x000A1023,
+ 0x034, 0x000A0006,
+ 0xA0000000, 0x00000000,
+ 0x034, 0x000AA794,
+ 0x034, 0x000A9791,
+ 0x034, 0x000A878E,
+ 0x034, 0x000A778B,
+ 0x034, 0x000A658D,
+ 0x034, 0x000A558A,
+ 0x034, 0x000A448D,
+ 0x034, 0x000A348A,
+ 0x034, 0x000A244C,
+ 0x034, 0x000A1449,
+ 0x034, 0x000A042B,
+ 0xB0000000, 0x00000000,
+ 0x80000001, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0008A3EF,
+ 0x034, 0x000893EC,
+ 0x034, 0x0008838C,
+ 0x034, 0x000871AD,
+ 0x034, 0x000861AA,
+ 0x034, 0x000850AD,
+ 0x034, 0x000840AA,
+ 0x034, 0x0008306A,
+ 0x034, 0x0008202D,
+ 0x034, 0x0008102A,
+ 0x034, 0x00080027,
+ 0x90000002, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0008A3EE,
+ 0x034, 0x000893AC,
+ 0x034, 0x0008838A,
+ 0x034, 0x0008718C,
+ 0x034, 0x00086189,
+ 0x034, 0x0008506D,
+ 0x034, 0x0008406A,
+ 0x034, 0x0008302C,
+ 0x034, 0x00082029,
+ 0x034, 0x00081026,
+ 0x034, 0x00080023,
+ 0x90000003, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0008A3EE,
+ 0x034, 0x000893AC,
+ 0x034, 0x0008838A,
+ 0x034, 0x0008718C,
+ 0x034, 0x00086189,
+ 0x034, 0x0008506D,
+ 0x034, 0x0008406A,
+ 0x034, 0x0008302C,
+ 0x034, 0x00082029,
+ 0x034, 0x00081026,
+ 0x034, 0x00080023,
+ 0x90000004, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0008A3EE,
+ 0x034, 0x000893AC,
+ 0x034, 0x0008838A,
+ 0x034, 0x0008718C,
+ 0x034, 0x00086189,
+ 0x034, 0x0008506D,
+ 0x034, 0x0008406A,
+ 0x034, 0x0008302C,
+ 0x034, 0x00082029,
+ 0x034, 0x00081026,
+ 0x034, 0x00080023,
+ 0x90000005, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0008A3EE,
+ 0x034, 0x000893AC,
+ 0x034, 0x0008838A,
+ 0x034, 0x0008718C,
+ 0x034, 0x00086189,
+ 0x034, 0x0008506D,
+ 0x034, 0x0008406A,
+ 0x034, 0x0008302C,
+ 0x034, 0x00082029,
+ 0x034, 0x00081026,
+ 0x034, 0x00080023,
+ 0x90000008, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0008A3F1,
+ 0x034, 0x000892B1,
+ 0x034, 0x000881CF,
+ 0x034, 0x00087170,
+ 0x034, 0x0008616D,
+ 0x034, 0x0008516A,
+ 0x034, 0x00084167,
+ 0x034, 0x0008302F,
+ 0x034, 0x0008202C,
+ 0x034, 0x00081029,
+ 0x034, 0x00080026,
+ 0x90000009, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0008AFF7,
+ 0x034, 0x00089FF6,
+ 0x034, 0x00088FF3,
+ 0x034, 0x00087FF0,
+ 0x034, 0x00086FED,
+ 0x034, 0x00085FEA,
+ 0x034, 0x00084FE7,
+ 0x034, 0x00083EC7,
+ 0x034, 0x00082EC4,
+ 0x034, 0x00081D4B,
+ 0x034, 0x00080D48,
+ 0x9000000a, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0008A3EE,
+ 0x034, 0x000893AC,
+ 0x034, 0x0008838A,
+ 0x034, 0x0008718C,
+ 0x034, 0x00086189,
+ 0x034, 0x0008506D,
+ 0x034, 0x0008406A,
+ 0x034, 0x0008302C,
+ 0x034, 0x00082029,
+ 0x034, 0x00081026,
+ 0x034, 0x00080023,
+ 0x9000000b, 0x00000000, 0x40000000, 0x00000000,
+ 0x034, 0x0008A3EE,
+ 0x034, 0x000893AC,
+ 0x034, 0x0008838A,
+ 0x034, 0x0008718C,
+ 0x034, 0x00086189,
+ 0x034, 0x0008506D,
+ 0x034, 0x0008406A,
+ 0x034, 0x0008302C,
+ 0x034, 0x00082029,
+ 0x034, 0x00081026,
+ 0x034, 0x00080023,
+ 0xA0000000, 0x00000000,
+ 0x034, 0x0008A794,
+ 0x034, 0x00089791,
+ 0x034, 0x0008878E,
+ 0x034, 0x0008778B,
+ 0x034, 0x0008658D,
+ 0x034, 0x0008558A,
+ 0x034, 0x0008448D,
+ 0x034, 0x0008348A,
+ 0x034, 0x0008244C,
+ 0x034, 0x00081449,
+ 0x034, 0x0008042B,
+ 0xB0000000, 0x00000000,
+ 0x0EF, 0x00000000,
+ 0x80000001, 0x00000000, 0x40000000, 0x00000000,
+ 0x0DF, 0x00000001,
+ 0x90000002, 0x00000000, 0x40000000, 0x00000000,
+ 0x0DF, 0x00000001,
+ 0x90000003, 0x00000000, 0x40000000, 0x00000000,
+ 0x0DF, 0x00000001,
+ 0x90000004, 0x00000000, 0x40000000, 0x00000000,
+ 0x0DF, 0x00000001,
+ 0x90000005, 0x00000000, 0x40000000, 0x00000000,
+ 0x0DF, 0x00000001,
+ 0x90000008, 0x00000000, 0x40000000, 0x00000000,
+ 0x0DF, 0x00000001,
+ 0x9000000a, 0x00000000, 0x40000000, 0x00000000,
+ 0x0DF, 0x00000001,
+ 0x9000000b, 0x00000000, 0x40000000, 0x00000000,
+ 0x0DF, 0x00000001,
+ 0xA0000000, 0x00000000,
+ 0x0DF, 0x00000000,
+ 0xB0000000, 0x00000000,
+ 0x018, 0x0001712A,
+ 0x0EF, 0x00000040,
+ 0x80000001, 0x00000000, 0x40000000, 0x00000000,
+ 0x035, 0x000006CC,
+ 0x035, 0x000086CC,
+ 0x035, 0x000106CC,
+ 0x035, 0x000206CC,
+ 0x035, 0x000286CC,
+ 0x035, 0x000306CC,
+ 0x035, 0x000406CC,
+ 0x035, 0x000486CC,
+ 0x035, 0x000506CC,
+ 0x035, 0x000806CC,
+ 0x035, 0x000886CC,
+ 0x035, 0x000906CC,
+ 0x035, 0x000A06CC,
+ 0x035, 0x000A86CC,
+ 0x035, 0x000B06CC,
+ 0x035, 0x000C06CC,
+ 0x035, 0x000C86CC,
+ 0x035, 0x000D06CC,
+ 0x90000002, 0x00000000, 0x40000000, 0x00000000,
+ 0x035, 0x000006CC,
+ 0x035, 0x000086CC,
+ 0x035, 0x000106CC,
+ 0x035, 0x000206CC,
+ 0x035, 0x000286CC,
+ 0x035, 0x000306CC,
+ 0x035, 0x000406CC,
+ 0x035, 0x000486CC,
+ 0x035, 0x000506CC,
+ 0x035, 0x000806CC,
+ 0x035, 0x000886CC,
+ 0x035, 0x000906CC,
+ 0x035, 0x000A06CC,
+ 0x035, 0x000A86CC,
+ 0x035, 0x000B06CC,
+ 0x035, 0x000C06CC,
+ 0x035, 0x000C86CC,
+ 0x035, 0x000D06CC,
+ 0x90000003, 0x00000000, 0x40000000, 0x00000000,
+ 0x035, 0x000006CC,
+ 0x035, 0x000086CC,
+ 0x035, 0x000106CC,
+ 0x035, 0x000206CC,
+ 0x035, 0x000286CC,
+ 0x035, 0x000306CC,
+ 0x035, 0x000406CC,
+ 0x035, 0x000486CC,
+ 0x035, 0x000506CC,
+ 0x035, 0x000806CC,
+ 0x035, 0x000886CC,
+ 0x035, 0x000906CC,
+ 0x035, 0x000A06CC,
+ 0x035, 0x000A86CC,
+ 0x035, 0x000B06CC,
+ 0x035, 0x000C06CC,
+ 0x035, 0x000C86CC,
+ 0x035, 0x000D06CC,
+ 0x90000004, 0x00000000, 0x40000000, 0x00000000,
+ 0x035, 0x000006CC,
+ 0x035, 0x000086CC,
+ 0x035, 0x000106CC,
+ 0x035, 0x000206CC,
+ 0x035, 0x000286CC,
+ 0x035, 0x000306CC,
+ 0x035, 0x000406CC,
+ 0x035, 0x000486CC,
+ 0x035, 0x000506CC,
+ 0x035, 0x000806CC,
+ 0x035, 0x000886CC,
+ 0x035, 0x000906CC,
+ 0x035, 0x000A06CC,
+ 0x035, 0x000A86CC,
+ 0x035, 0x000B06CC,
+ 0x035, 0x000C06CC,
+ 0x035, 0x000C86CC,
+ 0x035, 0x000D06CC,
+ 0x90000005, 0x00000000, 0x40000000, 0x00000000,
+ 0x035, 0x000006CC,
+ 0x035, 0x000086CC,
+ 0x035, 0x000106CC,
+ 0x035, 0x000206CC,
+ 0x035, 0x000286CC,
+ 0x035, 0x000306CC,
+ 0x035, 0x000406CC,
+ 0x035, 0x000486CC,
+ 0x035, 0x000506CC,
+ 0x035, 0x000806CC,
+ 0x035, 0x000886CC,
+ 0x035, 0x000906CC,
+ 0x035, 0x000A06CC,
+ 0x035, 0x000A86CC,
+ 0x035, 0x000B06CC,
+ 0x035, 0x000C06CC,
+ 0x035, 0x000C86CC,
+ 0x035, 0x000D06CC,
+ 0x90000008, 0x00000000, 0x40000000, 0x00000000,
+ 0x035, 0x000006CC,
+ 0x035, 0x000086CC,
+ 0x035, 0x000106CC,
+ 0x035, 0x000206CC,
+ 0x035, 0x000286CC,
+ 0x035, 0x000306CC,
+ 0x035, 0x000406CC,
+ 0x035, 0x000486CC,
+ 0x035, 0x000506CC,
+ 0x035, 0x000806CC,
+ 0x035, 0x000886CC,
+ 0x035, 0x000906CC,
+ 0x035, 0x000A06CC,
+ 0x035, 0x000A86CC,
+ 0x035, 0x000B06CC,
+ 0x035, 0x000C06CC,
+ 0x035, 0x000C86CC,
+ 0x035, 0x000D06CC,
+ 0x9000000a, 0x00000000, 0x40000000, 0x00000000,
+ 0x035, 0x000006CC,
+ 0x035, 0x000086CC,
+ 0x035, 0x000106CC,
+ 0x035, 0x000206CC,
+ 0x035, 0x000286CC,
+ 0x035, 0x000306CC,
+ 0x035, 0x000406CC,
+ 0x035, 0x000486CC,
+ 0x035, 0x000506CC,
+ 0x035, 0x000806CC,
+ 0x035, 0x000886CC,
+ 0x035, 0x000906CC,
+ 0x035, 0x000A06CC,
+ 0x035, 0x000A86CC,
+ 0x035, 0x000B06CC,
+ 0x035, 0x000C06CC,
+ 0x035, 0x000C86CC,
+ 0x035, 0x000D06CC,
+ 0x9000000b, 0x00000000, 0x40000000, 0x00000000,
+ 0x035, 0x000006CC,
+ 0x035, 0x000086CC,
+ 0x035, 0x000106CC,
+ 0x035, 0x000206CC,
+ 0x035, 0x000286CC,
+ 0x035, 0x000306CC,
+ 0x035, 0x000406CC,
+ 0x035, 0x000486CC,
+ 0x035, 0x000506CC,
+ 0x035, 0x000806CC,
+ 0x035, 0x000886CC,
+ 0x035, 0x000906CC,
+ 0x035, 0x000A06CC,
+ 0x035, 0x000A86CC,
+ 0x035, 0x000B06CC,
+ 0x035, 0x000C06CC,
+ 0x035, 0x000C86CC,
+ 0x035, 0x000D06CC,
+ 0xA0000000, 0x00000000,
+ 0x035, 0x00000484,
+ 0x035, 0x00008484,
+ 0x035, 0x00010484,
+ 0x035, 0x00020584,
+ 0x035, 0x00028584,
+ 0x035, 0x00030584,
+ 0x035, 0x00040584,
+ 0x035, 0x00048584,
+ 0x035, 0x00050584,
+ 0x035, 0x000805FB,
+ 0x035, 0x000885FB,
+ 0x035, 0x000905FB,
+ 0x035, 0x000A05FB,
+ 0x035, 0x000A85FB,
+ 0x035, 0x000B05FB,
+ 0x035, 0x000C05FB,
+ 0x035, 0x000C85FB,
+ 0x035, 0x000D05FB,
+ 0xB0000000, 0x00000000,
+ 0x0EF, 0x00000000,
+ 0x80000001, 0x00000000, 0x40000000, 0x00000000,
+ 0x0DF, 0x00000001,
+ 0x90000002, 0x00000000, 0x40000000, 0x00000000,
+ 0x0DF, 0x00000001,
+ 0x90000003, 0x00000000, 0x40000000, 0x00000000,
+ 0x0DF, 0x00000001,
+ 0x90000004, 0x00000000, 0x40000000, 0x00000000,
+ 0x0DF, 0x00000001,
+ 0x90000005, 0x00000000, 0x40000000, 0x00000000,
+ 0x0DF, 0x00000001,
+ 0x90000008, 0x00000000, 0x40000000, 0x00000000,
+ 0x0DF, 0x00000001,
+ 0x9000000a, 0x00000000, 0x40000000, 0x00000000,
+ 0x0DF, 0x00000001,
+ 0x9000000b, 0x00000000, 0x40000000, 0x00000000,
+ 0x0DF, 0x00000001,
+ 0xA0000000, 0x00000000,
+ 0x0DF, 0x00000000,
+ 0xB0000000, 0x00000000,
+ 0x018, 0x0001712A,
+ 0x0EF, 0x00000010,
+ 0x80000001, 0x00000000, 0x40000000, 0x00000000,
+ 0x036, 0x00000473,
+ 0x036, 0x00008473,
+ 0x036, 0x00010473,
+ 0x036, 0x00020473,
+ 0x036, 0x00028473,
+ 0x036, 0x00030473,
+ 0x036, 0x00040473,
+ 0x036, 0x00048473,
+ 0x036, 0x00050473,
+ 0x036, 0x00080473,
+ 0x036, 0x00088473,
+ 0x036, 0x00090473,
+ 0x036, 0x000A0473,
+ 0x036, 0x000A8473,
+ 0x036, 0x000B0473,
+ 0x036, 0x000C0473,
+ 0x036, 0x000C8473,
+ 0x036, 0x000D0473,
+ 0x90000002, 0x00000000, 0x40000000, 0x00000000,
+ 0x036, 0x00000475,
+ 0x036, 0x00008475,
+ 0x036, 0x00010475,
+ 0x036, 0x00020475,
+ 0x036, 0x00028475,
+ 0x036, 0x00030475,
+ 0x036, 0x00040475,
+ 0x036, 0x00048475,
+ 0x036, 0x00050475,
+ 0x036, 0x00080475,
+ 0x036, 0x00088475,
+ 0x036, 0x00090475,
+ 0x036, 0x000A0475,
+ 0x036, 0x000A8475,
+ 0x036, 0x000B0475,
+ 0x036, 0x000C0475,
+ 0x036, 0x000C8475,
+ 0x036, 0x000D0475,
+ 0x90000003, 0x00000000, 0x40000000, 0x00000000,
+ 0x036, 0x00000475,
+ 0x036, 0x00008475,
+ 0x036, 0x00010475,
+ 0x036, 0x00020475,
+ 0x036, 0x00028475,
+ 0x036, 0x00030475,
+ 0x036, 0x00040475,
+ 0x036, 0x00048475,
+ 0x036, 0x00050475,
+ 0x036, 0x00080475,
+ 0x036, 0x00088475,
+ 0x036, 0x00090475,
+ 0x036, 0x000A0475,
+ 0x036, 0x000A8475,
+ 0x036, 0x000B0475,
+ 0x036, 0x000C0475,
+ 0x036, 0x000C8475,
+ 0x036, 0x000D0475,
+ 0x90000004, 0x00000000, 0x40000000, 0x00000000,
+ 0x036, 0x00000475,
+ 0x036, 0x00008475,
+ 0x036, 0x00010475,
+ 0x036, 0x00020475,
+ 0x036, 0x00028475,
+ 0x036, 0x00030475,
+ 0x036, 0x00040475,
+ 0x036, 0x00048475,
+ 0x036, 0x00050475,
+ 0x036, 0x00080475,
+ 0x036, 0x00088475,
+ 0x036, 0x00090475,
+ 0x036, 0x000A0475,
+ 0x036, 0x000A8475,
+ 0x036, 0x000B0475,
+ 0x036, 0x000C0475,
+ 0x036, 0x000C8475,
+ 0x036, 0x000D0475,
+ 0x90000005, 0x00000000, 0x40000000, 0x00000000,
+ 0x036, 0x00000475,
+ 0x036, 0x00008475,
+ 0x036, 0x00010475,
+ 0x036, 0x00020475,
+ 0x036, 0x00028475,
+ 0x036, 0x00030475,
+ 0x036, 0x00040475,
+ 0x036, 0x00048475,
+ 0x036, 0x00050475,
+ 0x036, 0x00080475,
+ 0x036, 0x00088475,
+ 0x036, 0x00090475,
+ 0x036, 0x000A0475,
+ 0x036, 0x000A8475,
+ 0x036, 0x000B0475,
+ 0x036, 0x000C0475,
+ 0x036, 0x000C8475,
+ 0x036, 0x000D0475,
+ 0x90000008, 0x00000000, 0x40000000, 0x00000000,
+ 0x036, 0x00000475,
+ 0x036, 0x00008475,
+ 0x036, 0x00010475,
+ 0x036, 0x00020475,
+ 0x036, 0x00028475,
+ 0x036, 0x00030475,
+ 0x036, 0x00040475,
+ 0x036, 0x00048475,
+ 0x036, 0x00050475,
+ 0x036, 0x00080475,
+ 0x036, 0x00088475,
+ 0x036, 0x00090475,
+ 0x036, 0x000A0475,
+ 0x036, 0x000A8475,
+ 0x036, 0x000B0475,
+ 0x036, 0x000C0475,
+ 0x036, 0x000C8475,
+ 0x036, 0x000D0475,
+ 0x9000000a, 0x00000000, 0x40000000, 0x00000000,
+ 0x036, 0x00000475,
+ 0x036, 0x00008475,
+ 0x036, 0x00010475,
+ 0x036, 0x00020475,
+ 0x036, 0x00028475,
+ 0x036, 0x00030475,
+ 0x036, 0x00040475,
+ 0x036, 0x00048475,
+ 0x036, 0x00050475,
+ 0x036, 0x00080475,
+ 0x036, 0x00088475,
+ 0x036, 0x00090475,
+ 0x036, 0x000A0475,
+ 0x036, 0x000A8475,
+ 0x036, 0x000B0475,
+ 0x036, 0x000C0475,
+ 0x036, 0x000C8475,
+ 0x036, 0x000D0475,
+ 0x9000000b, 0x00000000, 0x40000000, 0x00000000,
+ 0x036, 0x00000475,
+ 0x036, 0x00008475,
+ 0x036, 0x00010475,
+ 0x036, 0x00020475,
+ 0x036, 0x00028475,
+ 0x036, 0x00030475,
+ 0x036, 0x00040475,
+ 0x036, 0x00048475,
+ 0x036, 0x00050475,
+ 0x036, 0x00080475,
+ 0x036, 0x00088475,
+ 0x036, 0x00090475,
+ 0x036, 0x000A0475,
+ 0x036, 0x000A8475,
+ 0x036, 0x000B0475,
+ 0x036, 0x000C0475,
+ 0x036, 0x000C8475,
+ 0x036, 0x000D0475,
+ 0xA0000000, 0x00000000,
+ 0x036, 0x00000474,
+ 0x036, 0x00008474,
+ 0x036, 0x00010474,
+ 0x036, 0x00020474,
+ 0x036, 0x00028474,
+ 0x036, 0x00030474,
+ 0x036, 0x00040474,
+ 0x036, 0x00048474,
+ 0x036, 0x00050474,
+ 0x036, 0x00080474,
+ 0x036, 0x00088474,
+ 0x036, 0x00090474,
+ 0x036, 0x000A0474,
+ 0x036, 0x000A8474,
+ 0x036, 0x000B0474,
+ 0x036, 0x000C0474,
+ 0x036, 0x000C8474,
+ 0x036, 0x000D0474,
+ 0xB0000000, 0x00000000,
+ 0x0EF, 0x00000000,
+ 0x80000001, 0x00000000, 0x40000000, 0x00000000,
+ 0x90000002, 0x00000000, 0x40000000, 0x00000000,
+ 0x90000003, 0x00000000, 0x40000000, 0x00000000,
+ 0x90000004, 0x00000000, 0x40000000, 0x00000000,
+ 0x90000005, 0x00000000, 0x40000000, 0x00000000,
+ 0x90000008, 0x00000000, 0x40000000, 0x00000000,
+ 0x9000000a, 0x00000000, 0x40000000, 0x00000000,
+ 0x9000000b, 0x00000000, 0x40000000, 0x00000000,
+ 0xA0000000, 0x00000000,
+ 0x0EF, 0x00000004,
+ 0x037, 0x00000000,
+ 0x038, 0x0000514E,
+ 0x037, 0x00004000,
+ 0x038, 0x0000514E,
+ 0x037, 0x00008000,
+ 0x038, 0x0000514E,
+ 0x037, 0x00010000,
+ 0x038, 0x0000514E,
+ 0x037, 0x00014000,
+ 0x038, 0x0000514E,
+ 0x037, 0x00018000,
+ 0x038, 0x0000514E,
+ 0x037, 0x0001C000,
+ 0x038, 0x0000514E,
+ 0x037, 0x00020000,
+ 0x038, 0x0000514E,
+ 0x037, 0x00024000,
+ 0x038, 0x0000514E,
+ 0x037, 0x00028000,
+ 0x038, 0x0000514E,
+ 0x037, 0x0002C000,
+ 0x038, 0x0000714E,
+ 0x037, 0x00030000,
+ 0x038, 0x0000514E,
+ 0x037, 0x00034000,
+ 0x038, 0x0000514E,
+ 0x037, 0x00038000,
+ 0x038, 0x0000514E,
+ 0x037, 0x0003C000,
+ 0x038, 0x0000514E,
+ 0x037, 0x00040000,
+ 0x038, 0x0000514E,
+ 0x037, 0x00044000,
+ 0x038, 0x0000514E,
+ 0x037, 0x00048000,
+ 0x038, 0x0000514E,
+ 0x037, 0x00080000,
+ 0x038, 0x00005ECE,
+ 0x037, 0x00084000,
+ 0x038, 0x00005ECE,
+ 0x037, 0x00088000,
+ 0x038, 0x00005ECE,
+ 0x037, 0x00090000,
+ 0x038, 0x00005ECE,
+ 0x037, 0x00094000,
+ 0x038, 0x00005ECE,
+ 0x037, 0x00098000,
+ 0x038, 0x00005ECE,
+ 0x037, 0x0009C000,
+ 0x038, 0x00005ECE,
+ 0x037, 0x000A0000,
+ 0x038, 0x00005ECE,
+ 0x037, 0x000A4000,
+ 0x038, 0x00005ECE,
+ 0x037, 0x000A8000,
+ 0x038, 0x00005ECE,
+ 0x037, 0x000AC000,
+ 0x038, 0x00005ECE,
+ 0x037, 0x000B0000,
+ 0x038, 0x00005ECE,
+ 0x037, 0x000B4000,
+ 0x038, 0x00005ECE,
+ 0x037, 0x000B8000,
+ 0x038, 0x00005ECE,
+ 0x037, 0x000BC000,
+ 0x038, 0x00005ECE,
+ 0x037, 0x000C0000,
+ 0x038, 0x00005ECE,
+ 0x037, 0x000C4000,
+ 0x038, 0x00005ECE,
+ 0x037, 0x000C8000,
+ 0x038, 0x00005ECE,
+ 0x0EF, 0x00000000,
+ 0xB0000000, 0x00000000,
+ 0x0EF, 0x00000008,
+ 0x80000001, 0x00000000, 0x40000000, 0x00000000,
+ 0x90000002, 0x00000000, 0x40000000, 0x00000000,
+ 0x90000003, 0x00000000, 0x40000000, 0x00000000,
+ 0x90000004, 0x00000000, 0x40000000, 0x00000000,
+ 0x90000005, 0x00000000, 0x40000000, 0x00000000,
+ 0x90000008, 0x00000000, 0x40000000, 0x00000000,
+ 0x9000000a, 0x00000000, 0x40000000, 0x00000000,
+ 0x9000000b, 0x00000000, 0x40000000, 0x00000000,
+ 0xA0000000, 0x00000000,
+ 0x03C, 0x0000007D,
+ 0x03C, 0x0000047D,
+ 0x03C, 0x0000087D,
+ 0x03C, 0x0000107D,
+ 0x03C, 0x0000147D,
+ 0x03C, 0x0000187D,
+ 0xB0000000, 0x00000000,
+ 0x80000001, 0x00000000, 0x40000000, 0x00000000,
+ 0x03C, 0x00000275,
+ 0x03C, 0x00000542,
+ 0x03C, 0x00000821,
+ 0x03C, 0x00001275,
+ 0x03C, 0x00001542,
+ 0x03C, 0x00001821,
+ 0x03C, 0x00002275,
+ 0x03C, 0x00002542,
+ 0x03C, 0x00002821,
+ 0x90000002, 0x00000000, 0x40000000, 0x00000000,
+ 0x03C, 0x0000027F,
+ 0x03C, 0x00000542,
+ 0x03C, 0x00000821,
+ 0x03C, 0x0000127F,
+ 0x03C, 0x00001542,
+ 0x03C, 0x00001821,
+ 0x03C, 0x0000227F,
+ 0x03C, 0x00002542,
+ 0x03C, 0x00002821,
+ 0x90000003, 0x00000000, 0x40000000, 0x00000000,
+ 0x03C, 0x0000027F,
+ 0x03C, 0x00000542,
+ 0x03C, 0x00000821,
+ 0x03C, 0x0000127F,
+ 0x03C, 0x00001542,
+ 0x03C, 0x00001821,
+ 0x03C, 0x0000227F,
+ 0x03C, 0x00002542,
+ 0x03C, 0x00002821,
+ 0x90000004, 0x00000000, 0x40000000, 0x00000000,
+ 0x03C, 0x0000027F,
+ 0x03C, 0x00000542,
+ 0x03C, 0x00000821,
+ 0x03C, 0x0000127F,
+ 0x03C, 0x00001542,
+ 0x03C, 0x00001821,
+ 0x03C, 0x0000227F,
+ 0x03C, 0x00002542,
+ 0x03C, 0x00002821,
+ 0x90000005, 0x00000000, 0x40000000, 0x00000000,
+ 0x03C, 0x0000027F,
+ 0x03C, 0x00000542,
+ 0x03C, 0x00000821,
+ 0x03C, 0x0000127F,
+ 0x03C, 0x00001542,
+ 0x03C, 0x00001821,
+ 0x03C, 0x0000227F,
+ 0x03C, 0x00002542,
+ 0x03C, 0x00002821,
+ 0x90000008, 0x00000000, 0x40000000, 0x00000000,
+ 0x03C, 0x0000027F,
+ 0x03C, 0x00000542,
+ 0x03C, 0x00000821,
+ 0x03C, 0x0000127F,
+ 0x03C, 0x00001542,
+ 0x03C, 0x00001821,
+ 0x03C, 0x0000227F,
+ 0x03C, 0x00002542,
+ 0x03C, 0x00002821,
+ 0x9000000a, 0x00000000, 0x40000000, 0x00000000,
+ 0x03C, 0x0000027F,
+ 0x03C, 0x00000542,
+ 0x03C, 0x00000821,
+ 0x03C, 0x0000127F,
+ 0x03C, 0x00001542,
+ 0x03C, 0x00001821,
+ 0x03C, 0x0000227F,
+ 0x03C, 0x00002542,
+ 0x03C, 0x00002821,
+ 0x9000000b, 0x00000000, 0x40000000, 0x00000000,
+ 0x03C, 0x0000027F,
+ 0x03C, 0x00000542,
+ 0x03C, 0x00000821,
+ 0x03C, 0x0000127F,
+ 0x03C, 0x00001542,
+ 0x03C, 0x00001821,
+ 0x03C, 0x0000227F,
+ 0x03C, 0x00002542,
+ 0x03C, 0x00002821,
+ 0xA0000000, 0x00000000,
+ 0x03C, 0x0000037E,
+ 0x03C, 0x00000575,
+ 0x03C, 0x00000971,
+ 0x03C, 0x0000127E,
+ 0x03C, 0x00001575,
+ 0x03C, 0x00001871,
+ 0x03C, 0x0000217E,
+ 0x03C, 0x00002575,
+ 0x03C, 0x00002871,
+ 0xB0000000, 0x00000000,
+ 0x0EF, 0x00000000,
+ 0x061, 0x000C0D47,
+ 0x062, 0x0000133C,
+ 0x80000001, 0x00000000, 0x40000000, 0x00000000,
+ 0x063, 0x000750E7,
+ 0x90000002, 0x00000000, 0x40000000, 0x00000000,
+ 0x063, 0x000750E7,
+ 0x90000003, 0x00000000, 0x40000000, 0x00000000,
+ 0x063, 0x000750E7,
+ 0x90000004, 0x00000000, 0x40000000, 0x00000000,
+ 0x063, 0x000750E7,
+ 0x90000005, 0x00000000, 0x40000000, 0x00000000,
+ 0x063, 0x000750E7,
+ 0x90000008, 0x00000000, 0x40000000, 0x00000000,
+ 0x063, 0x000750E7,
+ 0x9000000a, 0x00000000, 0x40000000, 0x00000000,
+ 0x063, 0x000750E7,
+ 0x9000000b, 0x00000000, 0x40000000, 0x00000000,
+ 0x063, 0x000750E7,
+ 0xA0000000, 0x00000000,
+ 0x063, 0x0007D0E7,
+ 0xB0000000, 0x00000000,
+ 0x064, 0x00014FEC,
+ 0x80000001, 0x00000000, 0x40000000, 0x00000000,
+ 0x065, 0x000920D0,
+ 0x90000002, 0x00000000, 0x40000000, 0x00000000,
+ 0x065, 0x000920D0,
+ 0x90000003, 0x00000000, 0x40000000, 0x00000000,
+ 0x065, 0x000920D0,
+ 0x90000004, 0x00000000, 0x40000000, 0x00000000,
+ 0x065, 0x000920D0,
+ 0x90000005, 0x00000000, 0x40000000, 0x00000000,
+ 0x065, 0x000920D0,
+ 0x90000008, 0x00000000, 0x40000000, 0x00000000,
+ 0x065, 0x000920D0,
+ 0x9000000a, 0x00000000, 0x40000000, 0x00000000,
+ 0x065, 0x000920D0,
+ 0x9000000b, 0x00000000, 0x40000000, 0x00000000,
+ 0x065, 0x000920D0,
+ 0xA0000000, 0x00000000,
+ 0x065, 0x000923FF,
+ 0xB0000000, 0x00000000,
+ 0x066, 0x00000040,
+ 0x057, 0x00050000,
+ 0x056, 0x00051DF0,
+ 0x80000001, 0x00000000, 0x40000000, 0x00000000,
+ 0x90000002, 0x00000000, 0x40000000, 0x00000000,
+ 0x90000003, 0x00000000, 0x40000000, 0x00000000,
+ 0x90000004, 0x00000000, 0x40000000, 0x00000000,
+ 0x90000005, 0x00000000, 0x40000000, 0x00000000,
+ 0x90000008, 0x00000000, 0x40000000, 0x00000000,
+ 0x9000000a, 0x00000000, 0x40000000, 0x00000000,
+ 0x9000000b, 0x00000000, 0x40000000, 0x00000000,
+ 0xA0000000, 0x00000000,
+ 0x055, 0x00082060,
+ 0xB0000000, 0x00000000,
+};
+
+RTW_DECL_TABLE_RF_RADIO(rtw8814a_rf_d, D);
+
+static const struct rtw_txpwr_lmt_cfg_pair rtw8814a_txpwr_lmt[] = {
+ { 0, 0, 0, 0, 1, 36, },
+ { 2, 0, 0, 0, 1, 32, },
+ { 1, 0, 0, 0, 1, 32, },
+ { 0, 0, 0, 0, 2, 36, },
+ { 2, 0, 0, 0, 2, 32, },
+ { 1, 0, 0, 0, 2, 32, },
+ { 0, 0, 0, 0, 3, 36, },
+ { 2, 0, 0, 0, 3, 32, },
+ { 1, 0, 0, 0, 3, 32, },
+ { 0, 0, 0, 0, 4, 36, },
+ { 2, 0, 0, 0, 4, 32, },
+ { 1, 0, 0, 0, 4, 32, },
+ { 0, 0, 0, 0, 5, 36, },
+ { 2, 0, 0, 0, 5, 32, },
+ { 1, 0, 0, 0, 5, 32, },
+ { 0, 0, 0, 0, 6, 36, },
+ { 2, 0, 0, 0, 6, 32, },
+ { 1, 0, 0, 0, 6, 32, },
+ { 0, 0, 0, 0, 7, 36, },
+ { 2, 0, 0, 0, 7, 32, },
+ { 1, 0, 0, 0, 7, 32, },
+ { 0, 0, 0, 0, 8, 36, },
+ { 2, 0, 0, 0, 8, 32, },
+ { 1, 0, 0, 0, 8, 32, },
+ { 0, 0, 0, 0, 9, 36, },
+ { 2, 0, 0, 0, 9, 32, },
+ { 1, 0, 0, 0, 9, 32, },
+ { 0, 0, 0, 0, 10, 36, },
+ { 2, 0, 0, 0, 10, 32, },
+ { 1, 0, 0, 0, 10, 32, },
+ { 0, 0, 0, 0, 11, 36, },
+ { 2, 0, 0, 0, 11, 32, },
+ { 1, 0, 0, 0, 11, 32, },
+ { 0, 0, 0, 0, 12, 63, },
+ { 2, 0, 0, 0, 12, 32, },
+ { 1, 0, 0, 0, 12, 32, },
+ { 0, 0, 0, 0, 13, 63, },
+ { 2, 0, 0, 0, 13, 32, },
+ { 1, 0, 0, 0, 13, 32, },
+ { 0, 0, 0, 0, 14, 63, },
+ { 2, 0, 0, 0, 14, 63, },
+ { 1, 0, 0, 0, 14, 32, },
+ { 0, 0, 0, 1, 1, 34, },
+ { 2, 0, 0, 1, 1, 32, },
+ { 1, 0, 0, 1, 1, 32, },
+ { 0, 0, 0, 1, 2, 36, },
+ { 2, 0, 0, 1, 2, 32, },
+ { 1, 0, 0, 1, 2, 32, },
+ { 0, 0, 0, 1, 3, 36, },
+ { 2, 0, 0, 1, 3, 32, },
+ { 1, 0, 0, 1, 3, 32, },
+ { 0, 0, 0, 1, 4, 36, },
+ { 2, 0, 0, 1, 4, 32, },
+ { 1, 0, 0, 1, 4, 32, },
+ { 0, 0, 0, 1, 5, 36, },
+ { 2, 0, 0, 1, 5, 32, },
+ { 1, 0, 0, 1, 5, 32, },
+ { 0, 0, 0, 1, 6, 36, },
+ { 2, 0, 0, 1, 6, 32, },
+ { 1, 0, 0, 1, 6, 32, },
+ { 0, 0, 0, 1, 7, 36, },
+ { 2, 0, 0, 1, 7, 32, },
+ { 1, 0, 0, 1, 7, 32, },
+ { 0, 0, 0, 1, 8, 36, },
+ { 2, 0, 0, 1, 8, 32, },
+ { 1, 0, 0, 1, 8, 32, },
+ { 0, 0, 0, 1, 9, 36, },
+ { 2, 0, 0, 1, 9, 32, },
+ { 1, 0, 0, 1, 9, 32, },
+ { 0, 0, 0, 1, 10, 36, },
+ { 2, 0, 0, 1, 10, 32, },
+ { 1, 0, 0, 1, 10, 32, },
+ { 0, 0, 0, 1, 11, 32, },
+ { 2, 0, 0, 1, 11, 32, },
+ { 1, 0, 0, 1, 11, 32, },
+ { 0, 0, 0, 1, 12, 63, },
+ { 2, 0, 0, 1, 12, 32, },
+ { 1, 0, 0, 1, 12, 32, },
+ { 0, 0, 0, 1, 13, 63, },
+ { 2, 0, 0, 1, 13, 32, },
+ { 1, 0, 0, 1, 13, 32, },
+ { 0, 0, 0, 1, 14, 63, },
+ { 2, 0, 0, 1, 14, 63, },
+ { 1, 0, 0, 1, 14, 63, },
+ { 0, 0, 0, 2, 1, 34, },
+ { 2, 0, 0, 2, 1, 32, },
+ { 1, 0, 0, 2, 1, 32, },
+ { 0, 0, 0, 2, 2, 36, },
+ { 2, 0, 0, 2, 2, 32, },
+ { 1, 0, 0, 2, 2, 32, },
+ { 0, 0, 0, 2, 3, 36, },
+ { 2, 0, 0, 2, 3, 32, },
+ { 1, 0, 0, 2, 3, 32, },
+ { 0, 0, 0, 2, 4, 36, },
+ { 2, 0, 0, 2, 4, 32, },
+ { 1, 0, 0, 2, 4, 32, },
+ { 0, 0, 0, 2, 5, 36, },
+ { 2, 0, 0, 2, 5, 32, },
+ { 1, 0, 0, 2, 5, 32, },
+ { 0, 0, 0, 2, 6, 36, },
+ { 2, 0, 0, 2, 6, 32, },
+ { 1, 0, 0, 2, 6, 32, },
+ { 0, 0, 0, 2, 7, 36, },
+ { 2, 0, 0, 2, 7, 32, },
+ { 1, 0, 0, 2, 7, 32, },
+ { 0, 0, 0, 2, 8, 36, },
+ { 2, 0, 0, 2, 8, 32, },
+ { 1, 0, 0, 2, 8, 32, },
+ { 0, 0, 0, 2, 9, 36, },
+ { 2, 0, 0, 2, 9, 32, },
+ { 1, 0, 0, 2, 9, 32, },
+ { 0, 0, 0, 2, 10, 36, },
+ { 2, 0, 0, 2, 10, 32, },
+ { 1, 0, 0, 2, 10, 32, },
+ { 0, 0, 0, 2, 11, 32, },
+ { 2, 0, 0, 2, 11, 32, },
+ { 1, 0, 0, 2, 11, 32, },
+ { 0, 0, 0, 2, 12, 63, },
+ { 2, 0, 0, 2, 12, 32, },
+ { 1, 0, 0, 2, 12, 32, },
+ { 0, 0, 0, 2, 13, 63, },
+ { 2, 0, 0, 2, 13, 32, },
+ { 1, 0, 0, 2, 13, 32, },
+ { 0, 0, 0, 2, 14, 63, },
+ { 2, 0, 0, 2, 14, 63, },
+ { 1, 0, 0, 2, 14, 63, },
+ { 0, 0, 0, 3, 1, 32, },
+ { 2, 0, 0, 3, 1, 30, },
+ { 1, 0, 0, 3, 1, 30, },
+ { 0, 0, 0, 3, 2, 34, },
+ { 2, 0, 0, 3, 2, 30, },
+ { 1, 0, 0, 3, 2, 30, },
+ { 0, 0, 0, 3, 3, 34, },
+ { 2, 0, 0, 3, 3, 30, },
+ { 1, 0, 0, 3, 3, 30, },
+ { 0, 0, 0, 3, 4, 34, },
+ { 2, 0, 0, 3, 4, 30, },
+ { 1, 0, 0, 3, 4, 30, },
+ { 0, 0, 0, 3, 5, 34, },
+ { 2, 0, 0, 3, 5, 30, },
+ { 1, 0, 0, 3, 5, 30, },
+ { 0, 0, 0, 3, 6, 34, },
+ { 2, 0, 0, 3, 6, 30, },
+ { 1, 0, 0, 3, 6, 30, },
+ { 0, 0, 0, 3, 7, 34, },
+ { 2, 0, 0, 3, 7, 30, },
+ { 1, 0, 0, 3, 7, 30, },
+ { 0, 0, 0, 3, 8, 34, },
+ { 2, 0, 0, 3, 8, 30, },
+ { 1, 0, 0, 3, 8, 30, },
+ { 0, 0, 0, 3, 9, 34, },
+ { 2, 0, 0, 3, 9, 30, },
+ { 1, 0, 0, 3, 9, 30, },
+ { 0, 0, 0, 3, 10, 34, },
+ { 2, 0, 0, 3, 10, 30, },
+ { 1, 0, 0, 3, 10, 30, },
+ { 0, 0, 0, 3, 11, 30, },
+ { 2, 0, 0, 3, 11, 30, },
+ { 1, 0, 0, 3, 11, 30, },
+ { 0, 0, 0, 3, 12, 63, },
+ { 2, 0, 0, 3, 12, 30, },
+ { 1, 0, 0, 3, 12, 30, },
+ { 0, 0, 0, 3, 13, 63, },
+ { 2, 0, 0, 3, 13, 30, },
+ { 1, 0, 0, 3, 13, 30, },
+ { 0, 0, 0, 3, 14, 63, },
+ { 2, 0, 0, 3, 14, 63, },
+ { 1, 0, 0, 3, 14, 63, },
+ { 0, 0, 0, 6, 1, 30, },
+ { 2, 0, 0, 6, 1, 28, },
+ { 1, 0, 0, 6, 1, 28, },
+ { 0, 0, 0, 6, 2, 32, },
+ { 2, 0, 0, 6, 2, 28, },
+ { 1, 0, 0, 6, 2, 28, },
+ { 0, 0, 0, 6, 3, 32, },
+ { 2, 0, 0, 6, 3, 28, },
+ { 1, 0, 0, 6, 3, 28, },
+ { 0, 0, 0, 6, 4, 32, },
+ { 2, 0, 0, 6, 4, 28, },
+ { 1, 0, 0, 6, 4, 28, },
+ { 0, 0, 0, 6, 5, 32, },
+ { 2, 0, 0, 6, 5, 28, },
+ { 1, 0, 0, 6, 5, 28, },
+ { 0, 0, 0, 6, 6, 32, },
+ { 2, 0, 0, 6, 6, 28, },
+ { 1, 0, 0, 6, 6, 28, },
+ { 0, 0, 0, 6, 7, 32, },
+ { 2, 0, 0, 6, 7, 28, },
+ { 1, 0, 0, 6, 7, 28, },
+ { 0, 0, 0, 6, 8, 32, },
+ { 2, 0, 0, 6, 8, 28, },
+ { 1, 0, 0, 6, 8, 28, },
+ { 0, 0, 0, 6, 9, 32, },
+ { 2, 0, 0, 6, 9, 28, },
+ { 1, 0, 0, 6, 9, 28, },
+ { 0, 0, 0, 6, 10, 32, },
+ { 2, 0, 0, 6, 10, 28, },
+ { 1, 0, 0, 6, 10, 28, },
+ { 0, 0, 0, 6, 11, 28, },
+ { 2, 0, 0, 6, 11, 28, },
+ { 1, 0, 0, 6, 11, 28, },
+ { 0, 0, 0, 6, 12, 63, },
+ { 2, 0, 0, 6, 12, 28, },
+ { 1, 0, 0, 6, 12, 28, },
+ { 0, 0, 0, 6, 13, 63, },
+ { 2, 0, 0, 6, 13, 28, },
+ { 1, 0, 0, 6, 13, 28, },
+ { 0, 0, 0, 6, 14, 63, },
+ { 2, 0, 0, 6, 14, 63, },
+ { 1, 0, 0, 6, 14, 63, },
+ { 0, 0, 0, 7, 1, 28, },
+ { 2, 0, 0, 7, 1, 26, },
+ { 1, 0, 0, 7, 1, 26, },
+ { 0, 0, 0, 7, 2, 30, },
+ { 2, 0, 0, 7, 2, 26, },
+ { 1, 0, 0, 7, 2, 26, },
+ { 0, 0, 0, 7, 3, 30, },
+ { 2, 0, 0, 7, 3, 26, },
+ { 1, 0, 0, 7, 3, 26, },
+ { 0, 0, 0, 7, 4, 30, },
+ { 2, 0, 0, 7, 4, 26, },
+ { 1, 0, 0, 7, 4, 26, },
+ { 0, 0, 0, 7, 5, 30, },
+ { 2, 0, 0, 7, 5, 26, },
+ { 1, 0, 0, 7, 5, 26, },
+ { 0, 0, 0, 7, 6, 30, },
+ { 2, 0, 0, 7, 6, 26, },
+ { 1, 0, 0, 7, 6, 26, },
+ { 0, 0, 0, 7, 7, 30, },
+ { 2, 0, 0, 7, 7, 26, },
+ { 1, 0, 0, 7, 7, 26, },
+ { 0, 0, 0, 7, 8, 30, },
+ { 2, 0, 0, 7, 8, 26, },
+ { 1, 0, 0, 7, 8, 26, },
+ { 0, 0, 0, 7, 9, 30, },
+ { 2, 0, 0, 7, 9, 26, },
+ { 1, 0, 0, 7, 9, 26, },
+ { 0, 0, 0, 7, 10, 30, },
+ { 2, 0, 0, 7, 10, 26, },
+ { 1, 0, 0, 7, 10, 26, },
+ { 0, 0, 0, 7, 11, 26, },
+ { 2, 0, 0, 7, 11, 26, },
+ { 1, 0, 0, 7, 11, 26, },
+ { 0, 0, 0, 7, 12, 63, },
+ { 2, 0, 0, 7, 12, 26, },
+ { 1, 0, 0, 7, 12, 26, },
+ { 0, 0, 0, 7, 13, 63, },
+ { 2, 0, 0, 7, 13, 26, },
+ { 1, 0, 0, 7, 13, 26, },
+ { 0, 0, 0, 7, 14, 63, },
+ { 2, 0, 0, 7, 14, 63, },
+ { 1, 0, 0, 7, 14, 63, },
+ { 0, 0, 1, 2, 1, 63, },
+ { 2, 0, 1, 2, 1, 63, },
+ { 1, 0, 1, 2, 1, 63, },
+ { 0, 0, 1, 2, 2, 63, },
+ { 2, 0, 1, 2, 2, 63, },
+ { 1, 0, 1, 2, 2, 63, },
+ { 0, 0, 1, 2, 3, 32, },
+ { 2, 0, 1, 2, 3, 32, },
+ { 1, 0, 1, 2, 3, 32, },
+ { 0, 0, 1, 2, 4, 36, },
+ { 2, 0, 1, 2, 4, 32, },
+ { 1, 0, 1, 2, 4, 32, },
+ { 0, 0, 1, 2, 5, 36, },
+ { 2, 0, 1, 2, 5, 32, },
+ { 1, 0, 1, 2, 5, 32, },
+ { 0, 0, 1, 2, 6, 36, },
+ { 2, 0, 1, 2, 6, 32, },
+ { 1, 0, 1, 2, 6, 32, },
+ { 0, 0, 1, 2, 7, 36, },
+ { 2, 0, 1, 2, 7, 32, },
+ { 1, 0, 1, 2, 7, 32, },
+ { 0, 0, 1, 2, 8, 36, },
+ { 2, 0, 1, 2, 8, 32, },
+ { 1, 0, 1, 2, 8, 32, },
+ { 0, 0, 1, 2, 9, 36, },
+ { 2, 0, 1, 2, 9, 32, },
+ { 1, 0, 1, 2, 9, 32, },
+ { 0, 0, 1, 2, 10, 36, },
+ { 2, 0, 1, 2, 10, 32, },
+ { 1, 0, 1, 2, 10, 32, },
+ { 0, 0, 1, 2, 11, 32, },
+ { 2, 0, 1, 2, 11, 32, },
+ { 1, 0, 1, 2, 11, 32, },
+ { 0, 0, 1, 2, 12, 63, },
+ { 2, 0, 1, 2, 12, 32, },
+ { 1, 0, 1, 2, 12, 32, },
+ { 0, 0, 1, 2, 13, 63, },
+ { 2, 0, 1, 2, 13, 32, },
+ { 1, 0, 1, 2, 13, 32, },
+ { 0, 0, 1, 2, 14, 63, },
+ { 2, 0, 1, 2, 14, 63, },
+ { 1, 0, 1, 2, 14, 63, },
+ { 0, 0, 1, 3, 1, 63, },
+ { 2, 0, 1, 3, 1, 63, },
+ { 1, 0, 1, 3, 1, 63, },
+ { 0, 0, 1, 3, 2, 63, },
+ { 2, 0, 1, 3, 2, 63, },
+ { 1, 0, 1, 3, 2, 63, },
+ { 0, 0, 1, 3, 3, 30, },
+ { 2, 0, 1, 3, 3, 30, },
+ { 1, 0, 1, 3, 3, 30, },
+ { 0, 0, 1, 3, 4, 34, },
+ { 2, 0, 1, 3, 4, 30, },
+ { 1, 0, 1, 3, 4, 30, },
+ { 0, 0, 1, 3, 5, 34, },
+ { 2, 0, 1, 3, 5, 30, },
+ { 1, 0, 1, 3, 5, 30, },
+ { 0, 0, 1, 3, 6, 34, },
+ { 2, 0, 1, 3, 6, 30, },
+ { 1, 0, 1, 3, 6, 30, },
+ { 0, 0, 1, 3, 7, 34, },
+ { 2, 0, 1, 3, 7, 30, },
+ { 1, 0, 1, 3, 7, 30, },
+ { 0, 0, 1, 3, 8, 34, },
+ { 2, 0, 1, 3, 8, 30, },
+ { 1, 0, 1, 3, 8, 30, },
+ { 0, 0, 1, 3, 9, 34, },
+ { 2, 0, 1, 3, 9, 30, },
+ { 1, 0, 1, 3, 9, 30, },
+ { 0, 0, 1, 3, 10, 34, },
+ { 2, 0, 1, 3, 10, 30, },
+ { 1, 0, 1, 3, 10, 30, },
+ { 0, 0, 1, 3, 11, 30, },
+ { 2, 0, 1, 3, 11, 30, },
+ { 1, 0, 1, 3, 11, 30, },
+ { 0, 0, 1, 3, 12, 63, },
+ { 2, 0, 1, 3, 12, 30, },
+ { 1, 0, 1, 3, 12, 30, },
+ { 0, 0, 1, 3, 13, 63, },
+ { 2, 0, 1, 3, 13, 30, },
+ { 1, 0, 1, 3, 13, 30, },
+ { 0, 0, 1, 3, 14, 63, },
+ { 2, 0, 1, 3, 14, 63, },
+ { 1, 0, 1, 3, 14, 63, },
+ { 0, 0, 1, 6, 1, 63, },
+ { 2, 0, 1, 6, 1, 63, },
+ { 1, 0, 1, 6, 1, 63, },
+ { 0, 0, 1, 6, 2, 63, },
+ { 2, 0, 1, 6, 2, 63, },
+ { 1, 0, 1, 6, 2, 63, },
+ { 0, 0, 1, 6, 3, 28, },
+ { 2, 0, 1, 6, 3, 28, },
+ { 1, 0, 1, 6, 3, 28, },
+ { 0, 0, 1, 6, 4, 32, },
+ { 2, 0, 1, 6, 4, 28, },
+ { 1, 0, 1, 6, 4, 28, },
+ { 0, 0, 1, 6, 5, 32, },
+ { 2, 0, 1, 6, 5, 28, },
+ { 1, 0, 1, 6, 5, 28, },
+ { 0, 0, 1, 6, 6, 32, },
+ { 2, 0, 1, 6, 6, 28, },
+ { 1, 0, 1, 6, 6, 28, },
+ { 0, 0, 1, 6, 7, 32, },
+ { 2, 0, 1, 6, 7, 28, },
+ { 1, 0, 1, 6, 7, 28, },
+ { 0, 0, 1, 6, 8, 32, },
+ { 2, 0, 1, 6, 8, 28, },
+ { 1, 0, 1, 6, 8, 28, },
+ { 0, 0, 1, 6, 9, 32, },
+ { 2, 0, 1, 6, 9, 28, },
+ { 1, 0, 1, 6, 9, 28, },
+ { 0, 0, 1, 6, 10, 32, },
+ { 2, 0, 1, 6, 10, 28, },
+ { 1, 0, 1, 6, 10, 28, },
+ { 0, 0, 1, 6, 11, 28, },
+ { 2, 0, 1, 6, 11, 28, },
+ { 1, 0, 1, 6, 11, 28, },
+ { 0, 0, 1, 6, 12, 63, },
+ { 2, 0, 1, 6, 12, 28, },
+ { 1, 0, 1, 6, 12, 28, },
+ { 0, 0, 1, 6, 13, 63, },
+ { 2, 0, 1, 6, 13, 28, },
+ { 1, 0, 1, 6, 13, 28, },
+ { 0, 0, 1, 6, 14, 63, },
+ { 2, 0, 1, 6, 14, 63, },
+ { 1, 0, 1, 6, 14, 63, },
+ { 0, 0, 1, 7, 1, 63, },
+ { 2, 0, 1, 7, 1, 63, },
+ { 1, 0, 1, 7, 1, 63, },
+ { 0, 0, 1, 7, 2, 63, },
+ { 2, 0, 1, 7, 2, 63, },
+ { 1, 0, 1, 7, 2, 63, },
+ { 0, 0, 1, 7, 3, 26, },
+ { 2, 0, 1, 7, 3, 26, },
+ { 1, 0, 1, 7, 3, 26, },
+ { 0, 0, 1, 7, 4, 30, },
+ { 2, 0, 1, 7, 4, 26, },
+ { 1, 0, 1, 7, 4, 26, },
+ { 0, 0, 1, 7, 5, 30, },
+ { 2, 0, 1, 7, 5, 26, },
+ { 1, 0, 1, 7, 5, 26, },
+ { 0, 0, 1, 7, 6, 30, },
+ { 2, 0, 1, 7, 6, 26, },
+ { 1, 0, 1, 7, 6, 26, },
+ { 0, 0, 1, 7, 7, 30, },
+ { 2, 0, 1, 7, 7, 26, },
+ { 1, 0, 1, 7, 7, 26, },
+ { 0, 0, 1, 7, 8, 30, },
+ { 2, 0, 1, 7, 8, 26, },
+ { 1, 0, 1, 7, 8, 26, },
+ { 0, 0, 1, 7, 9, 30, },
+ { 2, 0, 1, 7, 9, 26, },
+ { 1, 0, 1, 7, 9, 26, },
+ { 0, 0, 1, 7, 10, 30, },
+ { 2, 0, 1, 7, 10, 26, },
+ { 1, 0, 1, 7, 10, 26, },
+ { 0, 0, 1, 7, 11, 26, },
+ { 2, 0, 1, 7, 11, 26, },
+ { 1, 0, 1, 7, 11, 26, },
+ { 0, 0, 1, 7, 12, 63, },
+ { 2, 0, 1, 7, 12, 26, },
+ { 1, 0, 1, 7, 12, 26, },
+ { 0, 0, 1, 7, 13, 63, },
+ { 2, 0, 1, 7, 13, 26, },
+ { 1, 0, 1, 7, 13, 26, },
+ { 0, 0, 1, 7, 14, 63, },
+ { 2, 0, 1, 7, 14, 63, },
+ { 1, 0, 1, 7, 14, 63, },
+ { 0, 1, 0, 1, 36, 30, },
+ { 2, 1, 0, 1, 36, 32, },
+ { 1, 1, 0, 1, 36, 32, },
+ { 0, 1, 0, 1, 40, 30, },
+ { 2, 1, 0, 1, 40, 32, },
+ { 1, 1, 0, 1, 40, 32, },
+ { 0, 1, 0, 1, 44, 30, },
+ { 2, 1, 0, 1, 44, 32, },
+ { 1, 1, 0, 1, 44, 32, },
+ { 0, 1, 0, 1, 48, 30, },
+ { 2, 1, 0, 1, 48, 32, },
+ { 1, 1, 0, 1, 48, 32, },
+ { 0, 1, 0, 1, 52, 36, },
+ { 2, 1, 0, 1, 52, 32, },
+ { 1, 1, 0, 1, 52, 32, },
+ { 0, 1, 0, 1, 56, 34, },
+ { 2, 1, 0, 1, 56, 32, },
+ { 1, 1, 0, 1, 56, 32, },
+ { 0, 1, 0, 1, 60, 32, },
+ { 2, 1, 0, 1, 60, 32, },
+ { 1, 1, 0, 1, 60, 32, },
+ { 0, 1, 0, 1, 64, 28, },
+ { 2, 1, 0, 1, 64, 32, },
+ { 1, 1, 0, 1, 64, 32, },
+ { 0, 1, 0, 1, 100, 30, },
+ { 2, 1, 0, 1, 100, 32, },
+ { 1, 1, 0, 1, 100, 32, },
+ { 0, 1, 0, 1, 104, 30, },
+ { 2, 1, 0, 1, 104, 32, },
+ { 1, 1, 0, 1, 104, 32, },
+ { 0, 1, 0, 1, 108, 32, },
+ { 2, 1, 0, 1, 108, 32, },
+ { 1, 1, 0, 1, 108, 32, },
+ { 0, 1, 0, 1, 112, 34, },
+ { 2, 1, 0, 1, 112, 32, },
+ { 1, 1, 0, 1, 112, 32, },
+ { 0, 1, 0, 1, 116, 34, },
+ { 2, 1, 0, 1, 116, 32, },
+ { 1, 1, 0, 1, 116, 32, },
+ { 0, 1, 0, 1, 120, 36, },
+ { 2, 1, 0, 1, 120, 32, },
+ { 1, 1, 0, 1, 120, 32, },
+ { 0, 1, 0, 1, 124, 34, },
+ { 2, 1, 0, 1, 124, 32, },
+ { 1, 1, 0, 1, 124, 32, },
+ { 0, 1, 0, 1, 128, 32, },
+ { 2, 1, 0, 1, 128, 32, },
+ { 1, 1, 0, 1, 128, 32, },
+ { 0, 1, 0, 1, 132, 30, },
+ { 2, 1, 0, 1, 132, 32, },
+ { 1, 1, 0, 1, 132, 32, },
+ { 0, 1, 0, 1, 136, 30, },
+ { 2, 1, 0, 1, 136, 32, },
+ { 1, 1, 0, 1, 136, 32, },
+ { 0, 1, 0, 1, 140, 28, },
+ { 2, 1, 0, 1, 140, 32, },
+ { 1, 1, 0, 1, 140, 32, },
+ { 0, 1, 0, 1, 149, 36, },
+ { 2, 1, 0, 1, 149, 32, },
+ { 1, 1, 0, 1, 149, 63, },
+ { 0, 1, 0, 1, 153, 36, },
+ { 2, 1, 0, 1, 153, 32, },
+ { 1, 1, 0, 1, 153, 63, },
+ { 0, 1, 0, 1, 157, 36, },
+ { 2, 1, 0, 1, 157, 32, },
+ { 1, 1, 0, 1, 157, 63, },
+ { 0, 1, 0, 1, 161, 36, },
+ { 2, 1, 0, 1, 161, 32, },
+ { 1, 1, 0, 1, 161, 63, },
+ { 0, 1, 0, 1, 165, 36, },
+ { 2, 1, 0, 1, 165, 32, },
+ { 1, 1, 0, 1, 165, 63, },
+ { 0, 1, 0, 2, 36, 30, },
+ { 2, 1, 0, 2, 36, 32, },
+ { 1, 1, 0, 2, 36, 32, },
+ { 0, 1, 0, 2, 40, 30, },
+ { 2, 1, 0, 2, 40, 32, },
+ { 1, 1, 0, 2, 40, 32, },
+ { 0, 1, 0, 2, 44, 30, },
+ { 2, 1, 0, 2, 44, 32, },
+ { 1, 1, 0, 2, 44, 32, },
+ { 0, 1, 0, 2, 48, 30, },
+ { 2, 1, 0, 2, 48, 32, },
+ { 1, 1, 0, 2, 48, 32, },
+ { 0, 1, 0, 2, 52, 36, },
+ { 2, 1, 0, 2, 52, 32, },
+ { 1, 1, 0, 2, 52, 32, },
+ { 0, 1, 0, 2, 56, 34, },
+ { 2, 1, 0, 2, 56, 32, },
+ { 1, 1, 0, 2, 56, 32, },
+ { 0, 1, 0, 2, 60, 32, },
+ { 2, 1, 0, 2, 60, 32, },
+ { 1, 1, 0, 2, 60, 32, },
+ { 0, 1, 0, 2, 64, 28, },
+ { 2, 1, 0, 2, 64, 32, },
+ { 1, 1, 0, 2, 64, 32, },
+ { 0, 1, 0, 2, 100, 30, },
+ { 2, 1, 0, 2, 100, 32, },
+ { 1, 1, 0, 2, 100, 32, },
+ { 0, 1, 0, 2, 104, 30, },
+ { 2, 1, 0, 2, 104, 32, },
+ { 1, 1, 0, 2, 104, 32, },
+ { 0, 1, 0, 2, 108, 32, },
+ { 2, 1, 0, 2, 108, 32, },
+ { 1, 1, 0, 2, 108, 32, },
+ { 0, 1, 0, 2, 112, 34, },
+ { 2, 1, 0, 2, 112, 32, },
+ { 1, 1, 0, 2, 112, 32, },
+ { 0, 1, 0, 2, 116, 34, },
+ { 2, 1, 0, 2, 116, 32, },
+ { 1, 1, 0, 2, 116, 32, },
+ { 0, 1, 0, 2, 120, 36, },
+ { 2, 1, 0, 2, 120, 32, },
+ { 1, 1, 0, 2, 120, 32, },
+ { 0, 1, 0, 2, 124, 34, },
+ { 2, 1, 0, 2, 124, 32, },
+ { 1, 1, 0, 2, 124, 32, },
+ { 0, 1, 0, 2, 128, 32, },
+ { 2, 1, 0, 2, 128, 32, },
+ { 1, 1, 0, 2, 128, 32, },
+ { 0, 1, 0, 2, 132, 30, },
+ { 2, 1, 0, 2, 132, 32, },
+ { 1, 1, 0, 2, 132, 32, },
+ { 0, 1, 0, 2, 136, 30, },
+ { 2, 1, 0, 2, 136, 32, },
+ { 1, 1, 0, 2, 136, 32, },
+ { 0, 1, 0, 2, 140, 28, },
+ { 2, 1, 0, 2, 140, 32, },
+ { 1, 1, 0, 2, 140, 32, },
+ { 0, 1, 0, 2, 149, 36, },
+ { 2, 1, 0, 2, 149, 32, },
+ { 1, 1, 0, 2, 149, 63, },
+ { 0, 1, 0, 2, 153, 36, },
+ { 2, 1, 0, 2, 153, 32, },
+ { 1, 1, 0, 2, 153, 63, },
+ { 0, 1, 0, 2, 157, 36, },
+ { 2, 1, 0, 2, 157, 32, },
+ { 1, 1, 0, 2, 157, 63, },
+ { 0, 1, 0, 2, 161, 36, },
+ { 2, 1, 0, 2, 161, 32, },
+ { 1, 1, 0, 2, 161, 63, },
+ { 0, 1, 0, 2, 165, 36, },
+ { 2, 1, 0, 2, 165, 32, },
+ { 1, 1, 0, 2, 165, 63, },
+ { 0, 1, 0, 3, 36, 28, },
+ { 2, 1, 0, 3, 36, 30, },
+ { 1, 1, 0, 3, 36, 30, },
+ { 0, 1, 0, 3, 40, 28, },
+ { 2, 1, 0, 3, 40, 30, },
+ { 1, 1, 0, 3, 40, 30, },
+ { 0, 1, 0, 3, 44, 28, },
+ { 2, 1, 0, 3, 44, 30, },
+ { 1, 1, 0, 3, 44, 30, },
+ { 0, 1, 0, 3, 48, 28, },
+ { 2, 1, 0, 3, 48, 30, },
+ { 1, 1, 0, 3, 48, 30, },
+ { 0, 1, 0, 3, 52, 34, },
+ { 2, 1, 0, 3, 52, 30, },
+ { 1, 1, 0, 3, 52, 30, },
+ { 0, 1, 0, 3, 56, 32, },
+ { 2, 1, 0, 3, 56, 30, },
+ { 1, 1, 0, 3, 56, 30, },
+ { 0, 1, 0, 3, 60, 30, },
+ { 2, 1, 0, 3, 60, 30, },
+ { 1, 1, 0, 3, 60, 30, },
+ { 0, 1, 0, 3, 64, 26, },
+ { 2, 1, 0, 3, 64, 30, },
+ { 1, 1, 0, 3, 64, 30, },
+ { 0, 1, 0, 3, 100, 28, },
+ { 2, 1, 0, 3, 100, 30, },
+ { 1, 1, 0, 3, 100, 30, },
+ { 0, 1, 0, 3, 104, 28, },
+ { 2, 1, 0, 3, 104, 30, },
+ { 1, 1, 0, 3, 104, 30, },
+ { 0, 1, 0, 3, 108, 30, },
+ { 2, 1, 0, 3, 108, 30, },
+ { 1, 1, 0, 3, 108, 30, },
+ { 0, 1, 0, 3, 112, 32, },
+ { 2, 1, 0, 3, 112, 30, },
+ { 1, 1, 0, 3, 112, 30, },
+ { 0, 1, 0, 3, 116, 32, },
+ { 2, 1, 0, 3, 116, 30, },
+ { 1, 1, 0, 3, 116, 30, },
+ { 0, 1, 0, 3, 120, 34, },
+ { 2, 1, 0, 3, 120, 30, },
+ { 1, 1, 0, 3, 120, 30, },
+ { 0, 1, 0, 3, 124, 32, },
+ { 2, 1, 0, 3, 124, 30, },
+ { 1, 1, 0, 3, 124, 30, },
+ { 0, 1, 0, 3, 128, 30, },
+ { 2, 1, 0, 3, 128, 30, },
+ { 1, 1, 0, 3, 128, 30, },
+ { 0, 1, 0, 3, 132, 28, },
+ { 2, 1, 0, 3, 132, 30, },
+ { 1, 1, 0, 3, 132, 30, },
+ { 0, 1, 0, 3, 136, 28, },
+ { 2, 1, 0, 3, 136, 30, },
+ { 1, 1, 0, 3, 136, 30, },
+ { 0, 1, 0, 3, 140, 26, },
+ { 2, 1, 0, 3, 140, 30, },
+ { 1, 1, 0, 3, 140, 30, },
+ { 0, 1, 0, 3, 149, 34, },
+ { 2, 1, 0, 3, 149, 30, },
+ { 1, 1, 0, 3, 149, 63, },
+ { 0, 1, 0, 3, 153, 34, },
+ { 2, 1, 0, 3, 153, 30, },
+ { 1, 1, 0, 3, 153, 63, },
+ { 0, 1, 0, 3, 157, 34, },
+ { 2, 1, 0, 3, 157, 30, },
+ { 1, 1, 0, 3, 157, 63, },
+ { 0, 1, 0, 3, 161, 34, },
+ { 2, 1, 0, 3, 161, 30, },
+ { 1, 1, 0, 3, 161, 63, },
+ { 0, 1, 0, 3, 165, 34, },
+ { 2, 1, 0, 3, 165, 30, },
+ { 1, 1, 0, 3, 165, 63, },
+ { 0, 1, 0, 6, 36, 26, },
+ { 2, 1, 0, 6, 36, 28, },
+ { 1, 1, 0, 6, 36, 28, },
+ { 0, 1, 0, 6, 40, 26, },
+ { 2, 1, 0, 6, 40, 28, },
+ { 1, 1, 0, 6, 40, 28, },
+ { 0, 1, 0, 6, 44, 26, },
+ { 2, 1, 0, 6, 44, 28, },
+ { 1, 1, 0, 6, 44, 28, },
+ { 0, 1, 0, 6, 48, 26, },
+ { 2, 1, 0, 6, 48, 28, },
+ { 1, 1, 0, 6, 48, 28, },
+ { 0, 1, 0, 6, 52, 32, },
+ { 2, 1, 0, 6, 52, 28, },
+ { 1, 1, 0, 6, 52, 28, },
+ { 0, 1, 0, 6, 56, 30, },
+ { 2, 1, 0, 6, 56, 28, },
+ { 1, 1, 0, 6, 56, 28, },
+ { 0, 1, 0, 6, 60, 28, },
+ { 2, 1, 0, 6, 60, 28, },
+ { 1, 1, 0, 6, 60, 28, },
+ { 0, 1, 0, 6, 64, 24, },
+ { 2, 1, 0, 6, 64, 28, },
+ { 1, 1, 0, 6, 64, 28, },
+ { 0, 1, 0, 6, 100, 26, },
+ { 2, 1, 0, 6, 100, 28, },
+ { 1, 1, 0, 6, 100, 28, },
+ { 0, 1, 0, 6, 104, 26, },
+ { 2, 1, 0, 6, 104, 28, },
+ { 1, 1, 0, 6, 104, 28, },
+ { 0, 1, 0, 6, 108, 28, },
+ { 2, 1, 0, 6, 108, 28, },
+ { 1, 1, 0, 6, 108, 28, },
+ { 0, 1, 0, 6, 112, 30, },
+ { 2, 1, 0, 6, 112, 28, },
+ { 1, 1, 0, 6, 112, 28, },
+ { 0, 1, 0, 6, 116, 30, },
+ { 2, 1, 0, 6, 116, 28, },
+ { 1, 1, 0, 6, 116, 28, },
+ { 0, 1, 0, 6, 120, 32, },
+ { 2, 1, 0, 6, 120, 28, },
+ { 1, 1, 0, 6, 120, 28, },
+ { 0, 1, 0, 6, 124, 30, },
+ { 2, 1, 0, 6, 124, 28, },
+ { 1, 1, 0, 6, 124, 28, },
+ { 0, 1, 0, 6, 128, 28, },
+ { 2, 1, 0, 6, 128, 28, },
+ { 1, 1, 0, 6, 128, 28, },
+ { 0, 1, 0, 6, 132, 26, },
+ { 2, 1, 0, 6, 132, 28, },
+ { 1, 1, 0, 6, 132, 28, },
+ { 0, 1, 0, 6, 136, 26, },
+ { 2, 1, 0, 6, 136, 28, },
+ { 1, 1, 0, 6, 136, 28, },
+ { 0, 1, 0, 6, 140, 24, },
+ { 2, 1, 0, 6, 140, 28, },
+ { 1, 1, 0, 6, 140, 28, },
+ { 0, 1, 0, 6, 149, 32, },
+ { 2, 1, 0, 6, 149, 28, },
+ { 1, 1, 0, 6, 149, 63, },
+ { 0, 1, 0, 6, 153, 32, },
+ { 2, 1, 0, 6, 153, 28, },
+ { 1, 1, 0, 6, 153, 63, },
+ { 0, 1, 0, 6, 157, 32, },
+ { 2, 1, 0, 6, 157, 28, },
+ { 1, 1, 0, 6, 157, 63, },
+ { 0, 1, 0, 6, 161, 32, },
+ { 2, 1, 0, 6, 161, 28, },
+ { 1, 1, 0, 6, 161, 63, },
+ { 0, 1, 0, 6, 165, 32, },
+ { 2, 1, 0, 6, 165, 28, },
+ { 1, 1, 0, 6, 165, 63, },
+ { 0, 1, 0, 7, 36, 24, },
+ { 2, 1, 0, 7, 36, 26, },
+ { 1, 1, 0, 7, 36, 26, },
+ { 0, 1, 0, 7, 40, 24, },
+ { 2, 1, 0, 7, 40, 26, },
+ { 1, 1, 0, 7, 40, 26, },
+ { 0, 1, 0, 7, 44, 24, },
+ { 2, 1, 0, 7, 44, 26, },
+ { 1, 1, 0, 7, 44, 26, },
+ { 0, 1, 0, 7, 48, 24, },
+ { 2, 1, 0, 7, 48, 26, },
+ { 1, 1, 0, 7, 48, 26, },
+ { 0, 1, 0, 7, 52, 30, },
+ { 2, 1, 0, 7, 52, 26, },
+ { 1, 1, 0, 7, 52, 26, },
+ { 0, 1, 0, 7, 56, 28, },
+ { 2, 1, 0, 7, 56, 26, },
+ { 1, 1, 0, 7, 56, 26, },
+ { 0, 1, 0, 7, 60, 26, },
+ { 2, 1, 0, 7, 60, 26, },
+ { 1, 1, 0, 7, 60, 26, },
+ { 0, 1, 0, 7, 64, 22, },
+ { 2, 1, 0, 7, 64, 26, },
+ { 1, 1, 0, 7, 64, 26, },
+ { 0, 1, 0, 7, 100, 24, },
+ { 2, 1, 0, 7, 100, 26, },
+ { 1, 1, 0, 7, 100, 26, },
+ { 0, 1, 0, 7, 104, 24, },
+ { 2, 1, 0, 7, 104, 26, },
+ { 1, 1, 0, 7, 104, 26, },
+ { 0, 1, 0, 7, 108, 26, },
+ { 2, 1, 0, 7, 108, 26, },
+ { 1, 1, 0, 7, 108, 26, },
+ { 0, 1, 0, 7, 112, 28, },
+ { 2, 1, 0, 7, 112, 26, },
+ { 1, 1, 0, 7, 112, 26, },
+ { 0, 1, 0, 7, 116, 28, },
+ { 2, 1, 0, 7, 116, 26, },
+ { 1, 1, 0, 7, 116, 26, },
+ { 0, 1, 0, 7, 120, 30, },
+ { 2, 1, 0, 7, 120, 26, },
+ { 1, 1, 0, 7, 120, 26, },
+ { 0, 1, 0, 7, 124, 28, },
+ { 2, 1, 0, 7, 124, 26, },
+ { 1, 1, 0, 7, 124, 26, },
+ { 0, 1, 0, 7, 128, 26, },
+ { 2, 1, 0, 7, 128, 26, },
+ { 1, 1, 0, 7, 128, 26, },
+ { 0, 1, 0, 7, 132, 24, },
+ { 2, 1, 0, 7, 132, 26, },
+ { 1, 1, 0, 7, 132, 26, },
+ { 0, 1, 0, 7, 136, 24, },
+ { 2, 1, 0, 7, 136, 26, },
+ { 1, 1, 0, 7, 136, 26, },
+ { 0, 1, 0, 7, 140, 22, },
+ { 2, 1, 0, 7, 140, 26, },
+ { 1, 1, 0, 7, 140, 26, },
+ { 0, 1, 0, 7, 149, 30, },
+ { 2, 1, 0, 7, 149, 26, },
+ { 1, 1, 0, 7, 149, 63, },
+ { 0, 1, 0, 7, 153, 30, },
+ { 2, 1, 0, 7, 153, 26, },
+ { 1, 1, 0, 7, 153, 63, },
+ { 0, 1, 0, 7, 157, 30, },
+ { 2, 1, 0, 7, 157, 26, },
+ { 1, 1, 0, 7, 157, 63, },
+ { 0, 1, 0, 7, 161, 30, },
+ { 2, 1, 0, 7, 161, 26, },
+ { 1, 1, 0, 7, 161, 63, },
+ { 0, 1, 0, 7, 165, 30, },
+ { 2, 1, 0, 7, 165, 26, },
+ { 1, 1, 0, 7, 165, 63, },
+ { 0, 1, 1, 2, 38, 30, },
+ { 2, 1, 1, 2, 38, 32, },
+ { 1, 1, 1, 2, 38, 32, },
+ { 0, 1, 1, 2, 46, 30, },
+ { 2, 1, 1, 2, 46, 32, },
+ { 1, 1, 1, 2, 46, 32, },
+ { 0, 1, 1, 2, 54, 32, },
+ { 2, 1, 1, 2, 54, 32, },
+ { 1, 1, 1, 2, 54, 32, },
+ { 0, 1, 1, 2, 62, 32, },
+ { 2, 1, 1, 2, 62, 32, },
+ { 1, 1, 1, 2, 62, 32, },
+ { 0, 1, 1, 2, 102, 28, },
+ { 2, 1, 1, 2, 102, 32, },
+ { 1, 1, 1, 2, 102, 32, },
+ { 0, 1, 1, 2, 110, 32, },
+ { 2, 1, 1, 2, 110, 32, },
+ { 1, 1, 1, 2, 110, 32, },
+ { 0, 1, 1, 2, 118, 36, },
+ { 2, 1, 1, 2, 118, 32, },
+ { 1, 1, 1, 2, 118, 32, },
+ { 0, 1, 1, 2, 126, 34, },
+ { 2, 1, 1, 2, 126, 32, },
+ { 1, 1, 1, 2, 126, 32, },
+ { 0, 1, 1, 2, 134, 32, },
+ { 2, 1, 1, 2, 134, 32, },
+ { 1, 1, 1, 2, 134, 32, },
+ { 0, 1, 1, 2, 151, 36, },
+ { 2, 1, 1, 2, 151, 32, },
+ { 1, 1, 1, 2, 151, 63, },
+ { 0, 1, 1, 2, 159, 36, },
+ { 2, 1, 1, 2, 159, 32, },
+ { 1, 1, 1, 2, 159, 63, },
+ { 0, 1, 1, 3, 38, 28, },
+ { 2, 1, 1, 3, 38, 30, },
+ { 1, 1, 1, 3, 38, 30, },
+ { 0, 1, 1, 3, 46, 28, },
+ { 2, 1, 1, 3, 46, 30, },
+ { 1, 1, 1, 3, 46, 30, },
+ { 0, 1, 1, 3, 54, 30, },
+ { 2, 1, 1, 3, 54, 30, },
+ { 1, 1, 1, 3, 54, 30, },
+ { 0, 1, 1, 3, 62, 30, },
+ { 2, 1, 1, 3, 62, 30, },
+ { 1, 1, 1, 3, 62, 30, },
+ { 0, 1, 1, 3, 102, 26, },
+ { 2, 1, 1, 3, 102, 30, },
+ { 1, 1, 1, 3, 102, 30, },
+ { 0, 1, 1, 3, 110, 30, },
+ { 2, 1, 1, 3, 110, 30, },
+ { 1, 1, 1, 3, 110, 30, },
+ { 0, 1, 1, 3, 118, 34, },
+ { 2, 1, 1, 3, 118, 30, },
+ { 1, 1, 1, 3, 118, 30, },
+ { 0, 1, 1, 3, 126, 32, },
+ { 2, 1, 1, 3, 126, 30, },
+ { 1, 1, 1, 3, 126, 30, },
+ { 0, 1, 1, 3, 134, 30, },
+ { 2, 1, 1, 3, 134, 30, },
+ { 1, 1, 1, 3, 134, 30, },
+ { 0, 1, 1, 3, 151, 34, },
+ { 2, 1, 1, 3, 151, 30, },
+ { 1, 1, 1, 3, 151, 63, },
+ { 0, 1, 1, 3, 159, 34, },
+ { 2, 1, 1, 3, 159, 30, },
+ { 1, 1, 1, 3, 159, 63, },
+ { 0, 1, 1, 6, 38, 26, },
+ { 2, 1, 1, 6, 38, 28, },
+ { 1, 1, 1, 6, 38, 28, },
+ { 0, 1, 1, 6, 46, 26, },
+ { 2, 1, 1, 6, 46, 28, },
+ { 1, 1, 1, 6, 46, 28, },
+ { 0, 1, 1, 6, 54, 28, },
+ { 2, 1, 1, 6, 54, 28, },
+ { 1, 1, 1, 6, 54, 28, },
+ { 0, 1, 1, 6, 62, 28, },
+ { 2, 1, 1, 6, 62, 28, },
+ { 1, 1, 1, 6, 62, 28, },
+ { 0, 1, 1, 6, 102, 24, },
+ { 2, 1, 1, 6, 102, 28, },
+ { 1, 1, 1, 6, 102, 28, },
+ { 0, 1, 1, 6, 110, 28, },
+ { 2, 1, 1, 6, 110, 28, },
+ { 1, 1, 1, 6, 110, 28, },
+ { 0, 1, 1, 6, 118, 32, },
+ { 2, 1, 1, 6, 118, 28, },
+ { 1, 1, 1, 6, 118, 28, },
+ { 0, 1, 1, 6, 126, 30, },
+ { 2, 1, 1, 6, 126, 28, },
+ { 1, 1, 1, 6, 126, 28, },
+ { 0, 1, 1, 6, 134, 28, },
+ { 2, 1, 1, 6, 134, 28, },
+ { 1, 1, 1, 6, 134, 28, },
+ { 0, 1, 1, 6, 151, 32, },
+ { 2, 1, 1, 6, 151, 28, },
+ { 1, 1, 1, 6, 151, 63, },
+ { 0, 1, 1, 6, 159, 32, },
+ { 2, 1, 1, 6, 159, 28, },
+ { 1, 1, 1, 6, 159, 63, },
+ { 0, 1, 1, 7, 38, 24, },
+ { 2, 1, 1, 7, 38, 26, },
+ { 1, 1, 1, 7, 38, 26, },
+ { 0, 1, 1, 7, 46, 24, },
+ { 2, 1, 1, 7, 46, 26, },
+ { 1, 1, 1, 7, 46, 26, },
+ { 0, 1, 1, 7, 54, 26, },
+ { 2, 1, 1, 7, 54, 26, },
+ { 1, 1, 1, 7, 54, 26, },
+ { 0, 1, 1, 7, 62, 26, },
+ { 2, 1, 1, 7, 62, 26, },
+ { 1, 1, 1, 7, 62, 26, },
+ { 0, 1, 1, 7, 102, 22, },
+ { 2, 1, 1, 7, 102, 26, },
+ { 1, 1, 1, 7, 102, 26, },
+ { 0, 1, 1, 7, 110, 26, },
+ { 2, 1, 1, 7, 110, 26, },
+ { 1, 1, 1, 7, 110, 26, },
+ { 0, 1, 1, 7, 118, 30, },
+ { 2, 1, 1, 7, 118, 26, },
+ { 1, 1, 1, 7, 118, 26, },
+ { 0, 1, 1, 7, 126, 28, },
+ { 2, 1, 1, 7, 126, 26, },
+ { 1, 1, 1, 7, 126, 26, },
+ { 0, 1, 1, 7, 134, 26, },
+ { 2, 1, 1, 7, 134, 26, },
+ { 1, 1, 1, 7, 134, 26, },
+ { 0, 1, 1, 7, 151, 30, },
+ { 2, 1, 1, 7, 151, 26, },
+ { 1, 1, 1, 7, 151, 63, },
+ { 0, 1, 1, 7, 159, 30, },
+ { 2, 1, 1, 7, 159, 26, },
+ { 1, 1, 1, 7, 159, 63, },
+ { 0, 1, 2, 4, 42, 30, },
+ { 2, 1, 2, 4, 42, 32, },
+ { 1, 1, 2, 4, 42, 32, },
+ { 0, 1, 2, 4, 58, 28, },
+ { 2, 1, 2, 4, 58, 32, },
+ { 1, 1, 2, 4, 58, 32, },
+ { 0, 1, 2, 4, 106, 30, },
+ { 2, 1, 2, 4, 106, 32, },
+ { 1, 1, 2, 4, 106, 32, },
+ { 0, 1, 2, 4, 122, 34, },
+ { 2, 1, 2, 4, 122, 32, },
+ { 1, 1, 2, 4, 122, 32, },
+ { 0, 1, 2, 4, 155, 36, },
+ { 2, 1, 2, 4, 155, 32, },
+ { 1, 1, 2, 4, 155, 63, },
+ { 0, 1, 2, 5, 42, 28, },
+ { 2, 1, 2, 5, 42, 30, },
+ { 1, 1, 2, 5, 42, 30, },
+ { 0, 1, 2, 5, 58, 26, },
+ { 2, 1, 2, 5, 58, 30, },
+ { 1, 1, 2, 5, 58, 30, },
+ { 0, 1, 2, 5, 106, 28, },
+ { 2, 1, 2, 5, 106, 30, },
+ { 1, 1, 2, 5, 106, 30, },
+ { 0, 1, 2, 5, 122, 32, },
+ { 2, 1, 2, 5, 122, 30, },
+ { 1, 1, 2, 5, 122, 30, },
+ { 0, 1, 2, 5, 155, 34, },
+ { 2, 1, 2, 5, 155, 30, },
+ { 1, 1, 2, 5, 155, 63, },
+ { 0, 1, 2, 8, 42, 26, },
+ { 2, 1, 2, 8, 42, 28, },
+ { 1, 1, 2, 8, 42, 28, },
+ { 0, 1, 2, 8, 58, 24, },
+ { 2, 1, 2, 8, 58, 28, },
+ { 1, 1, 2, 8, 58, 28, },
+ { 0, 1, 2, 8, 106, 26, },
+ { 2, 1, 2, 8, 106, 28, },
+ { 1, 1, 2, 8, 106, 28, },
+ { 0, 1, 2, 8, 122, 30, },
+ { 2, 1, 2, 8, 122, 28, },
+ { 1, 1, 2, 8, 122, 28, },
+ { 0, 1, 2, 8, 155, 32, },
+ { 2, 1, 2, 8, 155, 28, },
+ { 1, 1, 2, 8, 155, 63, },
+ { 0, 1, 2, 9, 42, 24, },
+ { 2, 1, 2, 9, 42, 26, },
+ { 1, 1, 2, 9, 42, 26, },
+ { 0, 1, 2, 9, 58, 22, },
+ { 2, 1, 2, 9, 58, 26, },
+ { 1, 1, 2, 9, 58, 26, },
+ { 0, 1, 2, 9, 106, 24, },
+ { 2, 1, 2, 9, 106, 26, },
+ { 1, 1, 2, 9, 106, 26, },
+ { 0, 1, 2, 9, 122, 28, },
+ { 2, 1, 2, 9, 122, 26, },
+ { 1, 1, 2, 9, 122, 26, },
+ { 0, 1, 2, 9, 155, 30, },
+ { 2, 1, 2, 9, 155, 26, },
+ { 1, 1, 2, 9, 155, 63, },
+};
+
+RTW_DECL_TABLE_TXPWR_LMT(rtw8814a_txpwr_lmt);
+
+static const struct rtw_txpwr_lmt_cfg_pair rtw8814a_txpwr_lmt_type0[] = {
+ { 0, 0, 0, 0, 1, 32, },
+ { 2, 0, 0, 0, 1, 32, },
+ { 1, 0, 0, 0, 1, 32, },
+ { 0, 0, 0, 0, 2, 32, },
+ { 2, 0, 0, 0, 2, 32, },
+ { 1, 0, 0, 0, 2, 32, },
+ { 0, 0, 0, 0, 3, 32, },
+ { 2, 0, 0, 0, 3, 32, },
+ { 1, 0, 0, 0, 3, 32, },
+ { 0, 0, 0, 0, 4, 32, },
+ { 2, 0, 0, 0, 4, 32, },
+ { 1, 0, 0, 0, 4, 32, },
+ { 0, 0, 0, 0, 5, 32, },
+ { 2, 0, 0, 0, 5, 32, },
+ { 1, 0, 0, 0, 5, 32, },
+ { 0, 0, 0, 0, 6, 32, },
+ { 2, 0, 0, 0, 6, 32, },
+ { 1, 0, 0, 0, 6, 32, },
+ { 0, 0, 0, 0, 7, 32, },
+ { 2, 0, 0, 0, 7, 32, },
+ { 1, 0, 0, 0, 7, 32, },
+ { 0, 0, 0, 0, 8, 32, },
+ { 2, 0, 0, 0, 8, 32, },
+ { 1, 0, 0, 0, 8, 32, },
+ { 0, 0, 0, 0, 9, 32, },
+ { 2, 0, 0, 0, 9, 32, },
+ { 1, 0, 0, 0, 9, 32, },
+ { 0, 0, 0, 0, 10, 32, },
+ { 2, 0, 0, 0, 10, 32, },
+ { 1, 0, 0, 0, 10, 32, },
+ { 0, 0, 0, 0, 11, 32, },
+ { 2, 0, 0, 0, 11, 32, },
+ { 1, 0, 0, 0, 11, 32, },
+ { 0, 0, 0, 0, 12, 24, },
+ { 2, 0, 0, 0, 12, 32, },
+ { 1, 0, 0, 0, 12, 32, },
+ { 0, 0, 0, 0, 13, 16, },
+ { 2, 0, 0, 0, 13, 32, },
+ { 1, 0, 0, 0, 13, 32, },
+ { 0, 0, 0, 0, 14, 63, },
+ { 2, 0, 0, 0, 14, 63, },
+ { 1, 0, 0, 0, 14, 32, },
+ { 0, 0, 0, 1, 1, 28, },
+ { 2, 0, 0, 1, 1, 32, },
+ { 1, 0, 0, 1, 1, 32, },
+ { 0, 0, 0, 1, 2, 32, },
+ { 2, 0, 0, 1, 2, 32, },
+ { 1, 0, 0, 1, 2, 32, },
+ { 0, 0, 0, 1, 3, 32, },
+ { 2, 0, 0, 1, 3, 32, },
+ { 1, 0, 0, 1, 3, 32, },
+ { 0, 0, 0, 1, 4, 32, },
+ { 2, 0, 0, 1, 4, 32, },
+ { 1, 0, 0, 1, 4, 32, },
+ { 0, 0, 0, 1, 5, 32, },
+ { 2, 0, 0, 1, 5, 32, },
+ { 1, 0, 0, 1, 5, 32, },
+ { 0, 0, 0, 1, 6, 32, },
+ { 2, 0, 0, 1, 6, 32, },
+ { 1, 0, 0, 1, 6, 32, },
+ { 0, 0, 0, 1, 7, 32, },
+ { 2, 0, 0, 1, 7, 32, },
+ { 1, 0, 0, 1, 7, 32, },
+ { 0, 0, 0, 1, 8, 32, },
+ { 2, 0, 0, 1, 8, 32, },
+ { 1, 0, 0, 1, 8, 32, },
+ { 0, 0, 0, 1, 9, 32, },
+ { 2, 0, 0, 1, 9, 32, },
+ { 1, 0, 0, 1, 9, 32, },
+ { 0, 0, 0, 1, 10, 32, },
+ { 2, 0, 0, 1, 10, 32, },
+ { 1, 0, 0, 1, 10, 32, },
+ { 0, 0, 0, 1, 11, 28, },
+ { 2, 0, 0, 1, 11, 32, },
+ { 1, 0, 0, 1, 11, 32, },
+ { 0, 0, 0, 1, 12, 18, },
+ { 2, 0, 0, 1, 12, 32, },
+ { 1, 0, 0, 1, 12, 32, },
+ { 0, 0, 0, 1, 13, 8, },
+ { 2, 0, 0, 1, 13, 32, },
+ { 1, 0, 0, 1, 13, 32, },
+ { 0, 0, 0, 1, 14, 63, },
+ { 2, 0, 0, 1, 14, 63, },
+ { 1, 0, 0, 1, 14, 63, },
+ { 0, 0, 0, 2, 1, 26, },
+ { 2, 0, 0, 2, 1, 32, },
+ { 1, 0, 0, 2, 1, 32, },
+ { 0, 0, 0, 2, 2, 32, },
+ { 2, 0, 0, 2, 2, 32, },
+ { 1, 0, 0, 2, 2, 32, },
+ { 0, 0, 0, 2, 3, 32, },
+ { 2, 0, 0, 2, 3, 32, },
+ { 1, 0, 0, 2, 3, 32, },
+ { 0, 0, 0, 2, 4, 32, },
+ { 2, 0, 0, 2, 4, 32, },
+ { 1, 0, 0, 2, 4, 32, },
+ { 0, 0, 0, 2, 5, 32, },
+ { 2, 0, 0, 2, 5, 32, },
+ { 1, 0, 0, 2, 5, 32, },
+ { 0, 0, 0, 2, 6, 32, },
+ { 2, 0, 0, 2, 6, 32, },
+ { 1, 0, 0, 2, 6, 32, },
+ { 0, 0, 0, 2, 7, 32, },
+ { 2, 0, 0, 2, 7, 32, },
+ { 1, 0, 0, 2, 7, 32, },
+ { 0, 0, 0, 2, 8, 32, },
+ { 2, 0, 0, 2, 8, 32, },
+ { 1, 0, 0, 2, 8, 32, },
+ { 0, 0, 0, 2, 9, 32, },
+ { 2, 0, 0, 2, 9, 32, },
+ { 1, 0, 0, 2, 9, 32, },
+ { 0, 0, 0, 2, 10, 32, },
+ { 2, 0, 0, 2, 10, 32, },
+ { 1, 0, 0, 2, 10, 32, },
+ { 0, 0, 0, 2, 11, 26, },
+ { 2, 0, 0, 2, 11, 32, },
+ { 1, 0, 0, 2, 11, 32, },
+ { 0, 0, 0, 2, 12, 16, },
+ { 2, 0, 0, 2, 12, 32, },
+ { 1, 0, 0, 2, 12, 32, },
+ { 0, 0, 0, 2, 13, 6, },
+ { 2, 0, 0, 2, 13, 32, },
+ { 1, 0, 0, 2, 13, 32, },
+ { 0, 0, 0, 2, 14, 63, },
+ { 2, 0, 0, 2, 14, 63, },
+ { 1, 0, 0, 2, 14, 63, },
+ { 0, 0, 0, 3, 1, 24, },
+ { 2, 0, 0, 3, 1, 30, },
+ { 1, 0, 0, 3, 1, 30, },
+ { 0, 0, 0, 3, 2, 30, },
+ { 2, 0, 0, 3, 2, 30, },
+ { 1, 0, 0, 3, 2, 30, },
+ { 0, 0, 0, 3, 3, 30, },
+ { 2, 0, 0, 3, 3, 30, },
+ { 1, 0, 0, 3, 3, 30, },
+ { 0, 0, 0, 3, 4, 30, },
+ { 2, 0, 0, 3, 4, 30, },
+ { 1, 0, 0, 3, 4, 30, },
+ { 0, 0, 0, 3, 5, 30, },
+ { 2, 0, 0, 3, 5, 30, },
+ { 1, 0, 0, 3, 5, 30, },
+ { 0, 0, 0, 3, 6, 30, },
+ { 2, 0, 0, 3, 6, 30, },
+ { 1, 0, 0, 3, 6, 30, },
+ { 0, 0, 0, 3, 7, 30, },
+ { 2, 0, 0, 3, 7, 30, },
+ { 1, 0, 0, 3, 7, 30, },
+ { 0, 0, 0, 3, 8, 30, },
+ { 2, 0, 0, 3, 8, 30, },
+ { 1, 0, 0, 3, 8, 30, },
+ { 0, 0, 0, 3, 9, 30, },
+ { 2, 0, 0, 3, 9, 30, },
+ { 1, 0, 0, 3, 9, 30, },
+ { 0, 0, 0, 3, 10, 30, },
+ { 2, 0, 0, 3, 10, 30, },
+ { 1, 0, 0, 3, 10, 30, },
+ { 0, 0, 0, 3, 11, 24, },
+ { 2, 0, 0, 3, 11, 30, },
+ { 1, 0, 0, 3, 11, 30, },
+ { 0, 0, 0, 3, 12, 14, },
+ { 2, 0, 0, 3, 12, 30, },
+ { 1, 0, 0, 3, 12, 30, },
+ { 0, 0, 0, 3, 13, 4, },
+ { 2, 0, 0, 3, 13, 30, },
+ { 1, 0, 0, 3, 13, 30, },
+ { 0, 0, 0, 3, 14, 63, },
+ { 2, 0, 0, 3, 14, 63, },
+ { 1, 0, 0, 3, 14, 63, },
+ { 0, 0, 0, 6, 1, 22, },
+ { 2, 0, 0, 6, 1, 28, },
+ { 1, 0, 0, 6, 1, 28, },
+ { 0, 0, 0, 6, 2, 28, },
+ { 2, 0, 0, 6, 2, 28, },
+ { 1, 0, 0, 6, 2, 28, },
+ { 0, 0, 0, 6, 3, 28, },
+ { 2, 0, 0, 6, 3, 28, },
+ { 1, 0, 0, 6, 3, 28, },
+ { 0, 0, 0, 6, 4, 28, },
+ { 2, 0, 0, 6, 4, 28, },
+ { 1, 0, 0, 6, 4, 28, },
+ { 0, 0, 0, 6, 5, 28, },
+ { 2, 0, 0, 6, 5, 28, },
+ { 1, 0, 0, 6, 5, 28, },
+ { 0, 0, 0, 6, 6, 28, },
+ { 2, 0, 0, 6, 6, 28, },
+ { 1, 0, 0, 6, 6, 28, },
+ { 0, 0, 0, 6, 7, 28, },
+ { 2, 0, 0, 6, 7, 28, },
+ { 1, 0, 0, 6, 7, 28, },
+ { 0, 0, 0, 6, 8, 28, },
+ { 2, 0, 0, 6, 8, 28, },
+ { 1, 0, 0, 6, 8, 28, },
+ { 0, 0, 0, 6, 9, 28, },
+ { 2, 0, 0, 6, 9, 28, },
+ { 1, 0, 0, 6, 9, 28, },
+ { 0, 0, 0, 6, 10, 28, },
+ { 2, 0, 0, 6, 10, 28, },
+ { 1, 0, 0, 6, 10, 28, },
+ { 0, 0, 0, 6, 11, 22, },
+ { 2, 0, 0, 6, 11, 28, },
+ { 1, 0, 0, 6, 11, 28, },
+ { 0, 0, 0, 6, 12, 14, },
+ { 2, 0, 0, 6, 12, 28, },
+ { 1, 0, 0, 6, 12, 28, },
+ { 0, 0, 0, 6, 13, 4, },
+ { 2, 0, 0, 6, 13, 28, },
+ { 1, 0, 0, 6, 13, 28, },
+ { 0, 0, 0, 6, 14, 63, },
+ { 2, 0, 0, 6, 14, 63, },
+ { 1, 0, 0, 6, 14, 63, },
+ { 0, 0, 0, 7, 1, 20, },
+ { 2, 0, 0, 7, 1, 26, },
+ { 1, 0, 0, 7, 1, 26, },
+ { 0, 0, 0, 7, 2, 26, },
+ { 2, 0, 0, 7, 2, 26, },
+ { 1, 0, 0, 7, 2, 26, },
+ { 0, 0, 0, 7, 3, 26, },
+ { 2, 0, 0, 7, 3, 26, },
+ { 1, 0, 0, 7, 3, 26, },
+ { 0, 0, 0, 7, 4, 26, },
+ { 2, 0, 0, 7, 4, 26, },
+ { 1, 0, 0, 7, 4, 26, },
+ { 0, 0, 0, 7, 5, 26, },
+ { 2, 0, 0, 7, 5, 26, },
+ { 1, 0, 0, 7, 5, 26, },
+ { 0, 0, 0, 7, 6, 26, },
+ { 2, 0, 0, 7, 6, 26, },
+ { 1, 0, 0, 7, 6, 26, },
+ { 0, 0, 0, 7, 7, 26, },
+ { 2, 0, 0, 7, 7, 26, },
+ { 1, 0, 0, 7, 7, 26, },
+ { 0, 0, 0, 7, 8, 26, },
+ { 2, 0, 0, 7, 8, 26, },
+ { 1, 0, 0, 7, 8, 26, },
+ { 0, 0, 0, 7, 9, 26, },
+ { 2, 0, 0, 7, 9, 26, },
+ { 1, 0, 0, 7, 9, 26, },
+ { 0, 0, 0, 7, 10, 26, },
+ { 2, 0, 0, 7, 10, 26, },
+ { 1, 0, 0, 7, 10, 26, },
+ { 0, 0, 0, 7, 11, 20, },
+ { 2, 0, 0, 7, 11, 26, },
+ { 1, 0, 0, 7, 11, 26, },
+ { 0, 0, 0, 7, 12, 14, },
+ { 2, 0, 0, 7, 12, 26, },
+ { 1, 0, 0, 7, 12, 26, },
+ { 0, 0, 0, 7, 13, 4, },
+ { 2, 0, 0, 7, 13, 26, },
+ { 1, 0, 0, 7, 13, 26, },
+ { 0, 0, 0, 7, 14, 63, },
+ { 2, 0, 0, 7, 14, 63, },
+ { 1, 0, 0, 7, 14, 63, },
+ { 0, 0, 1, 2, 1, 63, },
+ { 2, 0, 1, 2, 1, 63, },
+ { 1, 0, 1, 2, 1, 63, },
+ { 0, 0, 1, 2, 2, 63, },
+ { 2, 0, 1, 2, 2, 63, },
+ { 1, 0, 1, 2, 2, 63, },
+ { 0, 0, 1, 2, 3, 26, },
+ { 2, 0, 1, 2, 3, 32, },
+ { 1, 0, 1, 2, 3, 32, },
+ { 0, 0, 1, 2, 4, 32, },
+ { 2, 0, 1, 2, 4, 32, },
+ { 1, 0, 1, 2, 4, 32, },
+ { 0, 0, 1, 2, 5, 32, },
+ { 2, 0, 1, 2, 5, 32, },
+ { 1, 0, 1, 2, 5, 32, },
+ { 0, 0, 1, 2, 6, 32, },
+ { 2, 0, 1, 2, 6, 32, },
+ { 1, 0, 1, 2, 6, 32, },
+ { 0, 0, 1, 2, 7, 32, },
+ { 2, 0, 1, 2, 7, 32, },
+ { 1, 0, 1, 2, 7, 32, },
+ { 0, 0, 1, 2, 8, 32, },
+ { 2, 0, 1, 2, 8, 32, },
+ { 1, 0, 1, 2, 8, 32, },
+ { 0, 0, 1, 2, 9, 32, },
+ { 2, 0, 1, 2, 9, 32, },
+ { 1, 0, 1, 2, 9, 32, },
+ { 0, 0, 1, 2, 10, 32, },
+ { 2, 0, 1, 2, 10, 32, },
+ { 1, 0, 1, 2, 10, 32, },
+ { 0, 0, 1, 2, 11, 26, },
+ { 2, 0, 1, 2, 11, 32, },
+ { 1, 0, 1, 2, 11, 32, },
+ { 0, 0, 1, 2, 12, 16, },
+ { 2, 0, 1, 2, 12, 32, },
+ { 1, 0, 1, 2, 12, 32, },
+ { 0, 0, 1, 2, 13, 10, },
+ { 2, 0, 1, 2, 13, 32, },
+ { 1, 0, 1, 2, 13, 32, },
+ { 0, 0, 1, 2, 14, 63, },
+ { 2, 0, 1, 2, 14, 63, },
+ { 1, 0, 1, 2, 14, 63, },
+ { 0, 0, 1, 3, 1, 63, },
+ { 2, 0, 1, 3, 1, 63, },
+ { 1, 0, 1, 3, 1, 63, },
+ { 0, 0, 1, 3, 2, 63, },
+ { 2, 0, 1, 3, 2, 63, },
+ { 1, 0, 1, 3, 2, 63, },
+ { 0, 0, 1, 3, 3, 24, },
+ { 2, 0, 1, 3, 3, 30, },
+ { 1, 0, 1, 3, 3, 30, },
+ { 0, 0, 1, 3, 4, 30, },
+ { 2, 0, 1, 3, 4, 30, },
+ { 1, 0, 1, 3, 4, 30, },
+ { 0, 0, 1, 3, 5, 30, },
+ { 2, 0, 1, 3, 5, 30, },
+ { 1, 0, 1, 3, 5, 30, },
+ { 0, 0, 1, 3, 6, 30, },
+ { 2, 0, 1, 3, 6, 30, },
+ { 1, 0, 1, 3, 6, 30, },
+ { 0, 0, 1, 3, 7, 30, },
+ { 2, 0, 1, 3, 7, 30, },
+ { 1, 0, 1, 3, 7, 30, },
+ { 0, 0, 1, 3, 8, 30, },
+ { 2, 0, 1, 3, 8, 30, },
+ { 1, 0, 1, 3, 8, 30, },
+ { 0, 0, 1, 3, 9, 30, },
+ { 2, 0, 1, 3, 9, 30, },
+ { 1, 0, 1, 3, 9, 30, },
+ { 0, 0, 1, 3, 10, 30, },
+ { 2, 0, 1, 3, 10, 30, },
+ { 1, 0, 1, 3, 10, 30, },
+ { 0, 0, 1, 3, 11, 24, },
+ { 2, 0, 1, 3, 11, 30, },
+ { 1, 0, 1, 3, 11, 30, },
+ { 0, 0, 1, 3, 12, 14, },
+ { 2, 0, 1, 3, 12, 30, },
+ { 1, 0, 1, 3, 12, 30, },
+ { 0, 0, 1, 3, 13, 8, },
+ { 2, 0, 1, 3, 13, 30, },
+ { 1, 0, 1, 3, 13, 30, },
+ { 0, 0, 1, 3, 14, 63, },
+ { 2, 0, 1, 3, 14, 63, },
+ { 1, 0, 1, 3, 14, 63, },
+ { 0, 0, 1, 6, 1, 63, },
+ { 2, 0, 1, 6, 1, 63, },
+ { 1, 0, 1, 6, 1, 63, },
+ { 0, 0, 1, 6, 2, 63, },
+ { 2, 0, 1, 6, 2, 63, },
+ { 1, 0, 1, 6, 2, 63, },
+ { 0, 0, 1, 6, 3, 22, },
+ { 2, 0, 1, 6, 3, 28, },
+ { 1, 0, 1, 6, 3, 28, },
+ { 0, 0, 1, 6, 4, 28, },
+ { 2, 0, 1, 6, 4, 28, },
+ { 1, 0, 1, 6, 4, 28, },
+ { 0, 0, 1, 6, 5, 28, },
+ { 2, 0, 1, 6, 5, 28, },
+ { 1, 0, 1, 6, 5, 28, },
+ { 0, 0, 1, 6, 6, 28, },
+ { 2, 0, 1, 6, 6, 28, },
+ { 1, 0, 1, 6, 6, 28, },
+ { 0, 0, 1, 6, 7, 28, },
+ { 2, 0, 1, 6, 7, 28, },
+ { 1, 0, 1, 6, 7, 28, },
+ { 0, 0, 1, 6, 8, 28, },
+ { 2, 0, 1, 6, 8, 28, },
+ { 1, 0, 1, 6, 8, 28, },
+ { 0, 0, 1, 6, 9, 28, },
+ { 2, 0, 1, 6, 9, 28, },
+ { 1, 0, 1, 6, 9, 28, },
+ { 0, 0, 1, 6, 10, 28, },
+ { 2, 0, 1, 6, 10, 28, },
+ { 1, 0, 1, 6, 10, 28, },
+ { 0, 0, 1, 6, 11, 22, },
+ { 2, 0, 1, 6, 11, 28, },
+ { 1, 0, 1, 6, 11, 28, },
+ { 0, 0, 1, 6, 12, 14, },
+ { 2, 0, 1, 6, 12, 28, },
+ { 1, 0, 1, 6, 12, 28, },
+ { 0, 0, 1, 6, 13, 8, },
+ { 2, 0, 1, 6, 13, 28, },
+ { 1, 0, 1, 6, 13, 28, },
+ { 0, 0, 1, 6, 14, 63, },
+ { 2, 0, 1, 6, 14, 63, },
+ { 1, 0, 1, 6, 14, 63, },
+ { 0, 0, 1, 7, 1, 63, },
+ { 2, 0, 1, 7, 1, 63, },
+ { 1, 0, 1, 7, 1, 63, },
+ { 0, 0, 1, 7, 2, 63, },
+ { 2, 0, 1, 7, 2, 63, },
+ { 1, 0, 1, 7, 2, 63, },
+ { 0, 0, 1, 7, 3, 20, },
+ { 2, 0, 1, 7, 3, 26, },
+ { 1, 0, 1, 7, 3, 26, },
+ { 0, 0, 1, 7, 4, 26, },
+ { 2, 0, 1, 7, 4, 26, },
+ { 1, 0, 1, 7, 4, 26, },
+ { 0, 0, 1, 7, 5, 26, },
+ { 2, 0, 1, 7, 5, 26, },
+ { 1, 0, 1, 7, 5, 26, },
+ { 0, 0, 1, 7, 6, 26, },
+ { 2, 0, 1, 7, 6, 26, },
+ { 1, 0, 1, 7, 6, 26, },
+ { 0, 0, 1, 7, 7, 26, },
+ { 2, 0, 1, 7, 7, 26, },
+ { 1, 0, 1, 7, 7, 26, },
+ { 0, 0, 1, 7, 8, 26, },
+ { 2, 0, 1, 7, 8, 26, },
+ { 1, 0, 1, 7, 8, 26, },
+ { 0, 0, 1, 7, 9, 26, },
+ { 2, 0, 1, 7, 9, 26, },
+ { 1, 0, 1, 7, 9, 26, },
+ { 0, 0, 1, 7, 10, 26, },
+ { 2, 0, 1, 7, 10, 26, },
+ { 1, 0, 1, 7, 10, 26, },
+ { 0, 0, 1, 7, 11, 20, },
+ { 2, 0, 1, 7, 11, 26, },
+ { 1, 0, 1, 7, 11, 26, },
+ { 0, 0, 1, 7, 12, 14, },
+ { 2, 0, 1, 7, 12, 26, },
+ { 1, 0, 1, 7, 12, 26, },
+ { 0, 0, 1, 7, 13, 8, },
+ { 2, 0, 1, 7, 13, 26, },
+ { 1, 0, 1, 7, 13, 26, },
+ { 0, 0, 1, 7, 14, 63, },
+ { 2, 0, 1, 7, 14, 63, },
+ { 1, 0, 1, 7, 14, 63, },
+ { 0, 1, 0, 1, 36, 28, },
+ { 2, 1, 0, 1, 36, 32, },
+ { 1, 1, 0, 1, 36, 32, },
+ { 0, 1, 0, 1, 40, 32, },
+ { 2, 1, 0, 1, 40, 32, },
+ { 1, 1, 0, 1, 40, 32, },
+ { 0, 1, 0, 1, 44, 32, },
+ { 2, 1, 0, 1, 44, 32, },
+ { 1, 1, 0, 1, 44, 32, },
+ { 0, 1, 0, 1, 48, 32, },
+ { 2, 1, 0, 1, 48, 32, },
+ { 1, 1, 0, 1, 48, 32, },
+ { 0, 1, 0, 1, 52, 32, },
+ { 2, 1, 0, 1, 52, 32, },
+ { 1, 1, 0, 1, 52, 32, },
+ { 0, 1, 0, 1, 56, 32, },
+ { 2, 1, 0, 1, 56, 32, },
+ { 1, 1, 0, 1, 56, 32, },
+ { 0, 1, 0, 1, 60, 32, },
+ { 2, 1, 0, 1, 60, 32, },
+ { 1, 1, 0, 1, 60, 32, },
+ { 0, 1, 0, 1, 64, 28, },
+ { 2, 1, 0, 1, 64, 32, },
+ { 1, 1, 0, 1, 64, 32, },
+ { 0, 1, 0, 1, 100, 28, },
+ { 2, 1, 0, 1, 100, 32, },
+ { 1, 1, 0, 1, 100, 32, },
+ { 0, 1, 0, 1, 104, 32, },
+ { 2, 1, 0, 1, 104, 32, },
+ { 1, 1, 0, 1, 104, 32, },
+ { 0, 1, 0, 1, 108, 32, },
+ { 2, 1, 0, 1, 108, 32, },
+ { 1, 1, 0, 1, 108, 32, },
+ { 0, 1, 0, 1, 112, 32, },
+ { 2, 1, 0, 1, 112, 32, },
+ { 1, 1, 0, 1, 112, 32, },
+ { 0, 1, 0, 1, 116, 32, },
+ { 2, 1, 0, 1, 116, 32, },
+ { 1, 1, 0, 1, 116, 32, },
+ { 0, 1, 0, 1, 120, 32, },
+ { 2, 1, 0, 1, 120, 32, },
+ { 1, 1, 0, 1, 120, 32, },
+ { 0, 1, 0, 1, 124, 32, },
+ { 2, 1, 0, 1, 124, 32, },
+ { 1, 1, 0, 1, 124, 32, },
+ { 0, 1, 0, 1, 128, 32, },
+ { 2, 1, 0, 1, 128, 32, },
+ { 1, 1, 0, 1, 128, 32, },
+ { 0, 1, 0, 1, 132, 32, },
+ { 2, 1, 0, 1, 132, 32, },
+ { 1, 1, 0, 1, 132, 32, },
+ { 0, 1, 0, 1, 136, 32, },
+ { 2, 1, 0, 1, 136, 32, },
+ { 1, 1, 0, 1, 136, 32, },
+ { 0, 1, 0, 1, 140, 28, },
+ { 2, 1, 0, 1, 140, 32, },
+ { 1, 1, 0, 1, 140, 32, },
+ { 0, 1, 0, 1, 149, 28, },
+ { 2, 1, 0, 1, 149, 32, },
+ { 1, 1, 0, 1, 149, 63, },
+ { 0, 1, 0, 1, 153, 32, },
+ { 2, 1, 0, 1, 153, 32, },
+ { 1, 1, 0, 1, 153, 63, },
+ { 0, 1, 0, 1, 157, 32, },
+ { 2, 1, 0, 1, 157, 32, },
+ { 1, 1, 0, 1, 157, 63, },
+ { 0, 1, 0, 1, 161, 32, },
+ { 2, 1, 0, 1, 161, 32, },
+ { 1, 1, 0, 1, 161, 63, },
+ { 0, 1, 0, 1, 165, 32, },
+ { 2, 1, 0, 1, 165, 32, },
+ { 1, 1, 0, 1, 165, 63, },
+ { 0, 1, 0, 2, 36, 26, },
+ { 2, 1, 0, 2, 36, 32, },
+ { 1, 1, 0, 2, 36, 32, },
+ { 0, 1, 0, 2, 40, 32, },
+ { 2, 1, 0, 2, 40, 32, },
+ { 1, 1, 0, 2, 40, 32, },
+ { 0, 1, 0, 2, 44, 32, },
+ { 2, 1, 0, 2, 44, 32, },
+ { 1, 1, 0, 2, 44, 32, },
+ { 0, 1, 0, 2, 48, 32, },
+ { 2, 1, 0, 2, 48, 32, },
+ { 1, 1, 0, 2, 48, 32, },
+ { 0, 1, 0, 2, 52, 32, },
+ { 2, 1, 0, 2, 52, 32, },
+ { 1, 1, 0, 2, 52, 32, },
+ { 0, 1, 0, 2, 56, 32, },
+ { 2, 1, 0, 2, 56, 32, },
+ { 1, 1, 0, 2, 56, 32, },
+ { 0, 1, 0, 2, 60, 32, },
+ { 2, 1, 0, 2, 60, 32, },
+ { 1, 1, 0, 2, 60, 32, },
+ { 0, 1, 0, 2, 64, 26, },
+ { 2, 1, 0, 2, 64, 32, },
+ { 1, 1, 0, 2, 64, 32, },
+ { 0, 1, 0, 2, 100, 26, },
+ { 2, 1, 0, 2, 100, 32, },
+ { 1, 1, 0, 2, 100, 32, },
+ { 0, 1, 0, 2, 104, 32, },
+ { 2, 1, 0, 2, 104, 32, },
+ { 1, 1, 0, 2, 104, 32, },
+ { 0, 1, 0, 2, 108, 32, },
+ { 2, 1, 0, 2, 108, 32, },
+ { 1, 1, 0, 2, 108, 32, },
+ { 0, 1, 0, 2, 112, 32, },
+ { 2, 1, 0, 2, 112, 32, },
+ { 1, 1, 0, 2, 112, 32, },
+ { 0, 1, 0, 2, 116, 32, },
+ { 2, 1, 0, 2, 116, 32, },
+ { 1, 1, 0, 2, 116, 32, },
+ { 0, 1, 0, 2, 120, 32, },
+ { 2, 1, 0, 2, 120, 32, },
+ { 1, 1, 0, 2, 120, 32, },
+ { 0, 1, 0, 2, 124, 32, },
+ { 2, 1, 0, 2, 124, 32, },
+ { 1, 1, 0, 2, 124, 32, },
+ { 0, 1, 0, 2, 128, 32, },
+ { 2, 1, 0, 2, 128, 32, },
+ { 1, 1, 0, 2, 128, 32, },
+ { 0, 1, 0, 2, 132, 32, },
+ { 2, 1, 0, 2, 132, 32, },
+ { 1, 1, 0, 2, 132, 32, },
+ { 0, 1, 0, 2, 136, 32, },
+ { 2, 1, 0, 2, 136, 32, },
+ { 1, 1, 0, 2, 136, 32, },
+ { 0, 1, 0, 2, 140, 26, },
+ { 2, 1, 0, 2, 140, 32, },
+ { 1, 1, 0, 2, 140, 32, },
+ { 0, 1, 0, 2, 149, 26, },
+ { 2, 1, 0, 2, 149, 32, },
+ { 1, 1, 0, 2, 149, 63, },
+ { 0, 1, 0, 2, 153, 32, },
+ { 2, 1, 0, 2, 153, 32, },
+ { 1, 1, 0, 2, 153, 63, },
+ { 0, 1, 0, 2, 157, 32, },
+ { 2, 1, 0, 2, 157, 32, },
+ { 1, 1, 0, 2, 157, 63, },
+ { 0, 1, 0, 2, 161, 32, },
+ { 2, 1, 0, 2, 161, 32, },
+ { 1, 1, 0, 2, 161, 63, },
+ { 0, 1, 0, 2, 165, 32, },
+ { 2, 1, 0, 2, 165, 32, },
+ { 1, 1, 0, 2, 165, 63, },
+ { 0, 1, 0, 3, 36, 24, },
+ { 2, 1, 0, 3, 36, 28, },
+ { 1, 1, 0, 3, 36, 28, },
+ { 0, 1, 0, 3, 40, 28, },
+ { 2, 1, 0, 3, 40, 28, },
+ { 1, 1, 0, 3, 40, 28, },
+ { 0, 1, 0, 3, 44, 28, },
+ { 2, 1, 0, 3, 44, 28, },
+ { 1, 1, 0, 3, 44, 28, },
+ { 0, 1, 0, 3, 48, 28, },
+ { 2, 1, 0, 3, 48, 28, },
+ { 1, 1, 0, 3, 48, 28, },
+ { 0, 1, 0, 3, 52, 28, },
+ { 2, 1, 0, 3, 52, 28, },
+ { 1, 1, 0, 3, 52, 28, },
+ { 0, 1, 0, 3, 56, 28, },
+ { 2, 1, 0, 3, 56, 28, },
+ { 1, 1, 0, 3, 56, 28, },
+ { 0, 1, 0, 3, 60, 28, },
+ { 2, 1, 0, 3, 60, 28, },
+ { 1, 1, 0, 3, 60, 28, },
+ { 0, 1, 0, 3, 64, 24, },
+ { 2, 1, 0, 3, 64, 28, },
+ { 1, 1, 0, 3, 64, 28, },
+ { 0, 1, 0, 3, 100, 24, },
+ { 2, 1, 0, 3, 100, 28, },
+ { 1, 1, 0, 3, 100, 28, },
+ { 0, 1, 0, 3, 104, 28, },
+ { 2, 1, 0, 3, 104, 28, },
+ { 1, 1, 0, 3, 104, 28, },
+ { 0, 1, 0, 3, 108, 28, },
+ { 2, 1, 0, 3, 108, 28, },
+ { 1, 1, 0, 3, 108, 28, },
+ { 0, 1, 0, 3, 112, 28, },
+ { 2, 1, 0, 3, 112, 28, },
+ { 1, 1, 0, 3, 112, 28, },
+ { 0, 1, 0, 3, 116, 28, },
+ { 2, 1, 0, 3, 116, 28, },
+ { 1, 1, 0, 3, 116, 28, },
+ { 0, 1, 0, 3, 120, 28, },
+ { 2, 1, 0, 3, 120, 28, },
+ { 1, 1, 0, 3, 120, 28, },
+ { 0, 1, 0, 3, 124, 28, },
+ { 2, 1, 0, 3, 124, 28, },
+ { 1, 1, 0, 3, 124, 28, },
+ { 0, 1, 0, 3, 128, 28, },
+ { 2, 1, 0, 3, 128, 28, },
+ { 1, 1, 0, 3, 128, 28, },
+ { 0, 1, 0, 3, 132, 28, },
+ { 2, 1, 0, 3, 132, 28, },
+ { 1, 1, 0, 3, 132, 28, },
+ { 0, 1, 0, 3, 136, 28, },
+ { 2, 1, 0, 3, 136, 28, },
+ { 1, 1, 0, 3, 136, 28, },
+ { 0, 1, 0, 3, 140, 24, },
+ { 2, 1, 0, 3, 140, 28, },
+ { 1, 1, 0, 3, 140, 28, },
+ { 0, 1, 0, 3, 149, 24, },
+ { 2, 1, 0, 3, 149, 28, },
+ { 1, 1, 0, 3, 149, 63, },
+ { 0, 1, 0, 3, 153, 28, },
+ { 2, 1, 0, 3, 153, 28, },
+ { 1, 1, 0, 3, 153, 63, },
+ { 0, 1, 0, 3, 157, 28, },
+ { 2, 1, 0, 3, 157, 28, },
+ { 1, 1, 0, 3, 157, 63, },
+ { 0, 1, 0, 3, 161, 28, },
+ { 2, 1, 0, 3, 161, 28, },
+ { 1, 1, 0, 3, 161, 63, },
+ { 0, 1, 0, 3, 165, 28, },
+ { 2, 1, 0, 3, 165, 28, },
+ { 1, 1, 0, 3, 165, 63, },
+ { 0, 1, 0, 6, 36, 22, },
+ { 2, 1, 0, 6, 36, 26, },
+ { 1, 1, 0, 6, 36, 26, },
+ { 0, 1, 0, 6, 40, 26, },
+ { 2, 1, 0, 6, 40, 26, },
+ { 1, 1, 0, 6, 40, 26, },
+ { 0, 1, 0, 6, 44, 26, },
+ { 2, 1, 0, 6, 44, 26, },
+ { 1, 1, 0, 6, 44, 26, },
+ { 0, 1, 0, 6, 48, 26, },
+ { 2, 1, 0, 6, 48, 26, },
+ { 1, 1, 0, 6, 48, 26, },
+ { 0, 1, 0, 6, 52, 26, },
+ { 2, 1, 0, 6, 52, 26, },
+ { 1, 1, 0, 6, 52, 26, },
+ { 0, 1, 0, 6, 56, 26, },
+ { 2, 1, 0, 6, 56, 26, },
+ { 1, 1, 0, 6, 56, 26, },
+ { 0, 1, 0, 6, 60, 26, },
+ { 2, 1, 0, 6, 60, 26, },
+ { 1, 1, 0, 6, 60, 26, },
+ { 0, 1, 0, 6, 64, 22, },
+ { 2, 1, 0, 6, 64, 26, },
+ { 1, 1, 0, 6, 64, 26, },
+ { 0, 1, 0, 6, 100, 22, },
+ { 2, 1, 0, 6, 100, 26, },
+ { 1, 1, 0, 6, 100, 26, },
+ { 0, 1, 0, 6, 104, 26, },
+ { 2, 1, 0, 6, 104, 26, },
+ { 1, 1, 0, 6, 104, 26, },
+ { 0, 1, 0, 6, 108, 26, },
+ { 2, 1, 0, 6, 108, 26, },
+ { 1, 1, 0, 6, 108, 26, },
+ { 0, 1, 0, 6, 112, 26, },
+ { 2, 1, 0, 6, 112, 26, },
+ { 1, 1, 0, 6, 112, 26, },
+ { 0, 1, 0, 6, 116, 26, },
+ { 2, 1, 0, 6, 116, 26, },
+ { 1, 1, 0, 6, 116, 26, },
+ { 0, 1, 0, 6, 120, 26, },
+ { 2, 1, 0, 6, 120, 26, },
+ { 1, 1, 0, 6, 120, 26, },
+ { 0, 1, 0, 6, 124, 26, },
+ { 2, 1, 0, 6, 124, 26, },
+ { 1, 1, 0, 6, 124, 26, },
+ { 0, 1, 0, 6, 128, 26, },
+ { 2, 1, 0, 6, 128, 26, },
+ { 1, 1, 0, 6, 128, 26, },
+ { 0, 1, 0, 6, 132, 26, },
+ { 2, 1, 0, 6, 132, 26, },
+ { 1, 1, 0, 6, 132, 26, },
+ { 0, 1, 0, 6, 136, 26, },
+ { 2, 1, 0, 6, 136, 26, },
+ { 1, 1, 0, 6, 136, 26, },
+ { 0, 1, 0, 6, 140, 22, },
+ { 2, 1, 0, 6, 140, 26, },
+ { 1, 1, 0, 6, 140, 26, },
+ { 0, 1, 0, 6, 149, 22, },
+ { 2, 1, 0, 6, 149, 26, },
+ { 1, 1, 0, 6, 149, 63, },
+ { 0, 1, 0, 6, 153, 26, },
+ { 2, 1, 0, 6, 153, 26, },
+ { 1, 1, 0, 6, 153, 63, },
+ { 0, 1, 0, 6, 157, 26, },
+ { 2, 1, 0, 6, 157, 26, },
+ { 1, 1, 0, 6, 157, 63, },
+ { 0, 1, 0, 6, 161, 26, },
+ { 2, 1, 0, 6, 161, 26, },
+ { 1, 1, 0, 6, 161, 63, },
+ { 0, 1, 0, 6, 165, 26, },
+ { 2, 1, 0, 6, 165, 26, },
+ { 1, 1, 0, 6, 165, 63, },
+ { 0, 1, 0, 7, 36, 20, },
+ { 2, 1, 0, 7, 36, 24, },
+ { 1, 1, 0, 7, 36, 24, },
+ { 0, 1, 0, 7, 40, 24, },
+ { 2, 1, 0, 7, 40, 24, },
+ { 1, 1, 0, 7, 40, 24, },
+ { 0, 1, 0, 7, 44, 24, },
+ { 2, 1, 0, 7, 44, 24, },
+ { 1, 1, 0, 7, 44, 24, },
+ { 0, 1, 0, 7, 48, 24, },
+ { 2, 1, 0, 7, 48, 24, },
+ { 1, 1, 0, 7, 48, 24, },
+ { 0, 1, 0, 7, 52, 24, },
+ { 2, 1, 0, 7, 52, 24, },
+ { 1, 1, 0, 7, 52, 24, },
+ { 0, 1, 0, 7, 56, 24, },
+ { 2, 1, 0, 7, 56, 24, },
+ { 1, 1, 0, 7, 56, 24, },
+ { 0, 1, 0, 7, 60, 24, },
+ { 2, 1, 0, 7, 60, 24, },
+ { 1, 1, 0, 7, 60, 24, },
+ { 0, 1, 0, 7, 64, 20, },
+ { 2, 1, 0, 7, 64, 24, },
+ { 1, 1, 0, 7, 64, 24, },
+ { 0, 1, 0, 7, 100, 20, },
+ { 2, 1, 0, 7, 100, 24, },
+ { 1, 1, 0, 7, 100, 24, },
+ { 0, 1, 0, 7, 104, 24, },
+ { 2, 1, 0, 7, 104, 24, },
+ { 1, 1, 0, 7, 104, 24, },
+ { 0, 1, 0, 7, 108, 24, },
+ { 2, 1, 0, 7, 108, 24, },
+ { 1, 1, 0, 7, 108, 24, },
+ { 0, 1, 0, 7, 112, 24, },
+ { 2, 1, 0, 7, 112, 24, },
+ { 1, 1, 0, 7, 112, 24, },
+ { 0, 1, 0, 7, 116, 24, },
+ { 2, 1, 0, 7, 116, 24, },
+ { 1, 1, 0, 7, 116, 24, },
+ { 0, 1, 0, 7, 120, 24, },
+ { 2, 1, 0, 7, 120, 24, },
+ { 1, 1, 0, 7, 120, 24, },
+ { 0, 1, 0, 7, 124, 24, },
+ { 2, 1, 0, 7, 124, 24, },
+ { 1, 1, 0, 7, 124, 24, },
+ { 0, 1, 0, 7, 128, 24, },
+ { 2, 1, 0, 7, 128, 24, },
+ { 1, 1, 0, 7, 128, 24, },
+ { 0, 1, 0, 7, 132, 24, },
+ { 2, 1, 0, 7, 132, 24, },
+ { 1, 1, 0, 7, 132, 24, },
+ { 0, 1, 0, 7, 136, 24, },
+ { 2, 1, 0, 7, 136, 24, },
+ { 1, 1, 0, 7, 136, 24, },
+ { 0, 1, 0, 7, 140, 20, },
+ { 2, 1, 0, 7, 140, 24, },
+ { 1, 1, 0, 7, 140, 24, },
+ { 0, 1, 0, 7, 149, 20, },
+ { 2, 1, 0, 7, 149, 24, },
+ { 1, 1, 0, 7, 149, 63, },
+ { 0, 1, 0, 7, 153, 24, },
+ { 2, 1, 0, 7, 153, 24, },
+ { 1, 1, 0, 7, 153, 63, },
+ { 0, 1, 0, 7, 157, 24, },
+ { 2, 1, 0, 7, 157, 24, },
+ { 1, 1, 0, 7, 157, 63, },
+ { 0, 1, 0, 7, 161, 24, },
+ { 2, 1, 0, 7, 161, 24, },
+ { 1, 1, 0, 7, 161, 63, },
+ { 0, 1, 0, 7, 165, 24, },
+ { 2, 1, 0, 7, 165, 24, },
+ { 1, 1, 0, 7, 165, 63, },
+ { 0, 1, 1, 2, 38, 26, },
+ { 2, 1, 1, 2, 38, 32, },
+ { 1, 1, 1, 2, 38, 32, },
+ { 0, 1, 1, 2, 46, 32, },
+ { 2, 1, 1, 2, 46, 32, },
+ { 1, 1, 1, 2, 46, 32, },
+ { 0, 1, 1, 2, 54, 32, },
+ { 2, 1, 1, 2, 54, 32, },
+ { 1, 1, 1, 2, 54, 32, },
+ { 0, 1, 1, 2, 62, 26, },
+ { 2, 1, 1, 2, 62, 32, },
+ { 1, 1, 1, 2, 62, 32, },
+ { 0, 1, 1, 2, 102, 26, },
+ { 2, 1, 1, 2, 102, 32, },
+ { 1, 1, 1, 2, 102, 32, },
+ { 0, 1, 1, 2, 110, 32, },
+ { 2, 1, 1, 2, 110, 32, },
+ { 1, 1, 1, 2, 110, 32, },
+ { 0, 1, 1, 2, 118, 32, },
+ { 2, 1, 1, 2, 118, 32, },
+ { 1, 1, 1, 2, 118, 32, },
+ { 0, 1, 1, 2, 126, 32, },
+ { 2, 1, 1, 2, 126, 32, },
+ { 1, 1, 1, 2, 126, 32, },
+ { 0, 1, 1, 2, 134, 32, },
+ { 2, 1, 1, 2, 134, 32, },
+ { 1, 1, 1, 2, 134, 32, },
+ { 0, 1, 1, 2, 151, 26, },
+ { 2, 1, 1, 2, 151, 32, },
+ { 1, 1, 1, 2, 151, 63, },
+ { 0, 1, 1, 2, 159, 32, },
+ { 2, 1, 1, 2, 159, 32, },
+ { 1, 1, 1, 2, 159, 63, },
+ { 0, 1, 1, 3, 38, 24, },
+ { 2, 1, 1, 3, 38, 28, },
+ { 1, 1, 1, 3, 38, 28, },
+ { 0, 1, 1, 3, 46, 28, },
+ { 2, 1, 1, 3, 46, 28, },
+ { 1, 1, 1, 3, 46, 28, },
+ { 0, 1, 1, 3, 54, 28, },
+ { 2, 1, 1, 3, 54, 28, },
+ { 1, 1, 1, 3, 54, 28, },
+ { 0, 1, 1, 3, 62, 24, },
+ { 2, 1, 1, 3, 62, 28, },
+ { 1, 1, 1, 3, 62, 28, },
+ { 0, 1, 1, 3, 102, 24, },
+ { 2, 1, 1, 3, 102, 28, },
+ { 1, 1, 1, 3, 102, 28, },
+ { 0, 1, 1, 3, 110, 28, },
+ { 2, 1, 1, 3, 110, 28, },
+ { 1, 1, 1, 3, 110, 28, },
+ { 0, 1, 1, 3, 118, 28, },
+ { 2, 1, 1, 3, 118, 28, },
+ { 1, 1, 1, 3, 118, 28, },
+ { 0, 1, 1, 3, 126, 28, },
+ { 2, 1, 1, 3, 126, 28, },
+ { 1, 1, 1, 3, 126, 28, },
+ { 0, 1, 1, 3, 134, 28, },
+ { 2, 1, 1, 3, 134, 28, },
+ { 1, 1, 1, 3, 134, 28, },
+ { 0, 1, 1, 3, 151, 24, },
+ { 2, 1, 1, 3, 151, 28, },
+ { 1, 1, 1, 3, 151, 63, },
+ { 0, 1, 1, 3, 159, 28, },
+ { 2, 1, 1, 3, 159, 28, },
+ { 1, 1, 1, 3, 159, 63, },
+ { 0, 1, 1, 6, 38, 20, },
+ { 2, 1, 1, 6, 38, 26, },
+ { 1, 1, 1, 6, 38, 26, },
+ { 0, 1, 1, 6, 46, 26, },
+ { 2, 1, 1, 6, 46, 26, },
+ { 1, 1, 1, 6, 46, 26, },
+ { 0, 1, 1, 6, 54, 26, },
+ { 2, 1, 1, 6, 54, 26, },
+ { 1, 1, 1, 6, 54, 26, },
+ { 0, 1, 1, 6, 62, 20, },
+ { 2, 1, 1, 6, 62, 26, },
+ { 1, 1, 1, 6, 62, 26, },
+ { 0, 1, 1, 6, 102, 20, },
+ { 2, 1, 1, 6, 102, 26, },
+ { 1, 1, 1, 6, 102, 26, },
+ { 0, 1, 1, 6, 110, 26, },
+ { 2, 1, 1, 6, 110, 26, },
+ { 1, 1, 1, 6, 110, 26, },
+ { 0, 1, 1, 6, 118, 26, },
+ { 2, 1, 1, 6, 118, 26, },
+ { 1, 1, 1, 6, 118, 26, },
+ { 0, 1, 1, 6, 126, 26, },
+ { 2, 1, 1, 6, 126, 26, },
+ { 1, 1, 1, 6, 126, 26, },
+ { 0, 1, 1, 6, 134, 26, },
+ { 2, 1, 1, 6, 134, 26, },
+ { 1, 1, 1, 6, 134, 26, },
+ { 0, 1, 1, 6, 151, 20, },
+ { 2, 1, 1, 6, 151, 26, },
+ { 1, 1, 1, 6, 151, 63, },
+ { 0, 1, 1, 6, 159, 26, },
+ { 2, 1, 1, 6, 159, 26, },
+ { 1, 1, 1, 6, 159, 63, },
+ { 0, 1, 1, 7, 38, 18, },
+ { 2, 1, 1, 7, 38, 24, },
+ { 1, 1, 1, 7, 38, 24, },
+ { 0, 1, 1, 7, 46, 24, },
+ { 2, 1, 1, 7, 46, 24, },
+ { 1, 1, 1, 7, 46, 24, },
+ { 0, 1, 1, 7, 54, 24, },
+ { 2, 1, 1, 7, 54, 24, },
+ { 1, 1, 1, 7, 54, 24, },
+ { 0, 1, 1, 7, 62, 18, },
+ { 2, 1, 1, 7, 62, 24, },
+ { 1, 1, 1, 7, 62, 24, },
+ { 0, 1, 1, 7, 102, 18, },
+ { 2, 1, 1, 7, 102, 24, },
+ { 1, 1, 1, 7, 102, 24, },
+ { 0, 1, 1, 7, 110, 24, },
+ { 2, 1, 1, 7, 110, 24, },
+ { 1, 1, 1, 7, 110, 24, },
+ { 0, 1, 1, 7, 118, 24, },
+ { 2, 1, 1, 7, 118, 24, },
+ { 1, 1, 1, 7, 118, 24, },
+ { 0, 1, 1, 7, 126, 24, },
+ { 2, 1, 1, 7, 126, 24, },
+ { 1, 1, 1, 7, 126, 24, },
+ { 0, 1, 1, 7, 134, 24, },
+ { 2, 1, 1, 7, 134, 24, },
+ { 1, 1, 1, 7, 134, 24, },
+ { 0, 1, 1, 7, 151, 18, },
+ { 2, 1, 1, 7, 151, 24, },
+ { 1, 1, 1, 7, 151, 63, },
+ { 0, 1, 1, 7, 159, 24, },
+ { 2, 1, 1, 7, 159, 24, },
+ { 1, 1, 1, 7, 159, 63, },
+ { 0, 1, 2, 4, 42, 22, },
+ { 2, 1, 2, 4, 42, 30, },
+ { 1, 1, 2, 4, 42, 30, },
+ { 0, 1, 2, 4, 58, 22, },
+ { 2, 1, 2, 4, 58, 30, },
+ { 1, 1, 2, 4, 58, 30, },
+ { 0, 1, 2, 4, 106, 22, },
+ { 2, 1, 2, 4, 106, 30, },
+ { 1, 1, 2, 4, 106, 30, },
+ { 0, 1, 2, 4, 122, 30, },
+ { 2, 1, 2, 4, 122, 30, },
+ { 1, 1, 2, 4, 122, 30, },
+ { 0, 1, 2, 4, 155, 22, },
+ { 2, 1, 2, 4, 155, 30, },
+ { 1, 1, 2, 4, 155, 63, },
+ { 0, 1, 2, 5, 42, 20, },
+ { 2, 1, 2, 5, 42, 28, },
+ { 1, 1, 2, 5, 42, 28, },
+ { 0, 1, 2, 5, 58, 20, },
+ { 2, 1, 2, 5, 58, 28, },
+ { 1, 1, 2, 5, 58, 28, },
+ { 0, 1, 2, 5, 106, 20, },
+ { 2, 1, 2, 5, 106, 28, },
+ { 1, 1, 2, 5, 106, 28, },
+ { 0, 1, 2, 5, 122, 28, },
+ { 2, 1, 2, 5, 122, 28, },
+ { 1, 1, 2, 5, 122, 28, },
+ { 0, 1, 2, 5, 155, 20, },
+ { 2, 1, 2, 5, 155, 28, },
+ { 1, 1, 2, 5, 155, 63, },
+ { 0, 1, 2, 8, 42, 18, },
+ { 2, 1, 2, 8, 42, 26, },
+ { 1, 1, 2, 8, 42, 26, },
+ { 0, 1, 2, 8, 58, 18, },
+ { 2, 1, 2, 8, 58, 26, },
+ { 1, 1, 2, 8, 58, 26, },
+ { 0, 1, 2, 8, 106, 18, },
+ { 2, 1, 2, 8, 106, 26, },
+ { 1, 1, 2, 8, 106, 26, },
+ { 0, 1, 2, 8, 122, 26, },
+ { 2, 1, 2, 8, 122, 26, },
+ { 1, 1, 2, 8, 122, 26, },
+ { 0, 1, 2, 8, 155, 18, },
+ { 2, 1, 2, 8, 155, 26, },
+ { 1, 1, 2, 8, 155, 63, },
+ { 0, 1, 2, 9, 42, 16, },
+ { 2, 1, 2, 9, 42, 24, },
+ { 1, 1, 2, 9, 42, 24, },
+ { 0, 1, 2, 9, 58, 16, },
+ { 2, 1, 2, 9, 58, 24, },
+ { 1, 1, 2, 9, 58, 24, },
+ { 0, 1, 2, 9, 106, 16, },
+ { 2, 1, 2, 9, 106, 24, },
+ { 1, 1, 2, 9, 106, 24, },
+ { 0, 1, 2, 9, 122, 24, },
+ { 2, 1, 2, 9, 122, 24, },
+ { 1, 1, 2, 9, 122, 24, },
+ { 0, 1, 2, 9, 155, 16, },
+ { 2, 1, 2, 9, 155, 24, },
+ { 1, 1, 2, 9, 155, 63, },
+};
+
+RTW_DECL_TABLE_TXPWR_LMT(rtw8814a_txpwr_lmt_type0);
+
+static const struct rtw_txpwr_lmt_cfg_pair rtw8814a_txpwr_lmt_type1[] = {
+ { 0, 0, 0, 0, 1, 34, },
+ { 2, 0, 0, 0, 1, 32, },
+ { 1, 0, 0, 0, 1, 32, },
+ { 0, 0, 0, 0, 2, 34, },
+ { 2, 0, 0, 0, 2, 32, },
+ { 1, 0, 0, 0, 2, 32, },
+ { 0, 0, 0, 0, 3, 34, },
+ { 2, 0, 0, 0, 3, 32, },
+ { 1, 0, 0, 0, 3, 32, },
+ { 0, 0, 0, 0, 4, 34, },
+ { 2, 0, 0, 0, 4, 32, },
+ { 1, 0, 0, 0, 4, 32, },
+ { 0, 0, 0, 0, 5, 34, },
+ { 2, 0, 0, 0, 5, 32, },
+ { 1, 0, 0, 0, 5, 32, },
+ { 0, 0, 0, 0, 6, 34, },
+ { 2, 0, 0, 0, 6, 32, },
+ { 1, 0, 0, 0, 6, 32, },
+ { 0, 0, 0, 0, 7, 34, },
+ { 2, 0, 0, 0, 7, 32, },
+ { 1, 0, 0, 0, 7, 32, },
+ { 0, 0, 0, 0, 8, 34, },
+ { 2, 0, 0, 0, 8, 32, },
+ { 1, 0, 0, 0, 8, 32, },
+ { 0, 0, 0, 0, 9, 34, },
+ { 2, 0, 0, 0, 9, 32, },
+ { 1, 0, 0, 0, 9, 32, },
+ { 0, 0, 0, 0, 10, 34, },
+ { 2, 0, 0, 0, 10, 32, },
+ { 1, 0, 0, 0, 10, 32, },
+ { 0, 0, 0, 0, 11, 34, },
+ { 2, 0, 0, 0, 11, 32, },
+ { 1, 0, 0, 0, 11, 32, },
+ { 0, 0, 0, 0, 12, 24, },
+ { 2, 0, 0, 0, 12, 32, },
+ { 1, 0, 0, 0, 12, 32, },
+ { 0, 0, 0, 0, 13, 16, },
+ { 2, 0, 0, 0, 13, 32, },
+ { 1, 0, 0, 0, 13, 32, },
+ { 0, 0, 0, 0, 14, 63, },
+ { 2, 0, 0, 0, 14, 63, },
+ { 1, 0, 0, 0, 14, 32, },
+ { 0, 0, 0, 1, 1, 30, },
+ { 2, 0, 0, 1, 1, 32, },
+ { 1, 0, 0, 1, 1, 32, },
+ { 0, 0, 0, 1, 2, 32, },
+ { 2, 0, 0, 1, 2, 32, },
+ { 1, 0, 0, 1, 2, 32, },
+ { 0, 0, 0, 1, 3, 32, },
+ { 2, 0, 0, 1, 3, 32, },
+ { 1, 0, 0, 1, 3, 32, },
+ { 0, 0, 0, 1, 4, 32, },
+ { 2, 0, 0, 1, 4, 32, },
+ { 1, 0, 0, 1, 4, 32, },
+ { 0, 0, 0, 1, 5, 32, },
+ { 2, 0, 0, 1, 5, 32, },
+ { 1, 0, 0, 1, 5, 32, },
+ { 0, 0, 0, 1, 6, 32, },
+ { 2, 0, 0, 1, 6, 32, },
+ { 1, 0, 0, 1, 6, 32, },
+ { 0, 0, 0, 1, 7, 32, },
+ { 2, 0, 0, 1, 7, 32, },
+ { 1, 0, 0, 1, 7, 32, },
+ { 0, 0, 0, 1, 8, 32, },
+ { 2, 0, 0, 1, 8, 32, },
+ { 1, 0, 0, 1, 8, 32, },
+ { 0, 0, 0, 1, 9, 32, },
+ { 2, 0, 0, 1, 9, 32, },
+ { 1, 0, 0, 1, 9, 32, },
+ { 0, 0, 0, 1, 10, 32, },
+ { 2, 0, 0, 1, 10, 32, },
+ { 1, 0, 0, 1, 10, 32, },
+ { 0, 0, 0, 1, 11, 30, },
+ { 2, 0, 0, 1, 11, 32, },
+ { 1, 0, 0, 1, 11, 32, },
+ { 0, 0, 0, 1, 12, 18, },
+ { 2, 0, 0, 1, 12, 32, },
+ { 1, 0, 0, 1, 12, 32, },
+ { 0, 0, 0, 1, 13, 8, },
+ { 2, 0, 0, 1, 13, 32, },
+ { 1, 0, 0, 1, 13, 32, },
+ { 0, 0, 0, 1, 14, 63, },
+ { 2, 0, 0, 1, 14, 63, },
+ { 1, 0, 0, 1, 14, 63, },
+ { 0, 0, 0, 2, 1, 28, },
+ { 2, 0, 0, 2, 1, 32, },
+ { 1, 0, 0, 2, 1, 32, },
+ { 0, 0, 0, 2, 2, 32, },
+ { 2, 0, 0, 2, 2, 32, },
+ { 1, 0, 0, 2, 2, 32, },
+ { 0, 0, 0, 2, 3, 32, },
+ { 2, 0, 0, 2, 3, 32, },
+ { 1, 0, 0, 2, 3, 32, },
+ { 0, 0, 0, 2, 4, 32, },
+ { 2, 0, 0, 2, 4, 32, },
+ { 1, 0, 0, 2, 4, 32, },
+ { 0, 0, 0, 2, 5, 32, },
+ { 2, 0, 0, 2, 5, 32, },
+ { 1, 0, 0, 2, 5, 32, },
+ { 0, 0, 0, 2, 6, 32, },
+ { 2, 0, 0, 2, 6, 32, },
+ { 1, 0, 0, 2, 6, 32, },
+ { 0, 0, 0, 2, 7, 32, },
+ { 2, 0, 0, 2, 7, 32, },
+ { 1, 0, 0, 2, 7, 32, },
+ { 0, 0, 0, 2, 8, 32, },
+ { 2, 0, 0, 2, 8, 32, },
+ { 1, 0, 0, 2, 8, 32, },
+ { 0, 0, 0, 2, 9, 32, },
+ { 2, 0, 0, 2, 9, 32, },
+ { 1, 0, 0, 2, 9, 32, },
+ { 0, 0, 0, 2, 10, 32, },
+ { 2, 0, 0, 2, 10, 32, },
+ { 1, 0, 0, 2, 10, 32, },
+ { 0, 0, 0, 2, 11, 28, },
+ { 2, 0, 0, 2, 11, 32, },
+ { 1, 0, 0, 2, 11, 32, },
+ { 0, 0, 0, 2, 12, 16, },
+ { 2, 0, 0, 2, 12, 32, },
+ { 1, 0, 0, 2, 12, 32, },
+ { 0, 0, 0, 2, 13, 6, },
+ { 2, 0, 0, 2, 13, 32, },
+ { 1, 0, 0, 2, 13, 32, },
+ { 0, 0, 0, 2, 14, 63, },
+ { 2, 0, 0, 2, 14, 63, },
+ { 1, 0, 0, 2, 14, 63, },
+ { 0, 0, 0, 3, 1, 26, },
+ { 2, 0, 0, 3, 1, 30, },
+ { 1, 0, 0, 3, 1, 30, },
+ { 0, 0, 0, 3, 2, 30, },
+ { 2, 0, 0, 3, 2, 30, },
+ { 1, 0, 0, 3, 2, 30, },
+ { 0, 0, 0, 3, 3, 30, },
+ { 2, 0, 0, 3, 3, 30, },
+ { 1, 0, 0, 3, 3, 30, },
+ { 0, 0, 0, 3, 4, 30, },
+ { 2, 0, 0, 3, 4, 30, },
+ { 1, 0, 0, 3, 4, 30, },
+ { 0, 0, 0, 3, 5, 30, },
+ { 2, 0, 0, 3, 5, 30, },
+ { 1, 0, 0, 3, 5, 30, },
+ { 0, 0, 0, 3, 6, 30, },
+ { 2, 0, 0, 3, 6, 30, },
+ { 1, 0, 0, 3, 6, 30, },
+ { 0, 0, 0, 3, 7, 30, },
+ { 2, 0, 0, 3, 7, 30, },
+ { 1, 0, 0, 3, 7, 30, },
+ { 0, 0, 0, 3, 8, 30, },
+ { 2, 0, 0, 3, 8, 30, },
+ { 1, 0, 0, 3, 8, 30, },
+ { 0, 0, 0, 3, 9, 30, },
+ { 2, 0, 0, 3, 9, 30, },
+ { 1, 0, 0, 3, 9, 30, },
+ { 0, 0, 0, 3, 10, 30, },
+ { 2, 0, 0, 3, 10, 30, },
+ { 1, 0, 0, 3, 10, 30, },
+ { 0, 0, 0, 3, 11, 26, },
+ { 2, 0, 0, 3, 11, 30, },
+ { 1, 0, 0, 3, 11, 30, },
+ { 0, 0, 0, 3, 12, 14, },
+ { 2, 0, 0, 3, 12, 30, },
+ { 1, 0, 0, 3, 12, 30, },
+ { 0, 0, 0, 3, 13, 4, },
+ { 2, 0, 0, 3, 13, 30, },
+ { 1, 0, 0, 3, 13, 30, },
+ { 0, 0, 0, 3, 14, 63, },
+ { 2, 0, 0, 3, 14, 63, },
+ { 1, 0, 0, 3, 14, 63, },
+ { 0, 0, 0, 6, 1, 24, },
+ { 2, 0, 0, 6, 1, 28, },
+ { 1, 0, 0, 6, 1, 28, },
+ { 0, 0, 0, 6, 2, 28, },
+ { 2, 0, 0, 6, 2, 28, },
+ { 1, 0, 0, 6, 2, 28, },
+ { 0, 0, 0, 6, 3, 28, },
+ { 2, 0, 0, 6, 3, 28, },
+ { 1, 0, 0, 6, 3, 28, },
+ { 0, 0, 0, 6, 4, 28, },
+ { 2, 0, 0, 6, 4, 28, },
+ { 1, 0, 0, 6, 4, 28, },
+ { 0, 0, 0, 6, 5, 28, },
+ { 2, 0, 0, 6, 5, 28, },
+ { 1, 0, 0, 6, 5, 28, },
+ { 0, 0, 0, 6, 6, 28, },
+ { 2, 0, 0, 6, 6, 28, },
+ { 1, 0, 0, 6, 6, 28, },
+ { 0, 0, 0, 6, 7, 28, },
+ { 2, 0, 0, 6, 7, 28, },
+ { 1, 0, 0, 6, 7, 28, },
+ { 0, 0, 0, 6, 8, 28, },
+ { 2, 0, 0, 6, 8, 28, },
+ { 1, 0, 0, 6, 8, 28, },
+ { 0, 0, 0, 6, 9, 28, },
+ { 2, 0, 0, 6, 9, 28, },
+ { 1, 0, 0, 6, 9, 28, },
+ { 0, 0, 0, 6, 10, 28, },
+ { 2, 0, 0, 6, 10, 28, },
+ { 1, 0, 0, 6, 10, 28, },
+ { 0, 0, 0, 6, 11, 24, },
+ { 2, 0, 0, 6, 11, 28, },
+ { 1, 0, 0, 6, 11, 28, },
+ { 0, 0, 0, 6, 12, 14, },
+ { 2, 0, 0, 6, 12, 28, },
+ { 1, 0, 0, 6, 12, 28, },
+ { 0, 0, 0, 6, 13, 4, },
+ { 2, 0, 0, 6, 13, 28, },
+ { 1, 0, 0, 6, 13, 28, },
+ { 0, 0, 0, 6, 14, 63, },
+ { 2, 0, 0, 6, 14, 63, },
+ { 1, 0, 0, 6, 14, 63, },
+ { 0, 0, 0, 7, 1, 22, },
+ { 2, 0, 0, 7, 1, 26, },
+ { 1, 0, 0, 7, 1, 26, },
+ { 0, 0, 0, 7, 2, 26, },
+ { 2, 0, 0, 7, 2, 26, },
+ { 1, 0, 0, 7, 2, 26, },
+ { 0, 0, 0, 7, 3, 26, },
+ { 2, 0, 0, 7, 3, 26, },
+ { 1, 0, 0, 7, 3, 26, },
+ { 0, 0, 0, 7, 4, 26, },
+ { 2, 0, 0, 7, 4, 26, },
+ { 1, 0, 0, 7, 4, 26, },
+ { 0, 0, 0, 7, 5, 26, },
+ { 2, 0, 0, 7, 5, 26, },
+ { 1, 0, 0, 7, 5, 26, },
+ { 0, 0, 0, 7, 6, 26, },
+ { 2, 0, 0, 7, 6, 26, },
+ { 1, 0, 0, 7, 6, 26, },
+ { 0, 0, 0, 7, 7, 26, },
+ { 2, 0, 0, 7, 7, 26, },
+ { 1, 0, 0, 7, 7, 26, },
+ { 0, 0, 0, 7, 8, 26, },
+ { 2, 0, 0, 7, 8, 26, },
+ { 1, 0, 0, 7, 8, 26, },
+ { 0, 0, 0, 7, 9, 26, },
+ { 2, 0, 0, 7, 9, 26, },
+ { 1, 0, 0, 7, 9, 26, },
+ { 0, 0, 0, 7, 10, 26, },
+ { 2, 0, 0, 7, 10, 26, },
+ { 1, 0, 0, 7, 10, 26, },
+ { 0, 0, 0, 7, 11, 22, },
+ { 2, 0, 0, 7, 11, 26, },
+ { 1, 0, 0, 7, 11, 26, },
+ { 0, 0, 0, 7, 12, 14, },
+ { 2, 0, 0, 7, 12, 26, },
+ { 1, 0, 0, 7, 12, 26, },
+ { 0, 0, 0, 7, 13, 4, },
+ { 2, 0, 0, 7, 13, 26, },
+ { 1, 0, 0, 7, 13, 26, },
+ { 0, 0, 0, 7, 14, 63, },
+ { 2, 0, 0, 7, 14, 63, },
+ { 1, 0, 0, 7, 14, 63, },
+ { 0, 0, 1, 2, 1, 63, },
+ { 2, 0, 1, 2, 1, 63, },
+ { 1, 0, 1, 2, 1, 63, },
+ { 0, 0, 1, 2, 2, 63, },
+ { 2, 0, 1, 2, 2, 63, },
+ { 1, 0, 1, 2, 2, 63, },
+ { 0, 0, 1, 2, 3, 28, },
+ { 2, 0, 1, 2, 3, 32, },
+ { 1, 0, 1, 2, 3, 32, },
+ { 0, 0, 1, 2, 4, 32, },
+ { 2, 0, 1, 2, 4, 32, },
+ { 1, 0, 1, 2, 4, 32, },
+ { 0, 0, 1, 2, 5, 32, },
+ { 2, 0, 1, 2, 5, 32, },
+ { 1, 0, 1, 2, 5, 32, },
+ { 0, 0, 1, 2, 6, 32, },
+ { 2, 0, 1, 2, 6, 32, },
+ { 1, 0, 1, 2, 6, 32, },
+ { 0, 0, 1, 2, 7, 32, },
+ { 2, 0, 1, 2, 7, 32, },
+ { 1, 0, 1, 2, 7, 32, },
+ { 0, 0, 1, 2, 8, 32, },
+ { 2, 0, 1, 2, 8, 32, },
+ { 1, 0, 1, 2, 8, 32, },
+ { 0, 0, 1, 2, 9, 32, },
+ { 2, 0, 1, 2, 9, 32, },
+ { 1, 0, 1, 2, 9, 32, },
+ { 0, 0, 1, 2, 10, 32, },
+ { 2, 0, 1, 2, 10, 32, },
+ { 1, 0, 1, 2, 10, 32, },
+ { 0, 0, 1, 2, 11, 28, },
+ { 2, 0, 1, 2, 11, 32, },
+ { 1, 0, 1, 2, 11, 32, },
+ { 0, 0, 1, 2, 12, 16, },
+ { 2, 0, 1, 2, 12, 32, },
+ { 1, 0, 1, 2, 12, 32, },
+ { 0, 0, 1, 2, 13, 10, },
+ { 2, 0, 1, 2, 13, 32, },
+ { 1, 0, 1, 2, 13, 32, },
+ { 0, 0, 1, 2, 14, 63, },
+ { 2, 0, 1, 2, 14, 63, },
+ { 1, 0, 1, 2, 14, 63, },
+ { 0, 0, 1, 3, 1, 63, },
+ { 2, 0, 1, 3, 1, 63, },
+ { 1, 0, 1, 3, 1, 63, },
+ { 0, 0, 1, 3, 2, 63, },
+ { 2, 0, 1, 3, 2, 63, },
+ { 1, 0, 1, 3, 2, 63, },
+ { 0, 0, 1, 3, 3, 26, },
+ { 2, 0, 1, 3, 3, 30, },
+ { 1, 0, 1, 3, 3, 30, },
+ { 0, 0, 1, 3, 4, 30, },
+ { 2, 0, 1, 3, 4, 30, },
+ { 1, 0, 1, 3, 4, 30, },
+ { 0, 0, 1, 3, 5, 30, },
+ { 2, 0, 1, 3, 5, 30, },
+ { 1, 0, 1, 3, 5, 30, },
+ { 0, 0, 1, 3, 6, 30, },
+ { 2, 0, 1, 3, 6, 30, },
+ { 1, 0, 1, 3, 6, 30, },
+ { 0, 0, 1, 3, 7, 30, },
+ { 2, 0, 1, 3, 7, 30, },
+ { 1, 0, 1, 3, 7, 30, },
+ { 0, 0, 1, 3, 8, 30, },
+ { 2, 0, 1, 3, 8, 30, },
+ { 1, 0, 1, 3, 8, 30, },
+ { 0, 0, 1, 3, 9, 30, },
+ { 2, 0, 1, 3, 9, 30, },
+ { 1, 0, 1, 3, 9, 30, },
+ { 0, 0, 1, 3, 10, 30, },
+ { 2, 0, 1, 3, 10, 30, },
+ { 1, 0, 1, 3, 10, 30, },
+ { 0, 0, 1, 3, 11, 26, },
+ { 2, 0, 1, 3, 11, 30, },
+ { 1, 0, 1, 3, 11, 30, },
+ { 0, 0, 1, 3, 12, 14, },
+ { 2, 0, 1, 3, 12, 30, },
+ { 1, 0, 1, 3, 12, 30, },
+ { 0, 0, 1, 3, 13, 8, },
+ { 2, 0, 1, 3, 13, 30, },
+ { 1, 0, 1, 3, 13, 30, },
+ { 0, 0, 1, 3, 14, 63, },
+ { 2, 0, 1, 3, 14, 63, },
+ { 1, 0, 1, 3, 14, 63, },
+ { 0, 0, 1, 6, 1, 63, },
+ { 2, 0, 1, 6, 1, 63, },
+ { 1, 0, 1, 6, 1, 63, },
+ { 0, 0, 1, 6, 2, 63, },
+ { 2, 0, 1, 6, 2, 63, },
+ { 1, 0, 1, 6, 2, 63, },
+ { 0, 0, 1, 6, 3, 24, },
+ { 2, 0, 1, 6, 3, 28, },
+ { 1, 0, 1, 6, 3, 28, },
+ { 0, 0, 1, 6, 4, 28, },
+ { 2, 0, 1, 6, 4, 28, },
+ { 1, 0, 1, 6, 4, 28, },
+ { 0, 0, 1, 6, 5, 28, },
+ { 2, 0, 1, 6, 5, 28, },
+ { 1, 0, 1, 6, 5, 28, },
+ { 0, 0, 1, 6, 6, 28, },
+ { 2, 0, 1, 6, 6, 28, },
+ { 1, 0, 1, 6, 6, 28, },
+ { 0, 0, 1, 6, 7, 28, },
+ { 2, 0, 1, 6, 7, 28, },
+ { 1, 0, 1, 6, 7, 28, },
+ { 0, 0, 1, 6, 8, 28, },
+ { 2, 0, 1, 6, 8, 28, },
+ { 1, 0, 1, 6, 8, 28, },
+ { 0, 0, 1, 6, 9, 28, },
+ { 2, 0, 1, 6, 9, 28, },
+ { 1, 0, 1, 6, 9, 28, },
+ { 0, 0, 1, 6, 10, 28, },
+ { 2, 0, 1, 6, 10, 28, },
+ { 1, 0, 1, 6, 10, 28, },
+ { 0, 0, 1, 6, 11, 24, },
+ { 2, 0, 1, 6, 11, 28, },
+ { 1, 0, 1, 6, 11, 28, },
+ { 0, 0, 1, 6, 12, 14, },
+ { 2, 0, 1, 6, 12, 28, },
+ { 1, 0, 1, 6, 12, 28, },
+ { 0, 0, 1, 6, 13, 8, },
+ { 2, 0, 1, 6, 13, 28, },
+ { 1, 0, 1, 6, 13, 28, },
+ { 0, 0, 1, 6, 14, 63, },
+ { 2, 0, 1, 6, 14, 63, },
+ { 1, 0, 1, 6, 14, 63, },
+ { 0, 0, 1, 7, 1, 63, },
+ { 2, 0, 1, 7, 1, 63, },
+ { 1, 0, 1, 7, 1, 63, },
+ { 0, 0, 1, 7, 2, 63, },
+ { 2, 0, 1, 7, 2, 63, },
+ { 1, 0, 1, 7, 2, 63, },
+ { 0, 0, 1, 7, 3, 22, },
+ { 2, 0, 1, 7, 3, 26, },
+ { 1, 0, 1, 7, 3, 26, },
+ { 0, 0, 1, 7, 4, 26, },
+ { 2, 0, 1, 7, 4, 26, },
+ { 1, 0, 1, 7, 4, 26, },
+ { 0, 0, 1, 7, 5, 26, },
+ { 2, 0, 1, 7, 5, 26, },
+ { 1, 0, 1, 7, 5, 26, },
+ { 0, 0, 1, 7, 6, 26, },
+ { 2, 0, 1, 7, 6, 26, },
+ { 1, 0, 1, 7, 6, 26, },
+ { 0, 0, 1, 7, 7, 26, },
+ { 2, 0, 1, 7, 7, 26, },
+ { 1, 0, 1, 7, 7, 26, },
+ { 0, 0, 1, 7, 8, 26, },
+ { 2, 0, 1, 7, 8, 26, },
+ { 1, 0, 1, 7, 8, 26, },
+ { 0, 0, 1, 7, 9, 26, },
+ { 2, 0, 1, 7, 9, 26, },
+ { 1, 0, 1, 7, 9, 26, },
+ { 0, 0, 1, 7, 10, 26, },
+ { 2, 0, 1, 7, 10, 26, },
+ { 1, 0, 1, 7, 10, 26, },
+ { 0, 0, 1, 7, 11, 22, },
+ { 2, 0, 1, 7, 11, 26, },
+ { 1, 0, 1, 7, 11, 26, },
+ { 0, 0, 1, 7, 12, 14, },
+ { 2, 0, 1, 7, 12, 26, },
+ { 1, 0, 1, 7, 12, 26, },
+ { 0, 0, 1, 7, 13, 8, },
+ { 2, 0, 1, 7, 13, 26, },
+ { 1, 0, 1, 7, 13, 26, },
+ { 0, 0, 1, 7, 14, 63, },
+ { 2, 0, 1, 7, 14, 63, },
+ { 1, 0, 1, 7, 14, 63, },
+ { 0, 1, 0, 1, 36, 28, },
+ { 2, 1, 0, 1, 36, 32, },
+ { 1, 1, 0, 1, 36, 32, },
+ { 0, 1, 0, 1, 40, 32, },
+ { 2, 1, 0, 1, 40, 32, },
+ { 1, 1, 0, 1, 40, 32, },
+ { 0, 1, 0, 1, 44, 32, },
+ { 2, 1, 0, 1, 44, 32, },
+ { 1, 1, 0, 1, 44, 32, },
+ { 0, 1, 0, 1, 48, 32, },
+ { 2, 1, 0, 1, 48, 32, },
+ { 1, 1, 0, 1, 48, 32, },
+ { 0, 1, 0, 1, 52, 32, },
+ { 2, 1, 0, 1, 52, 32, },
+ { 1, 1, 0, 1, 52, 32, },
+ { 0, 1, 0, 1, 56, 32, },
+ { 2, 1, 0, 1, 56, 32, },
+ { 1, 1, 0, 1, 56, 32, },
+ { 0, 1, 0, 1, 60, 32, },
+ { 2, 1, 0, 1, 60, 32, },
+ { 1, 1, 0, 1, 60, 32, },
+ { 0, 1, 0, 1, 64, 30, },
+ { 2, 1, 0, 1, 64, 32, },
+ { 1, 1, 0, 1, 64, 32, },
+ { 0, 1, 0, 1, 100, 28, },
+ { 2, 1, 0, 1, 100, 32, },
+ { 1, 1, 0, 1, 100, 32, },
+ { 0, 1, 0, 1, 104, 32, },
+ { 2, 1, 0, 1, 104, 32, },
+ { 1, 1, 0, 1, 104, 32, },
+ { 0, 1, 0, 1, 108, 32, },
+ { 2, 1, 0, 1, 108, 32, },
+ { 1, 1, 0, 1, 108, 32, },
+ { 0, 1, 0, 1, 112, 32, },
+ { 2, 1, 0, 1, 112, 32, },
+ { 1, 1, 0, 1, 112, 32, },
+ { 0, 1, 0, 1, 116, 32, },
+ { 2, 1, 0, 1, 116, 32, },
+ { 1, 1, 0, 1, 116, 32, },
+ { 0, 1, 0, 1, 120, 32, },
+ { 2, 1, 0, 1, 120, 32, },
+ { 1, 1, 0, 1, 120, 32, },
+ { 0, 1, 0, 1, 124, 32, },
+ { 2, 1, 0, 1, 124, 32, },
+ { 1, 1, 0, 1, 124, 32, },
+ { 0, 1, 0, 1, 128, 32, },
+ { 2, 1, 0, 1, 128, 32, },
+ { 1, 1, 0, 1, 128, 32, },
+ { 0, 1, 0, 1, 132, 32, },
+ { 2, 1, 0, 1, 132, 32, },
+ { 1, 1, 0, 1, 132, 32, },
+ { 0, 1, 0, 1, 136, 32, },
+ { 2, 1, 0, 1, 136, 32, },
+ { 1, 1, 0, 1, 136, 32, },
+ { 0, 1, 0, 1, 140, 28, },
+ { 2, 1, 0, 1, 140, 32, },
+ { 1, 1, 0, 1, 140, 32, },
+ { 0, 1, 0, 1, 149, 28, },
+ { 2, 1, 0, 1, 149, 32, },
+ { 1, 1, 0, 1, 149, 63, },
+ { 0, 1, 0, 1, 153, 32, },
+ { 2, 1, 0, 1, 153, 32, },
+ { 1, 1, 0, 1, 153, 63, },
+ { 0, 1, 0, 1, 157, 32, },
+ { 2, 1, 0, 1, 157, 32, },
+ { 1, 1, 0, 1, 157, 63, },
+ { 0, 1, 0, 1, 161, 32, },
+ { 2, 1, 0, 1, 161, 32, },
+ { 1, 1, 0, 1, 161, 63, },
+ { 0, 1, 0, 1, 165, 32, },
+ { 2, 1, 0, 1, 165, 32, },
+ { 1, 1, 0, 1, 165, 63, },
+ { 0, 1, 0, 2, 36, 28, },
+ { 2, 1, 0, 2, 36, 32, },
+ { 1, 1, 0, 2, 36, 32, },
+ { 0, 1, 0, 2, 40, 32, },
+ { 2, 1, 0, 2, 40, 32, },
+ { 1, 1, 0, 2, 40, 32, },
+ { 0, 1, 0, 2, 44, 32, },
+ { 2, 1, 0, 2, 44, 32, },
+ { 1, 1, 0, 2, 44, 32, },
+ { 0, 1, 0, 2, 48, 32, },
+ { 2, 1, 0, 2, 48, 32, },
+ { 1, 1, 0, 2, 48, 32, },
+ { 0, 1, 0, 2, 52, 32, },
+ { 2, 1, 0, 2, 52, 32, },
+ { 1, 1, 0, 2, 52, 32, },
+ { 0, 1, 0, 2, 56, 32, },
+ { 2, 1, 0, 2, 56, 32, },
+ { 1, 1, 0, 2, 56, 32, },
+ { 0, 1, 0, 2, 60, 32, },
+ { 2, 1, 0, 2, 60, 32, },
+ { 1, 1, 0, 2, 60, 32, },
+ { 0, 1, 0, 2, 64, 30, },
+ { 2, 1, 0, 2, 64, 32, },
+ { 1, 1, 0, 2, 64, 32, },
+ { 0, 1, 0, 2, 100, 28, },
+ { 2, 1, 0, 2, 100, 32, },
+ { 1, 1, 0, 2, 100, 32, },
+ { 0, 1, 0, 2, 104, 32, },
+ { 2, 1, 0, 2, 104, 32, },
+ { 1, 1, 0, 2, 104, 32, },
+ { 0, 1, 0, 2, 108, 32, },
+ { 2, 1, 0, 2, 108, 32, },
+ { 1, 1, 0, 2, 108, 32, },
+ { 0, 1, 0, 2, 112, 32, },
+ { 2, 1, 0, 2, 112, 32, },
+ { 1, 1, 0, 2, 112, 32, },
+ { 0, 1, 0, 2, 116, 32, },
+ { 2, 1, 0, 2, 116, 32, },
+ { 1, 1, 0, 2, 116, 32, },
+ { 0, 1, 0, 2, 120, 32, },
+ { 2, 1, 0, 2, 120, 32, },
+ { 1, 1, 0, 2, 120, 32, },
+ { 0, 1, 0, 2, 124, 32, },
+ { 2, 1, 0, 2, 124, 32, },
+ { 1, 1, 0, 2, 124, 32, },
+ { 0, 1, 0, 2, 128, 32, },
+ { 2, 1, 0, 2, 128, 32, },
+ { 1, 1, 0, 2, 128, 32, },
+ { 0, 1, 0, 2, 132, 32, },
+ { 2, 1, 0, 2, 132, 32, },
+ { 1, 1, 0, 2, 132, 32, },
+ { 0, 1, 0, 2, 136, 32, },
+ { 2, 1, 0, 2, 136, 32, },
+ { 1, 1, 0, 2, 136, 32, },
+ { 0, 1, 0, 2, 140, 28, },
+ { 2, 1, 0, 2, 140, 32, },
+ { 1, 1, 0, 2, 140, 32, },
+ { 0, 1, 0, 2, 149, 28, },
+ { 2, 1, 0, 2, 149, 32, },
+ { 1, 1, 0, 2, 149, 63, },
+ { 0, 1, 0, 2, 153, 32, },
+ { 2, 1, 0, 2, 153, 32, },
+ { 1, 1, 0, 2, 153, 63, },
+ { 0, 1, 0, 2, 157, 32, },
+ { 2, 1, 0, 2, 157, 32, },
+ { 1, 1, 0, 2, 157, 63, },
+ { 0, 1, 0, 2, 161, 32, },
+ { 2, 1, 0, 2, 161, 32, },
+ { 1, 1, 0, 2, 161, 63, },
+ { 0, 1, 0, 2, 165, 32, },
+ { 2, 1, 0, 2, 165, 32, },
+ { 1, 1, 0, 2, 165, 63, },
+ { 0, 1, 0, 3, 36, 26, },
+ { 2, 1, 0, 3, 36, 30, },
+ { 1, 1, 0, 3, 36, 30, },
+ { 0, 1, 0, 3, 40, 30, },
+ { 2, 1, 0, 3, 40, 30, },
+ { 1, 1, 0, 3, 40, 30, },
+ { 0, 1, 0, 3, 44, 30, },
+ { 2, 1, 0, 3, 44, 30, },
+ { 1, 1, 0, 3, 44, 30, },
+ { 0, 1, 0, 3, 48, 30, },
+ { 2, 1, 0, 3, 48, 30, },
+ { 1, 1, 0, 3, 48, 30, },
+ { 0, 1, 0, 3, 52, 30, },
+ { 2, 1, 0, 3, 52, 30, },
+ { 1, 1, 0, 3, 52, 30, },
+ { 0, 1, 0, 3, 56, 30, },
+ { 2, 1, 0, 3, 56, 30, },
+ { 1, 1, 0, 3, 56, 30, },
+ { 0, 1, 0, 3, 60, 30, },
+ { 2, 1, 0, 3, 60, 30, },
+ { 1, 1, 0, 3, 60, 30, },
+ { 0, 1, 0, 3, 64, 28, },
+ { 2, 1, 0, 3, 64, 30, },
+ { 1, 1, 0, 3, 64, 30, },
+ { 0, 1, 0, 3, 100, 28, },
+ { 2, 1, 0, 3, 100, 30, },
+ { 1, 1, 0, 3, 100, 30, },
+ { 0, 1, 0, 3, 104, 30, },
+ { 2, 1, 0, 3, 104, 30, },
+ { 1, 1, 0, 3, 104, 30, },
+ { 0, 1, 0, 3, 108, 30, },
+ { 2, 1, 0, 3, 108, 30, },
+ { 1, 1, 0, 3, 108, 30, },
+ { 0, 1, 0, 3, 112, 30, },
+ { 2, 1, 0, 3, 112, 30, },
+ { 1, 1, 0, 3, 112, 30, },
+ { 0, 1, 0, 3, 116, 30, },
+ { 2, 1, 0, 3, 116, 30, },
+ { 1, 1, 0, 3, 116, 30, },
+ { 0, 1, 0, 3, 120, 30, },
+ { 2, 1, 0, 3, 120, 30, },
+ { 1, 1, 0, 3, 120, 30, },
+ { 0, 1, 0, 3, 124, 30, },
+ { 2, 1, 0, 3, 124, 30, },
+ { 1, 1, 0, 3, 124, 30, },
+ { 0, 1, 0, 3, 128, 30, },
+ { 2, 1, 0, 3, 128, 30, },
+ { 1, 1, 0, 3, 128, 30, },
+ { 0, 1, 0, 3, 132, 30, },
+ { 2, 1, 0, 3, 132, 30, },
+ { 1, 1, 0, 3, 132, 30, },
+ { 0, 1, 0, 3, 136, 30, },
+ { 2, 1, 0, 3, 136, 30, },
+ { 1, 1, 0, 3, 136, 30, },
+ { 0, 1, 0, 3, 140, 26, },
+ { 2, 1, 0, 3, 140, 30, },
+ { 1, 1, 0, 3, 140, 30, },
+ { 0, 1, 0, 3, 149, 26, },
+ { 2, 1, 0, 3, 149, 30, },
+ { 1, 1, 0, 3, 149, 63, },
+ { 0, 1, 0, 3, 153, 30, },
+ { 2, 1, 0, 3, 153, 30, },
+ { 1, 1, 0, 3, 153, 63, },
+ { 0, 1, 0, 3, 157, 30, },
+ { 2, 1, 0, 3, 157, 30, },
+ { 1, 1, 0, 3, 157, 63, },
+ { 0, 1, 0, 3, 161, 30, },
+ { 2, 1, 0, 3, 161, 30, },
+ { 1, 1, 0, 3, 161, 63, },
+ { 0, 1, 0, 3, 165, 30, },
+ { 2, 1, 0, 3, 165, 30, },
+ { 1, 1, 0, 3, 165, 63, },
+ { 0, 1, 0, 6, 36, 24, },
+ { 2, 1, 0, 6, 36, 28, },
+ { 1, 1, 0, 6, 36, 28, },
+ { 0, 1, 0, 6, 40, 28, },
+ { 2, 1, 0, 6, 40, 28, },
+ { 1, 1, 0, 6, 40, 28, },
+ { 0, 1, 0, 6, 44, 28, },
+ { 2, 1, 0, 6, 44, 28, },
+ { 1, 1, 0, 6, 44, 28, },
+ { 0, 1, 0, 6, 48, 28, },
+ { 2, 1, 0, 6, 48, 28, },
+ { 1, 1, 0, 6, 48, 28, },
+ { 0, 1, 0, 6, 52, 28, },
+ { 2, 1, 0, 6, 52, 28, },
+ { 1, 1, 0, 6, 52, 28, },
+ { 0, 1, 0, 6, 56, 28, },
+ { 2, 1, 0, 6, 56, 28, },
+ { 1, 1, 0, 6, 56, 28, },
+ { 0, 1, 0, 6, 60, 28, },
+ { 2, 1, 0, 6, 60, 28, },
+ { 1, 1, 0, 6, 60, 28, },
+ { 0, 1, 0, 6, 64, 26, },
+ { 2, 1, 0, 6, 64, 28, },
+ { 1, 1, 0, 6, 64, 28, },
+ { 0, 1, 0, 6, 100, 24, },
+ { 2, 1, 0, 6, 100, 28, },
+ { 1, 1, 0, 6, 100, 28, },
+ { 0, 1, 0, 6, 104, 28, },
+ { 2, 1, 0, 6, 104, 28, },
+ { 1, 1, 0, 6, 104, 28, },
+ { 0, 1, 0, 6, 108, 28, },
+ { 2, 1, 0, 6, 108, 28, },
+ { 1, 1, 0, 6, 108, 28, },
+ { 0, 1, 0, 6, 112, 28, },
+ { 2, 1, 0, 6, 112, 28, },
+ { 1, 1, 0, 6, 112, 28, },
+ { 0, 1, 0, 6, 116, 28, },
+ { 2, 1, 0, 6, 116, 28, },
+ { 1, 1, 0, 6, 116, 28, },
+ { 0, 1, 0, 6, 120, 28, },
+ { 2, 1, 0, 6, 120, 28, },
+ { 1, 1, 0, 6, 120, 28, },
+ { 0, 1, 0, 6, 124, 28, },
+ { 2, 1, 0, 6, 124, 28, },
+ { 1, 1, 0, 6, 124, 28, },
+ { 0, 1, 0, 6, 128, 28, },
+ { 2, 1, 0, 6, 128, 28, },
+ { 1, 1, 0, 6, 128, 28, },
+ { 0, 1, 0, 6, 132, 28, },
+ { 2, 1, 0, 6, 132, 28, },
+ { 1, 1, 0, 6, 132, 28, },
+ { 0, 1, 0, 6, 136, 28, },
+ { 2, 1, 0, 6, 136, 28, },
+ { 1, 1, 0, 6, 136, 28, },
+ { 0, 1, 0, 6, 140, 24, },
+ { 2, 1, 0, 6, 140, 28, },
+ { 1, 1, 0, 6, 140, 28, },
+ { 0, 1, 0, 6, 149, 24, },
+ { 2, 1, 0, 6, 149, 28, },
+ { 1, 1, 0, 6, 149, 63, },
+ { 0, 1, 0, 6, 153, 28, },
+ { 2, 1, 0, 6, 153, 28, },
+ { 1, 1, 0, 6, 153, 63, },
+ { 0, 1, 0, 6, 157, 28, },
+ { 2, 1, 0, 6, 157, 28, },
+ { 1, 1, 0, 6, 157, 63, },
+ { 0, 1, 0, 6, 161, 28, },
+ { 2, 1, 0, 6, 161, 28, },
+ { 1, 1, 0, 6, 161, 63, },
+ { 0, 1, 0, 6, 165, 28, },
+ { 2, 1, 0, 6, 165, 28, },
+ { 1, 1, 0, 6, 165, 63, },
+ { 0, 1, 0, 7, 36, 22, },
+ { 2, 1, 0, 7, 36, 26, },
+ { 1, 1, 0, 7, 36, 26, },
+ { 0, 1, 0, 7, 40, 26, },
+ { 2, 1, 0, 7, 40, 26, },
+ { 1, 1, 0, 7, 40, 26, },
+ { 0, 1, 0, 7, 44, 26, },
+ { 2, 1, 0, 7, 44, 26, },
+ { 1, 1, 0, 7, 44, 26, },
+ { 0, 1, 0, 7, 48, 26, },
+ { 2, 1, 0, 7, 48, 26, },
+ { 1, 1, 0, 7, 48, 26, },
+ { 0, 1, 0, 7, 52, 26, },
+ { 2, 1, 0, 7, 52, 26, },
+ { 1, 1, 0, 7, 52, 26, },
+ { 0, 1, 0, 7, 56, 26, },
+ { 2, 1, 0, 7, 56, 26, },
+ { 1, 1, 0, 7, 56, 26, },
+ { 0, 1, 0, 7, 60, 26, },
+ { 2, 1, 0, 7, 60, 26, },
+ { 1, 1, 0, 7, 60, 26, },
+ { 0, 1, 0, 7, 64, 24, },
+ { 2, 1, 0, 7, 64, 26, },
+ { 1, 1, 0, 7, 64, 26, },
+ { 0, 1, 0, 7, 100, 22, },
+ { 2, 1, 0, 7, 100, 26, },
+ { 1, 1, 0, 7, 100, 26, },
+ { 0, 1, 0, 7, 104, 26, },
+ { 2, 1, 0, 7, 104, 26, },
+ { 1, 1, 0, 7, 104, 26, },
+ { 0, 1, 0, 7, 108, 26, },
+ { 2, 1, 0, 7, 108, 26, },
+ { 1, 1, 0, 7, 108, 26, },
+ { 0, 1, 0, 7, 112, 26, },
+ { 2, 1, 0, 7, 112, 26, },
+ { 1, 1, 0, 7, 112, 26, },
+ { 0, 1, 0, 7, 116, 26, },
+ { 2, 1, 0, 7, 116, 26, },
+ { 1, 1, 0, 7, 116, 26, },
+ { 0, 1, 0, 7, 120, 26, },
+ { 2, 1, 0, 7, 120, 26, },
+ { 1, 1, 0, 7, 120, 26, },
+ { 0, 1, 0, 7, 124, 26, },
+ { 2, 1, 0, 7, 124, 26, },
+ { 1, 1, 0, 7, 124, 26, },
+ { 0, 1, 0, 7, 128, 26, },
+ { 2, 1, 0, 7, 128, 26, },
+ { 1, 1, 0, 7, 128, 26, },
+ { 0, 1, 0, 7, 132, 26, },
+ { 2, 1, 0, 7, 132, 26, },
+ { 1, 1, 0, 7, 132, 26, },
+ { 0, 1, 0, 7, 136, 26, },
+ { 2, 1, 0, 7, 136, 26, },
+ { 1, 1, 0, 7, 136, 26, },
+ { 0, 1, 0, 7, 140, 22, },
+ { 2, 1, 0, 7, 140, 26, },
+ { 1, 1, 0, 7, 140, 26, },
+ { 0, 1, 0, 7, 149, 22, },
+ { 2, 1, 0, 7, 149, 26, },
+ { 1, 1, 0, 7, 149, 63, },
+ { 0, 1, 0, 7, 153, 26, },
+ { 2, 1, 0, 7, 153, 26, },
+ { 1, 1, 0, 7, 153, 63, },
+ { 0, 1, 0, 7, 157, 26, },
+ { 2, 1, 0, 7, 157, 26, },
+ { 1, 1, 0, 7, 157, 63, },
+ { 0, 1, 0, 7, 161, 26, },
+ { 2, 1, 0, 7, 161, 26, },
+ { 1, 1, 0, 7, 161, 63, },
+ { 0, 1, 0, 7, 165, 26, },
+ { 2, 1, 0, 7, 165, 26, },
+ { 1, 1, 0, 7, 165, 63, },
+ { 0, 1, 1, 2, 38, 28, },
+ { 2, 1, 1, 2, 38, 32, },
+ { 1, 1, 1, 2, 38, 32, },
+ { 0, 1, 1, 2, 46, 32, },
+ { 2, 1, 1, 2, 46, 32, },
+ { 1, 1, 1, 2, 46, 32, },
+ { 0, 1, 1, 2, 54, 32, },
+ { 2, 1, 1, 2, 54, 32, },
+ { 1, 1, 1, 2, 54, 32, },
+ { 0, 1, 1, 2, 62, 28, },
+ { 2, 1, 1, 2, 62, 32, },
+ { 1, 1, 1, 2, 62, 32, },
+ { 0, 1, 1, 2, 102, 28, },
+ { 2, 1, 1, 2, 102, 32, },
+ { 1, 1, 1, 2, 102, 32, },
+ { 0, 1, 1, 2, 110, 32, },
+ { 2, 1, 1, 2, 110, 32, },
+ { 1, 1, 1, 2, 110, 32, },
+ { 0, 1, 1, 2, 118, 32, },
+ { 2, 1, 1, 2, 118, 32, },
+ { 1, 1, 1, 2, 118, 32, },
+ { 0, 1, 1, 2, 126, 32, },
+ { 2, 1, 1, 2, 126, 32, },
+ { 1, 1, 1, 2, 126, 32, },
+ { 0, 1, 1, 2, 134, 30, },
+ { 2, 1, 1, 2, 134, 32, },
+ { 1, 1, 1, 2, 134, 32, },
+ { 0, 1, 1, 2, 151, 28, },
+ { 2, 1, 1, 2, 151, 32, },
+ { 1, 1, 1, 2, 151, 63, },
+ { 0, 1, 1, 2, 159, 32, },
+ { 2, 1, 1, 2, 159, 32, },
+ { 1, 1, 1, 2, 159, 63, },
+ { 0, 1, 1, 3, 38, 26, },
+ { 2, 1, 1, 3, 38, 30, },
+ { 1, 1, 1, 3, 38, 30, },
+ { 0, 1, 1, 3, 46, 30, },
+ { 2, 1, 1, 3, 46, 30, },
+ { 1, 1, 1, 3, 46, 30, },
+ { 0, 1, 1, 3, 54, 30, },
+ { 2, 1, 1, 3, 54, 30, },
+ { 1, 1, 1, 3, 54, 30, },
+ { 0, 1, 1, 3, 62, 26, },
+ { 2, 1, 1, 3, 62, 30, },
+ { 1, 1, 1, 3, 62, 30, },
+ { 0, 1, 1, 3, 102, 26, },
+ { 2, 1, 1, 3, 102, 30, },
+ { 1, 1, 1, 3, 102, 30, },
+ { 0, 1, 1, 3, 110, 30, },
+ { 2, 1, 1, 3, 110, 30, },
+ { 1, 1, 1, 3, 110, 30, },
+ { 0, 1, 1, 3, 118, 30, },
+ { 2, 1, 1, 3, 118, 30, },
+ { 1, 1, 1, 3, 118, 30, },
+ { 0, 1, 1, 3, 126, 30, },
+ { 2, 1, 1, 3, 126, 30, },
+ { 1, 1, 1, 3, 126, 30, },
+ { 0, 1, 1, 3, 134, 28, },
+ { 2, 1, 1, 3, 134, 30, },
+ { 1, 1, 1, 3, 134, 30, },
+ { 0, 1, 1, 3, 151, 26, },
+ { 2, 1, 1, 3, 151, 30, },
+ { 1, 1, 1, 3, 151, 63, },
+ { 0, 1, 1, 3, 159, 30, },
+ { 2, 1, 1, 3, 159, 30, },
+ { 1, 1, 1, 3, 159, 63, },
+ { 0, 1, 1, 6, 38, 20, },
+ { 2, 1, 1, 6, 38, 28, },
+ { 1, 1, 1, 6, 38, 28, },
+ { 0, 1, 1, 6, 46, 28, },
+ { 2, 1, 1, 6, 46, 28, },
+ { 1, 1, 1, 6, 46, 28, },
+ { 0, 1, 1, 6, 54, 28, },
+ { 2, 1, 1, 6, 54, 28, },
+ { 1, 1, 1, 6, 54, 28, },
+ { 0, 1, 1, 6, 62, 20, },
+ { 2, 1, 1, 6, 62, 28, },
+ { 1, 1, 1, 6, 62, 28, },
+ { 0, 1, 1, 6, 102, 22, },
+ { 2, 1, 1, 6, 102, 28, },
+ { 1, 1, 1, 6, 102, 28, },
+ { 0, 1, 1, 6, 110, 28, },
+ { 2, 1, 1, 6, 110, 28, },
+ { 1, 1, 1, 6, 110, 28, },
+ { 0, 1, 1, 6, 118, 28, },
+ { 2, 1, 1, 6, 118, 28, },
+ { 1, 1, 1, 6, 118, 28, },
+ { 0, 1, 1, 6, 126, 28, },
+ { 2, 1, 1, 6, 126, 28, },
+ { 1, 1, 1, 6, 126, 28, },
+ { 0, 1, 1, 6, 134, 26, },
+ { 2, 1, 1, 6, 134, 28, },
+ { 1, 1, 1, 6, 134, 28, },
+ { 0, 1, 1, 6, 151, 22, },
+ { 2, 1, 1, 6, 151, 28, },
+ { 1, 1, 1, 6, 151, 63, },
+ { 0, 1, 1, 6, 159, 28, },
+ { 2, 1, 1, 6, 159, 28, },
+ { 1, 1, 1, 6, 159, 63, },
+ { 0, 1, 1, 7, 38, 18, },
+ { 2, 1, 1, 7, 38, 26, },
+ { 1, 1, 1, 7, 38, 26, },
+ { 0, 1, 1, 7, 46, 26, },
+ { 2, 1, 1, 7, 46, 26, },
+ { 1, 1, 1, 7, 46, 26, },
+ { 0, 1, 1, 7, 54, 26, },
+ { 2, 1, 1, 7, 54, 26, },
+ { 1, 1, 1, 7, 54, 26, },
+ { 0, 1, 1, 7, 62, 18, },
+ { 2, 1, 1, 7, 62, 26, },
+ { 1, 1, 1, 7, 62, 26, },
+ { 0, 1, 1, 7, 102, 20, },
+ { 2, 1, 1, 7, 102, 26, },
+ { 1, 1, 1, 7, 102, 26, },
+ { 0, 1, 1, 7, 110, 26, },
+ { 2, 1, 1, 7, 110, 26, },
+ { 1, 1, 1, 7, 110, 26, },
+ { 0, 1, 1, 7, 118, 26, },
+ { 2, 1, 1, 7, 118, 26, },
+ { 1, 1, 1, 7, 118, 26, },
+ { 0, 1, 1, 7, 126, 26, },
+ { 2, 1, 1, 7, 126, 26, },
+ { 1, 1, 1, 7, 126, 26, },
+ { 0, 1, 1, 7, 134, 24, },
+ { 2, 1, 1, 7, 134, 26, },
+ { 1, 1, 1, 7, 134, 26, },
+ { 0, 1, 1, 7, 151, 20, },
+ { 2, 1, 1, 7, 151, 26, },
+ { 1, 1, 1, 7, 151, 63, },
+ { 0, 1, 1, 7, 159, 26, },
+ { 2, 1, 1, 7, 159, 26, },
+ { 1, 1, 1, 7, 159, 63, },
+ { 0, 1, 2, 4, 42, 24, },
+ { 2, 1, 2, 4, 42, 32, },
+ { 1, 1, 2, 4, 42, 32, },
+ { 0, 1, 2, 4, 58, 24, },
+ { 2, 1, 2, 4, 58, 32, },
+ { 1, 1, 2, 4, 58, 32, },
+ { 0, 1, 2, 4, 106, 24, },
+ { 2, 1, 2, 4, 106, 32, },
+ { 1, 1, 2, 4, 106, 32, },
+ { 0, 1, 2, 4, 122, 32, },
+ { 2, 1, 2, 4, 122, 32, },
+ { 1, 1, 2, 4, 122, 32, },
+ { 0, 1, 2, 4, 155, 26, },
+ { 2, 1, 2, 4, 155, 32, },
+ { 1, 1, 2, 4, 155, 63, },
+ { 0, 1, 2, 5, 42, 22, },
+ { 2, 1, 2, 5, 42, 30, },
+ { 1, 1, 2, 5, 42, 30, },
+ { 0, 1, 2, 5, 58, 22, },
+ { 2, 1, 2, 5, 58, 30, },
+ { 1, 1, 2, 5, 58, 30, },
+ { 0, 1, 2, 5, 106, 22, },
+ { 2, 1, 2, 5, 106, 30, },
+ { 1, 1, 2, 5, 106, 30, },
+ { 0, 1, 2, 5, 122, 30, },
+ { 2, 1, 2, 5, 122, 30, },
+ { 1, 1, 2, 5, 122, 30, },
+ { 0, 1, 2, 5, 155, 24, },
+ { 2, 1, 2, 5, 155, 30, },
+ { 1, 1, 2, 5, 155, 63, },
+ { 0, 1, 2, 8, 42, 20, },
+ { 2, 1, 2, 8, 42, 28, },
+ { 1, 1, 2, 8, 42, 28, },
+ { 0, 1, 2, 8, 58, 20, },
+ { 2, 1, 2, 8, 58, 28, },
+ { 1, 1, 2, 8, 58, 28, },
+ { 0, 1, 2, 8, 106, 20, },
+ { 2, 1, 2, 8, 106, 28, },
+ { 1, 1, 2, 8, 106, 28, },
+ { 0, 1, 2, 8, 122, 28, },
+ { 2, 1, 2, 8, 122, 28, },
+ { 1, 1, 2, 8, 122, 28, },
+ { 0, 1, 2, 8, 155, 20, },
+ { 2, 1, 2, 8, 155, 28, },
+ { 1, 1, 2, 8, 155, 63, },
+ { 0, 1, 2, 9, 42, 18, },
+ { 2, 1, 2, 9, 42, 26, },
+ { 1, 1, 2, 9, 42, 26, },
+ { 0, 1, 2, 9, 58, 18, },
+ { 2, 1, 2, 9, 58, 26, },
+ { 1, 1, 2, 9, 58, 26, },
+ { 0, 1, 2, 9, 106, 18, },
+ { 2, 1, 2, 9, 106, 26, },
+ { 1, 1, 2, 9, 106, 26, },
+ { 0, 1, 2, 9, 122, 26, },
+ { 2, 1, 2, 9, 122, 26, },
+ { 1, 1, 2, 9, 122, 26, },
+ { 0, 1, 2, 9, 155, 18, },
+ { 2, 1, 2, 9, 155, 26, },
+ { 1, 1, 2, 9, 155, 63, },
+};
+
+RTW_DECL_TABLE_TXPWR_LMT(rtw8814a_txpwr_lmt_type1);
+
+static const struct rtw_txpwr_lmt_cfg_pair rtw8814a_txpwr_lmt_type2[] = {
+ { 0, 0, 0, 0, 1, 42, },
+ { 2, 0, 0, 0, 1, 42, },
+ { 1, 0, 0, 0, 1, 42, },
+ { 0, 0, 0, 0, 2, 50, },
+ { 2, 0, 0, 0, 2, 42, },
+ { 1, 0, 0, 0, 2, 42, },
+ { 0, 0, 0, 0, 3, 50, },
+ { 2, 0, 0, 0, 3, 42, },
+ { 1, 0, 0, 0, 3, 42, },
+ { 0, 0, 0, 0, 4, 50, },
+ { 2, 0, 0, 0, 4, 42, },
+ { 1, 0, 0, 0, 4, 42, },
+ { 0, 0, 0, 0, 5, 50, },
+ { 2, 0, 0, 0, 5, 42, },
+ { 1, 0, 0, 0, 5, 42, },
+ { 0, 0, 0, 0, 6, 50, },
+ { 2, 0, 0, 0, 6, 42, },
+ { 1, 0, 0, 0, 6, 42, },
+ { 0, 0, 0, 0, 7, 50, },
+ { 2, 0, 0, 0, 7, 42, },
+ { 1, 0, 0, 0, 7, 42, },
+ { 0, 0, 0, 0, 8, 50, },
+ { 2, 0, 0, 0, 8, 42, },
+ { 1, 0, 0, 0, 8, 42, },
+ { 0, 0, 0, 0, 9, 50, },
+ { 2, 0, 0, 0, 9, 42, },
+ { 1, 0, 0, 0, 9, 42, },
+ { 0, 0, 0, 0, 10, 50, },
+ { 2, 0, 0, 0, 10, 42, },
+ { 1, 0, 0, 0, 10, 42, },
+ { 0, 0, 0, 0, 11, 44, },
+ { 2, 0, 0, 0, 11, 42, },
+ { 1, 0, 0, 0, 11, 42, },
+ { 0, 0, 0, 0, 12, 63, },
+ { 2, 0, 0, 0, 12, 42, },
+ { 1, 0, 0, 0, 12, 42, },
+ { 0, 0, 0, 0, 13, 63, },
+ { 2, 0, 0, 0, 13, 42, },
+ { 1, 0, 0, 0, 13, 42, },
+ { 0, 0, 0, 0, 14, 63, },
+ { 2, 0, 0, 0, 14, 63, },
+ { 1, 0, 0, 0, 14, 42, },
+ { 0, 0, 0, 1, 1, 32, },
+ { 2, 0, 0, 1, 1, 42, },
+ { 1, 0, 0, 1, 1, 42, },
+ { 0, 0, 0, 1, 2, 42, },
+ { 2, 0, 0, 1, 2, 42, },
+ { 1, 0, 0, 1, 2, 42, },
+ { 0, 0, 0, 1, 3, 42, },
+ { 2, 0, 0, 1, 3, 42, },
+ { 1, 0, 0, 1, 3, 42, },
+ { 0, 0, 0, 1, 4, 42, },
+ { 2, 0, 0, 1, 4, 42, },
+ { 1, 0, 0, 1, 4, 42, },
+ { 0, 0, 0, 1, 5, 42, },
+ { 2, 0, 0, 1, 5, 42, },
+ { 1, 0, 0, 1, 5, 42, },
+ { 0, 0, 0, 1, 6, 42, },
+ { 2, 0, 0, 1, 6, 42, },
+ { 1, 0, 0, 1, 6, 42, },
+ { 0, 0, 0, 1, 7, 42, },
+ { 2, 0, 0, 1, 7, 42, },
+ { 1, 0, 0, 1, 7, 42, },
+ { 0, 0, 0, 1, 8, 42, },
+ { 2, 0, 0, 1, 8, 42, },
+ { 1, 0, 0, 1, 8, 42, },
+ { 0, 0, 0, 1, 9, 42, },
+ { 2, 0, 0, 1, 9, 42, },
+ { 1, 0, 0, 1, 9, 42, },
+ { 0, 0, 0, 1, 10, 42, },
+ { 2, 0, 0, 1, 10, 42, },
+ { 1, 0, 0, 1, 10, 42, },
+ { 0, 0, 0, 1, 11, 32, },
+ { 2, 0, 0, 1, 11, 42, },
+ { 1, 0, 0, 1, 11, 42, },
+ { 0, 0, 0, 1, 12, 63, },
+ { 2, 0, 0, 1, 12, 42, },
+ { 1, 0, 0, 1, 12, 42, },
+ { 0, 0, 0, 1, 13, 63, },
+ { 2, 0, 0, 1, 13, 42, },
+ { 1, 0, 0, 1, 13, 42, },
+ { 0, 0, 0, 1, 14, 63, },
+ { 2, 0, 0, 1, 14, 63, },
+ { 1, 0, 0, 1, 14, 63, },
+ { 0, 0, 0, 2, 1, 32, },
+ { 2, 0, 0, 2, 1, 42, },
+ { 1, 0, 0, 2, 1, 42, },
+ { 0, 0, 0, 2, 2, 40, },
+ { 2, 0, 0, 2, 2, 42, },
+ { 1, 0, 0, 2, 2, 42, },
+ { 0, 0, 0, 2, 3, 40, },
+ { 2, 0, 0, 2, 3, 42, },
+ { 1, 0, 0, 2, 3, 42, },
+ { 0, 0, 0, 2, 4, 40, },
+ { 2, 0, 0, 2, 4, 42, },
+ { 1, 0, 0, 2, 4, 42, },
+ { 0, 0, 0, 2, 5, 40, },
+ { 2, 0, 0, 2, 5, 42, },
+ { 1, 0, 0, 2, 5, 42, },
+ { 0, 0, 0, 2, 6, 40, },
+ { 2, 0, 0, 2, 6, 42, },
+ { 1, 0, 0, 2, 6, 42, },
+ { 0, 0, 0, 2, 7, 40, },
+ { 2, 0, 0, 2, 7, 42, },
+ { 1, 0, 0, 2, 7, 42, },
+ { 0, 0, 0, 2, 8, 40, },
+ { 2, 0, 0, 2, 8, 42, },
+ { 1, 0, 0, 2, 8, 42, },
+ { 0, 0, 0, 2, 9, 40, },
+ { 2, 0, 0, 2, 9, 42, },
+ { 1, 0, 0, 2, 9, 42, },
+ { 0, 0, 0, 2, 10, 40, },
+ { 2, 0, 0, 2, 10, 42, },
+ { 1, 0, 0, 2, 10, 42, },
+ { 0, 0, 0, 2, 11, 28, },
+ { 2, 0, 0, 2, 11, 42, },
+ { 1, 0, 0, 2, 11, 42, },
+ { 0, 0, 0, 2, 12, 63, },
+ { 2, 0, 0, 2, 12, 42, },
+ { 1, 0, 0, 2, 12, 42, },
+ { 0, 0, 0, 2, 13, 63, },
+ { 2, 0, 0, 2, 13, 42, },
+ { 1, 0, 0, 2, 13, 42, },
+ { 0, 0, 0, 2, 14, 63, },
+ { 2, 0, 0, 2, 14, 63, },
+ { 1, 0, 0, 2, 14, 63, },
+ { 0, 0, 0, 3, 1, 32, },
+ { 2, 0, 0, 3, 1, 40, },
+ { 1, 0, 0, 3, 1, 40, },
+ { 0, 0, 0, 3, 2, 40, },
+ { 2, 0, 0, 3, 2, 40, },
+ { 1, 0, 0, 3, 2, 40, },
+ { 0, 0, 0, 3, 3, 40, },
+ { 2, 0, 0, 3, 3, 40, },
+ { 1, 0, 0, 3, 3, 40, },
+ { 0, 0, 0, 3, 4, 40, },
+ { 2, 0, 0, 3, 4, 40, },
+ { 1, 0, 0, 3, 4, 40, },
+ { 0, 0, 0, 3, 5, 40, },
+ { 2, 0, 0, 3, 5, 40, },
+ { 1, 0, 0, 3, 5, 40, },
+ { 0, 0, 0, 3, 6, 40, },
+ { 2, 0, 0, 3, 6, 40, },
+ { 1, 0, 0, 3, 6, 40, },
+ { 0, 0, 0, 3, 7, 40, },
+ { 2, 0, 0, 3, 7, 40, },
+ { 1, 0, 0, 3, 7, 40, },
+ { 0, 0, 0, 3, 8, 40, },
+ { 2, 0, 0, 3, 8, 40, },
+ { 1, 0, 0, 3, 8, 40, },
+ { 0, 0, 0, 3, 9, 40, },
+ { 2, 0, 0, 3, 9, 40, },
+ { 1, 0, 0, 3, 9, 40, },
+ { 0, 0, 0, 3, 10, 40, },
+ { 2, 0, 0, 3, 10, 40, },
+ { 1, 0, 0, 3, 10, 40, },
+ { 0, 0, 0, 3, 11, 28, },
+ { 2, 0, 0, 3, 11, 40, },
+ { 1, 0, 0, 3, 11, 40, },
+ { 0, 0, 0, 3, 12, 63, },
+ { 2, 0, 0, 3, 12, 40, },
+ { 1, 0, 0, 3, 12, 40, },
+ { 0, 0, 0, 3, 13, 63, },
+ { 2, 0, 0, 3, 13, 40, },
+ { 1, 0, 0, 3, 13, 40, },
+ { 0, 0, 0, 3, 14, 63, },
+ { 2, 0, 0, 3, 14, 63, },
+ { 1, 0, 0, 3, 14, 63, },
+ { 0, 0, 0, 6, 1, 32, },
+ { 2, 0, 0, 6, 1, 40, },
+ { 1, 0, 0, 6, 1, 40, },
+ { 0, 0, 0, 6, 2, 40, },
+ { 2, 0, 0, 6, 2, 40, },
+ { 1, 0, 0, 6, 2, 40, },
+ { 0, 0, 0, 6, 3, 40, },
+ { 2, 0, 0, 6, 3, 40, },
+ { 1, 0, 0, 6, 3, 40, },
+ { 0, 0, 0, 6, 4, 40, },
+ { 2, 0, 0, 6, 4, 40, },
+ { 1, 0, 0, 6, 4, 40, },
+ { 0, 0, 0, 6, 5, 40, },
+ { 2, 0, 0, 6, 5, 40, },
+ { 1, 0, 0, 6, 5, 40, },
+ { 0, 0, 0, 6, 6, 40, },
+ { 2, 0, 0, 6, 6, 40, },
+ { 1, 0, 0, 6, 6, 40, },
+ { 0, 0, 0, 6, 7, 40, },
+ { 2, 0, 0, 6, 7, 40, },
+ { 1, 0, 0, 6, 7, 40, },
+ { 0, 0, 0, 6, 8, 40, },
+ { 2, 0, 0, 6, 8, 40, },
+ { 1, 0, 0, 6, 8, 40, },
+ { 0, 0, 0, 6, 9, 40, },
+ { 2, 0, 0, 6, 9, 40, },
+ { 1, 0, 0, 6, 9, 40, },
+ { 0, 0, 0, 6, 10, 40, },
+ { 2, 0, 0, 6, 10, 40, },
+ { 1, 0, 0, 6, 10, 40, },
+ { 0, 0, 0, 6, 11, 28, },
+ { 2, 0, 0, 6, 11, 40, },
+ { 1, 0, 0, 6, 11, 40, },
+ { 0, 0, 0, 6, 12, 63, },
+ { 2, 0, 0, 6, 12, 40, },
+ { 1, 0, 0, 6, 12, 40, },
+ { 0, 0, 0, 6, 13, 63, },
+ { 2, 0, 0, 6, 13, 40, },
+ { 1, 0, 0, 6, 13, 40, },
+ { 0, 0, 0, 6, 14, 63, },
+ { 2, 0, 0, 6, 14, 63, },
+ { 1, 0, 0, 6, 14, 63, },
+ { 0, 0, 0, 7, 1, 32, },
+ { 2, 0, 0, 7, 1, 40, },
+ { 1, 0, 0, 7, 1, 40, },
+ { 0, 0, 0, 7, 2, 40, },
+ { 2, 0, 0, 7, 2, 40, },
+ { 1, 0, 0, 7, 2, 40, },
+ { 0, 0, 0, 7, 3, 40, },
+ { 2, 0, 0, 7, 3, 40, },
+ { 1, 0, 0, 7, 3, 40, },
+ { 0, 0, 0, 7, 4, 40, },
+ { 2, 0, 0, 7, 4, 40, },
+ { 1, 0, 0, 7, 4, 40, },
+ { 0, 0, 0, 7, 5, 40, },
+ { 2, 0, 0, 7, 5, 40, },
+ { 1, 0, 0, 7, 5, 40, },
+ { 0, 0, 0, 7, 6, 40, },
+ { 2, 0, 0, 7, 6, 40, },
+ { 1, 0, 0, 7, 6, 40, },
+ { 0, 0, 0, 7, 7, 40, },
+ { 2, 0, 0, 7, 7, 40, },
+ { 1, 0, 0, 7, 7, 40, },
+ { 0, 0, 0, 7, 8, 40, },
+ { 2, 0, 0, 7, 8, 40, },
+ { 1, 0, 0, 7, 8, 40, },
+ { 0, 0, 0, 7, 9, 40, },
+ { 2, 0, 0, 7, 9, 40, },
+ { 1, 0, 0, 7, 9, 40, },
+ { 0, 0, 0, 7, 10, 40, },
+ { 2, 0, 0, 7, 10, 40, },
+ { 1, 0, 0, 7, 10, 40, },
+ { 0, 0, 0, 7, 11, 28, },
+ { 2, 0, 0, 7, 11, 40, },
+ { 1, 0, 0, 7, 11, 40, },
+ { 0, 0, 0, 7, 12, 63, },
+ { 2, 0, 0, 7, 12, 40, },
+ { 1, 0, 0, 7, 12, 40, },
+ { 0, 0, 0, 7, 13, 63, },
+ { 2, 0, 0, 7, 13, 40, },
+ { 1, 0, 0, 7, 13, 40, },
+ { 0, 0, 0, 7, 14, 63, },
+ { 2, 0, 0, 7, 14, 63, },
+ { 1, 0, 0, 7, 14, 63, },
+ { 0, 0, 1, 2, 1, 63, },
+ { 2, 0, 1, 2, 1, 63, },
+ { 1, 0, 1, 2, 1, 63, },
+ { 0, 0, 1, 2, 2, 63, },
+ { 2, 0, 1, 2, 2, 63, },
+ { 1, 0, 1, 2, 2, 63, },
+ { 0, 0, 1, 2, 3, 30, },
+ { 2, 0, 1, 2, 3, 34, },
+ { 1, 0, 1, 2, 3, 34, },
+ { 0, 0, 1, 2, 4, 34, },
+ { 2, 0, 1, 2, 4, 34, },
+ { 1, 0, 1, 2, 4, 34, },
+ { 0, 0, 1, 2, 5, 34, },
+ { 2, 0, 1, 2, 5, 34, },
+ { 1, 0, 1, 2, 5, 34, },
+ { 0, 0, 1, 2, 6, 34, },
+ { 2, 0, 1, 2, 6, 34, },
+ { 1, 0, 1, 2, 6, 34, },
+ { 0, 0, 1, 2, 7, 34, },
+ { 2, 0, 1, 2, 7, 34, },
+ { 1, 0, 1, 2, 7, 34, },
+ { 0, 0, 1, 2, 8, 34, },
+ { 2, 0, 1, 2, 8, 34, },
+ { 1, 0, 1, 2, 8, 34, },
+ { 0, 0, 1, 2, 9, 34, },
+ { 2, 0, 1, 2, 9, 34, },
+ { 1, 0, 1, 2, 9, 34, },
+ { 0, 0, 1, 2, 10, 34, },
+ { 2, 0, 1, 2, 10, 34, },
+ { 1, 0, 1, 2, 10, 34, },
+ { 0, 0, 1, 2, 11, 28, },
+ { 2, 0, 1, 2, 11, 34, },
+ { 1, 0, 1, 2, 11, 34, },
+ { 0, 0, 1, 2, 12, 63, },
+ { 2, 0, 1, 2, 12, 34, },
+ { 1, 0, 1, 2, 12, 34, },
+ { 0, 0, 1, 2, 13, 63, },
+ { 2, 0, 1, 2, 13, 34, },
+ { 1, 0, 1, 2, 13, 34, },
+ { 0, 0, 1, 2, 14, 63, },
+ { 2, 0, 1, 2, 14, 63, },
+ { 1, 0, 1, 2, 14, 63, },
+ { 0, 0, 1, 3, 1, 63, },
+ { 2, 0, 1, 3, 1, 63, },
+ { 1, 0, 1, 3, 1, 63, },
+ { 0, 0, 1, 3, 2, 63, },
+ { 2, 0, 1, 3, 2, 63, },
+ { 1, 0, 1, 3, 2, 63, },
+ { 0, 0, 1, 3, 3, 30, },
+ { 2, 0, 1, 3, 3, 34, },
+ { 1, 0, 1, 3, 3, 34, },
+ { 0, 0, 1, 3, 4, 34, },
+ { 2, 0, 1, 3, 4, 34, },
+ { 1, 0, 1, 3, 4, 34, },
+ { 0, 0, 1, 3, 5, 34, },
+ { 2, 0, 1, 3, 5, 34, },
+ { 1, 0, 1, 3, 5, 34, },
+ { 0, 0, 1, 3, 6, 34, },
+ { 2, 0, 1, 3, 6, 34, },
+ { 1, 0, 1, 3, 6, 34, },
+ { 0, 0, 1, 3, 7, 34, },
+ { 2, 0, 1, 3, 7, 34, },
+ { 1, 0, 1, 3, 7, 34, },
+ { 0, 0, 1, 3, 8, 34, },
+ { 2, 0, 1, 3, 8, 34, },
+ { 1, 0, 1, 3, 8, 34, },
+ { 0, 0, 1, 3, 9, 34, },
+ { 2, 0, 1, 3, 9, 34, },
+ { 1, 0, 1, 3, 9, 34, },
+ { 0, 0, 1, 3, 10, 34, },
+ { 2, 0, 1, 3, 10, 34, },
+ { 1, 0, 1, 3, 10, 34, },
+ { 0, 0, 1, 3, 11, 28, },
+ { 2, 0, 1, 3, 11, 34, },
+ { 1, 0, 1, 3, 11, 34, },
+ { 0, 0, 1, 3, 12, 63, },
+ { 2, 0, 1, 3, 12, 34, },
+ { 1, 0, 1, 3, 12, 34, },
+ { 0, 0, 1, 3, 13, 63, },
+ { 2, 0, 1, 3, 13, 34, },
+ { 1, 0, 1, 3, 13, 34, },
+ { 0, 0, 1, 3, 14, 63, },
+ { 2, 0, 1, 3, 14, 63, },
+ { 1, 0, 1, 3, 14, 63, },
+ { 0, 0, 1, 6, 1, 63, },
+ { 2, 0, 1, 6, 1, 63, },
+ { 1, 0, 1, 6, 1, 63, },
+ { 0, 0, 1, 6, 2, 63, },
+ { 2, 0, 1, 6, 2, 63, },
+ { 1, 0, 1, 6, 2, 63, },
+ { 0, 0, 1, 6, 3, 30, },
+ { 2, 0, 1, 6, 3, 34, },
+ { 1, 0, 1, 6, 3, 34, },
+ { 0, 0, 1, 6, 4, 34, },
+ { 2, 0, 1, 6, 4, 34, },
+ { 1, 0, 1, 6, 4, 34, },
+ { 0, 0, 1, 6, 5, 34, },
+ { 2, 0, 1, 6, 5, 34, },
+ { 1, 0, 1, 6, 5, 34, },
+ { 0, 0, 1, 6, 6, 34, },
+ { 2, 0, 1, 6, 6, 34, },
+ { 1, 0, 1, 6, 6, 34, },
+ { 0, 0, 1, 6, 7, 34, },
+ { 2, 0, 1, 6, 7, 34, },
+ { 1, 0, 1, 6, 7, 34, },
+ { 0, 0, 1, 6, 8, 34, },
+ { 2, 0, 1, 6, 8, 34, },
+ { 1, 0, 1, 6, 8, 34, },
+ { 0, 0, 1, 6, 9, 34, },
+ { 2, 0, 1, 6, 9, 34, },
+ { 1, 0, 1, 6, 9, 34, },
+ { 0, 0, 1, 6, 10, 34, },
+ { 2, 0, 1, 6, 10, 34, },
+ { 1, 0, 1, 6, 10, 34, },
+ { 0, 0, 1, 6, 11, 28, },
+ { 2, 0, 1, 6, 11, 34, },
+ { 1, 0, 1, 6, 11, 34, },
+ { 0, 0, 1, 6, 12, 63, },
+ { 2, 0, 1, 6, 12, 34, },
+ { 1, 0, 1, 6, 12, 34, },
+ { 0, 0, 1, 6, 13, 63, },
+ { 2, 0, 1, 6, 13, 34, },
+ { 1, 0, 1, 6, 13, 34, },
+ { 0, 0, 1, 6, 14, 63, },
+ { 2, 0, 1, 6, 14, 63, },
+ { 1, 0, 1, 6, 14, 63, },
+ { 0, 0, 1, 7, 1, 63, },
+ { 2, 0, 1, 7, 1, 63, },
+ { 1, 0, 1, 7, 1, 63, },
+ { 0, 0, 1, 7, 2, 63, },
+ { 2, 0, 1, 7, 2, 63, },
+ { 1, 0, 1, 7, 2, 63, },
+ { 0, 0, 1, 7, 3, 30, },
+ { 2, 0, 1, 7, 3, 34, },
+ { 1, 0, 1, 7, 3, 34, },
+ { 0, 0, 1, 7, 4, 34, },
+ { 2, 0, 1, 7, 4, 34, },
+ { 1, 0, 1, 7, 4, 34, },
+ { 0, 0, 1, 7, 5, 34, },
+ { 2, 0, 1, 7, 5, 34, },
+ { 1, 0, 1, 7, 5, 34, },
+ { 0, 0, 1, 7, 6, 34, },
+ { 2, 0, 1, 7, 6, 34, },
+ { 1, 0, 1, 7, 6, 34, },
+ { 0, 0, 1, 7, 7, 34, },
+ { 2, 0, 1, 7, 7, 34, },
+ { 1, 0, 1, 7, 7, 34, },
+ { 0, 0, 1, 7, 8, 34, },
+ { 2, 0, 1, 7, 8, 34, },
+ { 1, 0, 1, 7, 8, 34, },
+ { 0, 0, 1, 7, 9, 34, },
+ { 2, 0, 1, 7, 9, 34, },
+ { 1, 0, 1, 7, 9, 34, },
+ { 0, 0, 1, 7, 10, 34, },
+ { 2, 0, 1, 7, 10, 34, },
+ { 1, 0, 1, 7, 10, 34, },
+ { 0, 0, 1, 7, 11, 28, },
+ { 2, 0, 1, 7, 11, 34, },
+ { 1, 0, 1, 7, 11, 34, },
+ { 0, 0, 1, 7, 12, 63, },
+ { 2, 0, 1, 7, 12, 34, },
+ { 1, 0, 1, 7, 12, 34, },
+ { 0, 0, 1, 7, 13, 63, },
+ { 2, 0, 1, 7, 13, 34, },
+ { 1, 0, 1, 7, 13, 34, },
+ { 0, 0, 1, 7, 14, 63, },
+ { 2, 0, 1, 7, 14, 63, },
+ { 1, 0, 1, 7, 14, 63, },
+ { 0, 1, 0, 1, 36, 42, },
+ { 2, 1, 0, 1, 36, 42, },
+ { 1, 1, 0, 1, 36, 42, },
+ { 0, 1, 0, 1, 40, 42, },
+ { 2, 1, 0, 1, 40, 42, },
+ { 1, 1, 0, 1, 40, 42, },
+ { 0, 1, 0, 1, 44, 42, },
+ { 2, 1, 0, 1, 44, 42, },
+ { 1, 1, 0, 1, 44, 42, },
+ { 0, 1, 0, 1, 48, 42, },
+ { 2, 1, 0, 1, 48, 42, },
+ { 1, 1, 0, 1, 48, 42, },
+ { 0, 1, 0, 1, 52, 42, },
+ { 2, 1, 0, 1, 52, 42, },
+ { 1, 1, 0, 1, 52, 42, },
+ { 0, 1, 0, 1, 56, 42, },
+ { 2, 1, 0, 1, 56, 42, },
+ { 1, 1, 0, 1, 56, 42, },
+ { 0, 1, 0, 1, 60, 42, },
+ { 2, 1, 0, 1, 60, 42, },
+ { 1, 1, 0, 1, 60, 42, },
+ { 0, 1, 0, 1, 64, 42, },
+ { 2, 1, 0, 1, 64, 42, },
+ { 1, 1, 0, 1, 64, 42, },
+ { 0, 1, 0, 1, 100, 42, },
+ { 2, 1, 0, 1, 100, 42, },
+ { 1, 1, 0, 1, 100, 42, },
+ { 0, 1, 0, 1, 104, 42, },
+ { 2, 1, 0, 1, 104, 42, },
+ { 1, 1, 0, 1, 104, 42, },
+ { 0, 1, 0, 1, 108, 42, },
+ { 2, 1, 0, 1, 108, 42, },
+ { 1, 1, 0, 1, 108, 42, },
+ { 0, 1, 0, 1, 112, 42, },
+ { 2, 1, 0, 1, 112, 42, },
+ { 1, 1, 0, 1, 112, 42, },
+ { 0, 1, 0, 1, 116, 42, },
+ { 2, 1, 0, 1, 116, 42, },
+ { 1, 1, 0, 1, 116, 42, },
+ { 0, 1, 0, 1, 120, 42, },
+ { 2, 1, 0, 1, 120, 42, },
+ { 1, 1, 0, 1, 120, 42, },
+ { 0, 1, 0, 1, 124, 42, },
+ { 2, 1, 0, 1, 124, 42, },
+ { 1, 1, 0, 1, 124, 42, },
+ { 0, 1, 0, 1, 128, 42, },
+ { 2, 1, 0, 1, 128, 42, },
+ { 1, 1, 0, 1, 128, 42, },
+ { 0, 1, 0, 1, 132, 42, },
+ { 2, 1, 0, 1, 132, 42, },
+ { 1, 1, 0, 1, 132, 42, },
+ { 0, 1, 0, 1, 136, 42, },
+ { 2, 1, 0, 1, 136, 42, },
+ { 1, 1, 0, 1, 136, 42, },
+ { 0, 1, 0, 1, 140, 40, },
+ { 2, 1, 0, 1, 140, 40, },
+ { 1, 1, 0, 1, 140, 40, },
+ { 0, 1, 0, 1, 149, 44, },
+ { 2, 1, 0, 1, 149, 44, },
+ { 1, 1, 0, 1, 149, 63, },
+ { 0, 1, 0, 1, 153, 44, },
+ { 2, 1, 0, 1, 153, 44, },
+ { 1, 1, 0, 1, 153, 63, },
+ { 0, 1, 0, 1, 157, 44, },
+ { 2, 1, 0, 1, 157, 44, },
+ { 1, 1, 0, 1, 157, 63, },
+ { 0, 1, 0, 1, 161, 44, },
+ { 2, 1, 0, 1, 161, 44, },
+ { 1, 1, 0, 1, 161, 63, },
+ { 0, 1, 0, 1, 165, 44, },
+ { 2, 1, 0, 1, 165, 44, },
+ { 1, 1, 0, 1, 165, 63, },
+ { 0, 1, 0, 2, 36, 32, },
+ { 2, 1, 0, 2, 36, 32, },
+ { 1, 1, 0, 2, 36, 32, },
+ { 0, 1, 0, 2, 40, 32, },
+ { 2, 1, 0, 2, 40, 32, },
+ { 1, 1, 0, 2, 40, 32, },
+ { 0, 1, 0, 2, 44, 32, },
+ { 2, 1, 0, 2, 44, 32, },
+ { 1, 1, 0, 2, 44, 32, },
+ { 0, 1, 0, 2, 48, 36, },
+ { 2, 1, 0, 2, 48, 36, },
+ { 1, 1, 0, 2, 48, 36, },
+ { 0, 1, 0, 2, 52, 36, },
+ { 2, 1, 0, 2, 52, 36, },
+ { 1, 1, 0, 2, 52, 36, },
+ { 0, 1, 0, 2, 56, 32, },
+ { 2, 1, 0, 2, 56, 32, },
+ { 1, 1, 0, 2, 56, 32, },
+ { 0, 1, 0, 2, 60, 32, },
+ { 2, 1, 0, 2, 60, 32, },
+ { 1, 1, 0, 2, 60, 32, },
+ { 0, 1, 0, 2, 64, 32, },
+ { 2, 1, 0, 2, 64, 32, },
+ { 1, 1, 0, 2, 64, 32, },
+ { 0, 1, 0, 2, 100, 32, },
+ { 2, 1, 0, 2, 100, 32, },
+ { 1, 1, 0, 2, 100, 32, },
+ { 0, 1, 0, 2, 104, 32, },
+ { 2, 1, 0, 2, 104, 32, },
+ { 1, 1, 0, 2, 104, 32, },
+ { 0, 1, 0, 2, 108, 32, },
+ { 2, 1, 0, 2, 108, 32, },
+ { 1, 1, 0, 2, 108, 32, },
+ { 0, 1, 0, 2, 112, 32, },
+ { 2, 1, 0, 2, 112, 32, },
+ { 1, 1, 0, 2, 112, 32, },
+ { 0, 1, 0, 2, 116, 32, },
+ { 2, 1, 0, 2, 116, 32, },
+ { 1, 1, 0, 2, 116, 32, },
+ { 0, 1, 0, 2, 120, 32, },
+ { 2, 1, 0, 2, 120, 32, },
+ { 1, 1, 0, 2, 120, 32, },
+ { 0, 1, 0, 2, 124, 32, },
+ { 2, 1, 0, 2, 124, 32, },
+ { 1, 1, 0, 2, 124, 32, },
+ { 0, 1, 0, 2, 128, 32, },
+ { 2, 1, 0, 2, 128, 32, },
+ { 1, 1, 0, 2, 128, 32, },
+ { 0, 1, 0, 2, 132, 32, },
+ { 2, 1, 0, 2, 132, 32, },
+ { 1, 1, 0, 2, 132, 32, },
+ { 0, 1, 0, 2, 136, 32, },
+ { 2, 1, 0, 2, 136, 32, },
+ { 1, 1, 0, 2, 136, 32, },
+ { 0, 1, 0, 2, 140, 30, },
+ { 2, 1, 0, 2, 140, 30, },
+ { 1, 1, 0, 2, 140, 30, },
+ { 0, 1, 0, 2, 149, 40, },
+ { 2, 1, 0, 2, 149, 40, },
+ { 1, 1, 0, 2, 149, 63, },
+ { 0, 1, 0, 2, 153, 40, },
+ { 2, 1, 0, 2, 153, 40, },
+ { 1, 1, 0, 2, 153, 63, },
+ { 0, 1, 0, 2, 157, 40, },
+ { 2, 1, 0, 2, 157, 40, },
+ { 1, 1, 0, 2, 157, 63, },
+ { 0, 1, 0, 2, 161, 40, },
+ { 2, 1, 0, 2, 161, 40, },
+ { 1, 1, 0, 2, 161, 63, },
+ { 0, 1, 0, 2, 165, 42, },
+ { 2, 1, 0, 2, 165, 42, },
+ { 1, 1, 0, 2, 165, 63, },
+ { 0, 1, 0, 3, 36, 32, },
+ { 2, 1, 0, 3, 36, 32, },
+ { 1, 1, 0, 3, 36, 32, },
+ { 0, 1, 0, 3, 40, 32, },
+ { 2, 1, 0, 3, 40, 32, },
+ { 1, 1, 0, 3, 40, 32, },
+ { 0, 1, 0, 3, 44, 32, },
+ { 2, 1, 0, 3, 44, 32, },
+ { 1, 1, 0, 3, 44, 32, },
+ { 0, 1, 0, 3, 48, 36, },
+ { 2, 1, 0, 3, 48, 36, },
+ { 1, 1, 0, 3, 48, 36, },
+ { 0, 1, 0, 3, 52, 36, },
+ { 2, 1, 0, 3, 52, 36, },
+ { 1, 1, 0, 3, 52, 36, },
+ { 0, 1, 0, 3, 56, 32, },
+ { 2, 1, 0, 3, 56, 32, },
+ { 1, 1, 0, 3, 56, 32, },
+ { 0, 1, 0, 3, 60, 32, },
+ { 2, 1, 0, 3, 60, 32, },
+ { 1, 1, 0, 3, 60, 32, },
+ { 0, 1, 0, 3, 64, 32, },
+ { 2, 1, 0, 3, 64, 32, },
+ { 1, 1, 0, 3, 64, 32, },
+ { 0, 1, 0, 3, 100, 32, },
+ { 2, 1, 0, 3, 100, 32, },
+ { 1, 1, 0, 3, 100, 32, },
+ { 0, 1, 0, 3, 104, 32, },
+ { 2, 1, 0, 3, 104, 32, },
+ { 1, 1, 0, 3, 104, 32, },
+ { 0, 1, 0, 3, 108, 32, },
+ { 2, 1, 0, 3, 108, 32, },
+ { 1, 1, 0, 3, 108, 32, },
+ { 0, 1, 0, 3, 112, 32, },
+ { 2, 1, 0, 3, 112, 32, },
+ { 1, 1, 0, 3, 112, 32, },
+ { 0, 1, 0, 3, 116, 32, },
+ { 2, 1, 0, 3, 116, 32, },
+ { 1, 1, 0, 3, 116, 32, },
+ { 0, 1, 0, 3, 120, 32, },
+ { 2, 1, 0, 3, 120, 32, },
+ { 1, 1, 0, 3, 120, 32, },
+ { 0, 1, 0, 3, 124, 32, },
+ { 2, 1, 0, 3, 124, 32, },
+ { 1, 1, 0, 3, 124, 32, },
+ { 0, 1, 0, 3, 128, 32, },
+ { 2, 1, 0, 3, 128, 32, },
+ { 1, 1, 0, 3, 128, 32, },
+ { 0, 1, 0, 3, 132, 32, },
+ { 2, 1, 0, 3, 132, 32, },
+ { 1, 1, 0, 3, 132, 32, },
+ { 0, 1, 0, 3, 136, 32, },
+ { 2, 1, 0, 3, 136, 32, },
+ { 1, 1, 0, 3, 136, 32, },
+ { 0, 1, 0, 3, 140, 30, },
+ { 2, 1, 0, 3, 140, 30, },
+ { 1, 1, 0, 3, 140, 30, },
+ { 0, 1, 0, 3, 149, 40, },
+ { 2, 1, 0, 3, 149, 40, },
+ { 1, 1, 0, 3, 149, 63, },
+ { 0, 1, 0, 3, 153, 40, },
+ { 2, 1, 0, 3, 153, 40, },
+ { 1, 1, 0, 3, 153, 63, },
+ { 0, 1, 0, 3, 157, 40, },
+ { 2, 1, 0, 3, 157, 40, },
+ { 1, 1, 0, 3, 157, 63, },
+ { 0, 1, 0, 3, 161, 40, },
+ { 2, 1, 0, 3, 161, 40, },
+ { 1, 1, 0, 3, 161, 63, },
+ { 0, 1, 0, 3, 165, 42, },
+ { 2, 1, 0, 3, 165, 42, },
+ { 1, 1, 0, 3, 165, 63, },
+ { 0, 1, 0, 6, 36, 32, },
+ { 2, 1, 0, 6, 36, 32, },
+ { 1, 1, 0, 6, 36, 32, },
+ { 0, 1, 0, 6, 40, 32, },
+ { 2, 1, 0, 6, 40, 32, },
+ { 1, 1, 0, 6, 40, 32, },
+ { 0, 1, 0, 6, 44, 32, },
+ { 2, 1, 0, 6, 44, 32, },
+ { 1, 1, 0, 6, 44, 32, },
+ { 0, 1, 0, 6, 48, 36, },
+ { 2, 1, 0, 6, 48, 36, },
+ { 1, 1, 0, 6, 48, 36, },
+ { 0, 1, 0, 6, 52, 36, },
+ { 2, 1, 0, 6, 52, 36, },
+ { 1, 1, 0, 6, 52, 36, },
+ { 0, 1, 0, 6, 56, 32, },
+ { 2, 1, 0, 6, 56, 32, },
+ { 1, 1, 0, 6, 56, 32, },
+ { 0, 1, 0, 6, 60, 32, },
+ { 2, 1, 0, 6, 60, 32, },
+ { 1, 1, 0, 6, 60, 32, },
+ { 0, 1, 0, 6, 64, 32, },
+ { 2, 1, 0, 6, 64, 32, },
+ { 1, 1, 0, 6, 64, 32, },
+ { 0, 1, 0, 6, 100, 32, },
+ { 2, 1, 0, 6, 100, 32, },
+ { 1, 1, 0, 6, 100, 32, },
+ { 0, 1, 0, 6, 104, 32, },
+ { 2, 1, 0, 6, 104, 32, },
+ { 1, 1, 0, 6, 104, 32, },
+ { 0, 1, 0, 6, 108, 32, },
+ { 2, 1, 0, 6, 108, 32, },
+ { 1, 1, 0, 6, 108, 32, },
+ { 0, 1, 0, 6, 112, 32, },
+ { 2, 1, 0, 6, 112, 32, },
+ { 1, 1, 0, 6, 112, 32, },
+ { 0, 1, 0, 6, 116, 32, },
+ { 2, 1, 0, 6, 116, 32, },
+ { 1, 1, 0, 6, 116, 32, },
+ { 0, 1, 0, 6, 120, 32, },
+ { 2, 1, 0, 6, 120, 32, },
+ { 1, 1, 0, 6, 120, 32, },
+ { 0, 1, 0, 6, 124, 32, },
+ { 2, 1, 0, 6, 124, 32, },
+ { 1, 1, 0, 6, 124, 32, },
+ { 0, 1, 0, 6, 128, 32, },
+ { 2, 1, 0, 6, 128, 32, },
+ { 1, 1, 0, 6, 128, 32, },
+ { 0, 1, 0, 6, 132, 32, },
+ { 2, 1, 0, 6, 132, 32, },
+ { 1, 1, 0, 6, 132, 32, },
+ { 0, 1, 0, 6, 136, 32, },
+ { 2, 1, 0, 6, 136, 32, },
+ { 1, 1, 0, 6, 136, 32, },
+ { 0, 1, 0, 6, 140, 30, },
+ { 2, 1, 0, 6, 140, 30, },
+ { 1, 1, 0, 6, 140, 30, },
+ { 0, 1, 0, 6, 149, 40, },
+ { 2, 1, 0, 6, 149, 40, },
+ { 1, 1, 0, 6, 149, 63, },
+ { 0, 1, 0, 6, 153, 40, },
+ { 2, 1, 0, 6, 153, 40, },
+ { 1, 1, 0, 6, 153, 63, },
+ { 0, 1, 0, 6, 157, 40, },
+ { 2, 1, 0, 6, 157, 40, },
+ { 1, 1, 0, 6, 157, 63, },
+ { 0, 1, 0, 6, 161, 40, },
+ { 2, 1, 0, 6, 161, 40, },
+ { 1, 1, 0, 6, 161, 63, },
+ { 0, 1, 0, 6, 165, 42, },
+ { 2, 1, 0, 6, 165, 42, },
+ { 1, 1, 0, 6, 165, 63, },
+ { 0, 1, 0, 7, 36, 32, },
+ { 2, 1, 0, 7, 36, 32, },
+ { 1, 1, 0, 7, 36, 32, },
+ { 0, 1, 0, 7, 40, 32, },
+ { 2, 1, 0, 7, 40, 32, },
+ { 1, 1, 0, 7, 40, 32, },
+ { 0, 1, 0, 7, 44, 32, },
+ { 2, 1, 0, 7, 44, 32, },
+ { 1, 1, 0, 7, 44, 32, },
+ { 0, 1, 0, 7, 48, 36, },
+ { 2, 1, 0, 7, 48, 36, },
+ { 1, 1, 0, 7, 48, 36, },
+ { 0, 1, 0, 7, 52, 36, },
+ { 2, 1, 0, 7, 52, 36, },
+ { 1, 1, 0, 7, 52, 36, },
+ { 0, 1, 0, 7, 56, 32, },
+ { 2, 1, 0, 7, 56, 32, },
+ { 1, 1, 0, 7, 56, 32, },
+ { 0, 1, 0, 7, 60, 32, },
+ { 2, 1, 0, 7, 60, 32, },
+ { 1, 1, 0, 7, 60, 32, },
+ { 0, 1, 0, 7, 64, 32, },
+ { 2, 1, 0, 7, 64, 32, },
+ { 1, 1, 0, 7, 64, 32, },
+ { 0, 1, 0, 7, 100, 32, },
+ { 2, 1, 0, 7, 100, 32, },
+ { 1, 1, 0, 7, 100, 32, },
+ { 0, 1, 0, 7, 104, 32, },
+ { 2, 1, 0, 7, 104, 32, },
+ { 1, 1, 0, 7, 104, 32, },
+ { 0, 1, 0, 7, 108, 32, },
+ { 2, 1, 0, 7, 108, 32, },
+ { 1, 1, 0, 7, 108, 32, },
+ { 0, 1, 0, 7, 112, 32, },
+ { 2, 1, 0, 7, 112, 32, },
+ { 1, 1, 0, 7, 112, 32, },
+ { 0, 1, 0, 7, 116, 32, },
+ { 2, 1, 0, 7, 116, 32, },
+ { 1, 1, 0, 7, 116, 32, },
+ { 0, 1, 0, 7, 120, 32, },
+ { 2, 1, 0, 7, 120, 32, },
+ { 1, 1, 0, 7, 120, 32, },
+ { 0, 1, 0, 7, 124, 32, },
+ { 2, 1, 0, 7, 124, 32, },
+ { 1, 1, 0, 7, 124, 32, },
+ { 0, 1, 0, 7, 128, 32, },
+ { 2, 1, 0, 7, 128, 32, },
+ { 1, 1, 0, 7, 128, 32, },
+ { 0, 1, 0, 7, 132, 32, },
+ { 2, 1, 0, 7, 132, 32, },
+ { 1, 1, 0, 7, 132, 32, },
+ { 0, 1, 0, 7, 136, 32, },
+ { 2, 1, 0, 7, 136, 32, },
+ { 1, 1, 0, 7, 136, 32, },
+ { 0, 1, 0, 7, 140, 30, },
+ { 2, 1, 0, 7, 140, 30, },
+ { 1, 1, 0, 7, 140, 30, },
+ { 0, 1, 0, 7, 149, 40, },
+ { 2, 1, 0, 7, 149, 40, },
+ { 1, 1, 0, 7, 149, 63, },
+ { 0, 1, 0, 7, 153, 40, },
+ { 2, 1, 0, 7, 153, 40, },
+ { 1, 1, 0, 7, 153, 63, },
+ { 0, 1, 0, 7, 157, 40, },
+ { 2, 1, 0, 7, 157, 40, },
+ { 1, 1, 0, 7, 157, 63, },
+ { 0, 1, 0, 7, 161, 40, },
+ { 2, 1, 0, 7, 161, 40, },
+ { 1, 1, 0, 7, 161, 63, },
+ { 0, 1, 0, 7, 165, 42, },
+ { 2, 1, 0, 7, 165, 42, },
+ { 1, 1, 0, 7, 165, 63, },
+ { 0, 1, 1, 2, 38, 32, },
+ { 2, 1, 1, 2, 38, 32, },
+ { 1, 1, 1, 2, 38, 32, },
+ { 0, 1, 1, 2, 46, 36, },
+ { 2, 1, 1, 2, 46, 36, },
+ { 1, 1, 1, 2, 46, 36, },
+ { 0, 1, 1, 2, 54, 36, },
+ { 2, 1, 1, 2, 54, 36, },
+ { 1, 1, 1, 2, 54, 36, },
+ { 0, 1, 1, 2, 62, 32, },
+ { 2, 1, 1, 2, 62, 32, },
+ { 1, 1, 1, 2, 62, 32, },
+ { 0, 1, 1, 2, 102, 30, },
+ { 2, 1, 1, 2, 102, 30, },
+ { 1, 1, 1, 2, 102, 30, },
+ { 0, 1, 1, 2, 110, 32, },
+ { 2, 1, 1, 2, 110, 32, },
+ { 1, 1, 1, 2, 110, 32, },
+ { 0, 1, 1, 2, 118, 32, },
+ { 2, 1, 1, 2, 118, 32, },
+ { 1, 1, 1, 2, 118, 32, },
+ { 0, 1, 1, 2, 126, 32, },
+ { 2, 1, 1, 2, 126, 32, },
+ { 1, 1, 1, 2, 126, 32, },
+ { 0, 1, 1, 2, 134, 36, },
+ { 2, 1, 1, 2, 134, 36, },
+ { 1, 1, 1, 2, 134, 36, },
+ { 0, 1, 1, 2, 151, 36, },
+ { 2, 1, 1, 2, 151, 36, },
+ { 1, 1, 1, 2, 151, 63, },
+ { 0, 1, 1, 2, 159, 40, },
+ { 2, 1, 1, 2, 159, 40, },
+ { 1, 1, 1, 2, 159, 63, },
+ { 0, 1, 1, 3, 38, 32, },
+ { 2, 1, 1, 3, 38, 32, },
+ { 1, 1, 1, 3, 38, 32, },
+ { 0, 1, 1, 3, 46, 36, },
+ { 2, 1, 1, 3, 46, 36, },
+ { 1, 1, 1, 3, 46, 36, },
+ { 0, 1, 1, 3, 54, 36, },
+ { 2, 1, 1, 3, 54, 36, },
+ { 1, 1, 1, 3, 54, 36, },
+ { 0, 1, 1, 3, 62, 32, },
+ { 2, 1, 1, 3, 62, 32, },
+ { 1, 1, 1, 3, 62, 32, },
+ { 0, 1, 1, 3, 102, 30, },
+ { 2, 1, 1, 3, 102, 30, },
+ { 1, 1, 1, 3, 102, 30, },
+ { 0, 1, 1, 3, 110, 32, },
+ { 2, 1, 1, 3, 110, 32, },
+ { 1, 1, 1, 3, 110, 32, },
+ { 0, 1, 1, 3, 118, 32, },
+ { 2, 1, 1, 3, 118, 32, },
+ { 1, 1, 1, 3, 118, 32, },
+ { 0, 1, 1, 3, 126, 32, },
+ { 2, 1, 1, 3, 126, 32, },
+ { 1, 1, 1, 3, 126, 32, },
+ { 0, 1, 1, 3, 134, 36, },
+ { 2, 1, 1, 3, 134, 36, },
+ { 1, 1, 1, 3, 134, 36, },
+ { 0, 1, 1, 3, 151, 36, },
+ { 2, 1, 1, 3, 151, 36, },
+ { 1, 1, 1, 3, 151, 63, },
+ { 0, 1, 1, 3, 159, 40, },
+ { 2, 1, 1, 3, 159, 40, },
+ { 1, 1, 1, 3, 159, 63, },
+ { 0, 1, 1, 6, 38, 32, },
+ { 2, 1, 1, 6, 38, 32, },
+ { 1, 1, 1, 6, 38, 32, },
+ { 0, 1, 1, 6, 46, 36, },
+ { 2, 1, 1, 6, 46, 36, },
+ { 1, 1, 1, 6, 46, 36, },
+ { 0, 1, 1, 6, 54, 36, },
+ { 2, 1, 1, 6, 54, 36, },
+ { 1, 1, 1, 6, 54, 36, },
+ { 0, 1, 1, 6, 62, 32, },
+ { 2, 1, 1, 6, 62, 32, },
+ { 1, 1, 1, 6, 62, 32, },
+ { 0, 1, 1, 6, 102, 30, },
+ { 2, 1, 1, 6, 102, 30, },
+ { 1, 1, 1, 6, 102, 30, },
+ { 0, 1, 1, 6, 110, 32, },
+ { 2, 1, 1, 6, 110, 32, },
+ { 1, 1, 1, 6, 110, 32, },
+ { 0, 1, 1, 6, 118, 32, },
+ { 2, 1, 1, 6, 118, 32, },
+ { 1, 1, 1, 6, 118, 32, },
+ { 0, 1, 1, 6, 126, 32, },
+ { 2, 1, 1, 6, 126, 32, },
+ { 1, 1, 1, 6, 126, 32, },
+ { 0, 1, 1, 6, 134, 36, },
+ { 2, 1, 1, 6, 134, 36, },
+ { 1, 1, 1, 6, 134, 36, },
+ { 0, 1, 1, 6, 151, 36, },
+ { 2, 1, 1, 6, 151, 36, },
+ { 1, 1, 1, 6, 151, 63, },
+ { 0, 1, 1, 6, 159, 40, },
+ { 2, 1, 1, 6, 159, 40, },
+ { 1, 1, 1, 6, 159, 63, },
+ { 0, 1, 1, 7, 38, 32, },
+ { 2, 1, 1, 7, 38, 32, },
+ { 1, 1, 1, 7, 38, 32, },
+ { 0, 1, 1, 7, 46, 36, },
+ { 2, 1, 1, 7, 46, 36, },
+ { 1, 1, 1, 7, 46, 36, },
+ { 0, 1, 1, 7, 54, 36, },
+ { 2, 1, 1, 7, 54, 36, },
+ { 1, 1, 1, 7, 54, 36, },
+ { 0, 1, 1, 7, 62, 32, },
+ { 2, 1, 1, 7, 62, 32, },
+ { 1, 1, 1, 7, 62, 32, },
+ { 0, 1, 1, 7, 102, 30, },
+ { 2, 1, 1, 7, 102, 30, },
+ { 1, 1, 1, 7, 102, 30, },
+ { 0, 1, 1, 7, 110, 32, },
+ { 2, 1, 1, 7, 110, 32, },
+ { 1, 1, 1, 7, 110, 32, },
+ { 0, 1, 1, 7, 118, 32, },
+ { 2, 1, 1, 7, 118, 32, },
+ { 1, 1, 1, 7, 118, 32, },
+ { 0, 1, 1, 7, 126, 32, },
+ { 2, 1, 1, 7, 126, 32, },
+ { 1, 1, 1, 7, 126, 32, },
+ { 0, 1, 1, 7, 134, 36, },
+ { 2, 1, 1, 7, 134, 36, },
+ { 1, 1, 1, 7, 134, 36, },
+ { 0, 1, 1, 7, 151, 36, },
+ { 2, 1, 1, 7, 151, 36, },
+ { 1, 1, 1, 7, 151, 63, },
+ { 0, 1, 1, 7, 159, 40, },
+ { 2, 1, 1, 7, 159, 40, },
+ { 1, 1, 1, 7, 159, 63, },
+ { 0, 1, 2, 4, 42, 34, },
+ { 2, 1, 2, 4, 42, 34, },
+ { 1, 1, 2, 4, 42, 34, },
+ { 0, 1, 2, 4, 58, 34, },
+ { 2, 1, 2, 4, 58, 34, },
+ { 1, 1, 2, 4, 58, 34, },
+ { 0, 1, 2, 4, 106, 32, },
+ { 2, 1, 2, 4, 106, 32, },
+ { 1, 1, 2, 4, 106, 32, },
+ { 0, 1, 2, 4, 122, 34, },
+ { 2, 1, 2, 4, 122, 34, },
+ { 1, 1, 2, 4, 122, 34, },
+ { 0, 1, 2, 4, 155, 34, },
+ { 2, 1, 2, 4, 155, 34, },
+ { 1, 1, 2, 4, 155, 63, },
+ { 0, 1, 2, 5, 42, 34, },
+ { 2, 1, 2, 5, 42, 34, },
+ { 1, 1, 2, 5, 42, 34, },
+ { 0, 1, 2, 5, 58, 34, },
+ { 2, 1, 2, 5, 58, 34, },
+ { 1, 1, 2, 5, 58, 34, },
+ { 0, 1, 2, 5, 106, 32, },
+ { 2, 1, 2, 5, 106, 32, },
+ { 1, 1, 2, 5, 106, 32, },
+ { 0, 1, 2, 5, 122, 34, },
+ { 2, 1, 2, 5, 122, 34, },
+ { 1, 1, 2, 5, 122, 34, },
+ { 0, 1, 2, 5, 155, 34, },
+ { 2, 1, 2, 5, 155, 34, },
+ { 1, 1, 2, 5, 155, 63, },
+ { 0, 1, 2, 8, 42, 34, },
+ { 2, 1, 2, 8, 42, 34, },
+ { 1, 1, 2, 8, 42, 34, },
+ { 0, 1, 2, 8, 58, 34, },
+ { 2, 1, 2, 8, 58, 34, },
+ { 1, 1, 2, 8, 58, 34, },
+ { 0, 1, 2, 8, 106, 32, },
+ { 2, 1, 2, 8, 106, 32, },
+ { 1, 1, 2, 8, 106, 32, },
+ { 0, 1, 2, 8, 122, 34, },
+ { 2, 1, 2, 8, 122, 34, },
+ { 1, 1, 2, 8, 122, 34, },
+ { 0, 1, 2, 8, 155, 34, },
+ { 2, 1, 2, 8, 155, 34, },
+ { 1, 1, 2, 8, 155, 63, },
+ { 0, 1, 2, 9, 42, 34, },
+ { 2, 1, 2, 9, 42, 34, },
+ { 1, 1, 2, 9, 42, 34, },
+ { 0, 1, 2, 9, 58, 34, },
+ { 2, 1, 2, 9, 58, 34, },
+ { 1, 1, 2, 9, 58, 34, },
+ { 0, 1, 2, 9, 106, 32, },
+ { 2, 1, 2, 9, 106, 32, },
+ { 1, 1, 2, 9, 106, 32, },
+ { 0, 1, 2, 9, 122, 34, },
+ { 2, 1, 2, 9, 122, 34, },
+ { 1, 1, 2, 9, 122, 34, },
+ { 0, 1, 2, 9, 155, 34, },
+ { 2, 1, 2, 9, 155, 34, },
+ { 1, 1, 2, 9, 155, 63, },
+};
+
+RTW_DECL_TABLE_TXPWR_LMT(rtw8814a_txpwr_lmt_type2);
+
+static const struct rtw_txpwr_lmt_cfg_pair rtw8814a_txpwr_lmt_type3[] = {
+ { 0, 0, 0, 0, 1, 46, },
+ { 2, 0, 0, 0, 1, 40, },
+ { 1, 0, 0, 0, 1, 40, },
+ { 0, 0, 0, 0, 2, 46, },
+ { 2, 0, 0, 0, 2, 40, },
+ { 1, 0, 0, 0, 2, 40, },
+ { 0, 0, 0, 0, 3, 46, },
+ { 2, 0, 0, 0, 3, 40, },
+ { 1, 0, 0, 0, 3, 40, },
+ { 0, 0, 0, 0, 4, 46, },
+ { 2, 0, 0, 0, 4, 40, },
+ { 1, 0, 0, 0, 4, 40, },
+ { 0, 0, 0, 0, 5, 46, },
+ { 2, 0, 0, 0, 5, 40, },
+ { 1, 0, 0, 0, 5, 40, },
+ { 0, 0, 0, 0, 6, 46, },
+ { 2, 0, 0, 0, 6, 40, },
+ { 1, 0, 0, 0, 6, 40, },
+ { 0, 0, 0, 0, 7, 46, },
+ { 2, 0, 0, 0, 7, 40, },
+ { 1, 0, 0, 0, 7, 40, },
+ { 0, 0, 0, 0, 8, 46, },
+ { 2, 0, 0, 0, 8, 40, },
+ { 1, 0, 0, 0, 8, 40, },
+ { 0, 0, 0, 0, 9, 46, },
+ { 2, 0, 0, 0, 9, 40, },
+ { 1, 0, 0, 0, 9, 40, },
+ { 0, 0, 0, 0, 10, 46, },
+ { 2, 0, 0, 0, 10, 40, },
+ { 1, 0, 0, 0, 10, 40, },
+ { 0, 0, 0, 0, 11, 46, },
+ { 2, 0, 0, 0, 11, 40, },
+ { 1, 0, 0, 0, 11, 40, },
+ { 0, 0, 0, 0, 12, 63, },
+ { 2, 0, 0, 0, 12, 40, },
+ { 1, 0, 0, 0, 12, 40, },
+ { 0, 0, 0, 0, 13, 63, },
+ { 2, 0, 0, 0, 13, 40, },
+ { 1, 0, 0, 0, 13, 40, },
+ { 0, 0, 0, 0, 14, 63, },
+ { 2, 0, 0, 0, 14, 63, },
+ { 1, 0, 0, 0, 14, 40, },
+ { 0, 0, 0, 1, 1, 46, },
+ { 2, 0, 0, 1, 1, 40, },
+ { 1, 0, 0, 1, 1, 40, },
+ { 0, 0, 0, 1, 2, 46, },
+ { 2, 0, 0, 1, 2, 40, },
+ { 1, 0, 0, 1, 2, 40, },
+ { 0, 0, 0, 1, 3, 46, },
+ { 2, 0, 0, 1, 3, 40, },
+ { 1, 0, 0, 1, 3, 40, },
+ { 0, 0, 0, 1, 4, 46, },
+ { 2, 0, 0, 1, 4, 40, },
+ { 1, 0, 0, 1, 4, 40, },
+ { 0, 0, 0, 1, 5, 46, },
+ { 2, 0, 0, 1, 5, 40, },
+ { 1, 0, 0, 1, 5, 40, },
+ { 0, 0, 0, 1, 6, 46, },
+ { 2, 0, 0, 1, 6, 40, },
+ { 1, 0, 0, 1, 6, 40, },
+ { 0, 0, 0, 1, 7, 46, },
+ { 2, 0, 0, 1, 7, 40, },
+ { 1, 0, 0, 1, 7, 40, },
+ { 0, 0, 0, 1, 8, 46, },
+ { 2, 0, 0, 1, 8, 40, },
+ { 1, 0, 0, 1, 8, 40, },
+ { 0, 0, 0, 1, 9, 46, },
+ { 2, 0, 0, 1, 9, 40, },
+ { 1, 0, 0, 1, 9, 40, },
+ { 0, 0, 0, 1, 10, 46, },
+ { 2, 0, 0, 1, 10, 40, },
+ { 1, 0, 0, 1, 10, 40, },
+ { 0, 0, 0, 1, 11, 46, },
+ { 2, 0, 0, 1, 11, 40, },
+ { 1, 0, 0, 1, 11, 40, },
+ { 0, 0, 0, 1, 12, 63, },
+ { 2, 0, 0, 1, 12, 40, },
+ { 1, 0, 0, 1, 12, 40, },
+ { 0, 0, 0, 1, 13, 63, },
+ { 2, 0, 0, 1, 13, 40, },
+ { 1, 0, 0, 1, 13, 40, },
+ { 0, 0, 0, 1, 14, 63, },
+ { 2, 0, 0, 1, 14, 63, },
+ { 1, 0, 0, 1, 14, 63, },
+ { 0, 0, 0, 2, 1, 46, },
+ { 2, 0, 0, 2, 1, 40, },
+ { 1, 0, 0, 2, 1, 40, },
+ { 0, 0, 0, 2, 2, 46, },
+ { 2, 0, 0, 2, 2, 40, },
+ { 1, 0, 0, 2, 2, 40, },
+ { 0, 0, 0, 2, 3, 46, },
+ { 2, 0, 0, 2, 3, 40, },
+ { 1, 0, 0, 2, 3, 40, },
+ { 0, 0, 0, 2, 4, 46, },
+ { 2, 0, 0, 2, 4, 40, },
+ { 1, 0, 0, 2, 4, 40, },
+ { 0, 0, 0, 2, 5, 46, },
+ { 2, 0, 0, 2, 5, 40, },
+ { 1, 0, 0, 2, 5, 40, },
+ { 0, 0, 0, 2, 6, 46, },
+ { 2, 0, 0, 2, 6, 40, },
+ { 1, 0, 0, 2, 6, 40, },
+ { 0, 0, 0, 2, 7, 46, },
+ { 2, 0, 0, 2, 7, 40, },
+ { 1, 0, 0, 2, 7, 40, },
+ { 0, 0, 0, 2, 8, 46, },
+ { 2, 0, 0, 2, 8, 40, },
+ { 1, 0, 0, 2, 8, 40, },
+ { 0, 0, 0, 2, 9, 46, },
+ { 2, 0, 0, 2, 9, 40, },
+ { 1, 0, 0, 2, 9, 40, },
+ { 0, 0, 0, 2, 10, 46, },
+ { 2, 0, 0, 2, 10, 40, },
+ { 1, 0, 0, 2, 10, 40, },
+ { 0, 0, 0, 2, 11, 46, },
+ { 2, 0, 0, 2, 11, 40, },
+ { 1, 0, 0, 2, 11, 40, },
+ { 0, 0, 0, 2, 12, 63, },
+ { 2, 0, 0, 2, 12, 40, },
+ { 1, 0, 0, 2, 12, 40, },
+ { 0, 0, 0, 2, 13, 63, },
+ { 2, 0, 0, 2, 13, 40, },
+ { 1, 0, 0, 2, 13, 40, },
+ { 0, 0, 0, 2, 14, 63, },
+ { 2, 0, 0, 2, 14, 63, },
+ { 1, 0, 0, 2, 14, 63, },
+ { 0, 0, 0, 3, 1, 46, },
+ { 2, 0, 0, 3, 1, 40, },
+ { 1, 0, 0, 3, 1, 40, },
+ { 0, 0, 0, 3, 2, 46, },
+ { 2, 0, 0, 3, 2, 40, },
+ { 1, 0, 0, 3, 2, 40, },
+ { 0, 0, 0, 3, 3, 46, },
+ { 2, 0, 0, 3, 3, 40, },
+ { 1, 0, 0, 3, 3, 40, },
+ { 0, 0, 0, 3, 4, 46, },
+ { 2, 0, 0, 3, 4, 40, },
+ { 1, 0, 0, 3, 4, 40, },
+ { 0, 0, 0, 3, 5, 46, },
+ { 2, 0, 0, 3, 5, 40, },
+ { 1, 0, 0, 3, 5, 40, },
+ { 0, 0, 0, 3, 6, 46, },
+ { 2, 0, 0, 3, 6, 40, },
+ { 1, 0, 0, 3, 6, 40, },
+ { 0, 0, 0, 3, 7, 46, },
+ { 2, 0, 0, 3, 7, 40, },
+ { 1, 0, 0, 3, 7, 40, },
+ { 0, 0, 0, 3, 8, 46, },
+ { 2, 0, 0, 3, 8, 40, },
+ { 1, 0, 0, 3, 8, 40, },
+ { 0, 0, 0, 3, 9, 46, },
+ { 2, 0, 0, 3, 9, 40, },
+ { 1, 0, 0, 3, 9, 40, },
+ { 0, 0, 0, 3, 10, 46, },
+ { 2, 0, 0, 3, 10, 40, },
+ { 1, 0, 0, 3, 10, 40, },
+ { 0, 0, 0, 3, 11, 46, },
+ { 2, 0, 0, 3, 11, 40, },
+ { 1, 0, 0, 3, 11, 40, },
+ { 0, 0, 0, 3, 12, 63, },
+ { 2, 0, 0, 3, 12, 40, },
+ { 1, 0, 0, 3, 12, 40, },
+ { 0, 0, 0, 3, 13, 63, },
+ { 2, 0, 0, 3, 13, 40, },
+ { 1, 0, 0, 3, 13, 40, },
+ { 0, 0, 0, 3, 14, 63, },
+ { 2, 0, 0, 3, 14, 63, },
+ { 1, 0, 0, 3, 14, 63, },
+ { 0, 0, 0, 6, 1, 46, },
+ { 2, 0, 0, 6, 1, 40, },
+ { 1, 0, 0, 6, 1, 40, },
+ { 0, 0, 0, 6, 2, 46, },
+ { 2, 0, 0, 6, 2, 40, },
+ { 1, 0, 0, 6, 2, 40, },
+ { 0, 0, 0, 6, 3, 46, },
+ { 2, 0, 0, 6, 3, 40, },
+ { 1, 0, 0, 6, 3, 40, },
+ { 0, 0, 0, 6, 4, 46, },
+ { 2, 0, 0, 6, 4, 40, },
+ { 1, 0, 0, 6, 4, 40, },
+ { 0, 0, 0, 6, 5, 46, },
+ { 2, 0, 0, 6, 5, 40, },
+ { 1, 0, 0, 6, 5, 40, },
+ { 0, 0, 0, 6, 6, 46, },
+ { 2, 0, 0, 6, 6, 40, },
+ { 1, 0, 0, 6, 6, 40, },
+ { 0, 0, 0, 6, 7, 46, },
+ { 2, 0, 0, 6, 7, 40, },
+ { 1, 0, 0, 6, 7, 40, },
+ { 0, 0, 0, 6, 8, 46, },
+ { 2, 0, 0, 6, 8, 40, },
+ { 1, 0, 0, 6, 8, 40, },
+ { 0, 0, 0, 6, 9, 46, },
+ { 2, 0, 0, 6, 9, 40, },
+ { 1, 0, 0, 6, 9, 40, },
+ { 0, 0, 0, 6, 10, 46, },
+ { 2, 0, 0, 6, 10, 40, },
+ { 1, 0, 0, 6, 10, 40, },
+ { 0, 0, 0, 6, 11, 46, },
+ { 2, 0, 0, 6, 11, 40, },
+ { 1, 0, 0, 6, 11, 40, },
+ { 0, 0, 0, 6, 12, 63, },
+ { 2, 0, 0, 6, 12, 40, },
+ { 1, 0, 0, 6, 12, 40, },
+ { 0, 0, 0, 6, 13, 63, },
+ { 2, 0, 0, 6, 13, 40, },
+ { 1, 0, 0, 6, 13, 40, },
+ { 0, 0, 0, 6, 14, 63, },
+ { 2, 0, 0, 6, 14, 63, },
+ { 1, 0, 0, 6, 14, 63, },
+ { 0, 0, 0, 7, 1, 46, },
+ { 2, 0, 0, 7, 1, 40, },
+ { 1, 0, 0, 7, 1, 40, },
+ { 0, 0, 0, 7, 2, 46, },
+ { 2, 0, 0, 7, 2, 40, },
+ { 1, 0, 0, 7, 2, 40, },
+ { 0, 0, 0, 7, 3, 46, },
+ { 2, 0, 0, 7, 3, 40, },
+ { 1, 0, 0, 7, 3, 40, },
+ { 0, 0, 0, 7, 4, 46, },
+ { 2, 0, 0, 7, 4, 40, },
+ { 1, 0, 0, 7, 4, 40, },
+ { 0, 0, 0, 7, 5, 46, },
+ { 2, 0, 0, 7, 5, 40, },
+ { 1, 0, 0, 7, 5, 40, },
+ { 0, 0, 0, 7, 6, 46, },
+ { 2, 0, 0, 7, 6, 40, },
+ { 1, 0, 0, 7, 6, 40, },
+ { 0, 0, 0, 7, 7, 46, },
+ { 2, 0, 0, 7, 7, 40, },
+ { 1, 0, 0, 7, 7, 40, },
+ { 0, 0, 0, 7, 8, 46, },
+ { 2, 0, 0, 7, 8, 40, },
+ { 1, 0, 0, 7, 8, 40, },
+ { 0, 0, 0, 7, 9, 46, },
+ { 2, 0, 0, 7, 9, 40, },
+ { 1, 0, 0, 7, 9, 40, },
+ { 0, 0, 0, 7, 10, 46, },
+ { 2, 0, 0, 7, 10, 40, },
+ { 1, 0, 0, 7, 10, 40, },
+ { 0, 0, 0, 7, 11, 46, },
+ { 2, 0, 0, 7, 11, 40, },
+ { 1, 0, 0, 7, 11, 40, },
+ { 0, 0, 0, 7, 12, 63, },
+ { 2, 0, 0, 7, 12, 40, },
+ { 1, 0, 0, 7, 12, 40, },
+ { 0, 0, 0, 7, 13, 63, },
+ { 2, 0, 0, 7, 13, 40, },
+ { 1, 0, 0, 7, 13, 40, },
+ { 0, 0, 0, 7, 14, 63, },
+ { 2, 0, 0, 7, 14, 63, },
+ { 1, 0, 0, 7, 14, 63, },
+ { 0, 0, 1, 2, 1, 63, },
+ { 2, 0, 1, 2, 1, 63, },
+ { 1, 0, 1, 2, 1, 63, },
+ { 0, 0, 1, 2, 2, 63, },
+ { 2, 0, 1, 2, 2, 63, },
+ { 1, 0, 1, 2, 2, 63, },
+ { 0, 0, 1, 2, 3, 46, },
+ { 2, 0, 1, 2, 3, 40, },
+ { 1, 0, 1, 2, 3, 40, },
+ { 0, 0, 1, 2, 4, 46, },
+ { 2, 0, 1, 2, 4, 40, },
+ { 1, 0, 1, 2, 4, 40, },
+ { 0, 0, 1, 2, 5, 46, },
+ { 2, 0, 1, 2, 5, 40, },
+ { 1, 0, 1, 2, 5, 40, },
+ { 0, 0, 1, 2, 6, 46, },
+ { 2, 0, 1, 2, 6, 40, },
+ { 1, 0, 1, 2, 6, 40, },
+ { 0, 0, 1, 2, 7, 46, },
+ { 2, 0, 1, 2, 7, 40, },
+ { 1, 0, 1, 2, 7, 40, },
+ { 0, 0, 1, 2, 8, 46, },
+ { 2, 0, 1, 2, 8, 40, },
+ { 1, 0, 1, 2, 8, 40, },
+ { 0, 0, 1, 2, 9, 46, },
+ { 2, 0, 1, 2, 9, 40, },
+ { 1, 0, 1, 2, 9, 40, },
+ { 0, 0, 1, 2, 10, 46, },
+ { 2, 0, 1, 2, 10, 40, },
+ { 1, 0, 1, 2, 10, 40, },
+ { 0, 0, 1, 2, 11, 46, },
+ { 2, 0, 1, 2, 11, 40, },
+ { 1, 0, 1, 2, 11, 40, },
+ { 0, 0, 1, 2, 12, 63, },
+ { 2, 0, 1, 2, 12, 40, },
+ { 1, 0, 1, 2, 12, 40, },
+ { 0, 0, 1, 2, 13, 63, },
+ { 2, 0, 1, 2, 13, 40, },
+ { 1, 0, 1, 2, 13, 40, },
+ { 0, 0, 1, 2, 14, 63, },
+ { 2, 0, 1, 2, 14, 63, },
+ { 1, 0, 1, 2, 14, 63, },
+ { 0, 0, 1, 3, 1, 63, },
+ { 2, 0, 1, 3, 1, 63, },
+ { 1, 0, 1, 3, 1, 63, },
+ { 0, 0, 1, 3, 2, 63, },
+ { 2, 0, 1, 3, 2, 63, },
+ { 1, 0, 1, 3, 2, 63, },
+ { 0, 0, 1, 3, 3, 46, },
+ { 2, 0, 1, 3, 3, 40, },
+ { 1, 0, 1, 3, 3, 40, },
+ { 0, 0, 1, 3, 4, 46, },
+ { 2, 0, 1, 3, 4, 40, },
+ { 1, 0, 1, 3, 4, 40, },
+ { 0, 0, 1, 3, 5, 46, },
+ { 2, 0, 1, 3, 5, 40, },
+ { 1, 0, 1, 3, 5, 40, },
+ { 0, 0, 1, 3, 6, 46, },
+ { 2, 0, 1, 3, 6, 40, },
+ { 1, 0, 1, 3, 6, 40, },
+ { 0, 0, 1, 3, 7, 46, },
+ { 2, 0, 1, 3, 7, 40, },
+ { 1, 0, 1, 3, 7, 40, },
+ { 0, 0, 1, 3, 8, 46, },
+ { 2, 0, 1, 3, 8, 40, },
+ { 1, 0, 1, 3, 8, 40, },
+ { 0, 0, 1, 3, 9, 46, },
+ { 2, 0, 1, 3, 9, 40, },
+ { 1, 0, 1, 3, 9, 40, },
+ { 0, 0, 1, 3, 10, 46, },
+ { 2, 0, 1, 3, 10, 40, },
+ { 1, 0, 1, 3, 10, 40, },
+ { 0, 0, 1, 3, 11, 46, },
+ { 2, 0, 1, 3, 11, 40, },
+ { 1, 0, 1, 3, 11, 40, },
+ { 0, 0, 1, 3, 12, 63, },
+ { 2, 0, 1, 3, 12, 40, },
+ { 1, 0, 1, 3, 12, 40, },
+ { 0, 0, 1, 3, 13, 63, },
+ { 2, 0, 1, 3, 13, 40, },
+ { 1, 0, 1, 3, 13, 40, },
+ { 0, 0, 1, 3, 14, 63, },
+ { 2, 0, 1, 3, 14, 63, },
+ { 1, 0, 1, 3, 14, 63, },
+ { 0, 0, 1, 6, 1, 63, },
+ { 2, 0, 1, 6, 1, 63, },
+ { 1, 0, 1, 6, 1, 63, },
+ { 0, 0, 1, 6, 2, 63, },
+ { 2, 0, 1, 6, 2, 63, },
+ { 1, 0, 1, 6, 2, 63, },
+ { 0, 0, 1, 6, 3, 46, },
+ { 2, 0, 1, 6, 3, 40, },
+ { 1, 0, 1, 6, 3, 40, },
+ { 0, 0, 1, 6, 4, 46, },
+ { 2, 0, 1, 6, 4, 40, },
+ { 1, 0, 1, 6, 4, 40, },
+ { 0, 0, 1, 6, 5, 46, },
+ { 2, 0, 1, 6, 5, 40, },
+ { 1, 0, 1, 6, 5, 40, },
+ { 0, 0, 1, 6, 6, 46, },
+ { 2, 0, 1, 6, 6, 40, },
+ { 1, 0, 1, 6, 6, 40, },
+ { 0, 0, 1, 6, 7, 46, },
+ { 2, 0, 1, 6, 7, 40, },
+ { 1, 0, 1, 6, 7, 40, },
+ { 0, 0, 1, 6, 8, 46, },
+ { 2, 0, 1, 6, 8, 40, },
+ { 1, 0, 1, 6, 8, 40, },
+ { 0, 0, 1, 6, 9, 46, },
+ { 2, 0, 1, 6, 9, 40, },
+ { 1, 0, 1, 6, 9, 40, },
+ { 0, 0, 1, 6, 10, 46, },
+ { 2, 0, 1, 6, 10, 40, },
+ { 1, 0, 1, 6, 10, 40, },
+ { 0, 0, 1, 6, 11, 46, },
+ { 2, 0, 1, 6, 11, 40, },
+ { 1, 0, 1, 6, 11, 40, },
+ { 0, 0, 1, 6, 12, 63, },
+ { 2, 0, 1, 6, 12, 40, },
+ { 1, 0, 1, 6, 12, 40, },
+ { 0, 0, 1, 6, 13, 63, },
+ { 2, 0, 1, 6, 13, 40, },
+ { 1, 0, 1, 6, 13, 40, },
+ { 0, 0, 1, 6, 14, 63, },
+ { 2, 0, 1, 6, 14, 63, },
+ { 1, 0, 1, 6, 14, 63, },
+ { 0, 0, 1, 7, 1, 63, },
+ { 2, 0, 1, 7, 1, 63, },
+ { 1, 0, 1, 7, 1, 63, },
+ { 0, 0, 1, 7, 2, 63, },
+ { 2, 0, 1, 7, 2, 63, },
+ { 1, 0, 1, 7, 2, 63, },
+ { 0, 0, 1, 7, 3, 46, },
+ { 2, 0, 1, 7, 3, 40, },
+ { 1, 0, 1, 7, 3, 40, },
+ { 0, 0, 1, 7, 4, 46, },
+ { 2, 0, 1, 7, 4, 40, },
+ { 1, 0, 1, 7, 4, 40, },
+ { 0, 0, 1, 7, 5, 46, },
+ { 2, 0, 1, 7, 5, 40, },
+ { 1, 0, 1, 7, 5, 40, },
+ { 0, 0, 1, 7, 6, 46, },
+ { 2, 0, 1, 7, 6, 40, },
+ { 1, 0, 1, 7, 6, 40, },
+ { 0, 0, 1, 7, 7, 46, },
+ { 2, 0, 1, 7, 7, 40, },
+ { 1, 0, 1, 7, 7, 40, },
+ { 0, 0, 1, 7, 8, 46, },
+ { 2, 0, 1, 7, 8, 40, },
+ { 1, 0, 1, 7, 8, 40, },
+ { 0, 0, 1, 7, 9, 46, },
+ { 2, 0, 1, 7, 9, 40, },
+ { 1, 0, 1, 7, 9, 40, },
+ { 0, 0, 1, 7, 10, 46, },
+ { 2, 0, 1, 7, 10, 40, },
+ { 1, 0, 1, 7, 10, 40, },
+ { 0, 0, 1, 7, 11, 46, },
+ { 2, 0, 1, 7, 11, 40, },
+ { 1, 0, 1, 7, 11, 40, },
+ { 0, 0, 1, 7, 12, 63, },
+ { 2, 0, 1, 7, 12, 40, },
+ { 1, 0, 1, 7, 12, 40, },
+ { 0, 0, 1, 7, 13, 63, },
+ { 2, 0, 1, 7, 13, 40, },
+ { 1, 0, 1, 7, 13, 40, },
+ { 0, 0, 1, 7, 14, 63, },
+ { 2, 0, 1, 7, 14, 63, },
+ { 1, 0, 1, 7, 14, 63, },
+ { 0, 1, 0, 1, 36, 46, },
+ { 2, 1, 0, 1, 36, 40, },
+ { 1, 1, 0, 1, 36, 40, },
+ { 0, 1, 0, 1, 40, 46, },
+ { 2, 1, 0, 1, 40, 40, },
+ { 1, 1, 0, 1, 40, 40, },
+ { 0, 1, 0, 1, 44, 46, },
+ { 2, 1, 0, 1, 44, 40, },
+ { 1, 1, 0, 1, 44, 40, },
+ { 0, 1, 0, 1, 48, 46, },
+ { 2, 1, 0, 1, 48, 40, },
+ { 1, 1, 0, 1, 48, 40, },
+ { 0, 1, 0, 1, 52, 46, },
+ { 2, 1, 0, 1, 52, 40, },
+ { 1, 1, 0, 1, 52, 40, },
+ { 0, 1, 0, 1, 56, 46, },
+ { 2, 1, 0, 1, 56, 40, },
+ { 1, 1, 0, 1, 56, 40, },
+ { 0, 1, 0, 1, 60, 46, },
+ { 2, 1, 0, 1, 60, 40, },
+ { 1, 1, 0, 1, 60, 40, },
+ { 0, 1, 0, 1, 64, 46, },
+ { 2, 1, 0, 1, 64, 40, },
+ { 1, 1, 0, 1, 64, 40, },
+ { 0, 1, 0, 1, 100, 46, },
+ { 2, 1, 0, 1, 100, 40, },
+ { 1, 1, 0, 1, 100, 40, },
+ { 0, 1, 0, 1, 104, 46, },
+ { 2, 1, 0, 1, 104, 40, },
+ { 1, 1, 0, 1, 104, 40, },
+ { 0, 1, 0, 1, 108, 46, },
+ { 2, 1, 0, 1, 108, 40, },
+ { 1, 1, 0, 1, 108, 40, },
+ { 0, 1, 0, 1, 112, 46, },
+ { 2, 1, 0, 1, 112, 40, },
+ { 1, 1, 0, 1, 112, 40, },
+ { 0, 1, 0, 1, 116, 46, },
+ { 2, 1, 0, 1, 116, 40, },
+ { 1, 1, 0, 1, 116, 40, },
+ { 0, 1, 0, 1, 120, 46, },
+ { 2, 1, 0, 1, 120, 40, },
+ { 1, 1, 0, 1, 120, 40, },
+ { 0, 1, 0, 1, 124, 46, },
+ { 2, 1, 0, 1, 124, 40, },
+ { 1, 1, 0, 1, 124, 40, },
+ { 0, 1, 0, 1, 128, 46, },
+ { 2, 1, 0, 1, 128, 40, },
+ { 1, 1, 0, 1, 128, 40, },
+ { 0, 1, 0, 1, 132, 46, },
+ { 2, 1, 0, 1, 132, 40, },
+ { 1, 1, 0, 1, 132, 40, },
+ { 0, 1, 0, 1, 136, 46, },
+ { 2, 1, 0, 1, 136, 40, },
+ { 1, 1, 0, 1, 136, 40, },
+ { 0, 1, 0, 1, 140, 46, },
+ { 2, 1, 0, 1, 140, 40, },
+ { 1, 1, 0, 1, 140, 40, },
+ { 0, 1, 0, 1, 149, 46, },
+ { 2, 1, 0, 1, 149, 40, },
+ { 1, 1, 0, 1, 149, 63, },
+ { 0, 1, 0, 1, 153, 46, },
+ { 2, 1, 0, 1, 153, 40, },
+ { 1, 1, 0, 1, 153, 63, },
+ { 0, 1, 0, 1, 157, 46, },
+ { 2, 1, 0, 1, 157, 40, },
+ { 1, 1, 0, 1, 157, 63, },
+ { 0, 1, 0, 1, 161, 46, },
+ { 2, 1, 0, 1, 161, 40, },
+ { 1, 1, 0, 1, 161, 63, },
+ { 0, 1, 0, 1, 165, 46, },
+ { 2, 1, 0, 1, 165, 40, },
+ { 1, 1, 0, 1, 165, 63, },
+ { 0, 1, 0, 2, 36, 46, },
+ { 2, 1, 0, 2, 36, 40, },
+ { 1, 1, 0, 2, 36, 40, },
+ { 0, 1, 0, 2, 40, 46, },
+ { 2, 1, 0, 2, 40, 40, },
+ { 1, 1, 0, 2, 40, 40, },
+ { 0, 1, 0, 2, 44, 46, },
+ { 2, 1, 0, 2, 44, 40, },
+ { 1, 1, 0, 2, 44, 40, },
+ { 0, 1, 0, 2, 48, 46, },
+ { 2, 1, 0, 2, 48, 40, },
+ { 1, 1, 0, 2, 48, 40, },
+ { 0, 1, 0, 2, 52, 46, },
+ { 2, 1, 0, 2, 52, 40, },
+ { 1, 1, 0, 2, 52, 40, },
+ { 0, 1, 0, 2, 56, 46, },
+ { 2, 1, 0, 2, 56, 40, },
+ { 1, 1, 0, 2, 56, 40, },
+ { 0, 1, 0, 2, 60, 46, },
+ { 2, 1, 0, 2, 60, 40, },
+ { 1, 1, 0, 2, 60, 40, },
+ { 0, 1, 0, 2, 64, 46, },
+ { 2, 1, 0, 2, 64, 40, },
+ { 1, 1, 0, 2, 64, 40, },
+ { 0, 1, 0, 2, 100, 46, },
+ { 2, 1, 0, 2, 100, 40, },
+ { 1, 1, 0, 2, 100, 40, },
+ { 0, 1, 0, 2, 104, 46, },
+ { 2, 1, 0, 2, 104, 40, },
+ { 1, 1, 0, 2, 104, 40, },
+ { 0, 1, 0, 2, 108, 46, },
+ { 2, 1, 0, 2, 108, 40, },
+ { 1, 1, 0, 2, 108, 40, },
+ { 0, 1, 0, 2, 112, 46, },
+ { 2, 1, 0, 2, 112, 40, },
+ { 1, 1, 0, 2, 112, 40, },
+ { 0, 1, 0, 2, 116, 46, },
+ { 2, 1, 0, 2, 116, 40, },
+ { 1, 1, 0, 2, 116, 40, },
+ { 0, 1, 0, 2, 120, 46, },
+ { 2, 1, 0, 2, 120, 40, },
+ { 1, 1, 0, 2, 120, 40, },
+ { 0, 1, 0, 2, 124, 46, },
+ { 2, 1, 0, 2, 124, 40, },
+ { 1, 1, 0, 2, 124, 40, },
+ { 0, 1, 0, 2, 128, 46, },
+ { 2, 1, 0, 2, 128, 40, },
+ { 1, 1, 0, 2, 128, 40, },
+ { 0, 1, 0, 2, 132, 46, },
+ { 2, 1, 0, 2, 132, 40, },
+ { 1, 1, 0, 2, 132, 40, },
+ { 0, 1, 0, 2, 136, 46, },
+ { 2, 1, 0, 2, 136, 40, },
+ { 1, 1, 0, 2, 136, 40, },
+ { 0, 1, 0, 2, 140, 46, },
+ { 2, 1, 0, 2, 140, 40, },
+ { 1, 1, 0, 2, 140, 40, },
+ { 0, 1, 0, 2, 149, 46, },
+ { 2, 1, 0, 2, 149, 40, },
+ { 1, 1, 0, 2, 149, 63, },
+ { 0, 1, 0, 2, 153, 46, },
+ { 2, 1, 0, 2, 153, 40, },
+ { 1, 1, 0, 2, 153, 63, },
+ { 0, 1, 0, 2, 157, 46, },
+ { 2, 1, 0, 2, 157, 40, },
+ { 1, 1, 0, 2, 157, 63, },
+ { 0, 1, 0, 2, 161, 46, },
+ { 2, 1, 0, 2, 161, 40, },
+ { 1, 1, 0, 2, 161, 63, },
+ { 0, 1, 0, 2, 165, 46, },
+ { 2, 1, 0, 2, 165, 40, },
+ { 1, 1, 0, 2, 165, 63, },
+ { 0, 1, 0, 3, 36, 46, },
+ { 2, 1, 0, 3, 36, 40, },
+ { 1, 1, 0, 3, 36, 40, },
+ { 0, 1, 0, 3, 40, 46, },
+ { 2, 1, 0, 3, 40, 40, },
+ { 1, 1, 0, 3, 40, 40, },
+ { 0, 1, 0, 3, 44, 46, },
+ { 2, 1, 0, 3, 44, 40, },
+ { 1, 1, 0, 3, 44, 40, },
+ { 0, 1, 0, 3, 48, 46, },
+ { 2, 1, 0, 3, 48, 40, },
+ { 1, 1, 0, 3, 48, 40, },
+ { 0, 1, 0, 3, 52, 46, },
+ { 2, 1, 0, 3, 52, 40, },
+ { 1, 1, 0, 3, 52, 40, },
+ { 0, 1, 0, 3, 56, 46, },
+ { 2, 1, 0, 3, 56, 40, },
+ { 1, 1, 0, 3, 56, 40, },
+ { 0, 1, 0, 3, 60, 46, },
+ { 2, 1, 0, 3, 60, 40, },
+ { 1, 1, 0, 3, 60, 40, },
+ { 0, 1, 0, 3, 64, 46, },
+ { 2, 1, 0, 3, 64, 40, },
+ { 1, 1, 0, 3, 64, 40, },
+ { 0, 1, 0, 3, 100, 46, },
+ { 2, 1, 0, 3, 100, 40, },
+ { 1, 1, 0, 3, 100, 40, },
+ { 0, 1, 0, 3, 104, 46, },
+ { 2, 1, 0, 3, 104, 40, },
+ { 1, 1, 0, 3, 104, 40, },
+ { 0, 1, 0, 3, 108, 46, },
+ { 2, 1, 0, 3, 108, 40, },
+ { 1, 1, 0, 3, 108, 40, },
+ { 0, 1, 0, 3, 112, 46, },
+ { 2, 1, 0, 3, 112, 40, },
+ { 1, 1, 0, 3, 112, 40, },
+ { 0, 1, 0, 3, 116, 46, },
+ { 2, 1, 0, 3, 116, 40, },
+ { 1, 1, 0, 3, 116, 40, },
+ { 0, 1, 0, 3, 120, 46, },
+ { 2, 1, 0, 3, 120, 40, },
+ { 1, 1, 0, 3, 120, 40, },
+ { 0, 1, 0, 3, 124, 46, },
+ { 2, 1, 0, 3, 124, 40, },
+ { 1, 1, 0, 3, 124, 40, },
+ { 0, 1, 0, 3, 128, 46, },
+ { 2, 1, 0, 3, 128, 40, },
+ { 1, 1, 0, 3, 128, 40, },
+ { 0, 1, 0, 3, 132, 46, },
+ { 2, 1, 0, 3, 132, 40, },
+ { 1, 1, 0, 3, 132, 40, },
+ { 0, 1, 0, 3, 136, 46, },
+ { 2, 1, 0, 3, 136, 40, },
+ { 1, 1, 0, 3, 136, 40, },
+ { 0, 1, 0, 3, 140, 46, },
+ { 2, 1, 0, 3, 140, 40, },
+ { 1, 1, 0, 3, 140, 40, },
+ { 0, 1, 0, 3, 149, 46, },
+ { 2, 1, 0, 3, 149, 40, },
+ { 1, 1, 0, 3, 149, 63, },
+ { 0, 1, 0, 3, 153, 46, },
+ { 2, 1, 0, 3, 153, 40, },
+ { 1, 1, 0, 3, 153, 63, },
+ { 0, 1, 0, 3, 157, 46, },
+ { 2, 1, 0, 3, 157, 40, },
+ { 1, 1, 0, 3, 157, 63, },
+ { 0, 1, 0, 3, 161, 46, },
+ { 2, 1, 0, 3, 161, 40, },
+ { 1, 1, 0, 3, 161, 63, },
+ { 0, 1, 0, 3, 165, 46, },
+ { 2, 1, 0, 3, 165, 40, },
+ { 1, 1, 0, 3, 165, 63, },
+ { 0, 1, 0, 6, 36, 46, },
+ { 2, 1, 0, 6, 36, 40, },
+ { 1, 1, 0, 6, 36, 40, },
+ { 0, 1, 0, 6, 40, 46, },
+ { 2, 1, 0, 6, 40, 40, },
+ { 1, 1, 0, 6, 40, 40, },
+ { 0, 1, 0, 6, 44, 46, },
+ { 2, 1, 0, 6, 44, 40, },
+ { 1, 1, 0, 6, 44, 40, },
+ { 0, 1, 0, 6, 48, 46, },
+ { 2, 1, 0, 6, 48, 40, },
+ { 1, 1, 0, 6, 48, 40, },
+ { 0, 1, 0, 6, 52, 46, },
+ { 2, 1, 0, 6, 52, 40, },
+ { 1, 1, 0, 6, 52, 40, },
+ { 0, 1, 0, 6, 56, 46, },
+ { 2, 1, 0, 6, 56, 40, },
+ { 1, 1, 0, 6, 56, 40, },
+ { 0, 1, 0, 6, 60, 46, },
+ { 2, 1, 0, 6, 60, 40, },
+ { 1, 1, 0, 6, 60, 40, },
+ { 0, 1, 0, 6, 64, 46, },
+ { 2, 1, 0, 6, 64, 40, },
+ { 1, 1, 0, 6, 64, 40, },
+ { 0, 1, 0, 6, 100, 46, },
+ { 2, 1, 0, 6, 100, 40, },
+ { 1, 1, 0, 6, 100, 40, },
+ { 0, 1, 0, 6, 104, 46, },
+ { 2, 1, 0, 6, 104, 40, },
+ { 1, 1, 0, 6, 104, 40, },
+ { 0, 1, 0, 6, 108, 46, },
+ { 2, 1, 0, 6, 108, 40, },
+ { 1, 1, 0, 6, 108, 40, },
+ { 0, 1, 0, 6, 112, 46, },
+ { 2, 1, 0, 6, 112, 40, },
+ { 1, 1, 0, 6, 112, 40, },
+ { 0, 1, 0, 6, 116, 46, },
+ { 2, 1, 0, 6, 116, 40, },
+ { 1, 1, 0, 6, 116, 40, },
+ { 0, 1, 0, 6, 120, 46, },
+ { 2, 1, 0, 6, 120, 40, },
+ { 1, 1, 0, 6, 120, 40, },
+ { 0, 1, 0, 6, 124, 46, },
+ { 2, 1, 0, 6, 124, 40, },
+ { 1, 1, 0, 6, 124, 40, },
+ { 0, 1, 0, 6, 128, 46, },
+ { 2, 1, 0, 6, 128, 40, },
+ { 1, 1, 0, 6, 128, 40, },
+ { 0, 1, 0, 6, 132, 46, },
+ { 2, 1, 0, 6, 132, 40, },
+ { 1, 1, 0, 6, 132, 40, },
+ { 0, 1, 0, 6, 136, 46, },
+ { 2, 1, 0, 6, 136, 40, },
+ { 1, 1, 0, 6, 136, 40, },
+ { 0, 1, 0, 6, 140, 46, },
+ { 2, 1, 0, 6, 140, 40, },
+ { 1, 1, 0, 6, 140, 40, },
+ { 0, 1, 0, 6, 149, 46, },
+ { 2, 1, 0, 6, 149, 40, },
+ { 1, 1, 0, 6, 149, 63, },
+ { 0, 1, 0, 6, 153, 46, },
+ { 2, 1, 0, 6, 153, 40, },
+ { 1, 1, 0, 6, 153, 63, },
+ { 0, 1, 0, 6, 157, 46, },
+ { 2, 1, 0, 6, 157, 40, },
+ { 1, 1, 0, 6, 157, 63, },
+ { 0, 1, 0, 6, 161, 46, },
+ { 2, 1, 0, 6, 161, 40, },
+ { 1, 1, 0, 6, 161, 63, },
+ { 0, 1, 0, 6, 165, 46, },
+ { 2, 1, 0, 6, 165, 40, },
+ { 1, 1, 0, 6, 165, 63, },
+ { 0, 1, 0, 7, 36, 46, },
+ { 2, 1, 0, 7, 36, 40, },
+ { 1, 1, 0, 7, 36, 40, },
+ { 0, 1, 0, 7, 40, 46, },
+ { 2, 1, 0, 7, 40, 40, },
+ { 1, 1, 0, 7, 40, 40, },
+ { 0, 1, 0, 7, 44, 46, },
+ { 2, 1, 0, 7, 44, 40, },
+ { 1, 1, 0, 7, 44, 40, },
+ { 0, 1, 0, 7, 48, 46, },
+ { 2, 1, 0, 7, 48, 40, },
+ { 1, 1, 0, 7, 48, 40, },
+ { 0, 1, 0, 7, 52, 46, },
+ { 2, 1, 0, 7, 52, 40, },
+ { 1, 1, 0, 7, 52, 40, },
+ { 0, 1, 0, 7, 56, 46, },
+ { 2, 1, 0, 7, 56, 40, },
+ { 1, 1, 0, 7, 56, 40, },
+ { 0, 1, 0, 7, 60, 46, },
+ { 2, 1, 0, 7, 60, 40, },
+ { 1, 1, 0, 7, 60, 40, },
+ { 0, 1, 0, 7, 64, 46, },
+ { 2, 1, 0, 7, 64, 40, },
+ { 1, 1, 0, 7, 64, 40, },
+ { 0, 1, 0, 7, 100, 46, },
+ { 2, 1, 0, 7, 100, 40, },
+ { 1, 1, 0, 7, 100, 40, },
+ { 0, 1, 0, 7, 104, 46, },
+ { 2, 1, 0, 7, 104, 40, },
+ { 1, 1, 0, 7, 104, 40, },
+ { 0, 1, 0, 7, 108, 46, },
+ { 2, 1, 0, 7, 108, 40, },
+ { 1, 1, 0, 7, 108, 40, },
+ { 0, 1, 0, 7, 112, 46, },
+ { 2, 1, 0, 7, 112, 40, },
+ { 1, 1, 0, 7, 112, 40, },
+ { 0, 1, 0, 7, 116, 46, },
+ { 2, 1, 0, 7, 116, 40, },
+ { 1, 1, 0, 7, 116, 40, },
+ { 0, 1, 0, 7, 120, 46, },
+ { 2, 1, 0, 7, 120, 40, },
+ { 1, 1, 0, 7, 120, 40, },
+ { 0, 1, 0, 7, 124, 46, },
+ { 2, 1, 0, 7, 124, 40, },
+ { 1, 1, 0, 7, 124, 40, },
+ { 0, 1, 0, 7, 128, 46, },
+ { 2, 1, 0, 7, 128, 40, },
+ { 1, 1, 0, 7, 128, 40, },
+ { 0, 1, 0, 7, 132, 46, },
+ { 2, 1, 0, 7, 132, 40, },
+ { 1, 1, 0, 7, 132, 40, },
+ { 0, 1, 0, 7, 136, 46, },
+ { 2, 1, 0, 7, 136, 40, },
+ { 1, 1, 0, 7, 136, 40, },
+ { 0, 1, 0, 7, 140, 46, },
+ { 2, 1, 0, 7, 140, 40, },
+ { 1, 1, 0, 7, 140, 40, },
+ { 0, 1, 0, 7, 149, 46, },
+ { 2, 1, 0, 7, 149, 40, },
+ { 1, 1, 0, 7, 149, 63, },
+ { 0, 1, 0, 7, 153, 46, },
+ { 2, 1, 0, 7, 153, 40, },
+ { 1, 1, 0, 7, 153, 63, },
+ { 0, 1, 0, 7, 157, 46, },
+ { 2, 1, 0, 7, 157, 40, },
+ { 1, 1, 0, 7, 157, 63, },
+ { 0, 1, 0, 7, 161, 46, },
+ { 2, 1, 0, 7, 161, 40, },
+ { 1, 1, 0, 7, 161, 63, },
+ { 0, 1, 0, 7, 165, 46, },
+ { 2, 1, 0, 7, 165, 40, },
+ { 1, 1, 0, 7, 165, 63, },
+ { 0, 1, 1, 2, 38, 46, },
+ { 2, 1, 1, 2, 38, 40, },
+ { 1, 1, 1, 2, 38, 40, },
+ { 0, 1, 1, 2, 46, 46, },
+ { 2, 1, 1, 2, 46, 40, },
+ { 1, 1, 1, 2, 46, 40, },
+ { 0, 1, 1, 2, 54, 46, },
+ { 2, 1, 1, 2, 54, 40, },
+ { 1, 1, 1, 2, 54, 40, },
+ { 0, 1, 1, 2, 62, 46, },
+ { 2, 1, 1, 2, 62, 40, },
+ { 1, 1, 1, 2, 62, 40, },
+ { 0, 1, 1, 2, 102, 46, },
+ { 2, 1, 1, 2, 102, 40, },
+ { 1, 1, 1, 2, 102, 40, },
+ { 0, 1, 1, 2, 110, 46, },
+ { 2, 1, 1, 2, 110, 40, },
+ { 1, 1, 1, 2, 110, 40, },
+ { 0, 1, 1, 2, 118, 46, },
+ { 2, 1, 1, 2, 118, 40, },
+ { 1, 1, 1, 2, 118, 40, },
+ { 0, 1, 1, 2, 126, 46, },
+ { 2, 1, 1, 2, 126, 40, },
+ { 1, 1, 1, 2, 126, 40, },
+ { 0, 1, 1, 2, 134, 46, },
+ { 2, 1, 1, 2, 134, 40, },
+ { 1, 1, 1, 2, 134, 40, },
+ { 0, 1, 1, 2, 151, 46, },
+ { 2, 1, 1, 2, 151, 40, },
+ { 1, 1, 1, 2, 151, 63, },
+ { 0, 1, 1, 2, 159, 46, },
+ { 2, 1, 1, 2, 159, 40, },
+ { 1, 1, 1, 2, 159, 63, },
+ { 0, 1, 1, 3, 38, 46, },
+ { 2, 1, 1, 3, 38, 40, },
+ { 1, 1, 1, 3, 38, 40, },
+ { 0, 1, 1, 3, 46, 46, },
+ { 2, 1, 1, 3, 46, 40, },
+ { 1, 1, 1, 3, 46, 40, },
+ { 0, 1, 1, 3, 54, 46, },
+ { 2, 1, 1, 3, 54, 40, },
+ { 1, 1, 1, 3, 54, 40, },
+ { 0, 1, 1, 3, 62, 46, },
+ { 2, 1, 1, 3, 62, 40, },
+ { 1, 1, 1, 3, 62, 40, },
+ { 0, 1, 1, 3, 102, 46, },
+ { 2, 1, 1, 3, 102, 40, },
+ { 1, 1, 1, 3, 102, 40, },
+ { 0, 1, 1, 3, 110, 46, },
+ { 2, 1, 1, 3, 110, 40, },
+ { 1, 1, 1, 3, 110, 40, },
+ { 0, 1, 1, 3, 118, 46, },
+ { 2, 1, 1, 3, 118, 40, },
+ { 1, 1, 1, 3, 118, 40, },
+ { 0, 1, 1, 3, 126, 46, },
+ { 2, 1, 1, 3, 126, 40, },
+ { 1, 1, 1, 3, 126, 40, },
+ { 0, 1, 1, 3, 134, 46, },
+ { 2, 1, 1, 3, 134, 40, },
+ { 1, 1, 1, 3, 134, 40, },
+ { 0, 1, 1, 3, 151, 46, },
+ { 2, 1, 1, 3, 151, 40, },
+ { 1, 1, 1, 3, 151, 63, },
+ { 0, 1, 1, 3, 159, 46, },
+ { 2, 1, 1, 3, 159, 40, },
+ { 1, 1, 1, 3, 159, 63, },
+ { 0, 1, 1, 6, 38, 46, },
+ { 2, 1, 1, 6, 38, 40, },
+ { 1, 1, 1, 6, 38, 40, },
+ { 0, 1, 1, 6, 46, 46, },
+ { 2, 1, 1, 6, 46, 40, },
+ { 1, 1, 1, 6, 46, 40, },
+ { 0, 1, 1, 6, 54, 46, },
+ { 2, 1, 1, 6, 54, 40, },
+ { 1, 1, 1, 6, 54, 40, },
+ { 0, 1, 1, 6, 62, 46, },
+ { 2, 1, 1, 6, 62, 40, },
+ { 1, 1, 1, 6, 62, 40, },
+ { 0, 1, 1, 6, 102, 46, },
+ { 2, 1, 1, 6, 102, 40, },
+ { 1, 1, 1, 6, 102, 40, },
+ { 0, 1, 1, 6, 110, 46, },
+ { 2, 1, 1, 6, 110, 40, },
+ { 1, 1, 1, 6, 110, 40, },
+ { 0, 1, 1, 6, 118, 46, },
+ { 2, 1, 1, 6, 118, 40, },
+ { 1, 1, 1, 6, 118, 40, },
+ { 0, 1, 1, 6, 126, 46, },
+ { 2, 1, 1, 6, 126, 40, },
+ { 1, 1, 1, 6, 126, 40, },
+ { 0, 1, 1, 6, 134, 46, },
+ { 2, 1, 1, 6, 134, 40, },
+ { 1, 1, 1, 6, 134, 40, },
+ { 0, 1, 1, 6, 151, 46, },
+ { 2, 1, 1, 6, 151, 40, },
+ { 1, 1, 1, 6, 151, 63, },
+ { 0, 1, 1, 6, 159, 46, },
+ { 2, 1, 1, 6, 159, 40, },
+ { 1, 1, 1, 6, 159, 63, },
+ { 0, 1, 1, 7, 38, 46, },
+ { 2, 1, 1, 7, 38, 40, },
+ { 1, 1, 1, 7, 38, 40, },
+ { 0, 1, 1, 7, 46, 46, },
+ { 2, 1, 1, 7, 46, 40, },
+ { 1, 1, 1, 7, 46, 40, },
+ { 0, 1, 1, 7, 54, 46, },
+ { 2, 1, 1, 7, 54, 40, },
+ { 1, 1, 1, 7, 54, 40, },
+ { 0, 1, 1, 7, 62, 46, },
+ { 2, 1, 1, 7, 62, 40, },
+ { 1, 1, 1, 7, 62, 40, },
+ { 0, 1, 1, 7, 102, 46, },
+ { 2, 1, 1, 7, 102, 40, },
+ { 1, 1, 1, 7, 102, 40, },
+ { 0, 1, 1, 7, 110, 46, },
+ { 2, 1, 1, 7, 110, 40, },
+ { 1, 1, 1, 7, 110, 40, },
+ { 0, 1, 1, 7, 118, 46, },
+ { 2, 1, 1, 7, 118, 40, },
+ { 1, 1, 1, 7, 118, 40, },
+ { 0, 1, 1, 7, 126, 46, },
+ { 2, 1, 1, 7, 126, 40, },
+ { 1, 1, 1, 7, 126, 40, },
+ { 0, 1, 1, 7, 134, 46, },
+ { 2, 1, 1, 7, 134, 40, },
+ { 1, 1, 1, 7, 134, 40, },
+ { 0, 1, 1, 7, 151, 46, },
+ { 2, 1, 1, 7, 151, 40, },
+ { 1, 1, 1, 7, 151, 63, },
+ { 0, 1, 1, 7, 159, 46, },
+ { 2, 1, 1, 7, 159, 40, },
+ { 1, 1, 1, 7, 159, 63, },
+ { 0, 1, 2, 4, 42, 46, },
+ { 2, 1, 2, 4, 42, 40, },
+ { 1, 1, 2, 4, 42, 40, },
+ { 0, 1, 2, 4, 58, 46, },
+ { 2, 1, 2, 4, 58, 40, },
+ { 1, 1, 2, 4, 58, 40, },
+ { 0, 1, 2, 4, 106, 46, },
+ { 2, 1, 2, 4, 106, 40, },
+ { 1, 1, 2, 4, 106, 40, },
+ { 0, 1, 2, 4, 122, 46, },
+ { 2, 1, 2, 4, 122, 40, },
+ { 1, 1, 2, 4, 122, 40, },
+ { 0, 1, 2, 4, 155, 46, },
+ { 2, 1, 2, 4, 155, 40, },
+ { 1, 1, 2, 4, 155, 63, },
+ { 0, 1, 2, 5, 42, 46, },
+ { 2, 1, 2, 5, 42, 40, },
+ { 1, 1, 2, 5, 42, 40, },
+ { 0, 1, 2, 5, 58, 46, },
+ { 2, 1, 2, 5, 58, 40, },
+ { 1, 1, 2, 5, 58, 40, },
+ { 0, 1, 2, 5, 106, 46, },
+ { 2, 1, 2, 5, 106, 40, },
+ { 1, 1, 2, 5, 106, 40, },
+ { 0, 1, 2, 5, 122, 46, },
+ { 2, 1, 2, 5, 122, 40, },
+ { 1, 1, 2, 5, 122, 40, },
+ { 0, 1, 2, 5, 155, 46, },
+ { 2, 1, 2, 5, 155, 40, },
+ { 1, 1, 2, 5, 155, 63, },
+ { 0, 1, 2, 8, 42, 46, },
+ { 2, 1, 2, 8, 42, 40, },
+ { 1, 1, 2, 8, 42, 40, },
+ { 0, 1, 2, 8, 58, 46, },
+ { 2, 1, 2, 8, 58, 40, },
+ { 1, 1, 2, 8, 58, 40, },
+ { 0, 1, 2, 8, 106, 46, },
+ { 2, 1, 2, 8, 106, 40, },
+ { 1, 1, 2, 8, 106, 40, },
+ { 0, 1, 2, 8, 122, 46, },
+ { 2, 1, 2, 8, 122, 40, },
+ { 1, 1, 2, 8, 122, 40, },
+ { 0, 1, 2, 8, 155, 46, },
+ { 2, 1, 2, 8, 155, 40, },
+ { 1, 1, 2, 8, 155, 63, },
+ { 0, 1, 2, 9, 42, 46, },
+ { 2, 1, 2, 9, 42, 40, },
+ { 1, 1, 2, 9, 42, 40, },
+ { 0, 1, 2, 9, 58, 46, },
+ { 2, 1, 2, 9, 58, 40, },
+ { 1, 1, 2, 9, 58, 40, },
+ { 0, 1, 2, 9, 106, 46, },
+ { 2, 1, 2, 9, 106, 40, },
+ { 1, 1, 2, 9, 106, 40, },
+ { 0, 1, 2, 9, 122, 46, },
+ { 2, 1, 2, 9, 122, 40, },
+ { 1, 1, 2, 9, 122, 40, },
+ { 0, 1, 2, 9, 155, 46, },
+ { 2, 1, 2, 9, 155, 40, },
+ { 1, 1, 2, 9, 155, 63, },
+};
+
+RTW_DECL_TABLE_TXPWR_LMT(rtw8814a_txpwr_lmt_type3);
+
+static const struct rtw_txpwr_lmt_cfg_pair rtw8814a_txpwr_lmt_type5[] = {
+ { 0, 0, 0, 0, 1, 46, },
+ { 2, 0, 0, 0, 1, 40, },
+ { 1, 0, 0, 0, 1, 40, },
+ { 0, 0, 0, 0, 2, 46, },
+ { 2, 0, 0, 0, 2, 40, },
+ { 1, 0, 0, 0, 2, 40, },
+ { 0, 0, 0, 0, 3, 46, },
+ { 2, 0, 0, 0, 3, 40, },
+ { 1, 0, 0, 0, 3, 40, },
+ { 0, 0, 0, 0, 4, 46, },
+ { 2, 0, 0, 0, 4, 40, },
+ { 1, 0, 0, 0, 4, 40, },
+ { 0, 0, 0, 0, 5, 46, },
+ { 2, 0, 0, 0, 5, 40, },
+ { 1, 0, 0, 0, 5, 40, },
+ { 0, 0, 0, 0, 6, 46, },
+ { 2, 0, 0, 0, 6, 40, },
+ { 1, 0, 0, 0, 6, 40, },
+ { 0, 0, 0, 0, 7, 46, },
+ { 2, 0, 0, 0, 7, 40, },
+ { 1, 0, 0, 0, 7, 40, },
+ { 0, 0, 0, 0, 8, 46, },
+ { 2, 0, 0, 0, 8, 40, },
+ { 1, 0, 0, 0, 8, 40, },
+ { 0, 0, 0, 0, 9, 46, },
+ { 2, 0, 0, 0, 9, 40, },
+ { 1, 0, 0, 0, 9, 40, },
+ { 0, 0, 0, 0, 10, 46, },
+ { 2, 0, 0, 0, 10, 40, },
+ { 1, 0, 0, 0, 10, 40, },
+ { 0, 0, 0, 0, 11, 46, },
+ { 2, 0, 0, 0, 11, 40, },
+ { 1, 0, 0, 0, 11, 40, },
+ { 0, 0, 0, 0, 12, 63, },
+ { 2, 0, 0, 0, 12, 40, },
+ { 1, 0, 0, 0, 12, 40, },
+ { 0, 0, 0, 0, 13, 63, },
+ { 2, 0, 0, 0, 13, 40, },
+ { 1, 0, 0, 0, 13, 40, },
+ { 0, 0, 0, 0, 14, 63, },
+ { 2, 0, 0, 0, 14, 63, },
+ { 1, 0, 0, 0, 14, 40, },
+ { 0, 0, 0, 1, 1, 46, },
+ { 2, 0, 0, 1, 1, 40, },
+ { 1, 0, 0, 1, 1, 40, },
+ { 0, 0, 0, 1, 2, 46, },
+ { 2, 0, 0, 1, 2, 40, },
+ { 1, 0, 0, 1, 2, 40, },
+ { 0, 0, 0, 1, 3, 46, },
+ { 2, 0, 0, 1, 3, 40, },
+ { 1, 0, 0, 1, 3, 40, },
+ { 0, 0, 0, 1, 4, 46, },
+ { 2, 0, 0, 1, 4, 40, },
+ { 1, 0, 0, 1, 4, 40, },
+ { 0, 0, 0, 1, 5, 46, },
+ { 2, 0, 0, 1, 5, 40, },
+ { 1, 0, 0, 1, 5, 40, },
+ { 0, 0, 0, 1, 6, 46, },
+ { 2, 0, 0, 1, 6, 40, },
+ { 1, 0, 0, 1, 6, 40, },
+ { 0, 0, 0, 1, 7, 46, },
+ { 2, 0, 0, 1, 7, 40, },
+ { 1, 0, 0, 1, 7, 40, },
+ { 0, 0, 0, 1, 8, 46, },
+ { 2, 0, 0, 1, 8, 40, },
+ { 1, 0, 0, 1, 8, 40, },
+ { 0, 0, 0, 1, 9, 46, },
+ { 2, 0, 0, 1, 9, 40, },
+ { 1, 0, 0, 1, 9, 40, },
+ { 0, 0, 0, 1, 10, 46, },
+ { 2, 0, 0, 1, 10, 40, },
+ { 1, 0, 0, 1, 10, 40, },
+ { 0, 0, 0, 1, 11, 46, },
+ { 2, 0, 0, 1, 11, 40, },
+ { 1, 0, 0, 1, 11, 40, },
+ { 0, 0, 0, 1, 12, 63, },
+ { 2, 0, 0, 1, 12, 40, },
+ { 1, 0, 0, 1, 12, 40, },
+ { 0, 0, 0, 1, 13, 63, },
+ { 2, 0, 0, 1, 13, 40, },
+ { 1, 0, 0, 1, 13, 40, },
+ { 0, 0, 0, 1, 14, 63, },
+ { 2, 0, 0, 1, 14, 63, },
+ { 1, 0, 0, 1, 14, 63, },
+ { 0, 0, 0, 2, 1, 46, },
+ { 2, 0, 0, 2, 1, 40, },
+ { 1, 0, 0, 2, 1, 40, },
+ { 0, 0, 0, 2, 2, 46, },
+ { 2, 0, 0, 2, 2, 40, },
+ { 1, 0, 0, 2, 2, 40, },
+ { 0, 0, 0, 2, 3, 46, },
+ { 2, 0, 0, 2, 3, 40, },
+ { 1, 0, 0, 2, 3, 40, },
+ { 0, 0, 0, 2, 4, 46, },
+ { 2, 0, 0, 2, 4, 40, },
+ { 1, 0, 0, 2, 4, 40, },
+ { 0, 0, 0, 2, 5, 46, },
+ { 2, 0, 0, 2, 5, 40, },
+ { 1, 0, 0, 2, 5, 40, },
+ { 0, 0, 0, 2, 6, 46, },
+ { 2, 0, 0, 2, 6, 40, },
+ { 1, 0, 0, 2, 6, 40, },
+ { 0, 0, 0, 2, 7, 46, },
+ { 2, 0, 0, 2, 7, 40, },
+ { 1, 0, 0, 2, 7, 40, },
+ { 0, 0, 0, 2, 8, 46, },
+ { 2, 0, 0, 2, 8, 40, },
+ { 1, 0, 0, 2, 8, 40, },
+ { 0, 0, 0, 2, 9, 46, },
+ { 2, 0, 0, 2, 9, 40, },
+ { 1, 0, 0, 2, 9, 40, },
+ { 0, 0, 0, 2, 10, 46, },
+ { 2, 0, 0, 2, 10, 40, },
+ { 1, 0, 0, 2, 10, 40, },
+ { 0, 0, 0, 2, 11, 46, },
+ { 2, 0, 0, 2, 11, 40, },
+ { 1, 0, 0, 2, 11, 40, },
+ { 0, 0, 0, 2, 12, 63, },
+ { 2, 0, 0, 2, 12, 40, },
+ { 1, 0, 0, 2, 12, 40, },
+ { 0, 0, 0, 2, 13, 63, },
+ { 2, 0, 0, 2, 13, 40, },
+ { 1, 0, 0, 2, 13, 40, },
+ { 0, 0, 0, 2, 14, 63, },
+ { 2, 0, 0, 2, 14, 63, },
+ { 1, 0, 0, 2, 14, 63, },
+ { 0, 0, 0, 3, 1, 46, },
+ { 2, 0, 0, 3, 1, 40, },
+ { 1, 0, 0, 3, 1, 40, },
+ { 0, 0, 0, 3, 2, 46, },
+ { 2, 0, 0, 3, 2, 40, },
+ { 1, 0, 0, 3, 2, 40, },
+ { 0, 0, 0, 3, 3, 46, },
+ { 2, 0, 0, 3, 3, 40, },
+ { 1, 0, 0, 3, 3, 40, },
+ { 0, 0, 0, 3, 4, 46, },
+ { 2, 0, 0, 3, 4, 40, },
+ { 1, 0, 0, 3, 4, 40, },
+ { 0, 0, 0, 3, 5, 46, },
+ { 2, 0, 0, 3, 5, 40, },
+ { 1, 0, 0, 3, 5, 40, },
+ { 0, 0, 0, 3, 6, 46, },
+ { 2, 0, 0, 3, 6, 40, },
+ { 1, 0, 0, 3, 6, 40, },
+ { 0, 0, 0, 3, 7, 46, },
+ { 2, 0, 0, 3, 7, 40, },
+ { 1, 0, 0, 3, 7, 40, },
+ { 0, 0, 0, 3, 8, 46, },
+ { 2, 0, 0, 3, 8, 40, },
+ { 1, 0, 0, 3, 8, 40, },
+ { 0, 0, 0, 3, 9, 46, },
+ { 2, 0, 0, 3, 9, 40, },
+ { 1, 0, 0, 3, 9, 40, },
+ { 0, 0, 0, 3, 10, 46, },
+ { 2, 0, 0, 3, 10, 40, },
+ { 1, 0, 0, 3, 10, 40, },
+ { 0, 0, 0, 3, 11, 46, },
+ { 2, 0, 0, 3, 11, 40, },
+ { 1, 0, 0, 3, 11, 40, },
+ { 0, 0, 0, 3, 12, 63, },
+ { 2, 0, 0, 3, 12, 40, },
+ { 1, 0, 0, 3, 12, 40, },
+ { 0, 0, 0, 3, 13, 63, },
+ { 2, 0, 0, 3, 13, 40, },
+ { 1, 0, 0, 3, 13, 40, },
+ { 0, 0, 0, 3, 14, 63, },
+ { 2, 0, 0, 3, 14, 63, },
+ { 1, 0, 0, 3, 14, 63, },
+ { 0, 0, 0, 6, 1, 46, },
+ { 2, 0, 0, 6, 1, 40, },
+ { 1, 0, 0, 6, 1, 40, },
+ { 0, 0, 0, 6, 2, 46, },
+ { 2, 0, 0, 6, 2, 40, },
+ { 1, 0, 0, 6, 2, 40, },
+ { 0, 0, 0, 6, 3, 46, },
+ { 2, 0, 0, 6, 3, 40, },
+ { 1, 0, 0, 6, 3, 40, },
+ { 0, 0, 0, 6, 4, 46, },
+ { 2, 0, 0, 6, 4, 40, },
+ { 1, 0, 0, 6, 4, 40, },
+ { 0, 0, 0, 6, 5, 46, },
+ { 2, 0, 0, 6, 5, 40, },
+ { 1, 0, 0, 6, 5, 40, },
+ { 0, 0, 0, 6, 6, 46, },
+ { 2, 0, 0, 6, 6, 40, },
+ { 1, 0, 0, 6, 6, 40, },
+ { 0, 0, 0, 6, 7, 46, },
+ { 2, 0, 0, 6, 7, 40, },
+ { 1, 0, 0, 6, 7, 40, },
+ { 0, 0, 0, 6, 8, 46, },
+ { 2, 0, 0, 6, 8, 40, },
+ { 1, 0, 0, 6, 8, 40, },
+ { 0, 0, 0, 6, 9, 46, },
+ { 2, 0, 0, 6, 9, 40, },
+ { 1, 0, 0, 6, 9, 40, },
+ { 0, 0, 0, 6, 10, 46, },
+ { 2, 0, 0, 6, 10, 40, },
+ { 1, 0, 0, 6, 10, 40, },
+ { 0, 0, 0, 6, 11, 46, },
+ { 2, 0, 0, 6, 11, 40, },
+ { 1, 0, 0, 6, 11, 40, },
+ { 0, 0, 0, 6, 12, 63, },
+ { 2, 0, 0, 6, 12, 40, },
+ { 1, 0, 0, 6, 12, 40, },
+ { 0, 0, 0, 6, 13, 63, },
+ { 2, 0, 0, 6, 13, 40, },
+ { 1, 0, 0, 6, 13, 40, },
+ { 0, 0, 0, 6, 14, 63, },
+ { 2, 0, 0, 6, 14, 63, },
+ { 1, 0, 0, 6, 14, 63, },
+ { 0, 0, 0, 7, 1, 46, },
+ { 2, 0, 0, 7, 1, 40, },
+ { 1, 0, 0, 7, 1, 40, },
+ { 0, 0, 0, 7, 2, 46, },
+ { 2, 0, 0, 7, 2, 40, },
+ { 1, 0, 0, 7, 2, 40, },
+ { 0, 0, 0, 7, 3, 46, },
+ { 2, 0, 0, 7, 3, 40, },
+ { 1, 0, 0, 7, 3, 40, },
+ { 0, 0, 0, 7, 4, 46, },
+ { 2, 0, 0, 7, 4, 40, },
+ { 1, 0, 0, 7, 4, 40, },
+ { 0, 0, 0, 7, 5, 46, },
+ { 2, 0, 0, 7, 5, 40, },
+ { 1, 0, 0, 7, 5, 40, },
+ { 0, 0, 0, 7, 6, 46, },
+ { 2, 0, 0, 7, 6, 40, },
+ { 1, 0, 0, 7, 6, 40, },
+ { 0, 0, 0, 7, 7, 46, },
+ { 2, 0, 0, 7, 7, 40, },
+ { 1, 0, 0, 7, 7, 40, },
+ { 0, 0, 0, 7, 8, 46, },
+ { 2, 0, 0, 7, 8, 40, },
+ { 1, 0, 0, 7, 8, 40, },
+ { 0, 0, 0, 7, 9, 46, },
+ { 2, 0, 0, 7, 9, 40, },
+ { 1, 0, 0, 7, 9, 40, },
+ { 0, 0, 0, 7, 10, 46, },
+ { 2, 0, 0, 7, 10, 40, },
+ { 1, 0, 0, 7, 10, 40, },
+ { 0, 0, 0, 7, 11, 46, },
+ { 2, 0, 0, 7, 11, 40, },
+ { 1, 0, 0, 7, 11, 40, },
+ { 0, 0, 0, 7, 12, 63, },
+ { 2, 0, 0, 7, 12, 40, },
+ { 1, 0, 0, 7, 12, 40, },
+ { 0, 0, 0, 7, 13, 63, },
+ { 2, 0, 0, 7, 13, 40, },
+ { 1, 0, 0, 7, 13, 40, },
+ { 0, 0, 0, 7, 14, 63, },
+ { 2, 0, 0, 7, 14, 63, },
+ { 1, 0, 0, 7, 14, 63, },
+ { 0, 0, 1, 2, 1, 63, },
+ { 2, 0, 1, 2, 1, 63, },
+ { 1, 0, 1, 2, 1, 63, },
+ { 0, 0, 1, 2, 2, 63, },
+ { 2, 0, 1, 2, 2, 63, },
+ { 1, 0, 1, 2, 2, 63, },
+ { 0, 0, 1, 2, 3, 46, },
+ { 2, 0, 1, 2, 3, 40, },
+ { 1, 0, 1, 2, 3, 40, },
+ { 0, 0, 1, 2, 4, 46, },
+ { 2, 0, 1, 2, 4, 40, },
+ { 1, 0, 1, 2, 4, 40, },
+ { 0, 0, 1, 2, 5, 46, },
+ { 2, 0, 1, 2, 5, 40, },
+ { 1, 0, 1, 2, 5, 40, },
+ { 0, 0, 1, 2, 6, 46, },
+ { 2, 0, 1, 2, 6, 40, },
+ { 1, 0, 1, 2, 6, 40, },
+ { 0, 0, 1, 2, 7, 46, },
+ { 2, 0, 1, 2, 7, 40, },
+ { 1, 0, 1, 2, 7, 40, },
+ { 0, 0, 1, 2, 8, 46, },
+ { 2, 0, 1, 2, 8, 40, },
+ { 1, 0, 1, 2, 8, 40, },
+ { 0, 0, 1, 2, 9, 46, },
+ { 2, 0, 1, 2, 9, 40, },
+ { 1, 0, 1, 2, 9, 40, },
+ { 0, 0, 1, 2, 10, 46, },
+ { 2, 0, 1, 2, 10, 40, },
+ { 1, 0, 1, 2, 10, 40, },
+ { 0, 0, 1, 2, 11, 46, },
+ { 2, 0, 1, 2, 11, 40, },
+ { 1, 0, 1, 2, 11, 40, },
+ { 0, 0, 1, 2, 12, 63, },
+ { 2, 0, 1, 2, 12, 40, },
+ { 1, 0, 1, 2, 12, 40, },
+ { 0, 0, 1, 2, 13, 63, },
+ { 2, 0, 1, 2, 13, 40, },
+ { 1, 0, 1, 2, 13, 40, },
+ { 0, 0, 1, 2, 14, 63, },
+ { 2, 0, 1, 2, 14, 63, },
+ { 1, 0, 1, 2, 14, 63, },
+ { 0, 0, 1, 3, 1, 63, },
+ { 2, 0, 1, 3, 1, 63, },
+ { 1, 0, 1, 3, 1, 63, },
+ { 0, 0, 1, 3, 2, 63, },
+ { 2, 0, 1, 3, 2, 63, },
+ { 1, 0, 1, 3, 2, 63, },
+ { 0, 0, 1, 3, 3, 46, },
+ { 2, 0, 1, 3, 3, 40, },
+ { 1, 0, 1, 3, 3, 40, },
+ { 0, 0, 1, 3, 4, 46, },
+ { 2, 0, 1, 3, 4, 40, },
+ { 1, 0, 1, 3, 4, 40, },
+ { 0, 0, 1, 3, 5, 46, },
+ { 2, 0, 1, 3, 5, 40, },
+ { 1, 0, 1, 3, 5, 40, },
+ { 0, 0, 1, 3, 6, 46, },
+ { 2, 0, 1, 3, 6, 40, },
+ { 1, 0, 1, 3, 6, 40, },
+ { 0, 0, 1, 3, 7, 46, },
+ { 2, 0, 1, 3, 7, 40, },
+ { 1, 0, 1, 3, 7, 40, },
+ { 0, 0, 1, 3, 8, 46, },
+ { 2, 0, 1, 3, 8, 40, },
+ { 1, 0, 1, 3, 8, 40, },
+ { 0, 0, 1, 3, 9, 46, },
+ { 2, 0, 1, 3, 9, 40, },
+ { 1, 0, 1, 3, 9, 40, },
+ { 0, 0, 1, 3, 10, 46, },
+ { 2, 0, 1, 3, 10, 40, },
+ { 1, 0, 1, 3, 10, 40, },
+ { 0, 0, 1, 3, 11, 46, },
+ { 2, 0, 1, 3, 11, 40, },
+ { 1, 0, 1, 3, 11, 40, },
+ { 0, 0, 1, 3, 12, 63, },
+ { 2, 0, 1, 3, 12, 40, },
+ { 1, 0, 1, 3, 12, 40, },
+ { 0, 0, 1, 3, 13, 63, },
+ { 2, 0, 1, 3, 13, 40, },
+ { 1, 0, 1, 3, 13, 40, },
+ { 0, 0, 1, 3, 14, 63, },
+ { 2, 0, 1, 3, 14, 63, },
+ { 1, 0, 1, 3, 14, 63, },
+ { 0, 0, 1, 6, 1, 63, },
+ { 2, 0, 1, 6, 1, 63, },
+ { 1, 0, 1, 6, 1, 63, },
+ { 0, 0, 1, 6, 2, 63, },
+ { 2, 0, 1, 6, 2, 63, },
+ { 1, 0, 1, 6, 2, 63, },
+ { 0, 0, 1, 6, 3, 46, },
+ { 2, 0, 1, 6, 3, 40, },
+ { 1, 0, 1, 6, 3, 40, },
+ { 0, 0, 1, 6, 4, 46, },
+ { 2, 0, 1, 6, 4, 40, },
+ { 1, 0, 1, 6, 4, 40, },
+ { 0, 0, 1, 6, 5, 46, },
+ { 2, 0, 1, 6, 5, 40, },
+ { 1, 0, 1, 6, 5, 40, },
+ { 0, 0, 1, 6, 6, 46, },
+ { 2, 0, 1, 6, 6, 40, },
+ { 1, 0, 1, 6, 6, 40, },
+ { 0, 0, 1, 6, 7, 46, },
+ { 2, 0, 1, 6, 7, 40, },
+ { 1, 0, 1, 6, 7, 40, },
+ { 0, 0, 1, 6, 8, 46, },
+ { 2, 0, 1, 6, 8, 40, },
+ { 1, 0, 1, 6, 8, 40, },
+ { 0, 0, 1, 6, 9, 46, },
+ { 2, 0, 1, 6, 9, 40, },
+ { 1, 0, 1, 6, 9, 40, },
+ { 0, 0, 1, 6, 10, 46, },
+ { 2, 0, 1, 6, 10, 40, },
+ { 1, 0, 1, 6, 10, 40, },
+ { 0, 0, 1, 6, 11, 46, },
+ { 2, 0, 1, 6, 11, 40, },
+ { 1, 0, 1, 6, 11, 40, },
+ { 0, 0, 1, 6, 12, 63, },
+ { 2, 0, 1, 6, 12, 40, },
+ { 1, 0, 1, 6, 12, 40, },
+ { 0, 0, 1, 6, 13, 63, },
+ { 2, 0, 1, 6, 13, 40, },
+ { 1, 0, 1, 6, 13, 40, },
+ { 0, 0, 1, 6, 14, 63, },
+ { 2, 0, 1, 6, 14, 63, },
+ { 1, 0, 1, 6, 14, 63, },
+ { 0, 0, 1, 7, 1, 63, },
+ { 2, 0, 1, 7, 1, 63, },
+ { 1, 0, 1, 7, 1, 63, },
+ { 0, 0, 1, 7, 2, 63, },
+ { 2, 0, 1, 7, 2, 63, },
+ { 1, 0, 1, 7, 2, 63, },
+ { 0, 0, 1, 7, 3, 46, },
+ { 2, 0, 1, 7, 3, 40, },
+ { 1, 0, 1, 7, 3, 40, },
+ { 0, 0, 1, 7, 4, 46, },
+ { 2, 0, 1, 7, 4, 40, },
+ { 1, 0, 1, 7, 4, 40, },
+ { 0, 0, 1, 7, 5, 46, },
+ { 2, 0, 1, 7, 5, 40, },
+ { 1, 0, 1, 7, 5, 40, },
+ { 0, 0, 1, 7, 6, 46, },
+ { 2, 0, 1, 7, 6, 40, },
+ { 1, 0, 1, 7, 6, 40, },
+ { 0, 0, 1, 7, 7, 46, },
+ { 2, 0, 1, 7, 7, 40, },
+ { 1, 0, 1, 7, 7, 40, },
+ { 0, 0, 1, 7, 8, 46, },
+ { 2, 0, 1, 7, 8, 40, },
+ { 1, 0, 1, 7, 8, 40, },
+ { 0, 0, 1, 7, 9, 46, },
+ { 2, 0, 1, 7, 9, 40, },
+ { 1, 0, 1, 7, 9, 40, },
+ { 0, 0, 1, 7, 10, 46, },
+ { 2, 0, 1, 7, 10, 40, },
+ { 1, 0, 1, 7, 10, 40, },
+ { 0, 0, 1, 7, 11, 46, },
+ { 2, 0, 1, 7, 11, 40, },
+ { 1, 0, 1, 7, 11, 40, },
+ { 0, 0, 1, 7, 12, 63, },
+ { 2, 0, 1, 7, 12, 40, },
+ { 1, 0, 1, 7, 12, 40, },
+ { 0, 0, 1, 7, 13, 63, },
+ { 2, 0, 1, 7, 13, 40, },
+ { 1, 0, 1, 7, 13, 40, },
+ { 0, 0, 1, 7, 14, 63, },
+ { 2, 0, 1, 7, 14, 63, },
+ { 1, 0, 1, 7, 14, 63, },
+ { 0, 1, 0, 1, 36, 46, },
+ { 2, 1, 0, 1, 36, 40, },
+ { 1, 1, 0, 1, 36, 40, },
+ { 0, 1, 0, 1, 40, 46, },
+ { 2, 1, 0, 1, 40, 40, },
+ { 1, 1, 0, 1, 40, 40, },
+ { 0, 1, 0, 1, 44, 46, },
+ { 2, 1, 0, 1, 44, 40, },
+ { 1, 1, 0, 1, 44, 40, },
+ { 0, 1, 0, 1, 48, 46, },
+ { 2, 1, 0, 1, 48, 40, },
+ { 1, 1, 0, 1, 48, 40, },
+ { 0, 1, 0, 1, 52, 46, },
+ { 2, 1, 0, 1, 52, 40, },
+ { 1, 1, 0, 1, 52, 40, },
+ { 0, 1, 0, 1, 56, 46, },
+ { 2, 1, 0, 1, 56, 40, },
+ { 1, 1, 0, 1, 56, 40, },
+ { 0, 1, 0, 1, 60, 46, },
+ { 2, 1, 0, 1, 60, 40, },
+ { 1, 1, 0, 1, 60, 40, },
+ { 0, 1, 0, 1, 64, 46, },
+ { 2, 1, 0, 1, 64, 40, },
+ { 1, 1, 0, 1, 64, 40, },
+ { 0, 1, 0, 1, 100, 46, },
+ { 2, 1, 0, 1, 100, 40, },
+ { 1, 1, 0, 1, 100, 40, },
+ { 0, 1, 0, 1, 104, 46, },
+ { 2, 1, 0, 1, 104, 40, },
+ { 1, 1, 0, 1, 104, 40, },
+ { 0, 1, 0, 1, 108, 46, },
+ { 2, 1, 0, 1, 108, 40, },
+ { 1, 1, 0, 1, 108, 40, },
+ { 0, 1, 0, 1, 112, 46, },
+ { 2, 1, 0, 1, 112, 40, },
+ { 1, 1, 0, 1, 112, 40, },
+ { 0, 1, 0, 1, 116, 46, },
+ { 2, 1, 0, 1, 116, 40, },
+ { 1, 1, 0, 1, 116, 40, },
+ { 0, 1, 0, 1, 120, 46, },
+ { 2, 1, 0, 1, 120, 40, },
+ { 1, 1, 0, 1, 120, 40, },
+ { 0, 1, 0, 1, 124, 46, },
+ { 2, 1, 0, 1, 124, 40, },
+ { 1, 1, 0, 1, 124, 40, },
+ { 0, 1, 0, 1, 128, 46, },
+ { 2, 1, 0, 1, 128, 40, },
+ { 1, 1, 0, 1, 128, 40, },
+ { 0, 1, 0, 1, 132, 46, },
+ { 2, 1, 0, 1, 132, 40, },
+ { 1, 1, 0, 1, 132, 40, },
+ { 0, 1, 0, 1, 136, 46, },
+ { 2, 1, 0, 1, 136, 40, },
+ { 1, 1, 0, 1, 136, 40, },
+ { 0, 1, 0, 1, 140, 46, },
+ { 2, 1, 0, 1, 140, 40, },
+ { 1, 1, 0, 1, 140, 40, },
+ { 0, 1, 0, 1, 149, 46, },
+ { 2, 1, 0, 1, 149, 40, },
+ { 1, 1, 0, 1, 149, 63, },
+ { 0, 1, 0, 1, 153, 46, },
+ { 2, 1, 0, 1, 153, 40, },
+ { 1, 1, 0, 1, 153, 63, },
+ { 0, 1, 0, 1, 157, 46, },
+ { 2, 1, 0, 1, 157, 40, },
+ { 1, 1, 0, 1, 157, 63, },
+ { 0, 1, 0, 1, 161, 46, },
+ { 2, 1, 0, 1, 161, 40, },
+ { 1, 1, 0, 1, 161, 63, },
+ { 0, 1, 0, 1, 165, 46, },
+ { 2, 1, 0, 1, 165, 40, },
+ { 1, 1, 0, 1, 165, 63, },
+ { 0, 1, 0, 2, 36, 46, },
+ { 2, 1, 0, 2, 36, 40, },
+ { 1, 1, 0, 2, 36, 40, },
+ { 0, 1, 0, 2, 40, 46, },
+ { 2, 1, 0, 2, 40, 40, },
+ { 1, 1, 0, 2, 40, 40, },
+ { 0, 1, 0, 2, 44, 46, },
+ { 2, 1, 0, 2, 44, 40, },
+ { 1, 1, 0, 2, 44, 40, },
+ { 0, 1, 0, 2, 48, 46, },
+ { 2, 1, 0, 2, 48, 40, },
+ { 1, 1, 0, 2, 48, 40, },
+ { 0, 1, 0, 2, 52, 46, },
+ { 2, 1, 0, 2, 52, 40, },
+ { 1, 1, 0, 2, 52, 40, },
+ { 0, 1, 0, 2, 56, 46, },
+ { 2, 1, 0, 2, 56, 40, },
+ { 1, 1, 0, 2, 56, 40, },
+ { 0, 1, 0, 2, 60, 46, },
+ { 2, 1, 0, 2, 60, 40, },
+ { 1, 1, 0, 2, 60, 40, },
+ { 0, 1, 0, 2, 64, 46, },
+ { 2, 1, 0, 2, 64, 40, },
+ { 1, 1, 0, 2, 64, 40, },
+ { 0, 1, 0, 2, 100, 46, },
+ { 2, 1, 0, 2, 100, 40, },
+ { 1, 1, 0, 2, 100, 40, },
+ { 0, 1, 0, 2, 104, 46, },
+ { 2, 1, 0, 2, 104, 40, },
+ { 1, 1, 0, 2, 104, 40, },
+ { 0, 1, 0, 2, 108, 46, },
+ { 2, 1, 0, 2, 108, 40, },
+ { 1, 1, 0, 2, 108, 40, },
+ { 0, 1, 0, 2, 112, 46, },
+ { 2, 1, 0, 2, 112, 40, },
+ { 1, 1, 0, 2, 112, 40, },
+ { 0, 1, 0, 2, 116, 46, },
+ { 2, 1, 0, 2, 116, 40, },
+ { 1, 1, 0, 2, 116, 40, },
+ { 0, 1, 0, 2, 120, 46, },
+ { 2, 1, 0, 2, 120, 40, },
+ { 1, 1, 0, 2, 120, 40, },
+ { 0, 1, 0, 2, 124, 46, },
+ { 2, 1, 0, 2, 124, 40, },
+ { 1, 1, 0, 2, 124, 40, },
+ { 0, 1, 0, 2, 128, 46, },
+ { 2, 1, 0, 2, 128, 40, },
+ { 1, 1, 0, 2, 128, 40, },
+ { 0, 1, 0, 2, 132, 46, },
+ { 2, 1, 0, 2, 132, 40, },
+ { 1, 1, 0, 2, 132, 40, },
+ { 0, 1, 0, 2, 136, 46, },
+ { 2, 1, 0, 2, 136, 40, },
+ { 1, 1, 0, 2, 136, 40, },
+ { 0, 1, 0, 2, 140, 46, },
+ { 2, 1, 0, 2, 140, 40, },
+ { 1, 1, 0, 2, 140, 40, },
+ { 0, 1, 0, 2, 149, 46, },
+ { 2, 1, 0, 2, 149, 40, },
+ { 1, 1, 0, 2, 149, 63, },
+ { 0, 1, 0, 2, 153, 46, },
+ { 2, 1, 0, 2, 153, 40, },
+ { 1, 1, 0, 2, 153, 63, },
+ { 0, 1, 0, 2, 157, 46, },
+ { 2, 1, 0, 2, 157, 40, },
+ { 1, 1, 0, 2, 157, 63, },
+ { 0, 1, 0, 2, 161, 46, },
+ { 2, 1, 0, 2, 161, 40, },
+ { 1, 1, 0, 2, 161, 63, },
+ { 0, 1, 0, 2, 165, 46, },
+ { 2, 1, 0, 2, 165, 40, },
+ { 1, 1, 0, 2, 165, 63, },
+ { 0, 1, 0, 3, 36, 46, },
+ { 2, 1, 0, 3, 36, 40, },
+ { 1, 1, 0, 3, 36, 40, },
+ { 0, 1, 0, 3, 40, 46, },
+ { 2, 1, 0, 3, 40, 40, },
+ { 1, 1, 0, 3, 40, 40, },
+ { 0, 1, 0, 3, 44, 46, },
+ { 2, 1, 0, 3, 44, 40, },
+ { 1, 1, 0, 3, 44, 40, },
+ { 0, 1, 0, 3, 48, 46, },
+ { 2, 1, 0, 3, 48, 40, },
+ { 1, 1, 0, 3, 48, 40, },
+ { 0, 1, 0, 3, 52, 46, },
+ { 2, 1, 0, 3, 52, 40, },
+ { 1, 1, 0, 3, 52, 40, },
+ { 0, 1, 0, 3, 56, 46, },
+ { 2, 1, 0, 3, 56, 40, },
+ { 1, 1, 0, 3, 56, 40, },
+ { 0, 1, 0, 3, 60, 46, },
+ { 2, 1, 0, 3, 60, 40, },
+ { 1, 1, 0, 3, 60, 40, },
+ { 0, 1, 0, 3, 64, 46, },
+ { 2, 1, 0, 3, 64, 40, },
+ { 1, 1, 0, 3, 64, 40, },
+ { 0, 1, 0, 3, 100, 46, },
+ { 2, 1, 0, 3, 100, 40, },
+ { 1, 1, 0, 3, 100, 40, },
+ { 0, 1, 0, 3, 104, 46, },
+ { 2, 1, 0, 3, 104, 40, },
+ { 1, 1, 0, 3, 104, 40, },
+ { 0, 1, 0, 3, 108, 46, },
+ { 2, 1, 0, 3, 108, 40, },
+ { 1, 1, 0, 3, 108, 40, },
+ { 0, 1, 0, 3, 112, 46, },
+ { 2, 1, 0, 3, 112, 40, },
+ { 1, 1, 0, 3, 112, 40, },
+ { 0, 1, 0, 3, 116, 46, },
+ { 2, 1, 0, 3, 116, 40, },
+ { 1, 1, 0, 3, 116, 40, },
+ { 0, 1, 0, 3, 120, 46, },
+ { 2, 1, 0, 3, 120, 40, },
+ { 1, 1, 0, 3, 120, 40, },
+ { 0, 1, 0, 3, 124, 46, },
+ { 2, 1, 0, 3, 124, 40, },
+ { 1, 1, 0, 3, 124, 40, },
+ { 0, 1, 0, 3, 128, 46, },
+ { 2, 1, 0, 3, 128, 40, },
+ { 1, 1, 0, 3, 128, 40, },
+ { 0, 1, 0, 3, 132, 46, },
+ { 2, 1, 0, 3, 132, 40, },
+ { 1, 1, 0, 3, 132, 40, },
+ { 0, 1, 0, 3, 136, 46, },
+ { 2, 1, 0, 3, 136, 40, },
+ { 1, 1, 0, 3, 136, 40, },
+ { 0, 1, 0, 3, 140, 46, },
+ { 2, 1, 0, 3, 140, 40, },
+ { 1, 1, 0, 3, 140, 40, },
+ { 0, 1, 0, 3, 149, 46, },
+ { 2, 1, 0, 3, 149, 40, },
+ { 1, 1, 0, 3, 149, 63, },
+ { 0, 1, 0, 3, 153, 46, },
+ { 2, 1, 0, 3, 153, 40, },
+ { 1, 1, 0, 3, 153, 63, },
+ { 0, 1, 0, 3, 157, 46, },
+ { 2, 1, 0, 3, 157, 40, },
+ { 1, 1, 0, 3, 157, 63, },
+ { 0, 1, 0, 3, 161, 46, },
+ { 2, 1, 0, 3, 161, 40, },
+ { 1, 1, 0, 3, 161, 63, },
+ { 0, 1, 0, 3, 165, 46, },
+ { 2, 1, 0, 3, 165, 40, },
+ { 1, 1, 0, 3, 165, 63, },
+ { 0, 1, 0, 6, 36, 46, },
+ { 2, 1, 0, 6, 36, 40, },
+ { 1, 1, 0, 6, 36, 40, },
+ { 0, 1, 0, 6, 40, 46, },
+ { 2, 1, 0, 6, 40, 40, },
+ { 1, 1, 0, 6, 40, 40, },
+ { 0, 1, 0, 6, 44, 46, },
+ { 2, 1, 0, 6, 44, 40, },
+ { 1, 1, 0, 6, 44, 40, },
+ { 0, 1, 0, 6, 48, 46, },
+ { 2, 1, 0, 6, 48, 40, },
+ { 1, 1, 0, 6, 48, 40, },
+ { 0, 1, 0, 6, 52, 46, },
+ { 2, 1, 0, 6, 52, 40, },
+ { 1, 1, 0, 6, 52, 40, },
+ { 0, 1, 0, 6, 56, 46, },
+ { 2, 1, 0, 6, 56, 40, },
+ { 1, 1, 0, 6, 56, 40, },
+ { 0, 1, 0, 6, 60, 46, },
+ { 2, 1, 0, 6, 60, 40, },
+ { 1, 1, 0, 6, 60, 40, },
+ { 0, 1, 0, 6, 64, 46, },
+ { 2, 1, 0, 6, 64, 40, },
+ { 1, 1, 0, 6, 64, 40, },
+ { 0, 1, 0, 6, 100, 46, },
+ { 2, 1, 0, 6, 100, 40, },
+ { 1, 1, 0, 6, 100, 40, },
+ { 0, 1, 0, 6, 104, 46, },
+ { 2, 1, 0, 6, 104, 40, },
+ { 1, 1, 0, 6, 104, 40, },
+ { 0, 1, 0, 6, 108, 46, },
+ { 2, 1, 0, 6, 108, 40, },
+ { 1, 1, 0, 6, 108, 40, },
+ { 0, 1, 0, 6, 112, 46, },
+ { 2, 1, 0, 6, 112, 40, },
+ { 1, 1, 0, 6, 112, 40, },
+ { 0, 1, 0, 6, 116, 46, },
+ { 2, 1, 0, 6, 116, 40, },
+ { 1, 1, 0, 6, 116, 40, },
+ { 0, 1, 0, 6, 120, 46, },
+ { 2, 1, 0, 6, 120, 40, },
+ { 1, 1, 0, 6, 120, 40, },
+ { 0, 1, 0, 6, 124, 46, },
+ { 2, 1, 0, 6, 124, 40, },
+ { 1, 1, 0, 6, 124, 40, },
+ { 0, 1, 0, 6, 128, 46, },
+ { 2, 1, 0, 6, 128, 40, },
+ { 1, 1, 0, 6, 128, 40, },
+ { 0, 1, 0, 6, 132, 46, },
+ { 2, 1, 0, 6, 132, 40, },
+ { 1, 1, 0, 6, 132, 40, },
+ { 0, 1, 0, 6, 136, 46, },
+ { 2, 1, 0, 6, 136, 40, },
+ { 1, 1, 0, 6, 136, 40, },
+ { 0, 1, 0, 6, 140, 46, },
+ { 2, 1, 0, 6, 140, 40, },
+ { 1, 1, 0, 6, 140, 40, },
+ { 0, 1, 0, 6, 149, 46, },
+ { 2, 1, 0, 6, 149, 40, },
+ { 1, 1, 0, 6, 149, 63, },
+ { 0, 1, 0, 6, 153, 46, },
+ { 2, 1, 0, 6, 153, 40, },
+ { 1, 1, 0, 6, 153, 63, },
+ { 0, 1, 0, 6, 157, 46, },
+ { 2, 1, 0, 6, 157, 40, },
+ { 1, 1, 0, 6, 157, 63, },
+ { 0, 1, 0, 6, 161, 46, },
+ { 2, 1, 0, 6, 161, 40, },
+ { 1, 1, 0, 6, 161, 63, },
+ { 0, 1, 0, 6, 165, 46, },
+ { 2, 1, 0, 6, 165, 40, },
+ { 1, 1, 0, 6, 165, 63, },
+ { 0, 1, 0, 7, 36, 46, },
+ { 2, 1, 0, 7, 36, 40, },
+ { 1, 1, 0, 7, 36, 40, },
+ { 0, 1, 0, 7, 40, 46, },
+ { 2, 1, 0, 7, 40, 40, },
+ { 1, 1, 0, 7, 40, 40, },
+ { 0, 1, 0, 7, 44, 46, },
+ { 2, 1, 0, 7, 44, 40, },
+ { 1, 1, 0, 7, 44, 40, },
+ { 0, 1, 0, 7, 48, 46, },
+ { 2, 1, 0, 7, 48, 40, },
+ { 1, 1, 0, 7, 48, 40, },
+ { 0, 1, 0, 7, 52, 46, },
+ { 2, 1, 0, 7, 52, 40, },
+ { 1, 1, 0, 7, 52, 40, },
+ { 0, 1, 0, 7, 56, 46, },
+ { 2, 1, 0, 7, 56, 40, },
+ { 1, 1, 0, 7, 56, 40, },
+ { 0, 1, 0, 7, 60, 46, },
+ { 2, 1, 0, 7, 60, 40, },
+ { 1, 1, 0, 7, 60, 40, },
+ { 0, 1, 0, 7, 64, 46, },
+ { 2, 1, 0, 7, 64, 40, },
+ { 1, 1, 0, 7, 64, 40, },
+ { 0, 1, 0, 7, 100, 46, },
+ { 2, 1, 0, 7, 100, 40, },
+ { 1, 1, 0, 7, 100, 40, },
+ { 0, 1, 0, 7, 104, 46, },
+ { 2, 1, 0, 7, 104, 40, },
+ { 1, 1, 0, 7, 104, 40, },
+ { 0, 1, 0, 7, 108, 46, },
+ { 2, 1, 0, 7, 108, 40, },
+ { 1, 1, 0, 7, 108, 40, },
+ { 0, 1, 0, 7, 112, 46, },
+ { 2, 1, 0, 7, 112, 40, },
+ { 1, 1, 0, 7, 112, 40, },
+ { 0, 1, 0, 7, 116, 46, },
+ { 2, 1, 0, 7, 116, 40, },
+ { 1, 1, 0, 7, 116, 40, },
+ { 0, 1, 0, 7, 120, 46, },
+ { 2, 1, 0, 7, 120, 40, },
+ { 1, 1, 0, 7, 120, 40, },
+ { 0, 1, 0, 7, 124, 46, },
+ { 2, 1, 0, 7, 124, 40, },
+ { 1, 1, 0, 7, 124, 40, },
+ { 0, 1, 0, 7, 128, 46, },
+ { 2, 1, 0, 7, 128, 40, },
+ { 1, 1, 0, 7, 128, 40, },
+ { 0, 1, 0, 7, 132, 46, },
+ { 2, 1, 0, 7, 132, 40, },
+ { 1, 1, 0, 7, 132, 40, },
+ { 0, 1, 0, 7, 136, 46, },
+ { 2, 1, 0, 7, 136, 40, },
+ { 1, 1, 0, 7, 136, 40, },
+ { 0, 1, 0, 7, 140, 46, },
+ { 2, 1, 0, 7, 140, 40, },
+ { 1, 1, 0, 7, 140, 40, },
+ { 0, 1, 0, 7, 149, 46, },
+ { 2, 1, 0, 7, 149, 40, },
+ { 1, 1, 0, 7, 149, 63, },
+ { 0, 1, 0, 7, 153, 46, },
+ { 2, 1, 0, 7, 153, 40, },
+ { 1, 1, 0, 7, 153, 63, },
+ { 0, 1, 0, 7, 157, 46, },
+ { 2, 1, 0, 7, 157, 40, },
+ { 1, 1, 0, 7, 157, 63, },
+ { 0, 1, 0, 7, 161, 46, },
+ { 2, 1, 0, 7, 161, 40, },
+ { 1, 1, 0, 7, 161, 63, },
+ { 0, 1, 0, 7, 165, 46, },
+ { 2, 1, 0, 7, 165, 40, },
+ { 1, 1, 0, 7, 165, 63, },
+ { 0, 1, 1, 2, 38, 46, },
+ { 2, 1, 1, 2, 38, 40, },
+ { 1, 1, 1, 2, 38, 40, },
+ { 0, 1, 1, 2, 46, 46, },
+ { 2, 1, 1, 2, 46, 40, },
+ { 1, 1, 1, 2, 46, 40, },
+ { 0, 1, 1, 2, 54, 46, },
+ { 2, 1, 1, 2, 54, 40, },
+ { 1, 1, 1, 2, 54, 40, },
+ { 0, 1, 1, 2, 62, 46, },
+ { 2, 1, 1, 2, 62, 40, },
+ { 1, 1, 1, 2, 62, 40, },
+ { 0, 1, 1, 2, 102, 46, },
+ { 2, 1, 1, 2, 102, 40, },
+ { 1, 1, 1, 2, 102, 40, },
+ { 0, 1, 1, 2, 110, 46, },
+ { 2, 1, 1, 2, 110, 40, },
+ { 1, 1, 1, 2, 110, 40, },
+ { 0, 1, 1, 2, 118, 46, },
+ { 2, 1, 1, 2, 118, 40, },
+ { 1, 1, 1, 2, 118, 40, },
+ { 0, 1, 1, 2, 126, 46, },
+ { 2, 1, 1, 2, 126, 40, },
+ { 1, 1, 1, 2, 126, 40, },
+ { 0, 1, 1, 2, 134, 46, },
+ { 2, 1, 1, 2, 134, 40, },
+ { 1, 1, 1, 2, 134, 40, },
+ { 0, 1, 1, 2, 151, 46, },
+ { 2, 1, 1, 2, 151, 40, },
+ { 1, 1, 1, 2, 151, 63, },
+ { 0, 1, 1, 2, 159, 46, },
+ { 2, 1, 1, 2, 159, 40, },
+ { 1, 1, 1, 2, 159, 63, },
+ { 0, 1, 1, 3, 38, 46, },
+ { 2, 1, 1, 3, 38, 40, },
+ { 1, 1, 1, 3, 38, 40, },
+ { 0, 1, 1, 3, 46, 46, },
+ { 2, 1, 1, 3, 46, 40, },
+ { 1, 1, 1, 3, 46, 40, },
+ { 0, 1, 1, 3, 54, 46, },
+ { 2, 1, 1, 3, 54, 40, },
+ { 1, 1, 1, 3, 54, 40, },
+ { 0, 1, 1, 3, 62, 46, },
+ { 2, 1, 1, 3, 62, 40, },
+ { 1, 1, 1, 3, 62, 40, },
+ { 0, 1, 1, 3, 102, 46, },
+ { 2, 1, 1, 3, 102, 40, },
+ { 1, 1, 1, 3, 102, 40, },
+ { 0, 1, 1, 3, 110, 46, },
+ { 2, 1, 1, 3, 110, 40, },
+ { 1, 1, 1, 3, 110, 40, },
+ { 0, 1, 1, 3, 118, 46, },
+ { 2, 1, 1, 3, 118, 40, },
+ { 1, 1, 1, 3, 118, 40, },
+ { 0, 1, 1, 3, 126, 46, },
+ { 2, 1, 1, 3, 126, 40, },
+ { 1, 1, 1, 3, 126, 40, },
+ { 0, 1, 1, 3, 134, 46, },
+ { 2, 1, 1, 3, 134, 40, },
+ { 1, 1, 1, 3, 134, 40, },
+ { 0, 1, 1, 3, 151, 46, },
+ { 2, 1, 1, 3, 151, 40, },
+ { 1, 1, 1, 3, 151, 63, },
+ { 0, 1, 1, 3, 159, 46, },
+ { 2, 1, 1, 3, 159, 40, },
+ { 1, 1, 1, 3, 159, 63, },
+ { 0, 1, 1, 6, 38, 46, },
+ { 2, 1, 1, 6, 38, 40, },
+ { 1, 1, 1, 6, 38, 40, },
+ { 0, 1, 1, 6, 46, 46, },
+ { 2, 1, 1, 6, 46, 40, },
+ { 1, 1, 1, 6, 46, 40, },
+ { 0, 1, 1, 6, 54, 46, },
+ { 2, 1, 1, 6, 54, 40, },
+ { 1, 1, 1, 6, 54, 40, },
+ { 0, 1, 1, 6, 62, 46, },
+ { 2, 1, 1, 6, 62, 40, },
+ { 1, 1, 1, 6, 62, 40, },
+ { 0, 1, 1, 6, 102, 46, },
+ { 2, 1, 1, 6, 102, 40, },
+ { 1, 1, 1, 6, 102, 40, },
+ { 0, 1, 1, 6, 110, 46, },
+ { 2, 1, 1, 6, 110, 40, },
+ { 1, 1, 1, 6, 110, 40, },
+ { 0, 1, 1, 6, 118, 46, },
+ { 2, 1, 1, 6, 118, 40, },
+ { 1, 1, 1, 6, 118, 40, },
+ { 0, 1, 1, 6, 126, 46, },
+ { 2, 1, 1, 6, 126, 40, },
+ { 1, 1, 1, 6, 126, 40, },
+ { 0, 1, 1, 6, 134, 46, },
+ { 2, 1, 1, 6, 134, 40, },
+ { 1, 1, 1, 6, 134, 40, },
+ { 0, 1, 1, 6, 151, 46, },
+ { 2, 1, 1, 6, 151, 40, },
+ { 1, 1, 1, 6, 151, 63, },
+ { 0, 1, 1, 6, 159, 46, },
+ { 2, 1, 1, 6, 159, 40, },
+ { 1, 1, 1, 6, 159, 63, },
+ { 0, 1, 1, 7, 38, 46, },
+ { 2, 1, 1, 7, 38, 40, },
+ { 1, 1, 1, 7, 38, 40, },
+ { 0, 1, 1, 7, 46, 46, },
+ { 2, 1, 1, 7, 46, 40, },
+ { 1, 1, 1, 7, 46, 40, },
+ { 0, 1, 1, 7, 54, 46, },
+ { 2, 1, 1, 7, 54, 40, },
+ { 1, 1, 1, 7, 54, 40, },
+ { 0, 1, 1, 7, 62, 46, },
+ { 2, 1, 1, 7, 62, 40, },
+ { 1, 1, 1, 7, 62, 40, },
+ { 0, 1, 1, 7, 102, 46, },
+ { 2, 1, 1, 7, 102, 40, },
+ { 1, 1, 1, 7, 102, 40, },
+ { 0, 1, 1, 7, 110, 46, },
+ { 2, 1, 1, 7, 110, 40, },
+ { 1, 1, 1, 7, 110, 40, },
+ { 0, 1, 1, 7, 118, 46, },
+ { 2, 1, 1, 7, 118, 40, },
+ { 1, 1, 1, 7, 118, 40, },
+ { 0, 1, 1, 7, 126, 46, },
+ { 2, 1, 1, 7, 126, 40, },
+ { 1, 1, 1, 7, 126, 40, },
+ { 0, 1, 1, 7, 134, 46, },
+ { 2, 1, 1, 7, 134, 40, },
+ { 1, 1, 1, 7, 134, 40, },
+ { 0, 1, 1, 7, 151, 46, },
+ { 2, 1, 1, 7, 151, 40, },
+ { 1, 1, 1, 7, 151, 63, },
+ { 0, 1, 1, 7, 159, 46, },
+ { 2, 1, 1, 7, 159, 40, },
+ { 1, 1, 1, 7, 159, 63, },
+ { 0, 1, 2, 4, 42, 46, },
+ { 2, 1, 2, 4, 42, 40, },
+ { 1, 1, 2, 4, 42, 40, },
+ { 0, 1, 2, 4, 58, 46, },
+ { 2, 1, 2, 4, 58, 40, },
+ { 1, 1, 2, 4, 58, 40, },
+ { 0, 1, 2, 4, 106, 46, },
+ { 2, 1, 2, 4, 106, 40, },
+ { 1, 1, 2, 4, 106, 40, },
+ { 0, 1, 2, 4, 122, 46, },
+ { 2, 1, 2, 4, 122, 40, },
+ { 1, 1, 2, 4, 122, 40, },
+ { 0, 1, 2, 4, 155, 46, },
+ { 2, 1, 2, 4, 155, 40, },
+ { 1, 1, 2, 4, 155, 63, },
+ { 0, 1, 2, 5, 42, 46, },
+ { 2, 1, 2, 5, 42, 40, },
+ { 1, 1, 2, 5, 42, 40, },
+ { 0, 1, 2, 5, 58, 46, },
+ { 2, 1, 2, 5, 58, 40, },
+ { 1, 1, 2, 5, 58, 40, },
+ { 0, 1, 2, 5, 106, 46, },
+ { 2, 1, 2, 5, 106, 40, },
+ { 1, 1, 2, 5, 106, 40, },
+ { 0, 1, 2, 5, 122, 46, },
+ { 2, 1, 2, 5, 122, 40, },
+ { 1, 1, 2, 5, 122, 40, },
+ { 0, 1, 2, 5, 155, 46, },
+ { 2, 1, 2, 5, 155, 40, },
+ { 1, 1, 2, 5, 155, 63, },
+ { 0, 1, 2, 8, 42, 46, },
+ { 2, 1, 2, 8, 42, 40, },
+ { 1, 1, 2, 8, 42, 40, },
+ { 0, 1, 2, 8, 58, 46, },
+ { 2, 1, 2, 8, 58, 40, },
+ { 1, 1, 2, 8, 58, 40, },
+ { 0, 1, 2, 8, 106, 46, },
+ { 2, 1, 2, 8, 106, 40, },
+ { 1, 1, 2, 8, 106, 40, },
+ { 0, 1, 2, 8, 122, 46, },
+ { 2, 1, 2, 8, 122, 40, },
+ { 1, 1, 2, 8, 122, 40, },
+ { 0, 1, 2, 8, 155, 46, },
+ { 2, 1, 2, 8, 155, 40, },
+ { 1, 1, 2, 8, 155, 63, },
+ { 0, 1, 2, 9, 42, 46, },
+ { 2, 1, 2, 9, 42, 40, },
+ { 1, 1, 2, 9, 42, 40, },
+ { 0, 1, 2, 9, 58, 46, },
+ { 2, 1, 2, 9, 58, 40, },
+ { 1, 1, 2, 9, 58, 40, },
+ { 0, 1, 2, 9, 106, 46, },
+ { 2, 1, 2, 9, 106, 40, },
+ { 1, 1, 2, 9, 106, 40, },
+ { 0, 1, 2, 9, 122, 46, },
+ { 2, 1, 2, 9, 122, 40, },
+ { 1, 1, 2, 9, 122, 40, },
+ { 0, 1, 2, 9, 155, 46, },
+ { 2, 1, 2, 9, 155, 40, },
+ { 1, 1, 2, 9, 155, 63, },
+};
+
+RTW_DECL_TABLE_TXPWR_LMT(rtw8814a_txpwr_lmt_type5);
+
+static const struct rtw_txpwr_lmt_cfg_pair rtw8814a_txpwr_lmt_type7[] = {
+ { 0, 0, 0, 0, 1, 44, },
+ { 2, 0, 0, 0, 1, 32, },
+ { 1, 0, 0, 0, 1, 32, },
+ { 0, 0, 0, 0, 2, 52, },
+ { 2, 0, 0, 0, 2, 32, },
+ { 1, 0, 0, 0, 2, 32, },
+ { 0, 0, 0, 0, 3, 52, },
+ { 2, 0, 0, 0, 3, 32, },
+ { 1, 0, 0, 0, 3, 32, },
+ { 0, 0, 0, 0, 4, 52, },
+ { 2, 0, 0, 0, 4, 32, },
+ { 1, 0, 0, 0, 4, 32, },
+ { 0, 0, 0, 0, 5, 52, },
+ { 2, 0, 0, 0, 5, 32, },
+ { 1, 0, 0, 0, 5, 32, },
+ { 0, 0, 0, 0, 6, 52, },
+ { 2, 0, 0, 0, 6, 32, },
+ { 1, 0, 0, 0, 6, 32, },
+ { 0, 0, 0, 0, 7, 52, },
+ { 2, 0, 0, 0, 7, 32, },
+ { 1, 0, 0, 0, 7, 32, },
+ { 0, 0, 0, 0, 8, 52, },
+ { 2, 0, 0, 0, 8, 32, },
+ { 1, 0, 0, 0, 8, 32, },
+ { 0, 0, 0, 0, 9, 52, },
+ { 2, 0, 0, 0, 9, 32, },
+ { 1, 0, 0, 0, 9, 32, },
+ { 0, 0, 0, 0, 10, 52, },
+ { 2, 0, 0, 0, 10, 32, },
+ { 1, 0, 0, 0, 10, 32, },
+ { 0, 0, 0, 0, 11, 44, },
+ { 2, 0, 0, 0, 11, 32, },
+ { 1, 0, 0, 0, 11, 32, },
+ { 0, 0, 0, 0, 12, 63, },
+ { 2, 0, 0, 0, 12, 32, },
+ { 1, 0, 0, 0, 12, 32, },
+ { 0, 0, 0, 0, 13, 63, },
+ { 2, 0, 0, 0, 13, 32, },
+ { 1, 0, 0, 0, 13, 32, },
+ { 0, 0, 0, 0, 14, 63, },
+ { 2, 0, 0, 0, 14, 63, },
+ { 1, 0, 0, 0, 14, 32, },
+ { 0, 0, 0, 1, 1, 38, },
+ { 2, 0, 0, 1, 1, 32, },
+ { 1, 0, 0, 1, 1, 32, },
+ { 0, 0, 0, 1, 2, 46, },
+ { 2, 0, 0, 1, 2, 32, },
+ { 1, 0, 0, 1, 2, 32, },
+ { 0, 0, 0, 1, 3, 46, },
+ { 2, 0, 0, 1, 3, 32, },
+ { 1, 0, 0, 1, 3, 32, },
+ { 0, 0, 0, 1, 4, 46, },
+ { 2, 0, 0, 1, 4, 32, },
+ { 1, 0, 0, 1, 4, 32, },
+ { 0, 0, 0, 1, 5, 46, },
+ { 2, 0, 0, 1, 5, 32, },
+ { 1, 0, 0, 1, 5, 32, },
+ { 0, 0, 0, 1, 6, 46, },
+ { 2, 0, 0, 1, 6, 32, },
+ { 1, 0, 0, 1, 6, 32, },
+ { 0, 0, 0, 1, 7, 46, },
+ { 2, 0, 0, 1, 7, 32, },
+ { 1, 0, 0, 1, 7, 32, },
+ { 0, 0, 0, 1, 8, 46, },
+ { 2, 0, 0, 1, 8, 32, },
+ { 1, 0, 0, 1, 8, 32, },
+ { 0, 0, 0, 1, 9, 46, },
+ { 2, 0, 0, 1, 9, 32, },
+ { 1, 0, 0, 1, 9, 32, },
+ { 0, 0, 0, 1, 10, 46, },
+ { 2, 0, 0, 1, 10, 32, },
+ { 1, 0, 0, 1, 10, 32, },
+ { 0, 0, 0, 1, 11, 38, },
+ { 2, 0, 0, 1, 11, 32, },
+ { 1, 0, 0, 1, 11, 32, },
+ { 0, 0, 0, 1, 12, 63, },
+ { 2, 0, 0, 1, 12, 32, },
+ { 1, 0, 0, 1, 12, 32, },
+ { 0, 0, 0, 1, 13, 63, },
+ { 2, 0, 0, 1, 13, 32, },
+ { 1, 0, 0, 1, 13, 32, },
+ { 0, 0, 0, 1, 14, 63, },
+ { 2, 0, 0, 1, 14, 63, },
+ { 1, 0, 0, 1, 14, 63, },
+ { 0, 0, 0, 2, 1, 34, },
+ { 2, 0, 0, 2, 1, 32, },
+ { 1, 0, 0, 2, 1, 32, },
+ { 0, 0, 0, 2, 2, 46, },
+ { 2, 0, 0, 2, 2, 32, },
+ { 1, 0, 0, 2, 2, 32, },
+ { 0, 0, 0, 2, 3, 46, },
+ { 2, 0, 0, 2, 3, 32, },
+ { 1, 0, 0, 2, 3, 32, },
+ { 0, 0, 0, 2, 4, 46, },
+ { 2, 0, 0, 2, 4, 32, },
+ { 1, 0, 0, 2, 4, 32, },
+ { 0, 0, 0, 2, 5, 46, },
+ { 2, 0, 0, 2, 5, 32, },
+ { 1, 0, 0, 2, 5, 32, },
+ { 0, 0, 0, 2, 6, 46, },
+ { 2, 0, 0, 2, 6, 32, },
+ { 1, 0, 0, 2, 6, 32, },
+ { 0, 0, 0, 2, 7, 46, },
+ { 2, 0, 0, 2, 7, 32, },
+ { 1, 0, 0, 2, 7, 32, },
+ { 0, 0, 0, 2, 8, 46, },
+ { 2, 0, 0, 2, 8, 32, },
+ { 1, 0, 0, 2, 8, 32, },
+ { 0, 0, 0, 2, 9, 46, },
+ { 2, 0, 0, 2, 9, 32, },
+ { 1, 0, 0, 2, 9, 32, },
+ { 0, 0, 0, 2, 10, 46, },
+ { 2, 0, 0, 2, 10, 32, },
+ { 1, 0, 0, 2, 10, 32, },
+ { 0, 0, 0, 2, 11, 34, },
+ { 2, 0, 0, 2, 11, 32, },
+ { 1, 0, 0, 2, 11, 32, },
+ { 0, 0, 0, 2, 12, 63, },
+ { 2, 0, 0, 2, 12, 32, },
+ { 1, 0, 0, 2, 12, 32, },
+ { 0, 0, 0, 2, 13, 63, },
+ { 2, 0, 0, 2, 13, 32, },
+ { 1, 0, 0, 2, 13, 32, },
+ { 0, 0, 0, 2, 14, 63, },
+ { 2, 0, 0, 2, 14, 63, },
+ { 1, 0, 0, 2, 14, 63, },
+ { 0, 0, 0, 3, 1, 32, },
+ { 2, 0, 0, 3, 1, 30, },
+ { 1, 0, 0, 3, 1, 30, },
+ { 0, 0, 0, 3, 2, 44, },
+ { 2, 0, 0, 3, 2, 30, },
+ { 1, 0, 0, 3, 2, 30, },
+ { 0, 0, 0, 3, 3, 44, },
+ { 2, 0, 0, 3, 3, 30, },
+ { 1, 0, 0, 3, 3, 30, },
+ { 0, 0, 0, 3, 4, 44, },
+ { 2, 0, 0, 3, 4, 30, },
+ { 1, 0, 0, 3, 4, 30, },
+ { 0, 0, 0, 3, 5, 44, },
+ { 2, 0, 0, 3, 5, 30, },
+ { 1, 0, 0, 3, 5, 30, },
+ { 0, 0, 0, 3, 6, 44, },
+ { 2, 0, 0, 3, 6, 30, },
+ { 1, 0, 0, 3, 6, 30, },
+ { 0, 0, 0, 3, 7, 44, },
+ { 2, 0, 0, 3, 7, 30, },
+ { 1, 0, 0, 3, 7, 30, },
+ { 0, 0, 0, 3, 8, 44, },
+ { 2, 0, 0, 3, 8, 30, },
+ { 1, 0, 0, 3, 8, 30, },
+ { 0, 0, 0, 3, 9, 44, },
+ { 2, 0, 0, 3, 9, 30, },
+ { 1, 0, 0, 3, 9, 30, },
+ { 0, 0, 0, 3, 10, 44, },
+ { 2, 0, 0, 3, 10, 30, },
+ { 1, 0, 0, 3, 10, 30, },
+ { 0, 0, 0, 3, 11, 32, },
+ { 2, 0, 0, 3, 11, 30, },
+ { 1, 0, 0, 3, 11, 30, },
+ { 0, 0, 0, 3, 12, 63, },
+ { 2, 0, 0, 3, 12, 30, },
+ { 1, 0, 0, 3, 12, 30, },
+ { 0, 0, 0, 3, 13, 63, },
+ { 2, 0, 0, 3, 13, 30, },
+ { 1, 0, 0, 3, 13, 30, },
+ { 0, 0, 0, 3, 14, 63, },
+ { 2, 0, 0, 3, 14, 63, },
+ { 1, 0, 0, 3, 14, 63, },
+ { 0, 0, 0, 6, 1, 30, },
+ { 2, 0, 0, 6, 1, 28, },
+ { 1, 0, 0, 6, 1, 28, },
+ { 0, 0, 0, 6, 2, 42, },
+ { 2, 0, 0, 6, 2, 28, },
+ { 1, 0, 0, 6, 2, 28, },
+ { 0, 0, 0, 6, 3, 42, },
+ { 2, 0, 0, 6, 3, 28, },
+ { 1, 0, 0, 6, 3, 28, },
+ { 0, 0, 0, 6, 4, 42, },
+ { 2, 0, 0, 6, 4, 28, },
+ { 1, 0, 0, 6, 4, 28, },
+ { 0, 0, 0, 6, 5, 42, },
+ { 2, 0, 0, 6, 5, 28, },
+ { 1, 0, 0, 6, 5, 28, },
+ { 0, 0, 0, 6, 6, 42, },
+ { 2, 0, 0, 6, 6, 28, },
+ { 1, 0, 0, 6, 6, 28, },
+ { 0, 0, 0, 6, 7, 42, },
+ { 2, 0, 0, 6, 7, 28, },
+ { 1, 0, 0, 6, 7, 28, },
+ { 0, 0, 0, 6, 8, 42, },
+ { 2, 0, 0, 6, 8, 28, },
+ { 1, 0, 0, 6, 8, 28, },
+ { 0, 0, 0, 6, 9, 42, },
+ { 2, 0, 0, 6, 9, 28, },
+ { 1, 0, 0, 6, 9, 28, },
+ { 0, 0, 0, 6, 10, 42, },
+ { 2, 0, 0, 6, 10, 28, },
+ { 1, 0, 0, 6, 10, 28, },
+ { 0, 0, 0, 6, 11, 30, },
+ { 2, 0, 0, 6, 11, 28, },
+ { 1, 0, 0, 6, 11, 28, },
+ { 0, 0, 0, 6, 12, 63, },
+ { 2, 0, 0, 6, 12, 28, },
+ { 1, 0, 0, 6, 12, 28, },
+ { 0, 0, 0, 6, 13, 63, },
+ { 2, 0, 0, 6, 13, 28, },
+ { 1, 0, 0, 6, 13, 28, },
+ { 0, 0, 0, 6, 14, 63, },
+ { 2, 0, 0, 6, 14, 63, },
+ { 1, 0, 0, 6, 14, 63, },
+ { 0, 0, 0, 7, 1, 28, },
+ { 2, 0, 0, 7, 1, 26, },
+ { 1, 0, 0, 7, 1, 26, },
+ { 0, 0, 0, 7, 2, 40, },
+ { 2, 0, 0, 7, 2, 26, },
+ { 1, 0, 0, 7, 2, 26, },
+ { 0, 0, 0, 7, 3, 40, },
+ { 2, 0, 0, 7, 3, 26, },
+ { 1, 0, 0, 7, 3, 26, },
+ { 0, 0, 0, 7, 4, 40, },
+ { 2, 0, 0, 7, 4, 26, },
+ { 1, 0, 0, 7, 4, 26, },
+ { 0, 0, 0, 7, 5, 40, },
+ { 2, 0, 0, 7, 5, 26, },
+ { 1, 0, 0, 7, 5, 26, },
+ { 0, 0, 0, 7, 6, 40, },
+ { 2, 0, 0, 7, 6, 26, },
+ { 1, 0, 0, 7, 6, 26, },
+ { 0, 0, 0, 7, 7, 40, },
+ { 2, 0, 0, 7, 7, 26, },
+ { 1, 0, 0, 7, 7, 26, },
+ { 0, 0, 0, 7, 8, 40, },
+ { 2, 0, 0, 7, 8, 26, },
+ { 1, 0, 0, 7, 8, 26, },
+ { 0, 0, 0, 7, 9, 40, },
+ { 2, 0, 0, 7, 9, 26, },
+ { 1, 0, 0, 7, 9, 26, },
+ { 0, 0, 0, 7, 10, 40, },
+ { 2, 0, 0, 7, 10, 26, },
+ { 1, 0, 0, 7, 10, 26, },
+ { 0, 0, 0, 7, 11, 28, },
+ { 2, 0, 0, 7, 11, 26, },
+ { 1, 0, 0, 7, 11, 26, },
+ { 0, 0, 0, 7, 12, 63, },
+ { 2, 0, 0, 7, 12, 26, },
+ { 1, 0, 0, 7, 12, 26, },
+ { 0, 0, 0, 7, 13, 63, },
+ { 2, 0, 0, 7, 13, 26, },
+ { 1, 0, 0, 7, 13, 26, },
+ { 0, 0, 0, 7, 14, 63, },
+ { 2, 0, 0, 7, 14, 63, },
+ { 1, 0, 0, 7, 14, 63, },
+ { 0, 0, 1, 2, 1, 63, },
+ { 2, 0, 1, 2, 1, 63, },
+ { 1, 0, 1, 2, 1, 63, },
+ { 0, 0, 1, 2, 2, 63, },
+ { 2, 0, 1, 2, 2, 63, },
+ { 1, 0, 1, 2, 2, 63, },
+ { 0, 0, 1, 2, 3, 36, },
+ { 2, 0, 1, 2, 3, 32, },
+ { 1, 0, 1, 2, 3, 32, },
+ { 0, 0, 1, 2, 4, 40, },
+ { 2, 0, 1, 2, 4, 32, },
+ { 1, 0, 1, 2, 4, 32, },
+ { 0, 0, 1, 2, 5, 40, },
+ { 2, 0, 1, 2, 5, 32, },
+ { 1, 0, 1, 2, 5, 32, },
+ { 0, 0, 1, 2, 6, 40, },
+ { 2, 0, 1, 2, 6, 32, },
+ { 1, 0, 1, 2, 6, 32, },
+ { 0, 0, 1, 2, 7, 40, },
+ { 2, 0, 1, 2, 7, 32, },
+ { 1, 0, 1, 2, 7, 32, },
+ { 0, 0, 1, 2, 8, 40, },
+ { 2, 0, 1, 2, 8, 32, },
+ { 1, 0, 1, 2, 8, 32, },
+ { 0, 0, 1, 2, 9, 40, },
+ { 2, 0, 1, 2, 9, 32, },
+ { 1, 0, 1, 2, 9, 32, },
+ { 0, 0, 1, 2, 10, 40, },
+ { 2, 0, 1, 2, 10, 32, },
+ { 1, 0, 1, 2, 10, 32, },
+ { 0, 0, 1, 2, 11, 34, },
+ { 2, 0, 1, 2, 11, 32, },
+ { 1, 0, 1, 2, 11, 32, },
+ { 0, 0, 1, 2, 12, 63, },
+ { 2, 0, 1, 2, 12, 32, },
+ { 1, 0, 1, 2, 12, 32, },
+ { 0, 0, 1, 2, 13, 63, },
+ { 2, 0, 1, 2, 13, 32, },
+ { 1, 0, 1, 2, 13, 32, },
+ { 0, 0, 1, 2, 14, 63, },
+ { 2, 0, 1, 2, 14, 63, },
+ { 1, 0, 1, 2, 14, 63, },
+ { 0, 0, 1, 3, 1, 63, },
+ { 2, 0, 1, 3, 1, 63, },
+ { 1, 0, 1, 3, 1, 63, },
+ { 0, 0, 1, 3, 2, 63, },
+ { 2, 0, 1, 3, 2, 63, },
+ { 1, 0, 1, 3, 2, 63, },
+ { 0, 0, 1, 3, 3, 34, },
+ { 2, 0, 1, 3, 3, 30, },
+ { 1, 0, 1, 3, 3, 30, },
+ { 0, 0, 1, 3, 4, 38, },
+ { 2, 0, 1, 3, 4, 30, },
+ { 1, 0, 1, 3, 4, 30, },
+ { 0, 0, 1, 3, 5, 38, },
+ { 2, 0, 1, 3, 5, 30, },
+ { 1, 0, 1, 3, 5, 30, },
+ { 0, 0, 1, 3, 6, 38, },
+ { 2, 0, 1, 3, 6, 30, },
+ { 1, 0, 1, 3, 6, 30, },
+ { 0, 0, 1, 3, 7, 38, },
+ { 2, 0, 1, 3, 7, 30, },
+ { 1, 0, 1, 3, 7, 30, },
+ { 0, 0, 1, 3, 8, 38, },
+ { 2, 0, 1, 3, 8, 30, },
+ { 1, 0, 1, 3, 8, 30, },
+ { 0, 0, 1, 3, 9, 38, },
+ { 2, 0, 1, 3, 9, 30, },
+ { 1, 0, 1, 3, 9, 30, },
+ { 0, 0, 1, 3, 10, 38, },
+ { 2, 0, 1, 3, 10, 30, },
+ { 1, 0, 1, 3, 10, 30, },
+ { 0, 0, 1, 3, 11, 32, },
+ { 2, 0, 1, 3, 11, 30, },
+ { 1, 0, 1, 3, 11, 30, },
+ { 0, 0, 1, 3, 12, 63, },
+ { 2, 0, 1, 3, 12, 30, },
+ { 1, 0, 1, 3, 12, 30, },
+ { 0, 0, 1, 3, 13, 63, },
+ { 2, 0, 1, 3, 13, 30, },
+ { 1, 0, 1, 3, 13, 30, },
+ { 0, 0, 1, 3, 14, 63, },
+ { 2, 0, 1, 3, 14, 63, },
+ { 1, 0, 1, 3, 14, 63, },
+ { 0, 0, 1, 6, 1, 63, },
+ { 2, 0, 1, 6, 1, 63, },
+ { 1, 0, 1, 6, 1, 63, },
+ { 0, 0, 1, 6, 2, 63, },
+ { 2, 0, 1, 6, 2, 63, },
+ { 1, 0, 1, 6, 2, 63, },
+ { 0, 0, 1, 6, 3, 32, },
+ { 2, 0, 1, 6, 3, 28, },
+ { 1, 0, 1, 6, 3, 28, },
+ { 0, 0, 1, 6, 4, 36, },
+ { 2, 0, 1, 6, 4, 28, },
+ { 1, 0, 1, 6, 4, 28, },
+ { 0, 0, 1, 6, 5, 36, },
+ { 2, 0, 1, 6, 5, 28, },
+ { 1, 0, 1, 6, 5, 28, },
+ { 0, 0, 1, 6, 6, 36, },
+ { 2, 0, 1, 6, 6, 28, },
+ { 1, 0, 1, 6, 6, 28, },
+ { 0, 0, 1, 6, 7, 36, },
+ { 2, 0, 1, 6, 7, 28, },
+ { 1, 0, 1, 6, 7, 28, },
+ { 0, 0, 1, 6, 8, 36, },
+ { 2, 0, 1, 6, 8, 28, },
+ { 1, 0, 1, 6, 8, 28, },
+ { 0, 0, 1, 6, 9, 36, },
+ { 2, 0, 1, 6, 9, 28, },
+ { 1, 0, 1, 6, 9, 28, },
+ { 0, 0, 1, 6, 10, 36, },
+ { 2, 0, 1, 6, 10, 28, },
+ { 1, 0, 1, 6, 10, 28, },
+ { 0, 0, 1, 6, 11, 30, },
+ { 2, 0, 1, 6, 11, 28, },
+ { 1, 0, 1, 6, 11, 28, },
+ { 0, 0, 1, 6, 12, 63, },
+ { 2, 0, 1, 6, 12, 28, },
+ { 1, 0, 1, 6, 12, 28, },
+ { 0, 0, 1, 6, 13, 63, },
+ { 2, 0, 1, 6, 13, 28, },
+ { 1, 0, 1, 6, 13, 28, },
+ { 0, 0, 1, 6, 14, 63, },
+ { 2, 0, 1, 6, 14, 63, },
+ { 1, 0, 1, 6, 14, 63, },
+ { 0, 0, 1, 7, 1, 63, },
+ { 2, 0, 1, 7, 1, 63, },
+ { 1, 0, 1, 7, 1, 63, },
+ { 0, 0, 1, 7, 2, 63, },
+ { 2, 0, 1, 7, 2, 63, },
+ { 1, 0, 1, 7, 2, 63, },
+ { 0, 0, 1, 7, 3, 32, },
+ { 2, 0, 1, 7, 3, 26, },
+ { 1, 0, 1, 7, 3, 26, },
+ { 0, 0, 1, 7, 4, 36, },
+ { 2, 0, 1, 7, 4, 26, },
+ { 1, 0, 1, 7, 4, 26, },
+ { 0, 0, 1, 7, 5, 36, },
+ { 2, 0, 1, 7, 5, 26, },
+ { 1, 0, 1, 7, 5, 26, },
+ { 0, 0, 1, 7, 6, 36, },
+ { 2, 0, 1, 7, 6, 26, },
+ { 1, 0, 1, 7, 6, 26, },
+ { 0, 0, 1, 7, 7, 36, },
+ { 2, 0, 1, 7, 7, 26, },
+ { 1, 0, 1, 7, 7, 26, },
+ { 0, 0, 1, 7, 8, 36, },
+ { 2, 0, 1, 7, 8, 26, },
+ { 1, 0, 1, 7, 8, 26, },
+ { 0, 0, 1, 7, 9, 36, },
+ { 2, 0, 1, 7, 9, 26, },
+ { 1, 0, 1, 7, 9, 26, },
+ { 0, 0, 1, 7, 10, 36, },
+ { 2, 0, 1, 7, 10, 26, },
+ { 1, 0, 1, 7, 10, 26, },
+ { 0, 0, 1, 7, 11, 30, },
+ { 2, 0, 1, 7, 11, 26, },
+ { 1, 0, 1, 7, 11, 26, },
+ { 0, 0, 1, 7, 12, 63, },
+ { 2, 0, 1, 7, 12, 26, },
+ { 1, 0, 1, 7, 12, 26, },
+ { 0, 0, 1, 7, 13, 63, },
+ { 2, 0, 1, 7, 13, 26, },
+ { 1, 0, 1, 7, 13, 26, },
+ { 0, 0, 1, 7, 14, 63, },
+ { 2, 0, 1, 7, 14, 63, },
+ { 1, 0, 1, 7, 14, 63, },
+ { 0, 1, 0, 1, 36, 38, },
+ { 2, 1, 0, 1, 36, 32, },
+ { 1, 1, 0, 1, 36, 32, },
+ { 0, 1, 0, 1, 40, 38, },
+ { 2, 1, 0, 1, 40, 32, },
+ { 1, 1, 0, 1, 40, 32, },
+ { 0, 1, 0, 1, 44, 38, },
+ { 2, 1, 0, 1, 44, 32, },
+ { 1, 1, 0, 1, 44, 32, },
+ { 0, 1, 0, 1, 48, 38, },
+ { 2, 1, 0, 1, 48, 32, },
+ { 1, 1, 0, 1, 48, 32, },
+ { 0, 1, 0, 1, 52, 38, },
+ { 2, 1, 0, 1, 52, 32, },
+ { 1, 1, 0, 1, 52, 32, },
+ { 0, 1, 0, 1, 56, 38, },
+ { 2, 1, 0, 1, 56, 32, },
+ { 1, 1, 0, 1, 56, 32, },
+ { 0, 1, 0, 1, 60, 38, },
+ { 2, 1, 0, 1, 60, 32, },
+ { 1, 1, 0, 1, 60, 32, },
+ { 0, 1, 0, 1, 64, 38, },
+ { 2, 1, 0, 1, 64, 32, },
+ { 1, 1, 0, 1, 64, 32, },
+ { 0, 1, 0, 1, 100, 36, },
+ { 2, 1, 0, 1, 100, 32, },
+ { 1, 1, 0, 1, 100, 32, },
+ { 0, 1, 0, 1, 104, 36, },
+ { 2, 1, 0, 1, 104, 32, },
+ { 1, 1, 0, 1, 104, 32, },
+ { 0, 1, 0, 1, 108, 36, },
+ { 2, 1, 0, 1, 108, 32, },
+ { 1, 1, 0, 1, 108, 32, },
+ { 0, 1, 0, 1, 112, 36, },
+ { 2, 1, 0, 1, 112, 32, },
+ { 1, 1, 0, 1, 112, 32, },
+ { 0, 1, 0, 1, 116, 36, },
+ { 2, 1, 0, 1, 116, 32, },
+ { 1, 1, 0, 1, 116, 32, },
+ { 0, 1, 0, 1, 120, 36, },
+ { 2, 1, 0, 1, 120, 32, },
+ { 1, 1, 0, 1, 120, 32, },
+ { 0, 1, 0, 1, 124, 36, },
+ { 2, 1, 0, 1, 124, 32, },
+ { 1, 1, 0, 1, 124, 32, },
+ { 0, 1, 0, 1, 128, 36, },
+ { 2, 1, 0, 1, 128, 32, },
+ { 1, 1, 0, 1, 128, 32, },
+ { 0, 1, 0, 1, 132, 36, },
+ { 2, 1, 0, 1, 132, 32, },
+ { 1, 1, 0, 1, 132, 32, },
+ { 0, 1, 0, 1, 136, 36, },
+ { 2, 1, 0, 1, 136, 32, },
+ { 1, 1, 0, 1, 136, 32, },
+ { 0, 1, 0, 1, 140, 36, },
+ { 2, 1, 0, 1, 140, 32, },
+ { 1, 1, 0, 1, 140, 32, },
+ { 0, 1, 0, 1, 149, 36, },
+ { 2, 1, 0, 1, 149, 32, },
+ { 1, 1, 0, 1, 149, 63, },
+ { 0, 1, 0, 1, 153, 36, },
+ { 2, 1, 0, 1, 153, 32, },
+ { 1, 1, 0, 1, 153, 63, },
+ { 0, 1, 0, 1, 157, 36, },
+ { 2, 1, 0, 1, 157, 32, },
+ { 1, 1, 0, 1, 157, 63, },
+ { 0, 1, 0, 1, 161, 36, },
+ { 2, 1, 0, 1, 161, 32, },
+ { 1, 1, 0, 1, 161, 63, },
+ { 0, 1, 0, 1, 165, 36, },
+ { 2, 1, 0, 1, 165, 32, },
+ { 1, 1, 0, 1, 165, 63, },
+ { 0, 1, 0, 2, 36, 36, },
+ { 2, 1, 0, 2, 36, 32, },
+ { 1, 1, 0, 2, 36, 32, },
+ { 0, 1, 0, 2, 40, 36, },
+ { 2, 1, 0, 2, 40, 32, },
+ { 1, 1, 0, 2, 40, 32, },
+ { 0, 1, 0, 2, 44, 36, },
+ { 2, 1, 0, 2, 44, 32, },
+ { 1, 1, 0, 2, 44, 32, },
+ { 0, 1, 0, 2, 48, 36, },
+ { 2, 1, 0, 2, 48, 32, },
+ { 1, 1, 0, 2, 48, 32, },
+ { 0, 1, 0, 2, 52, 36, },
+ { 2, 1, 0, 2, 52, 32, },
+ { 1, 1, 0, 2, 52, 32, },
+ { 0, 1, 0, 2, 56, 36, },
+ { 2, 1, 0, 2, 56, 32, },
+ { 1, 1, 0, 2, 56, 32, },
+ { 0, 1, 0, 2, 60, 36, },
+ { 2, 1, 0, 2, 60, 32, },
+ { 1, 1, 0, 2, 60, 32, },
+ { 0, 1, 0, 2, 64, 36, },
+ { 2, 1, 0, 2, 64, 32, },
+ { 1, 1, 0, 2, 64, 32, },
+ { 0, 1, 0, 2, 100, 36, },
+ { 2, 1, 0, 2, 100, 32, },
+ { 1, 1, 0, 2, 100, 32, },
+ { 0, 1, 0, 2, 104, 36, },
+ { 2, 1, 0, 2, 104, 32, },
+ { 1, 1, 0, 2, 104, 32, },
+ { 0, 1, 0, 2, 108, 36, },
+ { 2, 1, 0, 2, 108, 32, },
+ { 1, 1, 0, 2, 108, 32, },
+ { 0, 1, 0, 2, 112, 36, },
+ { 2, 1, 0, 2, 112, 32, },
+ { 1, 1, 0, 2, 112, 32, },
+ { 0, 1, 0, 2, 116, 36, },
+ { 2, 1, 0, 2, 116, 32, },
+ { 1, 1, 0, 2, 116, 32, },
+ { 0, 1, 0, 2, 120, 36, },
+ { 2, 1, 0, 2, 120, 32, },
+ { 1, 1, 0, 2, 120, 32, },
+ { 0, 1, 0, 2, 124, 36, },
+ { 2, 1, 0, 2, 124, 32, },
+ { 1, 1, 0, 2, 124, 32, },
+ { 0, 1, 0, 2, 128, 36, },
+ { 2, 1, 0, 2, 128, 32, },
+ { 1, 1, 0, 2, 128, 32, },
+ { 0, 1, 0, 2, 132, 36, },
+ { 2, 1, 0, 2, 132, 32, },
+ { 1, 1, 0, 2, 132, 32, },
+ { 0, 1, 0, 2, 136, 36, },
+ { 2, 1, 0, 2, 136, 32, },
+ { 1, 1, 0, 2, 136, 32, },
+ { 0, 1, 0, 2, 140, 34, },
+ { 2, 1, 0, 2, 140, 32, },
+ { 1, 1, 0, 2, 140, 32, },
+ { 0, 1, 0, 2, 149, 32, },
+ { 2, 1, 0, 2, 149, 32, },
+ { 1, 1, 0, 2, 149, 63, },
+ { 0, 1, 0, 2, 153, 38, },
+ { 2, 1, 0, 2, 153, 32, },
+ { 1, 1, 0, 2, 153, 63, },
+ { 0, 1, 0, 2, 157, 38, },
+ { 2, 1, 0, 2, 157, 32, },
+ { 1, 1, 0, 2, 157, 63, },
+ { 0, 1, 0, 2, 161, 38, },
+ { 2, 1, 0, 2, 161, 32, },
+ { 1, 1, 0, 2, 161, 63, },
+ { 0, 1, 0, 2, 165, 38, },
+ { 2, 1, 0, 2, 165, 32, },
+ { 1, 1, 0, 2, 165, 63, },
+ { 0, 1, 0, 3, 36, 34, },
+ { 2, 1, 0, 3, 36, 30, },
+ { 1, 1, 0, 3, 36, 30, },
+ { 0, 1, 0, 3, 40, 34, },
+ { 2, 1, 0, 3, 40, 30, },
+ { 1, 1, 0, 3, 40, 30, },
+ { 0, 1, 0, 3, 44, 34, },
+ { 2, 1, 0, 3, 44, 30, },
+ { 1, 1, 0, 3, 44, 30, },
+ { 0, 1, 0, 3, 48, 34, },
+ { 2, 1, 0, 3, 48, 30, },
+ { 1, 1, 0, 3, 48, 30, },
+ { 0, 1, 0, 3, 52, 34, },
+ { 2, 1, 0, 3, 52, 30, },
+ { 1, 1, 0, 3, 52, 30, },
+ { 0, 1, 0, 3, 56, 34, },
+ { 2, 1, 0, 3, 56, 30, },
+ { 1, 1, 0, 3, 56, 30, },
+ { 0, 1, 0, 3, 60, 34, },
+ { 2, 1, 0, 3, 60, 30, },
+ { 1, 1, 0, 3, 60, 30, },
+ { 0, 1, 0, 3, 64, 34, },
+ { 2, 1, 0, 3, 64, 30, },
+ { 1, 1, 0, 3, 64, 30, },
+ { 0, 1, 0, 3, 100, 34, },
+ { 2, 1, 0, 3, 100, 30, },
+ { 1, 1, 0, 3, 100, 30, },
+ { 0, 1, 0, 3, 104, 34, },
+ { 2, 1, 0, 3, 104, 30, },
+ { 1, 1, 0, 3, 104, 30, },
+ { 0, 1, 0, 3, 108, 34, },
+ { 2, 1, 0, 3, 108, 30, },
+ { 1, 1, 0, 3, 108, 30, },
+ { 0, 1, 0, 3, 112, 34, },
+ { 2, 1, 0, 3, 112, 30, },
+ { 1, 1, 0, 3, 112, 30, },
+ { 0, 1, 0, 3, 116, 34, },
+ { 2, 1, 0, 3, 116, 30, },
+ { 1, 1, 0, 3, 116, 30, },
+ { 0, 1, 0, 3, 120, 34, },
+ { 2, 1, 0, 3, 120, 30, },
+ { 1, 1, 0, 3, 120, 30, },
+ { 0, 1, 0, 3, 124, 34, },
+ { 2, 1, 0, 3, 124, 30, },
+ { 1, 1, 0, 3, 124, 30, },
+ { 0, 1, 0, 3, 128, 34, },
+ { 2, 1, 0, 3, 128, 30, },
+ { 1, 1, 0, 3, 128, 30, },
+ { 0, 1, 0, 3, 132, 34, },
+ { 2, 1, 0, 3, 132, 30, },
+ { 1, 1, 0, 3, 132, 30, },
+ { 0, 1, 0, 3, 136, 34, },
+ { 2, 1, 0, 3, 136, 30, },
+ { 1, 1, 0, 3, 136, 30, },
+ { 0, 1, 0, 3, 140, 32, },
+ { 2, 1, 0, 3, 140, 30, },
+ { 1, 1, 0, 3, 140, 30, },
+ { 0, 1, 0, 3, 149, 30, },
+ { 2, 1, 0, 3, 149, 30, },
+ { 1, 1, 0, 3, 149, 63, },
+ { 0, 1, 0, 3, 153, 36, },
+ { 2, 1, 0, 3, 153, 30, },
+ { 1, 1, 0, 3, 153, 63, },
+ { 0, 1, 0, 3, 157, 36, },
+ { 2, 1, 0, 3, 157, 30, },
+ { 1, 1, 0, 3, 157, 63, },
+ { 0, 1, 0, 3, 161, 36, },
+ { 2, 1, 0, 3, 161, 30, },
+ { 1, 1, 0, 3, 161, 63, },
+ { 0, 1, 0, 3, 165, 36, },
+ { 2, 1, 0, 3, 165, 30, },
+ { 1, 1, 0, 3, 165, 63, },
+ { 0, 1, 0, 6, 36, 32, },
+ { 2, 1, 0, 6, 36, 28, },
+ { 1, 1, 0, 6, 36, 28, },
+ { 0, 1, 0, 6, 40, 32, },
+ { 2, 1, 0, 6, 40, 28, },
+ { 1, 1, 0, 6, 40, 28, },
+ { 0, 1, 0, 6, 44, 32, },
+ { 2, 1, 0, 6, 44, 28, },
+ { 1, 1, 0, 6, 44, 28, },
+ { 0, 1, 0, 6, 48, 32, },
+ { 2, 1, 0, 6, 48, 28, },
+ { 1, 1, 0, 6, 48, 28, },
+ { 0, 1, 0, 6, 52, 32, },
+ { 2, 1, 0, 6, 52, 28, },
+ { 1, 1, 0, 6, 52, 28, },
+ { 0, 1, 0, 6, 56, 32, },
+ { 2, 1, 0, 6, 56, 28, },
+ { 1, 1, 0, 6, 56, 28, },
+ { 0, 1, 0, 6, 60, 32, },
+ { 2, 1, 0, 6, 60, 28, },
+ { 1, 1, 0, 6, 60, 28, },
+ { 0, 1, 0, 6, 64, 32, },
+ { 2, 1, 0, 6, 64, 28, },
+ { 1, 1, 0, 6, 64, 28, },
+ { 0, 1, 0, 6, 100, 32, },
+ { 2, 1, 0, 6, 100, 28, },
+ { 1, 1, 0, 6, 100, 28, },
+ { 0, 1, 0, 6, 104, 32, },
+ { 2, 1, 0, 6, 104, 28, },
+ { 1, 1, 0, 6, 104, 28, },
+ { 0, 1, 0, 6, 108, 32, },
+ { 2, 1, 0, 6, 108, 28, },
+ { 1, 1, 0, 6, 108, 28, },
+ { 0, 1, 0, 6, 112, 32, },
+ { 2, 1, 0, 6, 112, 28, },
+ { 1, 1, 0, 6, 112, 28, },
+ { 0, 1, 0, 6, 116, 32, },
+ { 2, 1, 0, 6, 116, 28, },
+ { 1, 1, 0, 6, 116, 28, },
+ { 0, 1, 0, 6, 120, 32, },
+ { 2, 1, 0, 6, 120, 28, },
+ { 1, 1, 0, 6, 120, 28, },
+ { 0, 1, 0, 6, 124, 32, },
+ { 2, 1, 0, 6, 124, 28, },
+ { 1, 1, 0, 6, 124, 28, },
+ { 0, 1, 0, 6, 128, 32, },
+ { 2, 1, 0, 6, 128, 28, },
+ { 1, 1, 0, 6, 128, 28, },
+ { 0, 1, 0, 6, 132, 32, },
+ { 2, 1, 0, 6, 132, 28, },
+ { 1, 1, 0, 6, 132, 28, },
+ { 0, 1, 0, 6, 136, 32, },
+ { 2, 1, 0, 6, 136, 28, },
+ { 1, 1, 0, 6, 136, 28, },
+ { 0, 1, 0, 6, 140, 30, },
+ { 2, 1, 0, 6, 140, 28, },
+ { 1, 1, 0, 6, 140, 28, },
+ { 0, 1, 0, 6, 149, 28, },
+ { 2, 1, 0, 6, 149, 28, },
+ { 1, 1, 0, 6, 149, 63, },
+ { 0, 1, 0, 6, 153, 34, },
+ { 2, 1, 0, 6, 153, 28, },
+ { 1, 1, 0, 6, 153, 63, },
+ { 0, 1, 0, 6, 157, 34, },
+ { 2, 1, 0, 6, 157, 28, },
+ { 1, 1, 0, 6, 157, 63, },
+ { 0, 1, 0, 6, 161, 34, },
+ { 2, 1, 0, 6, 161, 28, },
+ { 1, 1, 0, 6, 161, 63, },
+ { 0, 1, 0, 6, 165, 34, },
+ { 2, 1, 0, 6, 165, 28, },
+ { 1, 1, 0, 6, 165, 63, },
+ { 0, 1, 0, 7, 36, 30, },
+ { 2, 1, 0, 7, 36, 26, },
+ { 1, 1, 0, 7, 36, 26, },
+ { 0, 1, 0, 7, 40, 30, },
+ { 2, 1, 0, 7, 40, 26, },
+ { 1, 1, 0, 7, 40, 26, },
+ { 0, 1, 0, 7, 44, 30, },
+ { 2, 1, 0, 7, 44, 26, },
+ { 1, 1, 0, 7, 44, 26, },
+ { 0, 1, 0, 7, 48, 30, },
+ { 2, 1, 0, 7, 48, 26, },
+ { 1, 1, 0, 7, 48, 26, },
+ { 0, 1, 0, 7, 52, 30, },
+ { 2, 1, 0, 7, 52, 26, },
+ { 1, 1, 0, 7, 52, 26, },
+ { 0, 1, 0, 7, 56, 30, },
+ { 2, 1, 0, 7, 56, 26, },
+ { 1, 1, 0, 7, 56, 26, },
+ { 0, 1, 0, 7, 60, 30, },
+ { 2, 1, 0, 7, 60, 26, },
+ { 1, 1, 0, 7, 60, 26, },
+ { 0, 1, 0, 7, 64, 30, },
+ { 2, 1, 0, 7, 64, 26, },
+ { 1, 1, 0, 7, 64, 26, },
+ { 0, 1, 0, 7, 100, 30, },
+ { 2, 1, 0, 7, 100, 26, },
+ { 1, 1, 0, 7, 100, 26, },
+ { 0, 1, 0, 7, 104, 30, },
+ { 2, 1, 0, 7, 104, 26, },
+ { 1, 1, 0, 7, 104, 26, },
+ { 0, 1, 0, 7, 108, 30, },
+ { 2, 1, 0, 7, 108, 26, },
+ { 1, 1, 0, 7, 108, 26, },
+ { 0, 1, 0, 7, 112, 30, },
+ { 2, 1, 0, 7, 112, 26, },
+ { 1, 1, 0, 7, 112, 26, },
+ { 0, 1, 0, 7, 116, 30, },
+ { 2, 1, 0, 7, 116, 26, },
+ { 1, 1, 0, 7, 116, 26, },
+ { 0, 1, 0, 7, 120, 30, },
+ { 2, 1, 0, 7, 120, 26, },
+ { 1, 1, 0, 7, 120, 26, },
+ { 0, 1, 0, 7, 124, 30, },
+ { 2, 1, 0, 7, 124, 26, },
+ { 1, 1, 0, 7, 124, 26, },
+ { 0, 1, 0, 7, 128, 30, },
+ { 2, 1, 0, 7, 128, 26, },
+ { 1, 1, 0, 7, 128, 26, },
+ { 0, 1, 0, 7, 132, 30, },
+ { 2, 1, 0, 7, 132, 26, },
+ { 1, 1, 0, 7, 132, 26, },
+ { 0, 1, 0, 7, 136, 30, },
+ { 2, 1, 0, 7, 136, 26, },
+ { 1, 1, 0, 7, 136, 26, },
+ { 0, 1, 0, 7, 140, 28, },
+ { 2, 1, 0, 7, 140, 26, },
+ { 1, 1, 0, 7, 140, 26, },
+ { 0, 1, 0, 7, 149, 26, },
+ { 2, 1, 0, 7, 149, 26, },
+ { 1, 1, 0, 7, 149, 63, },
+ { 0, 1, 0, 7, 153, 32, },
+ { 2, 1, 0, 7, 153, 26, },
+ { 1, 1, 0, 7, 153, 63, },
+ { 0, 1, 0, 7, 157, 32, },
+ { 2, 1, 0, 7, 157, 26, },
+ { 1, 1, 0, 7, 157, 63, },
+ { 0, 1, 0, 7, 161, 32, },
+ { 2, 1, 0, 7, 161, 26, },
+ { 1, 1, 0, 7, 161, 63, },
+ { 0, 1, 0, 7, 165, 32, },
+ { 2, 1, 0, 7, 165, 26, },
+ { 1, 1, 0, 7, 165, 63, },
+ { 0, 1, 1, 2, 38, 32, },
+ { 2, 1, 1, 2, 38, 32, },
+ { 1, 1, 1, 2, 38, 32, },
+ { 0, 1, 1, 2, 46, 32, },
+ { 2, 1, 1, 2, 46, 32, },
+ { 1, 1, 1, 2, 46, 32, },
+ { 0, 1, 1, 2, 54, 32, },
+ { 2, 1, 1, 2, 54, 32, },
+ { 1, 1, 1, 2, 54, 32, },
+ { 0, 1, 1, 2, 62, 30, },
+ { 2, 1, 1, 2, 62, 32, },
+ { 1, 1, 1, 2, 62, 32, },
+ { 0, 1, 1, 2, 102, 30, },
+ { 2, 1, 1, 2, 102, 32, },
+ { 1, 1, 1, 2, 102, 32, },
+ { 0, 1, 1, 2, 110, 38, },
+ { 2, 1, 1, 2, 110, 32, },
+ { 1, 1, 1, 2, 110, 32, },
+ { 0, 1, 1, 2, 118, 38, },
+ { 2, 1, 1, 2, 118, 32, },
+ { 1, 1, 1, 2, 118, 32, },
+ { 0, 1, 1, 2, 126, 38, },
+ { 2, 1, 1, 2, 126, 32, },
+ { 1, 1, 1, 2, 126, 32, },
+ { 0, 1, 1, 2, 134, 38, },
+ { 2, 1, 1, 2, 134, 32, },
+ { 1, 1, 1, 2, 134, 32, },
+ { 0, 1, 1, 2, 151, 32, },
+ { 2, 1, 1, 2, 151, 32, },
+ { 1, 1, 1, 2, 151, 63, },
+ { 0, 1, 1, 2, 159, 32, },
+ { 2, 1, 1, 2, 159, 32, },
+ { 1, 1, 1, 2, 159, 63, },
+ { 0, 1, 1, 3, 38, 30, },
+ { 2, 1, 1, 3, 38, 30, },
+ { 1, 1, 1, 3, 38, 30, },
+ { 0, 1, 1, 3, 46, 30, },
+ { 2, 1, 1, 3, 46, 30, },
+ { 1, 1, 1, 3, 46, 30, },
+ { 0, 1, 1, 3, 54, 30, },
+ { 2, 1, 1, 3, 54, 30, },
+ { 1, 1, 1, 3, 54, 30, },
+ { 0, 1, 1, 3, 62, 28, },
+ { 2, 1, 1, 3, 62, 30, },
+ { 1, 1, 1, 3, 62, 30, },
+ { 0, 1, 1, 3, 102, 28, },
+ { 2, 1, 1, 3, 102, 30, },
+ { 1, 1, 1, 3, 102, 30, },
+ { 0, 1, 1, 3, 110, 36, },
+ { 2, 1, 1, 3, 110, 30, },
+ { 1, 1, 1, 3, 110, 30, },
+ { 0, 1, 1, 3, 118, 36, },
+ { 2, 1, 1, 3, 118, 30, },
+ { 1, 1, 1, 3, 118, 30, },
+ { 0, 1, 1, 3, 126, 36, },
+ { 2, 1, 1, 3, 126, 30, },
+ { 1, 1, 1, 3, 126, 30, },
+ { 0, 1, 1, 3, 134, 36, },
+ { 2, 1, 1, 3, 134, 30, },
+ { 1, 1, 1, 3, 134, 30, },
+ { 0, 1, 1, 3, 151, 30, },
+ { 2, 1, 1, 3, 151, 30, },
+ { 1, 1, 1, 3, 151, 63, },
+ { 0, 1, 1, 3, 159, 30, },
+ { 2, 1, 1, 3, 159, 30, },
+ { 1, 1, 1, 3, 159, 63, },
+ { 0, 1, 1, 6, 38, 28, },
+ { 2, 1, 1, 6, 38, 28, },
+ { 1, 1, 1, 6, 38, 28, },
+ { 0, 1, 1, 6, 46, 28, },
+ { 2, 1, 1, 6, 46, 28, },
+ { 1, 1, 1, 6, 46, 28, },
+ { 0, 1, 1, 6, 54, 28, },
+ { 2, 1, 1, 6, 54, 28, },
+ { 1, 1, 1, 6, 54, 28, },
+ { 0, 1, 1, 6, 62, 26, },
+ { 2, 1, 1, 6, 62, 28, },
+ { 1, 1, 1, 6, 62, 28, },
+ { 0, 1, 1, 6, 102, 26, },
+ { 2, 1, 1, 6, 102, 28, },
+ { 1, 1, 1, 6, 102, 28, },
+ { 0, 1, 1, 6, 110, 34, },
+ { 2, 1, 1, 6, 110, 28, },
+ { 1, 1, 1, 6, 110, 28, },
+ { 0, 1, 1, 6, 118, 34, },
+ { 2, 1, 1, 6, 118, 28, },
+ { 1, 1, 1, 6, 118, 28, },
+ { 0, 1, 1, 6, 126, 34, },
+ { 2, 1, 1, 6, 126, 28, },
+ { 1, 1, 1, 6, 126, 28, },
+ { 0, 1, 1, 6, 134, 34, },
+ { 2, 1, 1, 6, 134, 28, },
+ { 1, 1, 1, 6, 134, 28, },
+ { 0, 1, 1, 6, 151, 28, },
+ { 2, 1, 1, 6, 151, 28, },
+ { 1, 1, 1, 6, 151, 63, },
+ { 0, 1, 1, 6, 159, 28, },
+ { 2, 1, 1, 6, 159, 28, },
+ { 1, 1, 1, 6, 159, 63, },
+ { 0, 1, 1, 7, 38, 26, },
+ { 2, 1, 1, 7, 38, 26, },
+ { 1, 1, 1, 7, 38, 26, },
+ { 0, 1, 1, 7, 46, 26, },
+ { 2, 1, 1, 7, 46, 26, },
+ { 1, 1, 1, 7, 46, 26, },
+ { 0, 1, 1, 7, 54, 26, },
+ { 2, 1, 1, 7, 54, 26, },
+ { 1, 1, 1, 7, 54, 26, },
+ { 0, 1, 1, 7, 62, 24, },
+ { 2, 1, 1, 7, 62, 26, },
+ { 1, 1, 1, 7, 62, 26, },
+ { 0, 1, 1, 7, 102, 24, },
+ { 2, 1, 1, 7, 102, 26, },
+ { 1, 1, 1, 7, 102, 26, },
+ { 0, 1, 1, 7, 110, 32, },
+ { 2, 1, 1, 7, 110, 26, },
+ { 1, 1, 1, 7, 110, 26, },
+ { 0, 1, 1, 7, 118, 32, },
+ { 2, 1, 1, 7, 118, 26, },
+ { 1, 1, 1, 7, 118, 26, },
+ { 0, 1, 1, 7, 126, 32, },
+ { 2, 1, 1, 7, 126, 26, },
+ { 1, 1, 1, 7, 126, 26, },
+ { 0, 1, 1, 7, 134, 32, },
+ { 2, 1, 1, 7, 134, 26, },
+ { 1, 1, 1, 7, 134, 26, },
+ { 0, 1, 1, 7, 151, 26, },
+ { 2, 1, 1, 7, 151, 26, },
+ { 1, 1, 1, 7, 151, 63, },
+ { 0, 1, 1, 7, 159, 26, },
+ { 2, 1, 1, 7, 159, 26, },
+ { 1, 1, 1, 7, 159, 63, },
+ { 0, 1, 2, 4, 42, 26, },
+ { 2, 1, 2, 4, 42, 32, },
+ { 1, 1, 2, 4, 42, 32, },
+ { 0, 1, 2, 4, 58, 26, },
+ { 2, 1, 2, 4, 58, 32, },
+ { 1, 1, 2, 4, 58, 32, },
+ { 0, 1, 2, 4, 106, 28, },
+ { 2, 1, 2, 4, 106, 32, },
+ { 1, 1, 2, 4, 106, 32, },
+ { 0, 1, 2, 4, 122, 28, },
+ { 2, 1, 2, 4, 122, 32, },
+ { 1, 1, 2, 4, 122, 32, },
+ { 0, 1, 2, 4, 155, 28, },
+ { 2, 1, 2, 4, 155, 32, },
+ { 1, 1, 2, 4, 155, 63, },
+ { 0, 1, 2, 5, 42, 24, },
+ { 2, 1, 2, 5, 42, 30, },
+ { 1, 1, 2, 5, 42, 30, },
+ { 0, 1, 2, 5, 58, 24, },
+ { 2, 1, 2, 5, 58, 30, },
+ { 1, 1, 2, 5, 58, 30, },
+ { 0, 1, 2, 5, 106, 26, },
+ { 2, 1, 2, 5, 106, 30, },
+ { 1, 1, 2, 5, 106, 30, },
+ { 0, 1, 2, 5, 122, 26, },
+ { 2, 1, 2, 5, 122, 30, },
+ { 1, 1, 2, 5, 122, 30, },
+ { 0, 1, 2, 5, 155, 26, },
+ { 2, 1, 2, 5, 155, 30, },
+ { 1, 1, 2, 5, 155, 63, },
+ { 0, 1, 2, 8, 42, 22, },
+ { 2, 1, 2, 8, 42, 28, },
+ { 1, 1, 2, 8, 42, 28, },
+ { 0, 1, 2, 8, 58, 22, },
+ { 2, 1, 2, 8, 58, 28, },
+ { 1, 1, 2, 8, 58, 28, },
+ { 0, 1, 2, 8, 106, 24, },
+ { 2, 1, 2, 8, 106, 28, },
+ { 1, 1, 2, 8, 106, 28, },
+ { 0, 1, 2, 8, 122, 24, },
+ { 2, 1, 2, 8, 122, 28, },
+ { 1, 1, 2, 8, 122, 28, },
+ { 0, 1, 2, 8, 155, 24, },
+ { 2, 1, 2, 8, 155, 28, },
+ { 1, 1, 2, 8, 155, 63, },
+ { 0, 1, 2, 9, 42, 20, },
+ { 2, 1, 2, 9, 42, 26, },
+ { 1, 1, 2, 9, 42, 26, },
+ { 0, 1, 2, 9, 58, 20, },
+ { 2, 1, 2, 9, 58, 26, },
+ { 1, 1, 2, 9, 58, 26, },
+ { 0, 1, 2, 9, 106, 22, },
+ { 2, 1, 2, 9, 106, 26, },
+ { 1, 1, 2, 9, 106, 26, },
+ { 0, 1, 2, 9, 122, 22, },
+ { 2, 1, 2, 9, 122, 26, },
+ { 1, 1, 2, 9, 122, 26, },
+ { 0, 1, 2, 9, 155, 22, },
+ { 2, 1, 2, 9, 155, 26, },
+ { 1, 1, 2, 9, 155, 63, },
+};
+
+RTW_DECL_TABLE_TXPWR_LMT(rtw8814a_txpwr_lmt_type7);
+
+static const struct rtw_txpwr_lmt_cfg_pair rtw8814a_txpwr_lmt_type8[] = {
+ { 0, 0, 0, 0, 1, 46, },
+ { 2, 0, 0, 0, 1, 46, },
+ { 1, 0, 0, 0, 1, 46, },
+ { 0, 0, 0, 0, 2, 46, },
+ { 2, 0, 0, 0, 2, 46, },
+ { 1, 0, 0, 0, 2, 46, },
+ { 0, 0, 0, 0, 3, 46, },
+ { 2, 0, 0, 0, 3, 46, },
+ { 1, 0, 0, 0, 3, 46, },
+ { 0, 0, 0, 0, 4, 46, },
+ { 2, 0, 0, 0, 4, 46, },
+ { 1, 0, 0, 0, 4, 46, },
+ { 0, 0, 0, 0, 5, 46, },
+ { 2, 0, 0, 0, 5, 46, },
+ { 1, 0, 0, 0, 5, 46, },
+ { 0, 0, 0, 0, 6, 46, },
+ { 2, 0, 0, 0, 6, 46, },
+ { 1, 0, 0, 0, 6, 46, },
+ { 0, 0, 0, 0, 7, 46, },
+ { 2, 0, 0, 0, 7, 46, },
+ { 1, 0, 0, 0, 7, 46, },
+ { 0, 0, 0, 0, 8, 46, },
+ { 2, 0, 0, 0, 8, 46, },
+ { 1, 0, 0, 0, 8, 46, },
+ { 0, 0, 0, 0, 9, 46, },
+ { 2, 0, 0, 0, 9, 46, },
+ { 1, 0, 0, 0, 9, 46, },
+ { 0, 0, 0, 0, 10, 46, },
+ { 2, 0, 0, 0, 10, 46, },
+ { 1, 0, 0, 0, 10, 46, },
+ { 0, 0, 0, 0, 11, 46, },
+ { 2, 0, 0, 0, 11, 46, },
+ { 1, 0, 0, 0, 11, 46, },
+ { 0, 0, 0, 0, 12, 63, },
+ { 2, 0, 0, 0, 12, 46, },
+ { 1, 0, 0, 0, 12, 46, },
+ { 0, 0, 0, 0, 13, 63, },
+ { 2, 0, 0, 0, 13, 46, },
+ { 1, 0, 0, 0, 13, 46, },
+ { 0, 0, 0, 0, 14, 63, },
+ { 2, 0, 0, 0, 14, 63, },
+ { 1, 0, 0, 0, 14, 46, },
+ { 0, 0, 0, 1, 1, 46, },
+ { 2, 0, 0, 1, 1, 46, },
+ { 1, 0, 0, 1, 1, 46, },
+ { 0, 0, 0, 1, 2, 46, },
+ { 2, 0, 0, 1, 2, 46, },
+ { 1, 0, 0, 1, 2, 46, },
+ { 0, 0, 0, 1, 3, 46, },
+ { 2, 0, 0, 1, 3, 46, },
+ { 1, 0, 0, 1, 3, 46, },
+ { 0, 0, 0, 1, 4, 46, },
+ { 2, 0, 0, 1, 4, 46, },
+ { 1, 0, 0, 1, 4, 46, },
+ { 0, 0, 0, 1, 5, 46, },
+ { 2, 0, 0, 1, 5, 46, },
+ { 1, 0, 0, 1, 5, 46, },
+ { 0, 0, 0, 1, 6, 46, },
+ { 2, 0, 0, 1, 6, 46, },
+ { 1, 0, 0, 1, 6, 46, },
+ { 0, 0, 0, 1, 7, 46, },
+ { 2, 0, 0, 1, 7, 46, },
+ { 1, 0, 0, 1, 7, 46, },
+ { 0, 0, 0, 1, 8, 46, },
+ { 2, 0, 0, 1, 8, 46, },
+ { 1, 0, 0, 1, 8, 46, },
+ { 0, 0, 0, 1, 9, 46, },
+ { 2, 0, 0, 1, 9, 46, },
+ { 1, 0, 0, 1, 9, 46, },
+ { 0, 0, 0, 1, 10, 46, },
+ { 2, 0, 0, 1, 10, 46, },
+ { 1, 0, 0, 1, 10, 46, },
+ { 0, 0, 0, 1, 11, 46, },
+ { 2, 0, 0, 1, 11, 46, },
+ { 1, 0, 0, 1, 11, 46, },
+ { 0, 0, 0, 1, 12, 63, },
+ { 2, 0, 0, 1, 12, 46, },
+ { 1, 0, 0, 1, 12, 46, },
+ { 0, 0, 0, 1, 13, 63, },
+ { 2, 0, 0, 1, 13, 46, },
+ { 1, 0, 0, 1, 13, 46, },
+ { 0, 0, 0, 1, 14, 63, },
+ { 2, 0, 0, 1, 14, 63, },
+ { 1, 0, 0, 1, 14, 46, },
+ { 0, 0, 0, 2, 1, 46, },
+ { 2, 0, 0, 2, 1, 46, },
+ { 1, 0, 0, 2, 1, 46, },
+ { 0, 0, 0, 2, 2, 46, },
+ { 2, 0, 0, 2, 2, 46, },
+ { 1, 0, 0, 2, 2, 46, },
+ { 0, 0, 0, 2, 3, 46, },
+ { 2, 0, 0, 2, 3, 46, },
+ { 1, 0, 0, 2, 3, 46, },
+ { 0, 0, 0, 2, 4, 46, },
+ { 2, 0, 0, 2, 4, 46, },
+ { 1, 0, 0, 2, 4, 46, },
+ { 0, 0, 0, 2, 5, 46, },
+ { 2, 0, 0, 2, 5, 46, },
+ { 1, 0, 0, 2, 5, 46, },
+ { 0, 0, 0, 2, 6, 46, },
+ { 2, 0, 0, 2, 6, 46, },
+ { 1, 0, 0, 2, 6, 46, },
+ { 0, 0, 0, 2, 7, 46, },
+ { 2, 0, 0, 2, 7, 46, },
+ { 1, 0, 0, 2, 7, 46, },
+ { 0, 0, 0, 2, 8, 46, },
+ { 2, 0, 0, 2, 8, 46, },
+ { 1, 0, 0, 2, 8, 46, },
+ { 0, 0, 0, 2, 9, 46, },
+ { 2, 0, 0, 2, 9, 46, },
+ { 1, 0, 0, 2, 9, 46, },
+ { 0, 0, 0, 2, 10, 46, },
+ { 2, 0, 0, 2, 10, 46, },
+ { 1, 0, 0, 2, 10, 46, },
+ { 0, 0, 0, 2, 11, 46, },
+ { 2, 0, 0, 2, 11, 46, },
+ { 1, 0, 0, 2, 11, 46, },
+ { 0, 0, 0, 2, 12, 63, },
+ { 2, 0, 0, 2, 12, 46, },
+ { 1, 0, 0, 2, 12, 46, },
+ { 0, 0, 0, 2, 13, 63, },
+ { 2, 0, 0, 2, 13, 46, },
+ { 1, 0, 0, 2, 13, 46, },
+ { 0, 0, 0, 2, 14, 63, },
+ { 2, 0, 0, 2, 14, 63, },
+ { 1, 0, 0, 2, 14, 46, },
+ { 0, 0, 0, 3, 1, 46, },
+ { 2, 0, 0, 3, 1, 46, },
+ { 1, 0, 0, 3, 1, 46, },
+ { 0, 0, 0, 3, 2, 46, },
+ { 2, 0, 0, 3, 2, 46, },
+ { 1, 0, 0, 3, 2, 46, },
+ { 0, 0, 0, 3, 3, 46, },
+ { 2, 0, 0, 3, 3, 46, },
+ { 1, 0, 0, 3, 3, 46, },
+ { 0, 0, 0, 3, 4, 46, },
+ { 2, 0, 0, 3, 4, 46, },
+ { 1, 0, 0, 3, 4, 46, },
+ { 0, 0, 0, 3, 5, 46, },
+ { 2, 0, 0, 3, 5, 46, },
+ { 1, 0, 0, 3, 5, 46, },
+ { 0, 0, 0, 3, 6, 46, },
+ { 2, 0, 0, 3, 6, 46, },
+ { 1, 0, 0, 3, 6, 46, },
+ { 0, 0, 0, 3, 7, 46, },
+ { 2, 0, 0, 3, 7, 46, },
+ { 1, 0, 0, 3, 7, 46, },
+ { 0, 0, 0, 3, 8, 46, },
+ { 2, 0, 0, 3, 8, 46, },
+ { 1, 0, 0, 3, 8, 46, },
+ { 0, 0, 0, 3, 9, 46, },
+ { 2, 0, 0, 3, 9, 46, },
+ { 1, 0, 0, 3, 9, 46, },
+ { 0, 0, 0, 3, 10, 46, },
+ { 2, 0, 0, 3, 10, 46, },
+ { 1, 0, 0, 3, 10, 46, },
+ { 0, 0, 0, 3, 11, 46, },
+ { 2, 0, 0, 3, 11, 46, },
+ { 1, 0, 0, 3, 11, 46, },
+ { 0, 0, 0, 3, 12, 63, },
+ { 2, 0, 0, 3, 12, 46, },
+ { 1, 0, 0, 3, 12, 46, },
+ { 0, 0, 0, 3, 13, 63, },
+ { 2, 0, 0, 3, 13, 46, },
+ { 1, 0, 0, 3, 13, 46, },
+ { 0, 0, 0, 3, 14, 63, },
+ { 2, 0, 0, 3, 14, 63, },
+ { 1, 0, 0, 3, 14, 46, },
+ { 0, 0, 0, 6, 1, 46, },
+ { 2, 0, 0, 6, 1, 46, },
+ { 1, 0, 0, 6, 1, 46, },
+ { 0, 0, 0, 6, 2, 46, },
+ { 2, 0, 0, 6, 2, 46, },
+ { 1, 0, 0, 6, 2, 46, },
+ { 0, 0, 0, 6, 3, 46, },
+ { 2, 0, 0, 6, 3, 46, },
+ { 1, 0, 0, 6, 3, 46, },
+ { 0, 0, 0, 6, 4, 46, },
+ { 2, 0, 0, 6, 4, 46, },
+ { 1, 0, 0, 6, 4, 46, },
+ { 0, 0, 0, 6, 5, 46, },
+ { 2, 0, 0, 6, 5, 46, },
+ { 1, 0, 0, 6, 5, 46, },
+ { 0, 0, 0, 6, 6, 46, },
+ { 2, 0, 0, 6, 6, 46, },
+ { 1, 0, 0, 6, 6, 46, },
+ { 0, 0, 0, 6, 7, 46, },
+ { 2, 0, 0, 6, 7, 46, },
+ { 1, 0, 0, 6, 7, 46, },
+ { 0, 0, 0, 6, 8, 46, },
+ { 2, 0, 0, 6, 8, 46, },
+ { 1, 0, 0, 6, 8, 46, },
+ { 0, 0, 0, 6, 9, 46, },
+ { 2, 0, 0, 6, 9, 46, },
+ { 1, 0, 0, 6, 9, 46, },
+ { 0, 0, 0, 6, 10, 46, },
+ { 2, 0, 0, 6, 10, 46, },
+ { 1, 0, 0, 6, 10, 46, },
+ { 0, 0, 0, 6, 11, 46, },
+ { 2, 0, 0, 6, 11, 46, },
+ { 1, 0, 0, 6, 11, 46, },
+ { 0, 0, 0, 6, 12, 63, },
+ { 2, 0, 0, 6, 12, 46, },
+ { 1, 0, 0, 6, 12, 46, },
+ { 0, 0, 0, 6, 13, 63, },
+ { 2, 0, 0, 6, 13, 46, },
+ { 1, 0, 0, 6, 13, 46, },
+ { 0, 0, 0, 6, 14, 63, },
+ { 2, 0, 0, 6, 14, 63, },
+ { 1, 0, 0, 6, 14, 46, },
+ { 0, 0, 0, 7, 1, 46, },
+ { 2, 0, 0, 7, 1, 46, },
+ { 1, 0, 0, 7, 1, 46, },
+ { 0, 0, 0, 7, 2, 46, },
+ { 2, 0, 0, 7, 2, 46, },
+ { 1, 0, 0, 7, 2, 46, },
+ { 0, 0, 0, 7, 3, 46, },
+ { 2, 0, 0, 7, 3, 46, },
+ { 1, 0, 0, 7, 3, 46, },
+ { 0, 0, 0, 7, 4, 46, },
+ { 2, 0, 0, 7, 4, 46, },
+ { 1, 0, 0, 7, 4, 46, },
+ { 0, 0, 0, 7, 5, 46, },
+ { 2, 0, 0, 7, 5, 46, },
+ { 1, 0, 0, 7, 5, 46, },
+ { 0, 0, 0, 7, 6, 46, },
+ { 2, 0, 0, 7, 6, 46, },
+ { 1, 0, 0, 7, 6, 46, },
+ { 0, 0, 0, 7, 7, 46, },
+ { 2, 0, 0, 7, 7, 46, },
+ { 1, 0, 0, 7, 7, 46, },
+ { 0, 0, 0, 7, 8, 46, },
+ { 2, 0, 0, 7, 8, 46, },
+ { 1, 0, 0, 7, 8, 46, },
+ { 0, 0, 0, 7, 9, 46, },
+ { 2, 0, 0, 7, 9, 46, },
+ { 1, 0, 0, 7, 9, 46, },
+ { 0, 0, 0, 7, 10, 46, },
+ { 2, 0, 0, 7, 10, 46, },
+ { 1, 0, 0, 7, 10, 46, },
+ { 0, 0, 0, 7, 11, 46, },
+ { 2, 0, 0, 7, 11, 46, },
+ { 1, 0, 0, 7, 11, 46, },
+ { 0, 0, 0, 7, 12, 63, },
+ { 2, 0, 0, 7, 12, 46, },
+ { 1, 0, 0, 7, 12, 46, },
+ { 0, 0, 0, 7, 13, 63, },
+ { 2, 0, 0, 7, 13, 46, },
+ { 1, 0, 0, 7, 13, 46, },
+ { 0, 0, 0, 7, 14, 63, },
+ { 2, 0, 0, 7, 14, 63, },
+ { 1, 0, 0, 7, 14, 46, },
+ { 0, 0, 1, 2, 1, 63, },
+ { 2, 0, 1, 2, 1, 63, },
+ { 1, 0, 1, 2, 1, 63, },
+ { 0, 0, 1, 2, 2, 63, },
+ { 2, 0, 1, 2, 2, 63, },
+ { 1, 0, 1, 2, 2, 63, },
+ { 0, 0, 1, 2, 3, 30, },
+ { 2, 0, 1, 2, 3, 34, },
+ { 1, 0, 1, 2, 3, 34, },
+ { 0, 0, 1, 2, 4, 34, },
+ { 2, 0, 1, 2, 4, 34, },
+ { 1, 0, 1, 2, 4, 34, },
+ { 0, 0, 1, 2, 5, 34, },
+ { 2, 0, 1, 2, 5, 34, },
+ { 1, 0, 1, 2, 5, 34, },
+ { 0, 0, 1, 2, 6, 34, },
+ { 2, 0, 1, 2, 6, 34, },
+ { 1, 0, 1, 2, 6, 34, },
+ { 0, 0, 1, 2, 7, 34, },
+ { 2, 0, 1, 2, 7, 34, },
+ { 1, 0, 1, 2, 7, 34, },
+ { 0, 0, 1, 2, 8, 34, },
+ { 2, 0, 1, 2, 8, 34, },
+ { 1, 0, 1, 2, 8, 34, },
+ { 0, 0, 1, 2, 9, 34, },
+ { 2, 0, 1, 2, 9, 34, },
+ { 1, 0, 1, 2, 9, 34, },
+ { 0, 0, 1, 2, 10, 34, },
+ { 2, 0, 1, 2, 10, 34, },
+ { 1, 0, 1, 2, 10, 34, },
+ { 0, 0, 1, 2, 11, 28, },
+ { 2, 0, 1, 2, 11, 34, },
+ { 1, 0, 1, 2, 11, 34, },
+ { 0, 0, 1, 2, 12, 63, },
+ { 2, 0, 1, 2, 12, 34, },
+ { 1, 0, 1, 2, 12, 34, },
+ { 0, 0, 1, 2, 13, 63, },
+ { 2, 0, 1, 2, 13, 34, },
+ { 1, 0, 1, 2, 13, 34, },
+ { 0, 0, 1, 2, 14, 63, },
+ { 2, 0, 1, 2, 14, 63, },
+ { 1, 0, 1, 2, 14, 63, },
+ { 0, 0, 1, 3, 1, 63, },
+ { 2, 0, 1, 3, 1, 63, },
+ { 1, 0, 1, 3, 1, 63, },
+ { 0, 0, 1, 3, 2, 63, },
+ { 2, 0, 1, 3, 2, 63, },
+ { 1, 0, 1, 3, 2, 63, },
+ { 0, 0, 1, 3, 3, 30, },
+ { 2, 0, 1, 3, 3, 34, },
+ { 1, 0, 1, 3, 3, 34, },
+ { 0, 0, 1, 3, 4, 34, },
+ { 2, 0, 1, 3, 4, 34, },
+ { 1, 0, 1, 3, 4, 34, },
+ { 0, 0, 1, 3, 5, 34, },
+ { 2, 0, 1, 3, 5, 34, },
+ { 1, 0, 1, 3, 5, 34, },
+ { 0, 0, 1, 3, 6, 34, },
+ { 2, 0, 1, 3, 6, 34, },
+ { 1, 0, 1, 3, 6, 34, },
+ { 0, 0, 1, 3, 7, 34, },
+ { 2, 0, 1, 3, 7, 34, },
+ { 1, 0, 1, 3, 7, 34, },
+ { 0, 0, 1, 3, 8, 34, },
+ { 2, 0, 1, 3, 8, 34, },
+ { 1, 0, 1, 3, 8, 34, },
+ { 0, 0, 1, 3, 9, 34, },
+ { 2, 0, 1, 3, 9, 34, },
+ { 1, 0, 1, 3, 9, 34, },
+ { 0, 0, 1, 3, 10, 34, },
+ { 2, 0, 1, 3, 10, 34, },
+ { 1, 0, 1, 3, 10, 34, },
+ { 0, 0, 1, 3, 11, 28, },
+ { 2, 0, 1, 3, 11, 34, },
+ { 1, 0, 1, 3, 11, 34, },
+ { 0, 0, 1, 3, 12, 63, },
+ { 2, 0, 1, 3, 12, 34, },
+ { 1, 0, 1, 3, 12, 34, },
+ { 0, 0, 1, 3, 13, 63, },
+ { 2, 0, 1, 3, 13, 34, },
+ { 1, 0, 1, 3, 13, 34, },
+ { 0, 0, 1, 3, 14, 63, },
+ { 2, 0, 1, 3, 14, 63, },
+ { 1, 0, 1, 3, 14, 63, },
+ { 0, 0, 1, 6, 1, 63, },
+ { 2, 0, 1, 6, 1, 63, },
+ { 1, 0, 1, 6, 1, 63, },
+ { 0, 0, 1, 6, 2, 63, },
+ { 2, 0, 1, 6, 2, 63, },
+ { 1, 0, 1, 6, 2, 63, },
+ { 0, 0, 1, 6, 3, 30, },
+ { 2, 0, 1, 6, 3, 34, },
+ { 1, 0, 1, 6, 3, 34, },
+ { 0, 0, 1, 6, 4, 34, },
+ { 2, 0, 1, 6, 4, 34, },
+ { 1, 0, 1, 6, 4, 34, },
+ { 0, 0, 1, 6, 5, 34, },
+ { 2, 0, 1, 6, 5, 34, },
+ { 1, 0, 1, 6, 5, 34, },
+ { 0, 0, 1, 6, 6, 34, },
+ { 2, 0, 1, 6, 6, 34, },
+ { 1, 0, 1, 6, 6, 34, },
+ { 0, 0, 1, 6, 7, 34, },
+ { 2, 0, 1, 6, 7, 34, },
+ { 1, 0, 1, 6, 7, 34, },
+ { 0, 0, 1, 6, 8, 34, },
+ { 2, 0, 1, 6, 8, 34, },
+ { 1, 0, 1, 6, 8, 34, },
+ { 0, 0, 1, 6, 9, 34, },
+ { 2, 0, 1, 6, 9, 34, },
+ { 1, 0, 1, 6, 9, 34, },
+ { 0, 0, 1, 6, 10, 34, },
+ { 2, 0, 1, 6, 10, 34, },
+ { 1, 0, 1, 6, 10, 34, },
+ { 0, 0, 1, 6, 11, 28, },
+ { 2, 0, 1, 6, 11, 34, },
+ { 1, 0, 1, 6, 11, 34, },
+ { 0, 0, 1, 6, 12, 63, },
+ { 2, 0, 1, 6, 12, 34, },
+ { 1, 0, 1, 6, 12, 34, },
+ { 0, 0, 1, 6, 13, 63, },
+ { 2, 0, 1, 6, 13, 34, },
+ { 1, 0, 1, 6, 13, 34, },
+ { 0, 0, 1, 6, 14, 63, },
+ { 2, 0, 1, 6, 14, 63, },
+ { 1, 0, 1, 6, 14, 63, },
+ { 0, 0, 1, 7, 1, 63, },
+ { 2, 0, 1, 7, 1, 63, },
+ { 1, 0, 1, 7, 1, 63, },
+ { 0, 0, 1, 7, 2, 63, },
+ { 2, 0, 1, 7, 2, 63, },
+ { 1, 0, 1, 7, 2, 63, },
+ { 0, 0, 1, 7, 3, 30, },
+ { 2, 0, 1, 7, 3, 34, },
+ { 1, 0, 1, 7, 3, 34, },
+ { 0, 0, 1, 7, 4, 34, },
+ { 2, 0, 1, 7, 4, 34, },
+ { 1, 0, 1, 7, 4, 34, },
+ { 0, 0, 1, 7, 5, 34, },
+ { 2, 0, 1, 7, 5, 34, },
+ { 1, 0, 1, 7, 5, 34, },
+ { 0, 0, 1, 7, 6, 34, },
+ { 2, 0, 1, 7, 6, 34, },
+ { 1, 0, 1, 7, 6, 34, },
+ { 0, 0, 1, 7, 7, 34, },
+ { 2, 0, 1, 7, 7, 34, },
+ { 1, 0, 1, 7, 7, 34, },
+ { 0, 0, 1, 7, 8, 34, },
+ { 2, 0, 1, 7, 8, 34, },
+ { 1, 0, 1, 7, 8, 34, },
+ { 0, 0, 1, 7, 9, 34, },
+ { 2, 0, 1, 7, 9, 34, },
+ { 1, 0, 1, 7, 9, 34, },
+ { 0, 0, 1, 7, 10, 34, },
+ { 2, 0, 1, 7, 10, 34, },
+ { 1, 0, 1, 7, 10, 34, },
+ { 0, 0, 1, 7, 11, 28, },
+ { 2, 0, 1, 7, 11, 34, },
+ { 1, 0, 1, 7, 11, 34, },
+ { 0, 0, 1, 7, 12, 63, },
+ { 2, 0, 1, 7, 12, 34, },
+ { 1, 0, 1, 7, 12, 34, },
+ { 0, 0, 1, 7, 13, 63, },
+ { 2, 0, 1, 7, 13, 34, },
+ { 1, 0, 1, 7, 13, 34, },
+ { 0, 0, 1, 7, 14, 63, },
+ { 2, 0, 1, 7, 14, 63, },
+ { 1, 0, 1, 7, 14, 63, },
+ { 0, 1, 0, 1, 36, 46, },
+ { 2, 1, 0, 1, 36, 46, },
+ { 1, 1, 0, 1, 36, 46, },
+ { 0, 1, 0, 1, 40, 46, },
+ { 2, 1, 0, 1, 40, 46, },
+ { 1, 1, 0, 1, 40, 46, },
+ { 0, 1, 0, 1, 44, 46, },
+ { 2, 1, 0, 1, 44, 46, },
+ { 1, 1, 0, 1, 44, 46, },
+ { 0, 1, 0, 1, 48, 46, },
+ { 2, 1, 0, 1, 48, 46, },
+ { 1, 1, 0, 1, 48, 46, },
+ { 0, 1, 0, 1, 52, 46, },
+ { 2, 1, 0, 1, 52, 46, },
+ { 1, 1, 0, 1, 52, 46, },
+ { 0, 1, 0, 1, 56, 46, },
+ { 2, 1, 0, 1, 56, 46, },
+ { 1, 1, 0, 1, 56, 46, },
+ { 0, 1, 0, 1, 60, 46, },
+ { 2, 1, 0, 1, 60, 46, },
+ { 1, 1, 0, 1, 60, 46, },
+ { 0, 1, 0, 1, 64, 46, },
+ { 2, 1, 0, 1, 64, 46, },
+ { 1, 1, 0, 1, 64, 46, },
+ { 0, 1, 0, 1, 100, 46, },
+ { 2, 1, 0, 1, 100, 46, },
+ { 1, 1, 0, 1, 100, 46, },
+ { 0, 1, 0, 1, 104, 46, },
+ { 2, 1, 0, 1, 104, 46, },
+ { 1, 1, 0, 1, 104, 46, },
+ { 0, 1, 0, 1, 108, 46, },
+ { 2, 1, 0, 1, 108, 46, },
+ { 1, 1, 0, 1, 108, 46, },
+ { 0, 1, 0, 1, 112, 46, },
+ { 2, 1, 0, 1, 112, 46, },
+ { 1, 1, 0, 1, 112, 46, },
+ { 0, 1, 0, 1, 116, 46, },
+ { 2, 1, 0, 1, 116, 46, },
+ { 1, 1, 0, 1, 116, 46, },
+ { 0, 1, 0, 1, 120, 46, },
+ { 2, 1, 0, 1, 120, 46, },
+ { 1, 1, 0, 1, 120, 46, },
+ { 0, 1, 0, 1, 124, 46, },
+ { 2, 1, 0, 1, 124, 46, },
+ { 1, 1, 0, 1, 124, 46, },
+ { 0, 1, 0, 1, 128, 46, },
+ { 2, 1, 0, 1, 128, 46, },
+ { 1, 1, 0, 1, 128, 46, },
+ { 0, 1, 0, 1, 132, 46, },
+ { 2, 1, 0, 1, 132, 46, },
+ { 1, 1, 0, 1, 132, 46, },
+ { 0, 1, 0, 1, 136, 46, },
+ { 2, 1, 0, 1, 136, 46, },
+ { 1, 1, 0, 1, 136, 46, },
+ { 0, 1, 0, 1, 140, 46, },
+ { 2, 1, 0, 1, 140, 46, },
+ { 1, 1, 0, 1, 140, 46, },
+ { 0, 1, 0, 1, 149, 46, },
+ { 2, 1, 0, 1, 149, 46, },
+ { 1, 1, 0, 1, 149, 63, },
+ { 0, 1, 0, 1, 153, 46, },
+ { 2, 1, 0, 1, 153, 46, },
+ { 1, 1, 0, 1, 153, 63, },
+ { 0, 1, 0, 1, 157, 46, },
+ { 2, 1, 0, 1, 157, 46, },
+ { 1, 1, 0, 1, 157, 63, },
+ { 0, 1, 0, 1, 161, 46, },
+ { 2, 1, 0, 1, 161, 46, },
+ { 1, 1, 0, 1, 161, 63, },
+ { 0, 1, 0, 1, 165, 46, },
+ { 2, 1, 0, 1, 165, 46, },
+ { 1, 1, 0, 1, 165, 63, },
+ { 0, 1, 0, 2, 36, 46, },
+ { 2, 1, 0, 2, 36, 46, },
+ { 1, 1, 0, 2, 36, 46, },
+ { 0, 1, 0, 2, 40, 46, },
+ { 2, 1, 0, 2, 40, 46, },
+ { 1, 1, 0, 2, 40, 46, },
+ { 0, 1, 0, 2, 44, 46, },
+ { 2, 1, 0, 2, 44, 46, },
+ { 1, 1, 0, 2, 44, 46, },
+ { 0, 1, 0, 2, 48, 46, },
+ { 2, 1, 0, 2, 48, 46, },
+ { 1, 1, 0, 2, 48, 46, },
+ { 0, 1, 0, 2, 52, 46, },
+ { 2, 1, 0, 2, 52, 46, },
+ { 1, 1, 0, 2, 52, 46, },
+ { 0, 1, 0, 2, 56, 46, },
+ { 2, 1, 0, 2, 56, 46, },
+ { 1, 1, 0, 2, 56, 46, },
+ { 0, 1, 0, 2, 60, 46, },
+ { 2, 1, 0, 2, 60, 46, },
+ { 1, 1, 0, 2, 60, 46, },
+ { 0, 1, 0, 2, 64, 46, },
+ { 2, 1, 0, 2, 64, 46, },
+ { 1, 1, 0, 2, 64, 46, },
+ { 0, 1, 0, 2, 100, 46, },
+ { 2, 1, 0, 2, 100, 46, },
+ { 1, 1, 0, 2, 100, 46, },
+ { 0, 1, 0, 2, 104, 46, },
+ { 2, 1, 0, 2, 104, 46, },
+ { 1, 1, 0, 2, 104, 46, },
+ { 0, 1, 0, 2, 108, 46, },
+ { 2, 1, 0, 2, 108, 46, },
+ { 1, 1, 0, 2, 108, 46, },
+ { 0, 1, 0, 2, 112, 46, },
+ { 2, 1, 0, 2, 112, 46, },
+ { 1, 1, 0, 2, 112, 46, },
+ { 0, 1, 0, 2, 116, 46, },
+ { 2, 1, 0, 2, 116, 46, },
+ { 1, 1, 0, 2, 116, 46, },
+ { 0, 1, 0, 2, 120, 46, },
+ { 2, 1, 0, 2, 120, 46, },
+ { 1, 1, 0, 2, 120, 46, },
+ { 0, 1, 0, 2, 124, 46, },
+ { 2, 1, 0, 2, 124, 46, },
+ { 1, 1, 0, 2, 124, 46, },
+ { 0, 1, 0, 2, 128, 46, },
+ { 2, 1, 0, 2, 128, 46, },
+ { 1, 1, 0, 2, 128, 46, },
+ { 0, 1, 0, 2, 132, 46, },
+ { 2, 1, 0, 2, 132, 46, },
+ { 1, 1, 0, 2, 132, 46, },
+ { 0, 1, 0, 2, 136, 46, },
+ { 2, 1, 0, 2, 136, 46, },
+ { 1, 1, 0, 2, 136, 46, },
+ { 0, 1, 0, 2, 140, 46, },
+ { 2, 1, 0, 2, 140, 46, },
+ { 1, 1, 0, 2, 140, 46, },
+ { 0, 1, 0, 2, 149, 46, },
+ { 2, 1, 0, 2, 149, 46, },
+ { 1, 1, 0, 2, 149, 63, },
+ { 0, 1, 0, 2, 153, 46, },
+ { 2, 1, 0, 2, 153, 46, },
+ { 1, 1, 0, 2, 153, 63, },
+ { 0, 1, 0, 2, 157, 46, },
+ { 2, 1, 0, 2, 157, 46, },
+ { 1, 1, 0, 2, 157, 63, },
+ { 0, 1, 0, 2, 161, 46, },
+ { 2, 1, 0, 2, 161, 46, },
+ { 1, 1, 0, 2, 161, 63, },
+ { 0, 1, 0, 2, 165, 46, },
+ { 2, 1, 0, 2, 165, 46, },
+ { 1, 1, 0, 2, 165, 63, },
+ { 0, 1, 0, 3, 36, 46, },
+ { 2, 1, 0, 3, 36, 46, },
+ { 1, 1, 0, 3, 36, 46, },
+ { 0, 1, 0, 3, 40, 46, },
+ { 2, 1, 0, 3, 40, 46, },
+ { 1, 1, 0, 3, 40, 46, },
+ { 0, 1, 0, 3, 44, 46, },
+ { 2, 1, 0, 3, 44, 46, },
+ { 1, 1, 0, 3, 44, 46, },
+ { 0, 1, 0, 3, 48, 46, },
+ { 2, 1, 0, 3, 48, 46, },
+ { 1, 1, 0, 3, 48, 46, },
+ { 0, 1, 0, 3, 52, 46, },
+ { 2, 1, 0, 3, 52, 46, },
+ { 1, 1, 0, 3, 52, 46, },
+ { 0, 1, 0, 3, 56, 46, },
+ { 2, 1, 0, 3, 56, 46, },
+ { 1, 1, 0, 3, 56, 46, },
+ { 0, 1, 0, 3, 60, 46, },
+ { 2, 1, 0, 3, 60, 46, },
+ { 1, 1, 0, 3, 60, 46, },
+ { 0, 1, 0, 3, 64, 46, },
+ { 2, 1, 0, 3, 64, 46, },
+ { 1, 1, 0, 3, 64, 46, },
+ { 0, 1, 0, 3, 100, 46, },
+ { 2, 1, 0, 3, 100, 46, },
+ { 1, 1, 0, 3, 100, 46, },
+ { 0, 1, 0, 3, 104, 46, },
+ { 2, 1, 0, 3, 104, 46, },
+ { 1, 1, 0, 3, 104, 46, },
+ { 0, 1, 0, 3, 108, 46, },
+ { 2, 1, 0, 3, 108, 46, },
+ { 1, 1, 0, 3, 108, 46, },
+ { 0, 1, 0, 3, 112, 46, },
+ { 2, 1, 0, 3, 112, 46, },
+ { 1, 1, 0, 3, 112, 46, },
+ { 0, 1, 0, 3, 116, 46, },
+ { 2, 1, 0, 3, 116, 46, },
+ { 1, 1, 0, 3, 116, 46, },
+ { 0, 1, 0, 3, 120, 46, },
+ { 2, 1, 0, 3, 120, 46, },
+ { 1, 1, 0, 3, 120, 46, },
+ { 0, 1, 0, 3, 124, 46, },
+ { 2, 1, 0, 3, 124, 46, },
+ { 1, 1, 0, 3, 124, 46, },
+ { 0, 1, 0, 3, 128, 46, },
+ { 2, 1, 0, 3, 128, 46, },
+ { 1, 1, 0, 3, 128, 46, },
+ { 0, 1, 0, 3, 132, 46, },
+ { 2, 1, 0, 3, 132, 46, },
+ { 1, 1, 0, 3, 132, 46, },
+ { 0, 1, 0, 3, 136, 46, },
+ { 2, 1, 0, 3, 136, 46, },
+ { 1, 1, 0, 3, 136, 46, },
+ { 0, 1, 0, 3, 140, 46, },
+ { 2, 1, 0, 3, 140, 46, },
+ { 1, 1, 0, 3, 140, 46, },
+ { 0, 1, 0, 3, 149, 46, },
+ { 2, 1, 0, 3, 149, 46, },
+ { 1, 1, 0, 3, 149, 63, },
+ { 0, 1, 0, 3, 153, 46, },
+ { 2, 1, 0, 3, 153, 46, },
+ { 1, 1, 0, 3, 153, 63, },
+ { 0, 1, 0, 3, 157, 46, },
+ { 2, 1, 0, 3, 157, 46, },
+ { 1, 1, 0, 3, 157, 63, },
+ { 0, 1, 0, 3, 161, 46, },
+ { 2, 1, 0, 3, 161, 46, },
+ { 1, 1, 0, 3, 161, 63, },
+ { 0, 1, 0, 3, 165, 46, },
+ { 2, 1, 0, 3, 165, 46, },
+ { 1, 1, 0, 3, 165, 63, },
+ { 0, 1, 0, 6, 36, 46, },
+ { 2, 1, 0, 6, 36, 46, },
+ { 1, 1, 0, 6, 36, 46, },
+ { 0, 1, 0, 6, 40, 46, },
+ { 2, 1, 0, 6, 40, 46, },
+ { 1, 1, 0, 6, 40, 46, },
+ { 0, 1, 0, 6, 44, 46, },
+ { 2, 1, 0, 6, 44, 46, },
+ { 1, 1, 0, 6, 44, 46, },
+ { 0, 1, 0, 6, 48, 46, },
+ { 2, 1, 0, 6, 48, 46, },
+ { 1, 1, 0, 6, 48, 46, },
+ { 0, 1, 0, 6, 52, 46, },
+ { 2, 1, 0, 6, 52, 46, },
+ { 1, 1, 0, 6, 52, 46, },
+ { 0, 1, 0, 6, 56, 46, },
+ { 2, 1, 0, 6, 56, 46, },
+ { 1, 1, 0, 6, 56, 46, },
+ { 0, 1, 0, 6, 60, 46, },
+ { 2, 1, 0, 6, 60, 46, },
+ { 1, 1, 0, 6, 60, 46, },
+ { 0, 1, 0, 6, 64, 46, },
+ { 2, 1, 0, 6, 64, 46, },
+ { 1, 1, 0, 6, 64, 46, },
+ { 0, 1, 0, 6, 100, 46, },
+ { 2, 1, 0, 6, 100, 46, },
+ { 1, 1, 0, 6, 100, 46, },
+ { 0, 1, 0, 6, 104, 46, },
+ { 2, 1, 0, 6, 104, 46, },
+ { 1, 1, 0, 6, 104, 46, },
+ { 0, 1, 0, 6, 108, 46, },
+ { 2, 1, 0, 6, 108, 46, },
+ { 1, 1, 0, 6, 108, 46, },
+ { 0, 1, 0, 6, 112, 46, },
+ { 2, 1, 0, 6, 112, 46, },
+ { 1, 1, 0, 6, 112, 46, },
+ { 0, 1, 0, 6, 116, 46, },
+ { 2, 1, 0, 6, 116, 46, },
+ { 1, 1, 0, 6, 116, 46, },
+ { 0, 1, 0, 6, 120, 46, },
+ { 2, 1, 0, 6, 120, 46, },
+ { 1, 1, 0, 6, 120, 46, },
+ { 0, 1, 0, 6, 124, 46, },
+ { 2, 1, 0, 6, 124, 46, },
+ { 1, 1, 0, 6, 124, 46, },
+ { 0, 1, 0, 6, 128, 46, },
+ { 2, 1, 0, 6, 128, 46, },
+ { 1, 1, 0, 6, 128, 46, },
+ { 0, 1, 0, 6, 132, 46, },
+ { 2, 1, 0, 6, 132, 46, },
+ { 1, 1, 0, 6, 132, 46, },
+ { 0, 1, 0, 6, 136, 46, },
+ { 2, 1, 0, 6, 136, 46, },
+ { 1, 1, 0, 6, 136, 46, },
+ { 0, 1, 0, 6, 140, 46, },
+ { 2, 1, 0, 6, 140, 46, },
+ { 1, 1, 0, 6, 140, 46, },
+ { 0, 1, 0, 6, 149, 46, },
+ { 2, 1, 0, 6, 149, 46, },
+ { 1, 1, 0, 6, 149, 63, },
+ { 0, 1, 0, 6, 153, 46, },
+ { 2, 1, 0, 6, 153, 46, },
+ { 1, 1, 0, 6, 153, 63, },
+ { 0, 1, 0, 6, 157, 46, },
+ { 2, 1, 0, 6, 157, 46, },
+ { 1, 1, 0, 6, 157, 63, },
+ { 0, 1, 0, 6, 161, 46, },
+ { 2, 1, 0, 6, 161, 46, },
+ { 1, 1, 0, 6, 161, 63, },
+ { 0, 1, 0, 6, 165, 46, },
+ { 2, 1, 0, 6, 165, 46, },
+ { 1, 1, 0, 6, 165, 63, },
+ { 0, 1, 0, 7, 36, 46, },
+ { 2, 1, 0, 7, 36, 46, },
+ { 1, 1, 0, 7, 36, 46, },
+ { 0, 1, 0, 7, 40, 46, },
+ { 2, 1, 0, 7, 40, 46, },
+ { 1, 1, 0, 7, 40, 46, },
+ { 0, 1, 0, 7, 44, 46, },
+ { 2, 1, 0, 7, 44, 46, },
+ { 1, 1, 0, 7, 44, 46, },
+ { 0, 1, 0, 7, 48, 46, },
+ { 2, 1, 0, 7, 48, 46, },
+ { 1, 1, 0, 7, 48, 46, },
+ { 0, 1, 0, 7, 52, 46, },
+ { 2, 1, 0, 7, 52, 46, },
+ { 1, 1, 0, 7, 52, 46, },
+ { 0, 1, 0, 7, 56, 46, },
+ { 2, 1, 0, 7, 56, 46, },
+ { 1, 1, 0, 7, 56, 46, },
+ { 0, 1, 0, 7, 60, 46, },
+ { 2, 1, 0, 7, 60, 46, },
+ { 1, 1, 0, 7, 60, 46, },
+ { 0, 1, 0, 7, 64, 46, },
+ { 2, 1, 0, 7, 64, 46, },
+ { 1, 1, 0, 7, 64, 46, },
+ { 0, 1, 0, 7, 100, 46, },
+ { 2, 1, 0, 7, 100, 46, },
+ { 1, 1, 0, 7, 100, 46, },
+ { 0, 1, 0, 7, 104, 46, },
+ { 2, 1, 0, 7, 104, 46, },
+ { 1, 1, 0, 7, 104, 46, },
+ { 0, 1, 0, 7, 108, 46, },
+ { 2, 1, 0, 7, 108, 46, },
+ { 1, 1, 0, 7, 108, 46, },
+ { 0, 1, 0, 7, 112, 46, },
+ { 2, 1, 0, 7, 112, 46, },
+ { 1, 1, 0, 7, 112, 46, },
+ { 0, 1, 0, 7, 116, 46, },
+ { 2, 1, 0, 7, 116, 46, },
+ { 1, 1, 0, 7, 116, 46, },
+ { 0, 1, 0, 7, 120, 46, },
+ { 2, 1, 0, 7, 120, 46, },
+ { 1, 1, 0, 7, 120, 46, },
+ { 0, 1, 0, 7, 124, 46, },
+ { 2, 1, 0, 7, 124, 46, },
+ { 1, 1, 0, 7, 124, 46, },
+ { 0, 1, 0, 7, 128, 46, },
+ { 2, 1, 0, 7, 128, 46, },
+ { 1, 1, 0, 7, 128, 46, },
+ { 0, 1, 0, 7, 132, 46, },
+ { 2, 1, 0, 7, 132, 46, },
+ { 1, 1, 0, 7, 132, 46, },
+ { 0, 1, 0, 7, 136, 46, },
+ { 2, 1, 0, 7, 136, 46, },
+ { 1, 1, 0, 7, 136, 46, },
+ { 0, 1, 0, 7, 140, 46, },
+ { 2, 1, 0, 7, 140, 46, },
+ { 1, 1, 0, 7, 140, 46, },
+ { 0, 1, 0, 7, 149, 46, },
+ { 2, 1, 0, 7, 149, 46, },
+ { 1, 1, 0, 7, 149, 63, },
+ { 0, 1, 0, 7, 153, 46, },
+ { 2, 1, 0, 7, 153, 46, },
+ { 1, 1, 0, 7, 153, 63, },
+ { 0, 1, 0, 7, 157, 46, },
+ { 2, 1, 0, 7, 157, 46, },
+ { 1, 1, 0, 7, 157, 63, },
+ { 0, 1, 0, 7, 161, 46, },
+ { 2, 1, 0, 7, 161, 46, },
+ { 1, 1, 0, 7, 161, 63, },
+ { 0, 1, 0, 7, 165, 46, },
+ { 2, 1, 0, 7, 165, 46, },
+ { 1, 1, 0, 7, 165, 63, },
+ { 0, 1, 1, 2, 38, 46, },
+ { 2, 1, 1, 2, 38, 46, },
+ { 1, 1, 1, 2, 38, 46, },
+ { 0, 1, 1, 2, 46, 46, },
+ { 2, 1, 1, 2, 46, 46, },
+ { 1, 1, 1, 2, 46, 46, },
+ { 0, 1, 1, 2, 54, 46, },
+ { 2, 1, 1, 2, 54, 46, },
+ { 1, 1, 1, 2, 54, 46, },
+ { 0, 1, 1, 2, 62, 46, },
+ { 2, 1, 1, 2, 62, 46, },
+ { 1, 1, 1, 2, 62, 46, },
+ { 0, 1, 1, 2, 102, 46, },
+ { 2, 1, 1, 2, 102, 46, },
+ { 1, 1, 1, 2, 102, 46, },
+ { 0, 1, 1, 2, 110, 46, },
+ { 2, 1, 1, 2, 110, 46, },
+ { 1, 1, 1, 2, 110, 46, },
+ { 0, 1, 1, 2, 118, 46, },
+ { 2, 1, 1, 2, 118, 46, },
+ { 1, 1, 1, 2, 118, 46, },
+ { 0, 1, 1, 2, 126, 46, },
+ { 2, 1, 1, 2, 126, 46, },
+ { 1, 1, 1, 2, 126, 46, },
+ { 0, 1, 1, 2, 134, 46, },
+ { 2, 1, 1, 2, 134, 46, },
+ { 1, 1, 1, 2, 134, 46, },
+ { 0, 1, 1, 2, 151, 46, },
+ { 2, 1, 1, 2, 151, 46, },
+ { 1, 1, 1, 2, 151, 63, },
+ { 0, 1, 1, 2, 159, 46, },
+ { 2, 1, 1, 2, 159, 46, },
+ { 1, 1, 1, 2, 159, 63, },
+ { 0, 1, 1, 3, 38, 46, },
+ { 2, 1, 1, 3, 38, 46, },
+ { 1, 1, 1, 3, 38, 46, },
+ { 0, 1, 1, 3, 46, 46, },
+ { 2, 1, 1, 3, 46, 46, },
+ { 1, 1, 1, 3, 46, 46, },
+ { 0, 1, 1, 3, 54, 46, },
+ { 2, 1, 1, 3, 54, 46, },
+ { 1, 1, 1, 3, 54, 46, },
+ { 0, 1, 1, 3, 62, 46, },
+ { 2, 1, 1, 3, 62, 46, },
+ { 1, 1, 1, 3, 62, 46, },
+ { 0, 1, 1, 3, 102, 46, },
+ { 2, 1, 1, 3, 102, 46, },
+ { 1, 1, 1, 3, 102, 46, },
+ { 0, 1, 1, 3, 110, 46, },
+ { 2, 1, 1, 3, 110, 46, },
+ { 1, 1, 1, 3, 110, 46, },
+ { 0, 1, 1, 3, 118, 46, },
+ { 2, 1, 1, 3, 118, 46, },
+ { 1, 1, 1, 3, 118, 46, },
+ { 0, 1, 1, 3, 126, 46, },
+ { 2, 1, 1, 3, 126, 46, },
+ { 1, 1, 1, 3, 126, 46, },
+ { 0, 1, 1, 3, 134, 46, },
+ { 2, 1, 1, 3, 134, 46, },
+ { 1, 1, 1, 3, 134, 46, },
+ { 0, 1, 1, 3, 151, 46, },
+ { 2, 1, 1, 3, 151, 46, },
+ { 1, 1, 1, 3, 151, 63, },
+ { 0, 1, 1, 3, 159, 46, },
+ { 2, 1, 1, 3, 159, 46, },
+ { 1, 1, 1, 3, 159, 63, },
+ { 0, 1, 1, 6, 38, 46, },
+ { 2, 1, 1, 6, 38, 46, },
+ { 1, 1, 1, 6, 38, 46, },
+ { 0, 1, 1, 6, 46, 46, },
+ { 2, 1, 1, 6, 46, 46, },
+ { 1, 1, 1, 6, 46, 46, },
+ { 0, 1, 1, 6, 54, 46, },
+ { 2, 1, 1, 6, 54, 46, },
+ { 1, 1, 1, 6, 54, 46, },
+ { 0, 1, 1, 6, 62, 46, },
+ { 2, 1, 1, 6, 62, 46, },
+ { 1, 1, 1, 6, 62, 46, },
+ { 0, 1, 1, 6, 102, 46, },
+ { 2, 1, 1, 6, 102, 46, },
+ { 1, 1, 1, 6, 102, 46, },
+ { 0, 1, 1, 6, 110, 46, },
+ { 2, 1, 1, 6, 110, 46, },
+ { 1, 1, 1, 6, 110, 46, },
+ { 0, 1, 1, 6, 118, 46, },
+ { 2, 1, 1, 6, 118, 46, },
+ { 1, 1, 1, 6, 118, 46, },
+ { 0, 1, 1, 6, 126, 46, },
+ { 2, 1, 1, 6, 126, 46, },
+ { 1, 1, 1, 6, 126, 46, },
+ { 0, 1, 1, 6, 134, 46, },
+ { 2, 1, 1, 6, 134, 46, },
+ { 1, 1, 1, 6, 134, 46, },
+ { 0, 1, 1, 6, 151, 46, },
+ { 2, 1, 1, 6, 151, 46, },
+ { 1, 1, 1, 6, 151, 63, },
+ { 0, 1, 1, 6, 159, 46, },
+ { 2, 1, 1, 6, 159, 46, },
+ { 1, 1, 1, 6, 159, 63, },
+ { 0, 1, 1, 7, 38, 46, },
+ { 2, 1, 1, 7, 38, 46, },
+ { 1, 1, 1, 7, 38, 46, },
+ { 0, 1, 1, 7, 46, 46, },
+ { 2, 1, 1, 7, 46, 46, },
+ { 1, 1, 1, 7, 46, 46, },
+ { 0, 1, 1, 7, 54, 46, },
+ { 2, 1, 1, 7, 54, 46, },
+ { 1, 1, 1, 7, 54, 46, },
+ { 0, 1, 1, 7, 62, 46, },
+ { 2, 1, 1, 7, 62, 46, },
+ { 1, 1, 1, 7, 62, 46, },
+ { 0, 1, 1, 7, 102, 46, },
+ { 2, 1, 1, 7, 102, 46, },
+ { 1, 1, 1, 7, 102, 46, },
+ { 0, 1, 1, 7, 110, 46, },
+ { 2, 1, 1, 7, 110, 46, },
+ { 1, 1, 1, 7, 110, 46, },
+ { 0, 1, 1, 7, 118, 46, },
+ { 2, 1, 1, 7, 118, 46, },
+ { 1, 1, 1, 7, 118, 46, },
+ { 0, 1, 1, 7, 126, 46, },
+ { 2, 1, 1, 7, 126, 46, },
+ { 1, 1, 1, 7, 126, 46, },
+ { 0, 1, 1, 7, 134, 46, },
+ { 2, 1, 1, 7, 134, 46, },
+ { 1, 1, 1, 7, 134, 46, },
+ { 0, 1, 1, 7, 151, 46, },
+ { 2, 1, 1, 7, 151, 46, },
+ { 1, 1, 1, 7, 151, 63, },
+ { 0, 1, 1, 7, 159, 46, },
+ { 2, 1, 1, 7, 159, 46, },
+ { 1, 1, 1, 7, 159, 63, },
+ { 0, 1, 2, 4, 42, 46, },
+ { 2, 1, 2, 4, 42, 46, },
+ { 1, 1, 2, 4, 42, 46, },
+ { 0, 1, 2, 4, 58, 46, },
+ { 2, 1, 2, 4, 58, 46, },
+ { 1, 1, 2, 4, 58, 46, },
+ { 0, 1, 2, 4, 106, 46, },
+ { 2, 1, 2, 4, 106, 46, },
+ { 1, 1, 2, 4, 106, 46, },
+ { 0, 1, 2, 4, 122, 46, },
+ { 2, 1, 2, 4, 122, 46, },
+ { 1, 1, 2, 4, 122, 46, },
+ { 0, 1, 2, 4, 155, 46, },
+ { 2, 1, 2, 4, 155, 46, },
+ { 1, 1, 2, 4, 155, 63, },
+ { 0, 1, 2, 5, 42, 46, },
+ { 2, 1, 2, 5, 42, 46, },
+ { 1, 1, 2, 5, 42, 46, },
+ { 0, 1, 2, 5, 58, 46, },
+ { 2, 1, 2, 5, 58, 46, },
+ { 1, 1, 2, 5, 58, 46, },
+ { 0, 1, 2, 5, 106, 46, },
+ { 2, 1, 2, 5, 106, 46, },
+ { 1, 1, 2, 5, 106, 46, },
+ { 0, 1, 2, 5, 122, 46, },
+ { 2, 1, 2, 5, 122, 46, },
+ { 1, 1, 2, 5, 122, 46, },
+ { 0, 1, 2, 5, 155, 46, },
+ { 2, 1, 2, 5, 155, 46, },
+ { 1, 1, 2, 5, 155, 63, },
+ { 0, 1, 2, 8, 42, 46, },
+ { 2, 1, 2, 8, 42, 46, },
+ { 1, 1, 2, 8, 42, 46, },
+ { 0, 1, 2, 8, 58, 46, },
+ { 2, 1, 2, 8, 58, 46, },
+ { 1, 1, 2, 8, 58, 46, },
+ { 0, 1, 2, 8, 106, 46, },
+ { 2, 1, 2, 8, 106, 46, },
+ { 1, 1, 2, 8, 106, 46, },
+ { 0, 1, 2, 8, 122, 46, },
+ { 2, 1, 2, 8, 122, 46, },
+ { 1, 1, 2, 8, 122, 46, },
+ { 0, 1, 2, 8, 155, 46, },
+ { 2, 1, 2, 8, 155, 46, },
+ { 1, 1, 2, 8, 155, 63, },
+ { 0, 1, 2, 9, 42, 46, },
+ { 2, 1, 2, 9, 42, 46, },
+ { 1, 1, 2, 9, 42, 46, },
+ { 0, 1, 2, 9, 58, 46, },
+ { 2, 1, 2, 9, 58, 46, },
+ { 1, 1, 2, 9, 58, 46, },
+ { 0, 1, 2, 9, 106, 46, },
+ { 2, 1, 2, 9, 106, 46, },
+ { 1, 1, 2, 9, 106, 46, },
+ { 0, 1, 2, 9, 122, 46, },
+ { 2, 1, 2, 9, 122, 46, },
+ { 1, 1, 2, 9, 122, 46, },
+ { 0, 1, 2, 9, 155, 46, },
+ { 2, 1, 2, 9, 155, 46, },
+ { 1, 1, 2, 9, 155, 63, },
+};
+
+RTW_DECL_TABLE_TXPWR_LMT(rtw8814a_txpwr_lmt_type8);
+
+static const u8
+rtw8814a_pwrtrk_5gd_n[RTW_PWR_TRK_5G_NUM][RTW_PWR_TRK_TBL_SZ] = {
+ {0, 1, 1, 2, 3, 3, 4, 5, 5, 6, 7, 7, 8, 9, 9, 10, 11,
+ 11, 12, 13, 13, 14, 15, 15, 16, 17, 17, 18, 19, 19},
+ {0, 1, 1, 2, 2, 3, 4, 4, 5, 6, 6, 7, 7, 8, 9, 9, 10, 10,
+ 11, 12, 12, 13, 13, 14, 15, 15, 16, 17, 17, 18},
+ {0, 1, 1, 2, 3, 3, 4, 5, 5, 6, 6, 7, 8, 8, 9, 10, 10,
+ 11, 12, 12, 13, 14, 14, 15, 16, 16, 17, 17, 18, 19},
+};
+
+static const u8
+rtw8814a_pwrtrk_5gd_p[RTW_PWR_TRK_5G_NUM][RTW_PWR_TRK_TBL_SZ] = {
+ {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
+ 17, 18, 19, 20, 21, 22, 23, 24, 25, 25, 25, 25, 25},
+ {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
+ 17, 17, 18, 19, 20, 21, 22, 23, 24, 25, 25, 25, 25},
+ {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
+ 17, 17, 18, 19, 20, 21, 22, 23, 24, 25, 25, 25, 25},
+};
+
+static const u8
+rtw8814a_pwrtrk_5gc_n[RTW_PWR_TRK_5G_NUM][RTW_PWR_TRK_TBL_SZ] = {
+ {0, 1, 1, 2, 2, 3, 4, 4, 5, 5, 6, 7, 7, 8, 8, 9, 10, 10,
+ 11, 12, 13, 14, 15, 15, 15, 15, 16, 16, 17, 18},
+ {0, 1, 1, 2, 3, 3, 4, 5, 6, 6, 7, 8, 8, 9, 10, 10, 11,
+ 12, 12, 13, 14, 15, 15, 16, 17, 17, 18, 19, 19, 20},
+ {0, 1, 1, 2, 3, 4, 4, 5, 6, 6, 7, 8, 8, 9, 10, 11, 11,
+ 12, 13, 13, 14, 14, 15, 16, 17, 18, 18, 19, 20, 20},
+};
+
+static const u8
+rtw8814a_pwrtrk_5gc_p[RTW_PWR_TRK_5G_NUM][RTW_PWR_TRK_TBL_SZ] = {
+ {0, 1, 2, 3, 4, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
+ 16, 16, 17, 18, 19, 20, 21, 21, 22, 23, 24, 25, 25},
+ {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
+ 17, 17, 18, 19, 20, 21, 22, 23, 24, 24, 25, 25, 25},
+ {0, 1, 2, 3, 4, 5, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
+ 16, 16, 17, 18, 19, 20, 21, 22, 23, 23, 24, 25, 25},
+};
+
+static const u8
+rtw8814a_pwrtrk_5gb_n[RTW_PWR_TRK_5G_NUM][RTW_PWR_TRK_TBL_SZ] = {
+ {0, 1, 1, 2, 2, 3, 4, 4, 5, 5, 6, 7, 7, 8, 8, 9, 10, 10,
+ 11, 11, 12, 13, 13, 14, 14, 15, 15, 16, 17, 17},
+ {0, 1, 1, 2, 3, 3, 4, 5, 6, 6, 7, 8, 8, 9, 10, 10, 11,
+ 12, 12, 13, 14, 15, 15, 16, 17, 17, 18, 19, 19, 20},
+ {0, 1, 1, 2, 3, 4, 4, 5, 6, 6, 7, 8, 8, 9, 10, 11, 11,
+ 12, 13, 13, 14, 14, 15, 16, 17, 18, 18, 19, 20, 20},
+};
+
+static const u8
+rtw8814a_pwrtrk_5gb_p[RTW_PWR_TRK_5G_NUM][RTW_PWR_TRK_TBL_SZ] = {
+ {0, 1, 2, 3, 3, 4, 5, 6, 7, 8, 8, 9, 10, 11, 12, 13, 14,
+ 15, 15, 16, 17, 18, 18, 19, 20, 21, 22, 23, 23, 24},
+ {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
+ 17, 18, 18, 19, 20, 21, 22, 23, 24, 25, 25, 25, 25},
+ {0, 1, 2, 3, 4, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
+ 16, 16, 17, 18, 19, 20, 20, 21, 22, 23, 24, 25, 25},
+};
+
+static const u8
+rtw8814a_pwrtrk_5ga_n[RTW_PWR_TRK_5G_NUM][RTW_PWR_TRK_TBL_SZ] = {
+ {0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8,
+ 8, 9, 9, 10, 11, 11, 11, 11, 12, 12, 13, 13, 14},
+ {0, 1, 1, 2, 3, 4, 4, 5, 6, 6, 7, 8, 9, 9, 10, 11, 11,
+ 12, 13, 14, 14, 15, 16, 16, 17, 18, 19, 19, 20, 21},
+ {0, 1, 1, 2, 3, 3, 4, 5, 5, 6, 6, 7, 8, 8, 9, 10, 10,
+ 11, 12, 12, 13, 14, 14, 15, 16, 16, 17, 17, 18, 19},
+};
+
+static const u8
+rtw8814a_pwrtrk_5ga_p[RTW_PWR_TRK_5G_NUM][RTW_PWR_TRK_TBL_SZ] = {
+ {0, 1, 2, 2, 3, 4, 5, 6, 7, 7, 8, 9, 10, 11, 12, 12, 13,
+ 14, 15, 16, 16, 17, 18, 19, 20, 21, 21, 22, 23, 24},
+ {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
+ 17, 18, 19, 20, 21, 22, 23, 23, 24, 25, 25, 25, 25},
+ {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
+ 17, 17, 18, 19, 20, 21, 22, 23, 24, 25, 25, 25, 25},
+};
+
+static const u8 rtw8814a_pwrtrk_2gd_n[RTW_PWR_TRK_TBL_SZ] = {
+ 0, 0, 1, 1, 2, 2, 3, 3, 3, 4, 4, 5, 5, 6, 6, 6, 7,
+ 7, 8, 8, 9, 9, 9, 10, 10, 11, 11, 12, 12, 12
+};
+
+static const u8 rtw8814a_pwrtrk_2gd_p[RTW_PWR_TRK_TBL_SZ] = {
+ 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 4, 5, 5, 6, 6, 7, 7,
+ 8, 8, 9, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13
+};
+
+static const u8 rtw8814a_pwrtrk_2gc_n[RTW_PWR_TRK_TBL_SZ] = {
+ 0, 0, 1, 1, 2, 2, 2, 3, 3, 4, 4, 5, 5, 5, 6, 6, 7,
+ 7, 7, 8, 8, 9, 9, 10, 10, 10, 11, 11, 12, 12
+};
+
+static const u8 rtw8814a_pwrtrk_2gc_p[RTW_PWR_TRK_TBL_SZ] = {
+ 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 4, 5, 5, 6, 6, 7, 7,
+ 8, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 12, 13
+};
+
+static const u8 rtw8814a_pwrtrk_2gb_n[RTW_PWR_TRK_TBL_SZ] = {
+ 0, 0, 1, 1, 2, 2, 3, 3, 3, 4, 4, 5, 5, 5, 6, 6, 7,
+ 7, 8, 8, 8, 9, 9, 10, 10, 11, 11, 11, 12, 12
+};
+
+static const u8 rtw8814a_pwrtrk_2gb_p[RTW_PWR_TRK_TBL_SZ] = {
+ 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 4, 5, 5, 6, 6, 7, 7,
+ 8, 8, 9, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13
+};
+
+static const u8 rtw8814a_pwrtrk_2ga_n[RTW_PWR_TRK_TBL_SZ] = {
+ 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 4, 5, 5, 6, 6, 7, 7,
+ 7, 8, 8, 9, 9, 10, 10, 11, 11, 11, 12, 12, 13
+};
+
+static const u8 rtw8814a_pwrtrk_2ga_p[RTW_PWR_TRK_TBL_SZ] = {
+ 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8,
+ 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, 14, 14
+};
+
+static const u8 rtw8814a_pwrtrk_2g_cck_d_n[RTW_PWR_TRK_TBL_SZ] = {
+ 0, 0, 1, 1, 2, 2, 3, 3, 3, 4, 4, 5, 5, 5, 6, 6, 7,
+ 7, 8, 8, 8, 9, 9, 10, 10, 10, 11, 11, 12, 12
+};
+
+static const u8 rtw8814a_pwrtrk_2g_cck_d_p[RTW_PWR_TRK_TBL_SZ] = {
+ 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 4, 5, 5, 6, 6, 7, 7,
+ 8, 8, 9, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13
+};
+
+static const u8 rtw8814a_pwrtrk_2g_cck_c_n[RTW_PWR_TRK_TBL_SZ] = {
+ 0, 0, 1, 1, 2, 2, 2, 3, 3, 4, 4, 4, 5, 5, 6, 6, 6,
+ 7, 7, 8, 8, 8, 9, 9, 10, 10, 10, 11, 11, 12
+};
+
+static const u8 rtw8814a_pwrtrk_2g_cck_c_p[RTW_PWR_TRK_TBL_SZ] = {
+ 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 4, 5, 5, 6, 6, 7, 7,
+ 7, 8, 8, 9, 9, 10, 10, 11, 11, 11, 12, 12, 13
+};
+
+static const u8 rtw8814a_pwrtrk_2g_cck_b_n[RTW_PWR_TRK_TBL_SZ] = {
+ 0, 0, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 5, 5, 5, 6,
+ 6, 6, 7, 7, 8, 8, 8, 9, 9, 10, 10, 10, 11, 11
+};
+
+static const u8 rtw8814a_pwrtrk_2g_cck_b_p[RTW_PWR_TRK_TBL_SZ] = {
+ 0, 0, 1, 1, 2, 2, 3, 3, 3, 4, 4, 5, 5, 6, 6, 6, 7,
+ 7, 8, 8, 9, 9, 9, 10, 10, 11, 11, 12, 12, 12
+};
+
+static const u8 rtw8814a_pwrtrk_2g_cck_a_n[RTW_PWR_TRK_TBL_SZ] = {
+ 0, 0, 1, 1, 2, 2, 3, 3, 3, 4, 4, 5, 5, 5, 6, 6, 7,
+ 7, 8, 8, 8, 9, 9, 10, 10, 11, 11, 11, 12, 12
+};
+
+static const u8 rtw8814a_pwrtrk_2g_cck_a_p[RTW_PWR_TRK_TBL_SZ] = {
+ 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 7,
+ 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, 14
+};
+
+const struct rtw_pwr_track_tbl rtw8814a_rtw_pwrtrk_tbl = {
+ .pwrtrk_5gd_n[RTW_PWR_TRK_5G_1] = rtw8814a_pwrtrk_5gd_n[RTW_PWR_TRK_5G_1],
+ .pwrtrk_5gd_n[RTW_PWR_TRK_5G_2] = rtw8814a_pwrtrk_5gd_n[RTW_PWR_TRK_5G_2],
+ .pwrtrk_5gd_n[RTW_PWR_TRK_5G_3] = rtw8814a_pwrtrk_5gd_n[RTW_PWR_TRK_5G_3],
+ .pwrtrk_5gd_p[RTW_PWR_TRK_5G_1] = rtw8814a_pwrtrk_5gd_p[RTW_PWR_TRK_5G_1],
+ .pwrtrk_5gd_p[RTW_PWR_TRK_5G_2] = rtw8814a_pwrtrk_5gd_p[RTW_PWR_TRK_5G_2],
+ .pwrtrk_5gd_p[RTW_PWR_TRK_5G_3] = rtw8814a_pwrtrk_5gd_p[RTW_PWR_TRK_5G_3],
+ .pwrtrk_5gc_n[RTW_PWR_TRK_5G_1] = rtw8814a_pwrtrk_5gc_n[RTW_PWR_TRK_5G_1],
+ .pwrtrk_5gc_n[RTW_PWR_TRK_5G_2] = rtw8814a_pwrtrk_5gc_n[RTW_PWR_TRK_5G_2],
+ .pwrtrk_5gc_n[RTW_PWR_TRK_5G_3] = rtw8814a_pwrtrk_5gc_n[RTW_PWR_TRK_5G_3],
+ .pwrtrk_5gc_p[RTW_PWR_TRK_5G_1] = rtw8814a_pwrtrk_5gc_p[RTW_PWR_TRK_5G_1],
+ .pwrtrk_5gc_p[RTW_PWR_TRK_5G_2] = rtw8814a_pwrtrk_5gc_p[RTW_PWR_TRK_5G_2],
+ .pwrtrk_5gc_p[RTW_PWR_TRK_5G_3] = rtw8814a_pwrtrk_5gc_p[RTW_PWR_TRK_5G_3],
+ .pwrtrk_5gb_n[RTW_PWR_TRK_5G_1] = rtw8814a_pwrtrk_5gb_n[RTW_PWR_TRK_5G_1],
+ .pwrtrk_5gb_n[RTW_PWR_TRK_5G_2] = rtw8814a_pwrtrk_5gb_n[RTW_PWR_TRK_5G_2],
+ .pwrtrk_5gb_n[RTW_PWR_TRK_5G_3] = rtw8814a_pwrtrk_5gb_n[RTW_PWR_TRK_5G_3],
+ .pwrtrk_5gb_p[RTW_PWR_TRK_5G_1] = rtw8814a_pwrtrk_5gb_p[RTW_PWR_TRK_5G_1],
+ .pwrtrk_5gb_p[RTW_PWR_TRK_5G_2] = rtw8814a_pwrtrk_5gb_p[RTW_PWR_TRK_5G_2],
+ .pwrtrk_5gb_p[RTW_PWR_TRK_5G_3] = rtw8814a_pwrtrk_5gb_p[RTW_PWR_TRK_5G_3],
+ .pwrtrk_5ga_n[RTW_PWR_TRK_5G_1] = rtw8814a_pwrtrk_5ga_n[RTW_PWR_TRK_5G_1],
+ .pwrtrk_5ga_n[RTW_PWR_TRK_5G_2] = rtw8814a_pwrtrk_5ga_n[RTW_PWR_TRK_5G_2],
+ .pwrtrk_5ga_n[RTW_PWR_TRK_5G_3] = rtw8814a_pwrtrk_5ga_n[RTW_PWR_TRK_5G_3],
+ .pwrtrk_5ga_p[RTW_PWR_TRK_5G_1] = rtw8814a_pwrtrk_5ga_p[RTW_PWR_TRK_5G_1],
+ .pwrtrk_5ga_p[RTW_PWR_TRK_5G_2] = rtw8814a_pwrtrk_5ga_p[RTW_PWR_TRK_5G_2],
+ .pwrtrk_5ga_p[RTW_PWR_TRK_5G_3] = rtw8814a_pwrtrk_5ga_p[RTW_PWR_TRK_5G_3],
+ .pwrtrk_2gd_n = rtw8814a_pwrtrk_2gd_n,
+ .pwrtrk_2gd_p = rtw8814a_pwrtrk_2gd_p,
+ .pwrtrk_2gc_n = rtw8814a_pwrtrk_2gc_n,
+ .pwrtrk_2gc_p = rtw8814a_pwrtrk_2gc_p,
+ .pwrtrk_2gb_n = rtw8814a_pwrtrk_2gb_n,
+ .pwrtrk_2gb_p = rtw8814a_pwrtrk_2gb_p,
+ .pwrtrk_2ga_n = rtw8814a_pwrtrk_2ga_n,
+ .pwrtrk_2ga_p = rtw8814a_pwrtrk_2ga_p,
+ .pwrtrk_2g_cckd_n = rtw8814a_pwrtrk_2g_cck_d_n,
+ .pwrtrk_2g_cckd_p = rtw8814a_pwrtrk_2g_cck_d_p,
+ .pwrtrk_2g_cckc_n = rtw8814a_pwrtrk_2g_cck_c_n,
+ .pwrtrk_2g_cckc_p = rtw8814a_pwrtrk_2g_cck_c_p,
+ .pwrtrk_2g_cckb_n = rtw8814a_pwrtrk_2g_cck_b_n,
+ .pwrtrk_2g_cckb_p = rtw8814a_pwrtrk_2g_cck_b_p,
+ .pwrtrk_2g_ccka_n = rtw8814a_pwrtrk_2g_cck_a_n,
+ .pwrtrk_2g_ccka_p = rtw8814a_pwrtrk_2g_cck_a_p,
+};
+
+static const u8
+rtw8814a_pwrtrk_type0_5gd_n[RTW_PWR_TRK_5G_NUM][RTW_PWR_TRK_TBL_SZ] = {
+ {0, 0, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8,
+ 8, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10},
+ {0, 0, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8,
+ 8, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10},
+ {0, 0, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8,
+ 8, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10},
+};
+
+static const u8
+rtw8814a_pwrtrk_type0_5gd_p[RTW_PWR_TRK_5G_NUM][RTW_PWR_TRK_TBL_SZ] = {
+ {0, 0, 1, 1, 2, 3, 3, 4, 5, 5, 6, 6, 7, 8, 8, 9, 10, 10,
+ 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12},
+ {0, 0, 1, 1, 2, 3, 3, 4, 5, 5, 6, 6, 7, 8, 8, 9, 10, 10,
+ 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12},
+ {0, 0, 1, 1, 2, 3, 3, 4, 5, 5, 6, 6, 7, 8, 8, 9, 10, 10,
+ 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12},
+};
+
+static const u8
+rtw8814a_pwrtrk_type0_5gc_n[RTW_PWR_TRK_5G_NUM][RTW_PWR_TRK_TBL_SZ] = {
+ {0, 0, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8,
+ 8, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10},
+ {0, 0, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8,
+ 8, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10},
+ {0, 0, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8,
+ 8, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10},
+};
+
+static const u8
+rtw8814a_pwrtrk_type0_5gc_p[RTW_PWR_TRK_5G_NUM][RTW_PWR_TRK_TBL_SZ] = {
+ {0, 0, 1, 1, 2, 3, 3, 4, 5, 5, 6, 6, 7, 8, 8, 9, 10, 10,
+ 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12},
+ {0, 0, 1, 1, 2, 3, 3, 4, 5, 5, 6, 6, 7, 8, 8, 9, 10, 10,
+ 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12},
+ {0, 0, 1, 1, 2, 3, 3, 4, 5, 5, 6, 6, 7, 8, 8, 9, 10, 10,
+ 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12},
+};
+
+static const u8
+rtw8814a_pwrtrk_type0_5gb_n[RTW_PWR_TRK_5G_NUM][RTW_PWR_TRK_TBL_SZ] = {
+ {0, 0, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8,
+ 8, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10},
+ {0, 0, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8,
+ 8, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10},
+ {0, 0, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8,
+ 8, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10},
+};
+
+static const u8
+rtw8814a_pwrtrk_type0_5gb_p[RTW_PWR_TRK_5G_NUM][RTW_PWR_TRK_TBL_SZ] = {
+ {0, 0, 1, 1, 2, 3, 3, 4, 5, 5, 6, 6, 7, 8, 8, 9, 10, 10,
+ 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12},
+ {0, 0, 1, 1, 2, 3, 3, 4, 5, 5, 6, 6, 7, 8, 8, 9, 10, 10,
+ 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12},
+ {0, 0, 1, 1, 2, 3, 3, 4, 5, 5, 6, 6, 7, 8, 8, 9, 10, 10,
+ 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12},
+};
+
+static const u8
+rtw8814a_pwrtrk_type0_5ga_n[RTW_PWR_TRK_5G_NUM][RTW_PWR_TRK_TBL_SZ] = {
+ {0, 0, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8,
+ 8, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10},
+ {0, 0, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8,
+ 8, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10},
+ {0, 0, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8,
+ 8, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10},
+};
+
+static const u8
+rtw8814a_pwrtrk_type0_5ga_p[RTW_PWR_TRK_5G_NUM][RTW_PWR_TRK_TBL_SZ] = {
+ {0, 0, 1, 1, 2, 3, 3, 4, 5, 5, 6, 6, 7, 8, 8, 9, 10, 10,
+ 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12},
+ {0, 0, 1, 1, 2, 3, 3, 4, 5, 5, 6, 6, 7, 8, 8, 9, 10, 10,
+ 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12},
+ {0, 0, 1, 1, 2, 3, 3, 4, 5, 5, 6, 6, 7, 8, 8, 9, 10, 10,
+ 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12},
+};
+
+static const u8 rtw8814a_pwrtrk_type0_2gd_n[RTW_PWR_TRK_TBL_SZ] = {
+ 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 5, 6, 7, 7,
+ 7, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9
+};
+
+static const u8 rtw8814a_pwrtrk_type0_2gd_p[RTW_PWR_TRK_TBL_SZ] = {
+ 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7,
+ 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9
+};
+
+static const u8 rtw8814a_pwrtrk_type0_2gc_n[RTW_PWR_TRK_TBL_SZ] = {
+ 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 5, 6, 7, 7,
+ 7, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9
+};
+
+static const u8 rtw8814a_pwrtrk_type0_2gc_p[RTW_PWR_TRK_TBL_SZ] = {
+ 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7,
+ 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9
+};
+
+static const u8 rtw8814a_pwrtrk_type0_2gb_n[RTW_PWR_TRK_TBL_SZ] = {
+ 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 5, 6, 7, 7,
+ 7, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9
+};
+
+static const u8 rtw8814a_pwrtrk_type0_2gb_p[RTW_PWR_TRK_TBL_SZ] = {
+ 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7,
+ 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9
+};
+
+static const u8 rtw8814a_pwrtrk_type0_2ga_n[RTW_PWR_TRK_TBL_SZ] = {
+ 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 5, 6, 7, 7,
+ 7, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9
+};
+
+static const u8 rtw8814a_pwrtrk_type0_2ga_p[RTW_PWR_TRK_TBL_SZ] = {
+ 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7,
+ 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9
+};
+
+static const u8 rtw8814a_pwrtrk_type0_2g_cck_d_n[RTW_PWR_TRK_TBL_SZ] = {
+ 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 5, 6, 7, 7,
+ 7, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9
+};
+
+static const u8 rtw8814a_pwrtrk_type0_2g_cck_d_p[RTW_PWR_TRK_TBL_SZ] = {
+ 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7,
+ 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9
+};
+
+static const u8 rtw8814a_pwrtrk_type0_2g_cck_c_n[RTW_PWR_TRK_TBL_SZ] = {
+ 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 5, 6, 7, 7,
+ 7, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9
+};
+
+static const u8 rtw8814a_pwrtrk_type0_2g_cck_c_p[RTW_PWR_TRK_TBL_SZ] = {
+ 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7,
+ 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9
+};
+
+static const u8 rtw8814a_pwrtrk_type0_2g_cck_b_n[RTW_PWR_TRK_TBL_SZ] = {
+ 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 5, 6, 7, 7,
+ 7, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9
+};
+
+static const u8 rtw8814a_pwrtrk_type0_2g_cck_b_p[RTW_PWR_TRK_TBL_SZ] = {
+ 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7,
+ 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9
+};
+
+static const u8 rtw8814a_pwrtrk_type0_2g_cck_a_n[RTW_PWR_TRK_TBL_SZ] = {
+ 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 5, 6, 7, 7,
+ 7, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9
+};
+
+static const u8 rtw8814a_pwrtrk_type0_2g_cck_a_p[RTW_PWR_TRK_TBL_SZ] = {
+ 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7,
+ 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9
+};
+
+const struct rtw_pwr_track_tbl rtw8814a_rtw_pwrtrk_type0_tbl = {
+ .pwrtrk_5gd_n[RTW_PWR_TRK_5G_1] = rtw8814a_pwrtrk_type0_5gd_n[RTW_PWR_TRK_5G_1],
+ .pwrtrk_5gd_n[RTW_PWR_TRK_5G_2] = rtw8814a_pwrtrk_type0_5gd_n[RTW_PWR_TRK_5G_2],
+ .pwrtrk_5gd_n[RTW_PWR_TRK_5G_3] = rtw8814a_pwrtrk_type0_5gd_n[RTW_PWR_TRK_5G_3],
+ .pwrtrk_5gd_p[RTW_PWR_TRK_5G_1] = rtw8814a_pwrtrk_type0_5gd_p[RTW_PWR_TRK_5G_1],
+ .pwrtrk_5gd_p[RTW_PWR_TRK_5G_2] = rtw8814a_pwrtrk_type0_5gd_p[RTW_PWR_TRK_5G_2],
+ .pwrtrk_5gd_p[RTW_PWR_TRK_5G_3] = rtw8814a_pwrtrk_type0_5gd_p[RTW_PWR_TRK_5G_3],
+ .pwrtrk_5gc_n[RTW_PWR_TRK_5G_1] = rtw8814a_pwrtrk_type0_5gc_n[RTW_PWR_TRK_5G_1],
+ .pwrtrk_5gc_n[RTW_PWR_TRK_5G_2] = rtw8814a_pwrtrk_type0_5gc_n[RTW_PWR_TRK_5G_2],
+ .pwrtrk_5gc_n[RTW_PWR_TRK_5G_3] = rtw8814a_pwrtrk_type0_5gc_n[RTW_PWR_TRK_5G_3],
+ .pwrtrk_5gc_p[RTW_PWR_TRK_5G_1] = rtw8814a_pwrtrk_type0_5gc_p[RTW_PWR_TRK_5G_1],
+ .pwrtrk_5gc_p[RTW_PWR_TRK_5G_2] = rtw8814a_pwrtrk_type0_5gc_p[RTW_PWR_TRK_5G_2],
+ .pwrtrk_5gc_p[RTW_PWR_TRK_5G_3] = rtw8814a_pwrtrk_type0_5gc_p[RTW_PWR_TRK_5G_3],
+ .pwrtrk_5gb_n[RTW_PWR_TRK_5G_1] = rtw8814a_pwrtrk_type0_5gb_n[RTW_PWR_TRK_5G_1],
+ .pwrtrk_5gb_n[RTW_PWR_TRK_5G_2] = rtw8814a_pwrtrk_type0_5gb_n[RTW_PWR_TRK_5G_2],
+ .pwrtrk_5gb_n[RTW_PWR_TRK_5G_3] = rtw8814a_pwrtrk_type0_5gb_n[RTW_PWR_TRK_5G_3],
+ .pwrtrk_5gb_p[RTW_PWR_TRK_5G_1] = rtw8814a_pwrtrk_type0_5gb_p[RTW_PWR_TRK_5G_1],
+ .pwrtrk_5gb_p[RTW_PWR_TRK_5G_2] = rtw8814a_pwrtrk_type0_5gb_p[RTW_PWR_TRK_5G_2],
+ .pwrtrk_5gb_p[RTW_PWR_TRK_5G_3] = rtw8814a_pwrtrk_type0_5gb_p[RTW_PWR_TRK_5G_3],
+ .pwrtrk_5ga_n[RTW_PWR_TRK_5G_1] = rtw8814a_pwrtrk_type0_5ga_n[RTW_PWR_TRK_5G_1],
+ .pwrtrk_5ga_n[RTW_PWR_TRK_5G_2] = rtw8814a_pwrtrk_type0_5ga_n[RTW_PWR_TRK_5G_2],
+ .pwrtrk_5ga_n[RTW_PWR_TRK_5G_3] = rtw8814a_pwrtrk_type0_5ga_n[RTW_PWR_TRK_5G_3],
+ .pwrtrk_5ga_p[RTW_PWR_TRK_5G_1] = rtw8814a_pwrtrk_type0_5ga_p[RTW_PWR_TRK_5G_1],
+ .pwrtrk_5ga_p[RTW_PWR_TRK_5G_2] = rtw8814a_pwrtrk_type0_5ga_p[RTW_PWR_TRK_5G_2],
+ .pwrtrk_5ga_p[RTW_PWR_TRK_5G_3] = rtw8814a_pwrtrk_type0_5ga_p[RTW_PWR_TRK_5G_3],
+ .pwrtrk_2gd_n = rtw8814a_pwrtrk_type0_2gd_n,
+ .pwrtrk_2gd_p = rtw8814a_pwrtrk_type0_2gd_p,
+ .pwrtrk_2gc_n = rtw8814a_pwrtrk_type0_2gc_n,
+ .pwrtrk_2gc_p = rtw8814a_pwrtrk_type0_2gc_p,
+ .pwrtrk_2gb_n = rtw8814a_pwrtrk_type0_2gb_n,
+ .pwrtrk_2gb_p = rtw8814a_pwrtrk_type0_2gb_p,
+ .pwrtrk_2ga_n = rtw8814a_pwrtrk_type0_2ga_n,
+ .pwrtrk_2ga_p = rtw8814a_pwrtrk_type0_2ga_p,
+ .pwrtrk_2g_cckd_n = rtw8814a_pwrtrk_type0_2g_cck_d_n,
+ .pwrtrk_2g_cckd_p = rtw8814a_pwrtrk_type0_2g_cck_d_p,
+ .pwrtrk_2g_cckc_n = rtw8814a_pwrtrk_type0_2g_cck_c_n,
+ .pwrtrk_2g_cckc_p = rtw8814a_pwrtrk_type0_2g_cck_c_p,
+ .pwrtrk_2g_cckb_n = rtw8814a_pwrtrk_type0_2g_cck_b_n,
+ .pwrtrk_2g_cckb_p = rtw8814a_pwrtrk_type0_2g_cck_b_p,
+ .pwrtrk_2g_ccka_n = rtw8814a_pwrtrk_type0_2g_cck_a_n,
+ .pwrtrk_2g_ccka_p = rtw8814a_pwrtrk_type0_2g_cck_a_p,
+};
+
+static const u8
+rtw8814a_pwrtrk_type2_5gd_n[RTW_PWR_TRK_5G_NUM][RTW_PWR_TRK_TBL_SZ] = {
+ {0, 1, 2, 3, 4, 5, 6, 7, 7, 8, 9, 10, 10, 11, 11, 12, 12,
+ 12, 13, 14, 15, 15, 16, 16, 16, 16, 16, 16, 16, 16},
+ {0, 1, 2, 3, 4, 5, 5, 6, 6, 7, 8, 9, 9, 10, 10, 10, 10,
+ 11, 11, 12, 12, 13, 14, 15, 16, 16, 16, 16, 16, 16},
+ {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 9, 10, 10, 11, 11, 11, 12,
+ 12, 13, 14, 14, 15, 15, 15, 15, 15, 15, 15, 15, 15},
+};
+
+static const u8
+rtw8814a_pwrtrk_type2_5gd_p[RTW_PWR_TRK_5G_NUM][RTW_PWR_TRK_TBL_SZ] = {
+ {0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 9, 10, 11, 12, 13, 14,
+ 15, 16, 17, 18, 19, 20, 21, 22, 22, 22, 22, 22, 22},
+ {0, 1, 2, 3, 4, 4, 5, 6, 7, 7, 8, 9, 10, 11, 12, 13, 14,
+ 15, 16, 17, 18, 19, 20, 21, 22, 22, 22, 22, 22, 22},
+ {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
+ 17, 18, 19, 20, 21, 22, 23, 23, 23, 23, 23, 23, 23},
+};
+
+static const u8
+rtw8814a_pwrtrk_type2_5gc_n[RTW_PWR_TRK_5G_NUM][RTW_PWR_TRK_TBL_SZ] = {
+ {0, 1, 2, 3, 3, 4, 5, 6, 7, 7, 8, 9, 10, 10, 11, 11, 12,
+ 12, 13, 14, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15},
+ {0, 1, 2, 3, 4, 5, 5, 6, 7, 8, 9, 9, 10, 10, 11, 11, 12,
+ 13, 13, 14, 14, 15, 15, 15, 15, 15, 15, 15, 15, 15},
+ {0, 1, 2, 3, 4, 4, 5, 6, 6, 7, 8, 8, 9, 9, 10, 11, 11,
+ 12, 12, 13, 13, 13, 14, 14, 14, 14, 14, 14, 14, 14},
+};
+
+static const u8
+rtw8814a_pwrtrk_type2_5gc_p[RTW_PWR_TRK_5G_NUM][RTW_PWR_TRK_TBL_SZ] = {
+ {0, 1, 2, 3, 4, 5, 6, 6, 7, 8, 9, 10, 11, 11, 12, 13, 14,
+ 15, 16, 17, 18, 19, 20, 21, 21, 21, 21, 21, 21, 21},
+ {0, 1, 2, 3, 4, 5, 6, 6, 7, 8, 8, 9, 10, 11, 12, 13, 14,
+ 15, 16, 17, 18, 19, 20, 20, 20, 21, 21, 21, 21, 21},
+ {0, 1, 2, 3, 3, 4, 5, 6, 7, 7, 8, 9, 10, 11, 12, 13, 14,
+ 15, 16, 17, 18, 19, 20, 21, 21, 21, 21, 21, 21, 21},
+};
+
+static const u8
+rtw8814a_pwrtrk_type2_5gb_n[RTW_PWR_TRK_5G_NUM][RTW_PWR_TRK_TBL_SZ] = {
+ {0, 1, 2, 2, 3, 4, 5, 6, 7, 8, 9, 10, 10, 10, 11, 12, 13,
+ 13, 13, 14, 14, 14, 15, 15, 15, 15, 15, 15, 15, 15},
+ {0, 1, 2, 3, 4, 5, 5, 6, 7, 8, 9, 9, 10, 10, 11, 11, 12,
+ 13, 13, 14, 14, 15, 15, 15, 15, 15, 15, 15, 15, 15},
+ {0, 1, 2, 3, 4, 4, 5, 6, 6, 7, 8, 8, 9, 9, 10, 11, 11,
+ 12, 12, 13, 13, 13, 14, 14, 14, 14, 14, 14, 14, 14},
+};
+
+static const u8
+rtw8814a_pwrtrk_type2_5gb_p[RTW_PWR_TRK_5G_NUM][RTW_PWR_TRK_TBL_SZ] = {
+ {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 11, 12, 13, 14, 15,
+ 16, 17, 18, 19, 20, 21, 21, 21, 21, 21, 21, 21, 21},
+ {0, 0, 1, 2, 3, 4, 5, 6, 6, 7, 8, 9, 10, 10, 11, 12, 13,
+ 14, 15, 16, 17, 18, 19, 20, 20, 20, 20, 20, 20, 20},
+ {0, 1, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 11, 12, 13, 14,
+ 15, 16, 17, 18, 19, 20, 21, 21, 21, 21, 21, 21, 21},
+};
+
+static const u8
+rtw8814a_pwrtrk_type2_5ga_n[RTW_PWR_TRK_5G_NUM][RTW_PWR_TRK_TBL_SZ] = {
+ {0, 1, 2, 3, 4, 5, 5, 6, 7, 7, 8, 9, 10, 10, 11, 11, 11,
+ 12, 13, 13, 13, 13, 14, 15, 15, 15, 15, 15, 15, 15},
+ {0, 1, 2, 3, 3, 4, 5, 6, 7, 8, 8, 9, 10, 10, 11, 12, 12,
+ 12, 13, 13, 13, 14, 14, 14, 14, 14, 14, 14, 14, 14},
+ {0, 1, 2, 3, 4, 4, 5, 6, 7, 7, 8, 9, 10, 11, 11, 11, 12,
+ 12, 12, 12, 12, 13, 13, 13, 13, 13, 13, 13, 13, 13},
+};
+
+static const u8
+rtw8814a_pwrtrk_type2_5ga_p[RTW_PWR_TRK_5G_NUM][RTW_PWR_TRK_TBL_SZ] = {
+ {0, 1, 2, 3, 4, 5, 5, 6, 7, 8, 9, 10, 10, 11, 12, 13, 14,
+ 15, 15, 16, 17, 18, 18, 19, 19, 20, 20, 20, 20, 20},
+ {0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 10, 11, 12, 13, 14,
+ 15, 16, 16, 17, 18, 19, 20, 20, 20, 20, 20, 20, 20},
+ {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 11, 12, 13, 14, 15,
+ 15, 16, 17, 18, 19, 19, 20, 20, 20, 20, 20, 20, 20},
+};
+
+static const u8 rtw8814a_pwrtrk_type2_2gd_n[RTW_PWR_TRK_TBL_SZ] = {
+ 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 5,
+ 6, 6, 7, 8, 9, 10, 11, 11, 11, 11, 11, 11, 11
+};
+
+static const u8 rtw8814a_pwrtrk_type2_2gd_p[RTW_PWR_TRK_TBL_SZ] = {
+ 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 6, 6, 7, 7, 8, 9, 9, 10,
+ 10, 11, 12, 12, 13, 14, 14, 14, 14, 14, 14, 14
+};
+
+static const u8 rtw8814a_pwrtrk_type2_2gc_n[RTW_PWR_TRK_TBL_SZ] = {
+ 0, 1, 1, 2, 2, 3, 3, 4, 5, 6, 6, 6, 7, 7, 8, 8, 9, 10,
+ 10, 11, 12, 12, 13, 13, 13, 13, 14, 14, 14, 14
+};
+
+static const u8 rtw8814a_pwrtrk_type2_2gc_p[RTW_PWR_TRK_TBL_SZ] = {
+ 0, 1, 1, 2, 3, 3, 4, 4, 4, 5, 5, 6, 7, 8, 8, 9, 10, 10,
+ 11, 12, 13, 13, 14, 14, 14, 14, 14, 14, 14, 14
+};
+
+static const u8 rtw8814a_pwrtrk_type2_2gb_n[RTW_PWR_TRK_TBL_SZ] = {
+ 0, 1, 1, 2, 2, 3, 3, 4, 5, 5, 6, 6, 7, 7, 8, 8, 8,
+ 9, 9, 10, 10, 11, 11, 12, 12, 12, 13, 14, 14, 14
+};
+
+static const u8 rtw8814a_pwrtrk_type2_2gb_p[RTW_PWR_TRK_TBL_SZ] = {
+ 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 6, 6, 7, 8, 8, 9, 9, 10,
+ 10, 11, 12, 12, 13, 13, 13, 13, 13, 14, 14, 14
+};
+
+static const u8 rtw8814a_pwrtrk_type2_2ga_n[RTW_PWR_TRK_TBL_SZ] = {
+ 0, 0, 1, 2, 2, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 9, 9, 9,
+ 10, 11, 11, 12, 12, 13, 13, 13, 13, 13, 13, 13
+};
+
+static const u8 rtw8814a_pwrtrk_type2_2ga_p[RTW_PWR_TRK_TBL_SZ] = {
+ 0, 1, 1, 2, 3, 3, 4, 4, 4, 5, 6, 6, 7, 7, 8, 8, 9, 10,
+ 11, 12, 12, 13, 13, 14, 14, 14, 14, 14, 14, 14
+};
+
+static const u8 rtw8814a_pwrtrk_type2_2g_cck_d_n[RTW_PWR_TRK_TBL_SZ] = {
+ 0, 1, 1, 2, 3, 3, 3, 4, 4, 4, 5, 5, 5, 6, 6, 7, 8,
+ 9, 9, 9, 9, 9, 9, 10, 10, 11, 11, 12, 12, 12
+};
+
+static const u8 rtw8814a_pwrtrk_type2_2g_cck_d_p[RTW_PWR_TRK_TBL_SZ] = {
+ 0, 1, 1, 1, 2, 3, 3, 4, 4, 5, 6, 6, 7, 8, 8, 9, 10, 10,
+ 10, 11, 12, 12, 13, 14, 14, 14, 14, 14, 14, 14
+};
+
+static const u8 rtw8814a_pwrtrk_type2_2g_cck_c_n[RTW_PWR_TRK_TBL_SZ] = {
+ 0, 0, 1, 2, 2, 3, 4, 5, 5, 6, 6, 6, 6, 7, 8, 9, 9, 10,
+ 10, 11, 11, 11, 12, 13, 13, 13, 13, 13, 13, 13
+};
+
+static const u8 rtw8814a_pwrtrk_type2_2g_cck_c_p[RTW_PWR_TRK_TBL_SZ] = {
+ 0, 0, 1, 1, 2, 2, 3, 3, 4, 5, 5, 6, 7, 7, 8, 9, 9, 10,
+ 10, 11, 12, 12, 13, 13, 13, 13, 13, 13, 13, 13
+};
+
+static const u8 rtw8814a_pwrtrk_type2_2g_cck_b_n[RTW_PWR_TRK_TBL_SZ] = {
+ 0, 1, 1, 2, 2, 2, 3, 3, 4, 5, 6, 6, 7, 7, 8, 8, 9, 10,
+ 10, 10, 11, 11, 12, 12, 12, 12, 12, 12, 12, 12
+};
+
+static const u8 rtw8814a_pwrtrk_type2_2g_cck_b_p[RTW_PWR_TRK_TBL_SZ] = {
+ 0, 1, 1, 1, 2, 3, 3, 4, 4, 5, 5, 6, 7, 8, 8, 9, 9, 10,
+ 10, 11, 12, 12, 13, 13, 13, 13, 13, 13, 13, 13
+};
+
+static const u8 rtw8814a_pwrtrk_type2_2g_cck_a_n[RTW_PWR_TRK_TBL_SZ] = {
+ 0, 1, 2, 2, 3, 4, 4, 4, 5, 5, 6, 6, 7, 8, 9, 9, 9, 9,
+ 10, 10, 11, 11, 11, 12, 12, 12, 12, 12, 12, 12
+};
+
+static const u8 rtw8814a_pwrtrk_type2_2g_cck_a_p[RTW_PWR_TRK_TBL_SZ] = {
+ 0, 0, 1, 2, 2, 3, 3, 4, 5, 5, 6, 7, 8, 9, 9, 10, 10,
+ 11, 11, 12, 12, 12, 13, 13, 13, 13, 13, 13, 13, 13
+};
+
+const struct rtw_pwr_track_tbl rtw8814a_rtw_pwrtrk_type2_tbl = {
+ .pwrtrk_5gd_n[RTW_PWR_TRK_5G_1] = rtw8814a_pwrtrk_type2_5gd_n[RTW_PWR_TRK_5G_1],
+ .pwrtrk_5gd_n[RTW_PWR_TRK_5G_2] = rtw8814a_pwrtrk_type2_5gd_n[RTW_PWR_TRK_5G_2],
+ .pwrtrk_5gd_n[RTW_PWR_TRK_5G_3] = rtw8814a_pwrtrk_type2_5gd_n[RTW_PWR_TRK_5G_3],
+ .pwrtrk_5gd_p[RTW_PWR_TRK_5G_1] = rtw8814a_pwrtrk_type2_5gd_p[RTW_PWR_TRK_5G_1],
+ .pwrtrk_5gd_p[RTW_PWR_TRK_5G_2] = rtw8814a_pwrtrk_type2_5gd_p[RTW_PWR_TRK_5G_2],
+ .pwrtrk_5gd_p[RTW_PWR_TRK_5G_3] = rtw8814a_pwrtrk_type2_5gd_p[RTW_PWR_TRK_5G_3],
+ .pwrtrk_5gc_n[RTW_PWR_TRK_5G_1] = rtw8814a_pwrtrk_type2_5gc_n[RTW_PWR_TRK_5G_1],
+ .pwrtrk_5gc_n[RTW_PWR_TRK_5G_2] = rtw8814a_pwrtrk_type2_5gc_n[RTW_PWR_TRK_5G_2],
+ .pwrtrk_5gc_n[RTW_PWR_TRK_5G_3] = rtw8814a_pwrtrk_type2_5gc_n[RTW_PWR_TRK_5G_3],
+ .pwrtrk_5gc_p[RTW_PWR_TRK_5G_1] = rtw8814a_pwrtrk_type2_5gc_p[RTW_PWR_TRK_5G_1],
+ .pwrtrk_5gc_p[RTW_PWR_TRK_5G_2] = rtw8814a_pwrtrk_type2_5gc_p[RTW_PWR_TRK_5G_2],
+ .pwrtrk_5gc_p[RTW_PWR_TRK_5G_3] = rtw8814a_pwrtrk_type2_5gc_p[RTW_PWR_TRK_5G_3],
+ .pwrtrk_5gb_n[RTW_PWR_TRK_5G_1] = rtw8814a_pwrtrk_type2_5gb_n[RTW_PWR_TRK_5G_1],
+ .pwrtrk_5gb_n[RTW_PWR_TRK_5G_2] = rtw8814a_pwrtrk_type2_5gb_n[RTW_PWR_TRK_5G_2],
+ .pwrtrk_5gb_n[RTW_PWR_TRK_5G_3] = rtw8814a_pwrtrk_type2_5gb_n[RTW_PWR_TRK_5G_3],
+ .pwrtrk_5gb_p[RTW_PWR_TRK_5G_1] = rtw8814a_pwrtrk_type2_5gb_p[RTW_PWR_TRK_5G_1],
+ .pwrtrk_5gb_p[RTW_PWR_TRK_5G_2] = rtw8814a_pwrtrk_type2_5gb_p[RTW_PWR_TRK_5G_2],
+ .pwrtrk_5gb_p[RTW_PWR_TRK_5G_3] = rtw8814a_pwrtrk_type2_5gb_p[RTW_PWR_TRK_5G_3],
+ .pwrtrk_5ga_n[RTW_PWR_TRK_5G_1] = rtw8814a_pwrtrk_type2_5ga_n[RTW_PWR_TRK_5G_1],
+ .pwrtrk_5ga_n[RTW_PWR_TRK_5G_2] = rtw8814a_pwrtrk_type2_5ga_n[RTW_PWR_TRK_5G_2],
+ .pwrtrk_5ga_n[RTW_PWR_TRK_5G_3] = rtw8814a_pwrtrk_type2_5ga_n[RTW_PWR_TRK_5G_3],
+ .pwrtrk_5ga_p[RTW_PWR_TRK_5G_1] = rtw8814a_pwrtrk_type2_5ga_p[RTW_PWR_TRK_5G_1],
+ .pwrtrk_5ga_p[RTW_PWR_TRK_5G_2] = rtw8814a_pwrtrk_type2_5ga_p[RTW_PWR_TRK_5G_2],
+ .pwrtrk_5ga_p[RTW_PWR_TRK_5G_3] = rtw8814a_pwrtrk_type2_5ga_p[RTW_PWR_TRK_5G_3],
+ .pwrtrk_2gd_n = rtw8814a_pwrtrk_type2_2gd_n,
+ .pwrtrk_2gd_p = rtw8814a_pwrtrk_type2_2gd_p,
+ .pwrtrk_2gc_n = rtw8814a_pwrtrk_type2_2gc_n,
+ .pwrtrk_2gc_p = rtw8814a_pwrtrk_type2_2gc_p,
+ .pwrtrk_2gb_n = rtw8814a_pwrtrk_type2_2gb_n,
+ .pwrtrk_2gb_p = rtw8814a_pwrtrk_type2_2gb_p,
+ .pwrtrk_2ga_n = rtw8814a_pwrtrk_type2_2ga_n,
+ .pwrtrk_2ga_p = rtw8814a_pwrtrk_type2_2ga_p,
+ .pwrtrk_2g_cckd_n = rtw8814a_pwrtrk_type2_2g_cck_d_n,
+ .pwrtrk_2g_cckd_p = rtw8814a_pwrtrk_type2_2g_cck_d_p,
+ .pwrtrk_2g_cckc_n = rtw8814a_pwrtrk_type2_2g_cck_c_n,
+ .pwrtrk_2g_cckc_p = rtw8814a_pwrtrk_type2_2g_cck_c_p,
+ .pwrtrk_2g_cckb_n = rtw8814a_pwrtrk_type2_2g_cck_b_n,
+ .pwrtrk_2g_cckb_p = rtw8814a_pwrtrk_type2_2g_cck_b_p,
+ .pwrtrk_2g_ccka_n = rtw8814a_pwrtrk_type2_2g_cck_a_n,
+ .pwrtrk_2g_ccka_p = rtw8814a_pwrtrk_type2_2g_cck_a_p,
+};
+
+static const u8
+rtw8814a_pwrtrk_type5_5gd_n[RTW_PWR_TRK_5G_NUM][RTW_PWR_TRK_TBL_SZ] = {
+ {0, 3, 3, 3, 4, 6, 6, 7, 7, 8, 9, 10, 11, 12, 13, 13, 14,
+ 14, 14, 14, 14, 15, 15, 15, 15, 15, 15, 15, 15, 15},
+ {0, 4, 5, 6, 7, 7, 8, 7, 8, 10, 11, 12, 12, 13, 13, 14, 14,
+ 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15},
+ {0, 5, 5, 6, 7, 8, 9, 9, 10, 11, 12, 13, 13, 14, 15, 15, 16,
+ 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16},
+};
+
+static const u8
+rtw8814a_pwrtrk_type5_5gd_p[RTW_PWR_TRK_5G_NUM][RTW_PWR_TRK_TBL_SZ] = {
+ {0, 0, 0, 1, 2, 2, 3, 4, 5, 5, 6, 7, 9, 10, 10, 11, 12,
+ 12, 12, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13},
+ {0, 0, 0, 1, 1, 1, 2, 3, 4, 5, 6, 7, 8, 8, 9, 11, 12,
+ 12, 13, 13, 13, 13, 13, 14, 14, 14, 14, 14, 14, 14},
+ {0, 0, 0, 1, 1, 2, 2, 3, 5, 7, 8, 9, 10, 11, 12, 13, 13,
+ 13, 13, 13, 13, 14, 14, 14, 14, 14, 14, 14, 14, 14},
+};
+
+static const u8
+rtw8814a_pwrtrk_type5_5gc_n[RTW_PWR_TRK_5G_NUM][RTW_PWR_TRK_TBL_SZ] = {
+ {0, 1, 1, 2, 3, 3, 4, 6, 7, 7, 8, 9, 9, 9, 10, 10, 10,
+ 10, 11, 11, 11, 11, 11, 12, 12, 12, 12, 12, 12, 12},
+ {0, 1, 2, 3, 3, 7, 7, 8, 8, 9, 11, 12, 12, 13, 14, 14, 15,
+ 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15},
+ {0, 0, 1, 2, 3, 4, 5, 7, 8, 8, 10, 11, 12, 12, 13, 13, 14,
+ 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14},
+};
+
+static const u8
+rtw8814a_pwrtrk_type5_5gc_p[RTW_PWR_TRK_5G_NUM][RTW_PWR_TRK_TBL_SZ] = {
+ {0, 0, 0, 1, 1, 2, 2, 3, 4, 5, 6, 6, 7, 8, 9, 11, 11,
+ 11, 11, 11, 11, 12, 12, 12, 12, 12, 13, 13, 13, 13},
+ {0, 0, 1, 2, 3, 3, 5, 5, 6, 8, 8, 9, 10, 11, 13, 13, 13,
+ 13, 13, 13, 13, 14, 14, 14, 14, 14, 14, 14, 14, 14},
+ {0, 0, 1, 2, 3, 4, 4, 5, 7, 8, 9, 9, 10, 11, 12, 12, 12,
+ 12, 13, 13, 13, 13, 13, 13, 14, 14, 14, 14, 14, 14},
+};
+
+static const u8
+rtw8814a_pwrtrk_type5_5gb_n[RTW_PWR_TRK_5G_NUM][RTW_PWR_TRK_TBL_SZ] = {
+ {0, 1, 1, 2, 2, 2, 3, 4, 5, 6, 7, 9, 10, 10, 10, 10, 10,
+ 10, 11, 11, 11, 11, 11, 12, 12, 12, 12, 12, 12, 12},
+ {0, 1, 2, 3, 3, 7, 7, 8, 8, 9, 11, 12, 12, 13, 14, 14, 15,
+ 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15},
+ {0, 0, 1, 2, 3, 4, 5, 7, 8, 8, 10, 11, 12, 12, 13, 13, 14,
+ 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14},
+};
+
+static const u8
+rtw8814a_pwrtrk_type5_5gb_p[RTW_PWR_TRK_5G_NUM][RTW_PWR_TRK_TBL_SZ] = {
+ {0, 1, 1, 1, 2, 2, 4, 5, 6, 6, 7, 8, 9, 10, 11, 11, 11,
+ 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12},
+ {0, 0, 0, 2, 3, 4, 5, 6, 8, 8, 9, 9, 11, 12, 13, 13, 13,
+ 13, 13, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14},
+ {0, 0, 0, 1, 2, 3, 3, 4, 6, 7, 8, 9, 10, 11, 12, 12, 12,
+ 12, 13, 13, 13, 13, 13, 13, 14, 14, 14, 14, 14, 14},
+};
+
+static const u8
+rtw8814a_pwrtrk_type5_5ga_n[RTW_PWR_TRK_5G_NUM][RTW_PWR_TRK_TBL_SZ] = {
+ {0, 0, 0, 1, 2, 3, 3, 3, 4, 5, 6, 6, 7, 8, 8, 9, 10, 10,
+ 10, 11, 11, 11, 11, 11, 11, 12, 12, 12, 12, 12},
+ {0, 2, 3, 4, 5, 7, 7, 8, 9, 10, 12, 13, 14, 15, 16, 17, 17,
+ 17, 17, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18},
+ {0, 1, 2, 3, 3, 4, 6, 7, 8, 8, 10, 11, 11, 12, 13, 13, 13,
+ 13, 13, 13, 13, 14, 14, 14, 14, 14, 14, 14, 14, 14},
+};
+
+static const u8
+rtw8814a_pwrtrk_type5_5ga_p[RTW_PWR_TRK_5G_NUM][RTW_PWR_TRK_TBL_SZ] = {
+ {0, 1, 1, 3, 3, 3, 5, 5, 6, 6, 8, 8, 9, 10, 11, 11, 11,
+ 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12},
+ {0, 1, 2, 3, 4, 4, 5, 6, 7, 8, 8, 9, 11, 12, 13, 14, 15,
+ 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15},
+ {0, 0, 1, 3, 3, 4, 5, 5, 6, 7, 7, 8, 10, 10, 11, 11, 11,
+ 11, 12, 12, 12, 12, 12, 13, 13, 13, 13, 13, 13, 13},
+};
+
+static const u8 rtw8814a_pwrtrk_type5_2gd_n[RTW_PWR_TRK_TBL_SZ] = {
+ 0, 1, 2, 2, 3, 4, 4, 5, 6, 6, 7, 7, 7, 8, 9, 9, 9,
+ 9, 9, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11
+};
+
+static const u8 rtw8814a_pwrtrk_type5_2gd_p[RTW_PWR_TRK_TBL_SZ] = {
+ 0, 1, 1, 2, 2, 2, 3, 4, 4, 5, 6, 6, 7, 7, 8, 8,
+ 8, 8, 8, 9, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10
+};
+
+static const u8 rtw8814a_pwrtrk_type5_2gc_n[RTW_PWR_TRK_TBL_SZ] = {
+ 0, 1, 2, 2, 3, 4, 4, 5, 6, 6, 7, 8, 8, 9, 10, 10, 10,
+ 10, 10, 10, 11, 11, 11, 11, 11, 12, 12, 12, 12, 12
+};
+
+static const u8 rtw8814a_pwrtrk_type5_2gc_p[RTW_PWR_TRK_TBL_SZ] = {
+ 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 8, 8, 9, 9, 9,
+ 9, 9, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11
+};
+
+static const u8 rtw8814a_pwrtrk_type5_2gb_n[RTW_PWR_TRK_TBL_SZ] = {
+ 0, 1, 2, 2, 3, 4, 4, 5, 6, 6, 7, 7, 8, 9, 10, 10, 10,
+ 10, 10, 11, 11, 11, 11, 11, 12, 12, 12, 12, 12, 12
+};
+
+static const u8 rtw8814a_pwrtrk_type5_2gb_p[RTW_PWR_TRK_TBL_SZ] = {
+ 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 8, 9, 9, 9, 9,
+ 9, 9, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11
+};
+
+static const u8 rtw8814a_pwrtrk_type5_2ga_n[RTW_PWR_TRK_TBL_SZ] = {
+ 0, 1, 2, 2, 3, 4, 5, 5, 6, 7, 7, 8, 8, 9, 10, 10, 10,
+ 10, 11, 11, 11, 11, 111, 12, 12, 12, 12, 12, 12, 12
+};
+
+static const u8 rtw8814a_pwrtrk_type5_2ga_p[RTW_PWR_TRK_TBL_SZ] = {
+ 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 6, 7, 8, 8, 9, 9, 9,
+ 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11
+};
+
+static const u8 rtw8814a_pwrtrk_type5_2g_cck_d_n[RTW_PWR_TRK_TBL_SZ] = {
+ 0, 1, 2, 2, 3, 4, 5, 5, 6, 7, 7, 8, 8, 8, 9, 9, 9, 9,
+ 10, 10, 10, 10, 10, 11, 11, 10, 11, 11, 11, 11
+};
+
+static const u8 rtw8814a_pwrtrk_type5_2g_cck_d_p[RTW_PWR_TRK_TBL_SZ] = {
+ 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 7, 7, 8, 8, 8,
+ 9, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10
+};
+
+static const u8 rtw8814a_pwrtrk_type5_2g_cck_c_n[RTW_PWR_TRK_TBL_SZ] = {
+ 0, 2, 3, 4, 4, 5, 6, 6, 7, 8, 8, 8, 8, 9, 10, 10, 10,
+ 10, 11, 11, 11, 11, 11, 11, 12, 12, 12, 12, 12, 12
+};
+
+static const u8 rtw8814a_pwrtrk_type5_2g_cck_c_p[RTW_PWR_TRK_TBL_SZ] = {
+ 0, 0, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 7, 7, 8, 8, 8,
+ 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10
+};
+
+static const u8 rtw8814a_pwrtrk_type5_2g_cck_b_n[RTW_PWR_TRK_TBL_SZ] = {
+ 0, 2, 3, 4, 4, 5, 6, 6, 6, 7, 7, 8, 8, 9, 10, 10, 10,
+ 10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11
+};
+
+static const u8 rtw8814a_pwrtrk_type5_2g_cck_b_p[RTW_PWR_TRK_TBL_SZ] = {
+ 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 7, 7, 8, 8, 8,
+ 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10
+};
+
+static const u8 rtw8814a_pwrtrk_type5_2g_cck_a_n[RTW_PWR_TRK_TBL_SZ] = {
+ 0, 1, 2, 2, 3, 4, 5, 5, 6, 7, 7, 8, 8, 9, 9, 9, 9, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10
+};
+
+static const u8 rtw8814a_pwrtrk_type5_2g_cck_a_p[RTW_PWR_TRK_TBL_SZ] = {
+ 0, 1, 1, 2, 2, 2, 3, 4, 4, 5, 6, 7, 8, 8, 9, 9, 9,
+ 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10
+};
+
+const struct rtw_pwr_track_tbl rtw8814a_rtw_pwrtrk_type5_tbl = {
+ .pwrtrk_5gd_n[RTW_PWR_TRK_5G_1] = rtw8814a_pwrtrk_type5_5gd_n[RTW_PWR_TRK_5G_1],
+ .pwrtrk_5gd_n[RTW_PWR_TRK_5G_2] = rtw8814a_pwrtrk_type5_5gd_n[RTW_PWR_TRK_5G_2],
+ .pwrtrk_5gd_n[RTW_PWR_TRK_5G_3] = rtw8814a_pwrtrk_type5_5gd_n[RTW_PWR_TRK_5G_3],
+ .pwrtrk_5gd_p[RTW_PWR_TRK_5G_1] = rtw8814a_pwrtrk_type5_5gd_p[RTW_PWR_TRK_5G_1],
+ .pwrtrk_5gd_p[RTW_PWR_TRK_5G_2] = rtw8814a_pwrtrk_type5_5gd_p[RTW_PWR_TRK_5G_2],
+ .pwrtrk_5gd_p[RTW_PWR_TRK_5G_3] = rtw8814a_pwrtrk_type5_5gd_p[RTW_PWR_TRK_5G_3],
+ .pwrtrk_5gc_n[RTW_PWR_TRK_5G_1] = rtw8814a_pwrtrk_type5_5gc_n[RTW_PWR_TRK_5G_1],
+ .pwrtrk_5gc_n[RTW_PWR_TRK_5G_2] = rtw8814a_pwrtrk_type5_5gc_n[RTW_PWR_TRK_5G_2],
+ .pwrtrk_5gc_n[RTW_PWR_TRK_5G_3] = rtw8814a_pwrtrk_type5_5gc_n[RTW_PWR_TRK_5G_3],
+ .pwrtrk_5gc_p[RTW_PWR_TRK_5G_1] = rtw8814a_pwrtrk_type5_5gc_p[RTW_PWR_TRK_5G_1],
+ .pwrtrk_5gc_p[RTW_PWR_TRK_5G_2] = rtw8814a_pwrtrk_type5_5gc_p[RTW_PWR_TRK_5G_2],
+ .pwrtrk_5gc_p[RTW_PWR_TRK_5G_3] = rtw8814a_pwrtrk_type5_5gc_p[RTW_PWR_TRK_5G_3],
+ .pwrtrk_5gb_n[RTW_PWR_TRK_5G_1] = rtw8814a_pwrtrk_type5_5gb_n[RTW_PWR_TRK_5G_1],
+ .pwrtrk_5gb_n[RTW_PWR_TRK_5G_2] = rtw8814a_pwrtrk_type5_5gb_n[RTW_PWR_TRK_5G_2],
+ .pwrtrk_5gb_n[RTW_PWR_TRK_5G_3] = rtw8814a_pwrtrk_type5_5gb_n[RTW_PWR_TRK_5G_3],
+ .pwrtrk_5gb_p[RTW_PWR_TRK_5G_1] = rtw8814a_pwrtrk_type5_5gb_p[RTW_PWR_TRK_5G_1],
+ .pwrtrk_5gb_p[RTW_PWR_TRK_5G_2] = rtw8814a_pwrtrk_type5_5gb_p[RTW_PWR_TRK_5G_2],
+ .pwrtrk_5gb_p[RTW_PWR_TRK_5G_3] = rtw8814a_pwrtrk_type5_5gb_p[RTW_PWR_TRK_5G_3],
+ .pwrtrk_5ga_n[RTW_PWR_TRK_5G_1] = rtw8814a_pwrtrk_type5_5ga_n[RTW_PWR_TRK_5G_1],
+ .pwrtrk_5ga_n[RTW_PWR_TRK_5G_2] = rtw8814a_pwrtrk_type5_5ga_n[RTW_PWR_TRK_5G_2],
+ .pwrtrk_5ga_n[RTW_PWR_TRK_5G_3] = rtw8814a_pwrtrk_type5_5ga_n[RTW_PWR_TRK_5G_3],
+ .pwrtrk_5ga_p[RTW_PWR_TRK_5G_1] = rtw8814a_pwrtrk_type5_5ga_p[RTW_PWR_TRK_5G_1],
+ .pwrtrk_5ga_p[RTW_PWR_TRK_5G_2] = rtw8814a_pwrtrk_type5_5ga_p[RTW_PWR_TRK_5G_2],
+ .pwrtrk_5ga_p[RTW_PWR_TRK_5G_3] = rtw8814a_pwrtrk_type5_5ga_p[RTW_PWR_TRK_5G_3],
+ .pwrtrk_2gd_n = rtw8814a_pwrtrk_type5_2gd_n,
+ .pwrtrk_2gd_p = rtw8814a_pwrtrk_type5_2gd_p,
+ .pwrtrk_2gc_n = rtw8814a_pwrtrk_type5_2gc_n,
+ .pwrtrk_2gc_p = rtw8814a_pwrtrk_type5_2gc_p,
+ .pwrtrk_2gb_n = rtw8814a_pwrtrk_type5_2gb_n,
+ .pwrtrk_2gb_p = rtw8814a_pwrtrk_type5_2gb_p,
+ .pwrtrk_2ga_n = rtw8814a_pwrtrk_type5_2ga_n,
+ .pwrtrk_2ga_p = rtw8814a_pwrtrk_type5_2ga_p,
+ .pwrtrk_2g_cckd_n = rtw8814a_pwrtrk_type5_2g_cck_d_n,
+ .pwrtrk_2g_cckd_p = rtw8814a_pwrtrk_type5_2g_cck_d_p,
+ .pwrtrk_2g_cckc_n = rtw8814a_pwrtrk_type5_2g_cck_c_n,
+ .pwrtrk_2g_cckc_p = rtw8814a_pwrtrk_type5_2g_cck_c_p,
+ .pwrtrk_2g_cckb_n = rtw8814a_pwrtrk_type5_2g_cck_b_n,
+ .pwrtrk_2g_cckb_p = rtw8814a_pwrtrk_type5_2g_cck_b_p,
+ .pwrtrk_2g_ccka_n = rtw8814a_pwrtrk_type5_2g_cck_a_n,
+ .pwrtrk_2g_ccka_p = rtw8814a_pwrtrk_type5_2g_cck_a_p,
+};
+
+static const u8
+rtw8814a_pwrtrk_type7_5gd_n[RTW_PWR_TRK_5G_NUM][RTW_PWR_TRK_TBL_SZ] = {
+ {0, 0, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8,
+ 8, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10},
+ {0, 0, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8,
+ 8, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10},
+ {0, 0, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8,
+ 8, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10},
+};
+
+static const u8
+rtw8814a_pwrtrk_type7_5gd_p[RTW_PWR_TRK_5G_NUM][RTW_PWR_TRK_TBL_SZ] = {
+ {0, 0, 1, 1, 2, 3, 3, 4, 5, 5, 6, 6, 7, 8, 8, 9, 10, 10,
+ 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12},
+ {0, 0, 1, 1, 2, 3, 3, 4, 5, 5, 6, 6, 7, 8, 8, 9, 10, 10,
+ 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12},
+ {0, 0, 1, 1, 2, 3, 3, 4, 5, 5, 6, 6, 7, 8, 8, 9, 10, 10,
+ 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12},
+};
+
+static const u8
+rtw8814a_pwrtrk_type7_5gc_n[RTW_PWR_TRK_5G_NUM][RTW_PWR_TRK_TBL_SZ] = {
+ {0, 0, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8,
+ 8, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10},
+ {0, 0, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8,
+ 8, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10},
+ {0, 0, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8,
+ 8, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10},
+};
+
+static const u8
+rtw8814a_pwrtrk_type7_5gc_p[RTW_PWR_TRK_5G_NUM][RTW_PWR_TRK_TBL_SZ] = {
+ {0, 0, 1, 1, 2, 3, 3, 4, 5, 5, 6, 6, 7, 8, 8, 9, 10, 10,
+ 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12},
+ {0, 0, 1, 1, 2, 3, 3, 4, 5, 5, 6, 6, 7, 8, 8, 9, 10, 10,
+ 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12},
+ {0, 0, 1, 1, 2, 3, 3, 4, 5, 5, 6, 6, 7, 8, 8, 9, 10, 10,
+ 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12},
+};
+
+static const u8
+rtw8814a_pwrtrk_type7_5gb_n[RTW_PWR_TRK_5G_NUM][RTW_PWR_TRK_TBL_SZ] = {
+ {0, 0, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8,
+ 8, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10},
+ {0, 0, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8,
+ 8, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10},
+ {0, 0, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8,
+ 8, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10},
+};
+
+static const u8
+rtw8814a_pwrtrk_type7_5gb_p[RTW_PWR_TRK_5G_NUM][RTW_PWR_TRK_TBL_SZ] = {
+ {0, 0, 1, 1, 2, 3, 3, 4, 5, 5, 6, 6, 7, 8, 8, 9, 10, 10,
+ 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12},
+ {0, 0, 1, 1, 2, 3, 3, 4, 5, 5, 6, 6, 7, 8, 8, 9, 10, 10,
+ 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12},
+ {0, 0, 1, 1, 2, 3, 3, 4, 5, 5, 6, 6, 7, 8, 8, 9, 10, 10,
+ 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12},
+};
+
+static const u8
+rtw8814a_pwrtrk_type7_5ga_n[RTW_PWR_TRK_5G_NUM][RTW_PWR_TRK_TBL_SZ] = {
+ {0, 0, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8,
+ 8, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10},
+ {0, 0, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8,
+ 8, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10},
+ {0, 0, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8,
+ 8, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10},
+};
+
+static const u8
+rtw8814a_pwrtrk_type7_5ga_p[RTW_PWR_TRK_5G_NUM][RTW_PWR_TRK_TBL_SZ] = {
+ {0, 0, 1, 1, 2, 3, 3, 4, 5, 5, 6, 6, 7, 8, 8, 9, 10, 10,
+ 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12},
+ {0, 0, 1, 1, 2, 3, 3, 4, 5, 5, 6, 6, 7, 8, 8, 9, 10, 10,
+ 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12},
+ {0, 0, 1, 1, 2, 3, 3, 4, 5, 5, 6, 6, 7, 8, 8, 9, 10, 10,
+ 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12},
+};
+
+static const u8 rtw8814a_pwrtrk_type7_2gd_n[RTW_PWR_TRK_TBL_SZ] = {
+ 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 5, 6, 7, 7,
+ 7, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9
+};
+
+static const u8 rtw8814a_pwrtrk_type7_2gd_p[RTW_PWR_TRK_TBL_SZ] = {
+ 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7,
+ 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9
+};
+
+static const u8 rtw8814a_pwrtrk_type7_2gc_n[RTW_PWR_TRK_TBL_SZ] = {
+ 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 5, 6, 7, 7,
+ 7, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9
+};
+
+static const u8 rtw8814a_pwrtrk_type7_2gc_p[RTW_PWR_TRK_TBL_SZ] = {
+ 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7,
+ 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9
+};
+
+static const u8 rtw8814a_pwrtrk_type7_2gb_n[RTW_PWR_TRK_TBL_SZ] = {
+ 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 5, 6, 7, 7,
+ 7, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9
+};
+
+static const u8 rtw8814a_pwrtrk_type7_2gb_p[RTW_PWR_TRK_TBL_SZ] = {
+ 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7,
+ 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9
+};
+
+static const u8 rtw8814a_pwrtrk_type7_2ga_n[RTW_PWR_TRK_TBL_SZ] = {
+ 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 5, 6, 7, 7,
+ 7, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9
+};
+
+static const u8 rtw8814a_pwrtrk_type7_2ga_p[RTW_PWR_TRK_TBL_SZ] = {
+ 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7,
+ 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9
+};
+
+static const u8 rtw8814a_pwrtrk_type7_2g_cck_d_n[RTW_PWR_TRK_TBL_SZ] = {
+ 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 5, 6, 7, 7,
+ 7, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9
+};
+
+static const u8 rtw8814a_pwrtrk_type7_2g_cck_d_p[RTW_PWR_TRK_TBL_SZ] = {
+ 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7,
+ 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9
+};
+
+static const u8 rtw8814a_pwrtrk_type7_2g_cck_c_n[RTW_PWR_TRK_TBL_SZ] = {
+ 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 5, 6, 7, 7,
+ 7, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9
+};
+
+static const u8 rtw8814a_pwrtrk_type7_2g_cck_c_p[RTW_PWR_TRK_TBL_SZ] = {
+ 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7,
+ 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9
+};
+
+static const u8 rtw8814a_pwrtrk_type7_2g_cck_b_n[RTW_PWR_TRK_TBL_SZ] = {
+ 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 5, 6, 7, 7,
+ 7, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9
+};
+
+static const u8 rtw8814a_pwrtrk_type7_2g_cck_b_p[RTW_PWR_TRK_TBL_SZ] = {
+ 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7,
+ 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9
+};
+
+static const u8 rtw8814a_pwrtrk_type7_2g_cck_a_n[RTW_PWR_TRK_TBL_SZ] = {
+ 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 5, 6, 7, 7,
+ 7, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9
+};
+
+static const u8 rtw8814a_pwrtrk_type7_2g_cck_a_p[RTW_PWR_TRK_TBL_SZ] = {
+ 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7,
+ 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9
+};
+
+const struct rtw_pwr_track_tbl rtw8814a_rtw_pwrtrk_type7_tbl = {
+ .pwrtrk_5gd_n[RTW_PWR_TRK_5G_1] = rtw8814a_pwrtrk_type7_5gd_n[RTW_PWR_TRK_5G_1],
+ .pwrtrk_5gd_n[RTW_PWR_TRK_5G_2] = rtw8814a_pwrtrk_type7_5gd_n[RTW_PWR_TRK_5G_2],
+ .pwrtrk_5gd_n[RTW_PWR_TRK_5G_3] = rtw8814a_pwrtrk_type7_5gd_n[RTW_PWR_TRK_5G_3],
+ .pwrtrk_5gd_p[RTW_PWR_TRK_5G_1] = rtw8814a_pwrtrk_type7_5gd_p[RTW_PWR_TRK_5G_1],
+ .pwrtrk_5gd_p[RTW_PWR_TRK_5G_2] = rtw8814a_pwrtrk_type7_5gd_p[RTW_PWR_TRK_5G_2],
+ .pwrtrk_5gd_p[RTW_PWR_TRK_5G_3] = rtw8814a_pwrtrk_type7_5gd_p[RTW_PWR_TRK_5G_3],
+ .pwrtrk_5gc_n[RTW_PWR_TRK_5G_1] = rtw8814a_pwrtrk_type7_5gc_n[RTW_PWR_TRK_5G_1],
+ .pwrtrk_5gc_n[RTW_PWR_TRK_5G_2] = rtw8814a_pwrtrk_type7_5gc_n[RTW_PWR_TRK_5G_2],
+ .pwrtrk_5gc_n[RTW_PWR_TRK_5G_3] = rtw8814a_pwrtrk_type7_5gc_n[RTW_PWR_TRK_5G_3],
+ .pwrtrk_5gc_p[RTW_PWR_TRK_5G_1] = rtw8814a_pwrtrk_type7_5gc_p[RTW_PWR_TRK_5G_1],
+ .pwrtrk_5gc_p[RTW_PWR_TRK_5G_2] = rtw8814a_pwrtrk_type7_5gc_p[RTW_PWR_TRK_5G_2],
+ .pwrtrk_5gc_p[RTW_PWR_TRK_5G_3] = rtw8814a_pwrtrk_type7_5gc_p[RTW_PWR_TRK_5G_3],
+ .pwrtrk_5gb_n[RTW_PWR_TRK_5G_1] = rtw8814a_pwrtrk_type7_5gb_n[RTW_PWR_TRK_5G_1],
+ .pwrtrk_5gb_n[RTW_PWR_TRK_5G_2] = rtw8814a_pwrtrk_type7_5gb_n[RTW_PWR_TRK_5G_2],
+ .pwrtrk_5gb_n[RTW_PWR_TRK_5G_3] = rtw8814a_pwrtrk_type7_5gb_n[RTW_PWR_TRK_5G_3],
+ .pwrtrk_5gb_p[RTW_PWR_TRK_5G_1] = rtw8814a_pwrtrk_type7_5gb_p[RTW_PWR_TRK_5G_1],
+ .pwrtrk_5gb_p[RTW_PWR_TRK_5G_2] = rtw8814a_pwrtrk_type7_5gb_p[RTW_PWR_TRK_5G_2],
+ .pwrtrk_5gb_p[RTW_PWR_TRK_5G_3] = rtw8814a_pwrtrk_type7_5gb_p[RTW_PWR_TRK_5G_3],
+ .pwrtrk_5ga_n[RTW_PWR_TRK_5G_1] = rtw8814a_pwrtrk_type7_5ga_n[RTW_PWR_TRK_5G_1],
+ .pwrtrk_5ga_n[RTW_PWR_TRK_5G_2] = rtw8814a_pwrtrk_type7_5ga_n[RTW_PWR_TRK_5G_2],
+ .pwrtrk_5ga_n[RTW_PWR_TRK_5G_3] = rtw8814a_pwrtrk_type7_5ga_n[RTW_PWR_TRK_5G_3],
+ .pwrtrk_5ga_p[RTW_PWR_TRK_5G_1] = rtw8814a_pwrtrk_type7_5ga_p[RTW_PWR_TRK_5G_1],
+ .pwrtrk_5ga_p[RTW_PWR_TRK_5G_2] = rtw8814a_pwrtrk_type7_5ga_p[RTW_PWR_TRK_5G_2],
+ .pwrtrk_5ga_p[RTW_PWR_TRK_5G_3] = rtw8814a_pwrtrk_type7_5ga_p[RTW_PWR_TRK_5G_3],
+ .pwrtrk_2gd_n = rtw8814a_pwrtrk_type7_2gd_n,
+ .pwrtrk_2gd_p = rtw8814a_pwrtrk_type7_2gd_p,
+ .pwrtrk_2gc_n = rtw8814a_pwrtrk_type7_2gc_n,
+ .pwrtrk_2gc_p = rtw8814a_pwrtrk_type7_2gc_p,
+ .pwrtrk_2gb_n = rtw8814a_pwrtrk_type7_2gb_n,
+ .pwrtrk_2gb_p = rtw8814a_pwrtrk_type7_2gb_p,
+ .pwrtrk_2ga_n = rtw8814a_pwrtrk_type7_2ga_n,
+ .pwrtrk_2ga_p = rtw8814a_pwrtrk_type7_2ga_p,
+ .pwrtrk_2g_cckd_n = rtw8814a_pwrtrk_type7_2g_cck_d_n,
+ .pwrtrk_2g_cckd_p = rtw8814a_pwrtrk_type7_2g_cck_d_p,
+ .pwrtrk_2g_cckc_n = rtw8814a_pwrtrk_type7_2g_cck_c_n,
+ .pwrtrk_2g_cckc_p = rtw8814a_pwrtrk_type7_2g_cck_c_p,
+ .pwrtrk_2g_cckb_n = rtw8814a_pwrtrk_type7_2g_cck_b_n,
+ .pwrtrk_2g_cckb_p = rtw8814a_pwrtrk_type7_2g_cck_b_p,
+ .pwrtrk_2g_ccka_n = rtw8814a_pwrtrk_type7_2g_cck_a_n,
+ .pwrtrk_2g_ccka_p = rtw8814a_pwrtrk_type7_2g_cck_a_p,
+};
+
+static const u8
+rtw8814a_pwrtrk_type8_5gd_n[RTW_PWR_TRK_5G_NUM][RTW_PWR_TRK_TBL_SZ] = {
+ {0, 1, 2, 3, 4, 4, 6, 7, 7, 8, 8, 9, 10, 10, 11, 11, 12,
+ 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13},
+ {0, 1, 2, 3, 4, 4, 6, 7, 7, 8, 9, 9, 10, 10, 11, 11, 12,
+ 12, 12, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13},
+ {0, 1, 2, 3, 4, 4, 5, 6, 6, 7, 7, 8, 8, 8, 9, 10, 10,
+ 10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11},
+};
+
+static const u8
+rtw8814a_pwrtrk_type8_5gd_p[RTW_PWR_TRK_5G_NUM][RTW_PWR_TRK_TBL_SZ] = {
+ {0, 0, 1, 2, 4, 4, 5, 6, 7, 8, 9, 10, 11, 12, 14, 14,
+ 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15},
+ {0, 0, 1, 2, 3, 5, 5, 6, 6, 7, 8, 9, 10, 11, 12, 13, 14,
+ 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14},
+ {0, 0, 1, 2, 3, 4, 5, 6, 6, 7, 8, 9, 10, 11, 12, 13, 14,
+ 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14},
+};
+
+static const u8
+rtw8814a_pwrtrk_type8_5gc_n[RTW_PWR_TRK_5G_NUM][RTW_PWR_TRK_TBL_SZ] = {
+ {0, 1, 2, 4, 4, 5, 6, 7, 7, 8, 8, 9, 9, 10, 11, 11, 12,
+ 12, 12, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13},
+ {0, 1, 2, 3, 4, 5, 6, 7, 7, 8, 9, 9, 10, 10, 10, 11, 11,
+ 12, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13},
+ {0, 1, 2, 3, 4, 4, 5, 6, 7, 7, 8, 8, 9, 10, 10, 11, 11,
+ 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12},
+};
+
+static const u8
+rtw8814a_pwrtrk_type8_5gc_p[RTW_PWR_TRK_5G_NUM][RTW_PWR_TRK_TBL_SZ] = {
+ {0, 0, 1, 2, 4, 4, 5, 6, 7, 7, 8, 9, 10, 11, 12, 13, 14,
+ 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14},
+ {0, 0, 1, 3, 4, 4, 5, 6, 7, 7, 8, 10, 11, 12, 13, 14,
+ 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15},
+ {0, 0, 1, 2, 4, 5, 5, 6, 6, 7, 7, 9, 10, 11, 12, 13, 14,
+ 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14},
+};
+
+static const u8
+rtw8814a_pwrtrk_type8_5gb_n[RTW_PWR_TRK_5G_NUM][RTW_PWR_TRK_TBL_SZ] = {
+ {0, 1, 2, 3, 4, 4, 5, 6, 7, 7, 8, 8, 9, 9, 10, 11, 11,
+ 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12},
+ {0, 1, 2, 3, 4, 5, 6, 7, 7, 8, 9, 9, 10, 10, 10, 11, 11,
+ 12, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13},
+ {0, 1, 2, 3, 4, 4, 5, 6, 7, 7, 8, 8, 9, 10, 10, 11, 11,
+ 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12},
+};
+
+static const u8
+rtw8814a_pwrtrk_type8_5gb_p[RTW_PWR_TRK_5G_NUM][RTW_PWR_TRK_TBL_SZ] = {
+ {0, 0, 1, 2, 3, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
+ 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14},
+ {0, 0, 1, 2, 3, 4, 5, 5, 6, 7, 8, 10, 11, 11, 13, 13,
+ 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14},
+ {0, 0, 1, 2, 3, 4, 4, 5, 6, 7, 8, 10, 10, 11, 13, 13,
+ 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14},
+};
+
+static const u8
+rtw8814a_pwrtrk_type8_5ga_n[RTW_PWR_TRK_5G_NUM][RTW_PWR_TRK_TBL_SZ] = {
+ {0, 1, 2, 3, 4, 4, 5, 6, 6, 7, 8, 8, 9, 9, 10, 11, 11,
+ 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12},
+ {0, 1, 2, 3, 4, 5, 5, 6, 7, 8, 9, 9, 10, 10, 10, 11, 11,
+ 12, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13},
+ {0, 1, 2, 3, 4, 4, 5, 6, 7, 7, 8, 9, 10, 10, 10, 11, 12,
+ 12, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13},
+};
+
+static const u8
+rtw8814a_pwrtrk_type8_5ga_p[RTW_PWR_TRK_5G_NUM][RTW_PWR_TRK_TBL_SZ] = {
+ {0, 0, 1, 2, 3, 3, 4, 5, 6, 7, 7, 9, 10, 11, 12, 13, 14,
+ 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14},
+ {0, 0, 1, 2, 3, 4, 5, 5, 6, 7, 8, 9, 10, 11, 12, 13, 13,
+ 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14},
+ {0, 0, 1, 2, 3, 3, 4, 4, 6, 7, 7, 9, 10, 11, 12, 13, 14,
+ 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14},
+};
+
+static const u8 rtw8814a_pwrtrk_type8_2gd_n[RTW_PWR_TRK_TBL_SZ] = {
+ 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 5,
+ 6, 6, 7, 8, 9, 10, 11, 11, 11, 11, 11, 11, 11
+};
+
+static const u8 rtw8814a_pwrtrk_type8_2gd_p[RTW_PWR_TRK_TBL_SZ] = {
+ 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 6, 6, 7, 7, 8, 9, 9, 10,
+ 10, 11, 12, 12, 13, 14, 14, 14, 14, 14, 14, 14
+};
+
+static const u8 rtw8814a_pwrtrk_type8_2gc_n[RTW_PWR_TRK_TBL_SZ] = {
+ 0, 1, 1, 2, 2, 3, 3, 4, 5, 6, 6, 6, 7, 7, 8, 8, 9, 10,
+ 10, 11, 12, 12, 13, 13, 13, 13, 14, 14, 14, 14
+};
+
+static const u8 rtw8814a_pwrtrk_type8_2gc_p[RTW_PWR_TRK_TBL_SZ] = {
+ 0, 1, 1, 2, 3, 3, 4, 4, 4, 5, 5, 6, 7, 8, 8, 9, 10, 10,
+ 11, 12, 13, 13, 14, 14, 14, 14, 14, 14, 14, 14
+};
+
+static const u8 rtw8814a_pwrtrk_type8_2gb_n[RTW_PWR_TRK_TBL_SZ] = {
+ 0, 1, 1, 2, 2, 3, 3, 4, 5, 5, 6, 6, 7, 7, 8, 8, 8,
+ 9, 9, 10, 10, 11, 11, 12, 12, 12, 13, 14, 14, 14
+};
+
+static const u8 rtw8814a_pwrtrk_type8_2gb_p[RTW_PWR_TRK_TBL_SZ] = {
+ 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 6, 6, 7, 8, 8, 9, 9, 10,
+ 10, 11, 12, 12, 13, 13, 13, 13, 13, 14, 14, 14
+};
+
+static const u8 rtw8814a_pwrtrk_type8_2ga_n[RTW_PWR_TRK_TBL_SZ] = {
+ 0, 0, 1, 2, 2, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 9, 9, 9,
+ 10, 11, 11, 12, 12, 13, 13, 13, 13, 13, 13, 13
+};
+
+static const u8 rtw8814a_pwrtrk_type8_2ga_p[RTW_PWR_TRK_TBL_SZ] = {
+ 0, 1, 1, 2, 3, 3, 4, 4, 4, 5, 6, 6, 7, 7, 8, 8, 9, 10,
+ 11, 12, 12, 13, 13, 14, 14, 14, 14, 14, 14, 14
+};
+
+static const u8 rtw8814a_pwrtrk_type8_2g_cck_d_n[RTW_PWR_TRK_TBL_SZ] = {
+ 0, 1, 1, 2, 3, 3, 3, 4, 4, 4, 5, 5, 5, 6, 6, 7, 8,
+ 9, 9, 9, 9, 9, 9, 10, 10, 11, 11, 12, 12, 12
+};
+
+static const u8 rtw8814a_pwrtrk_type8_2g_cck_d_p[RTW_PWR_TRK_TBL_SZ] = {
+ 0, 1, 1, 1, 2, 3, 3, 4, 4, 5, 6, 6, 7, 8, 8, 9, 10, 10,
+ 10, 11, 12, 12, 13, 14, 14, 14, 14, 14, 14, 14
+};
+
+static const u8 rtw8814a_pwrtrk_type8_2g_cck_c_n[RTW_PWR_TRK_TBL_SZ] = {
+ 0, 0, 1, 2, 2, 3, 4, 5, 5, 6, 6, 6, 6, 7, 8, 9, 9, 10,
+ 10, 11, 11, 11, 12, 13, 13, 13, 13, 13, 13, 13
+};
+
+static const u8 rtw8814a_pwrtrk_type8_2g_cck_c_p[RTW_PWR_TRK_TBL_SZ] = {
+ 0, 0, 1, 1, 2, 2, 3, 3, 4, 5, 5, 6, 7, 7, 8, 9, 9, 10,
+ 10, 11, 12, 12, 13, 13, 13, 13, 13, 13, 13, 13
+};
+
+static const u8 rtw8814a_pwrtrk_type8_2g_cck_b_n[RTW_PWR_TRK_TBL_SZ] = {
+ 0, 1, 1, 2, 2, 2, 3, 3, 4, 5, 6, 6, 7, 7, 8, 8, 9, 10,
+ 10, 10, 11, 11, 12, 12, 12, 12, 12, 12, 12, 12
+};
+
+static const u8 rtw8814a_pwrtrk_type8_2g_cck_b_p[RTW_PWR_TRK_TBL_SZ] = {
+ 0, 1, 1, 1, 2, 3, 3, 4, 4, 5, 5, 6, 7, 8, 8, 9, 9, 10,
+ 10, 11, 12, 12, 13, 13, 13, 13, 13, 13, 13, 13
+};
+
+static const u8 rtw8814a_pwrtrk_type8_2g_cck_a_n[RTW_PWR_TRK_TBL_SZ] = {
+ 0, 1, 2, 2, 3, 4, 4, 4, 5, 5, 6, 6, 7, 8, 9, 9, 9, 9,
+ 10, 10, 11, 11, 11, 12, 12, 12, 12, 12, 12, 12
+};
+
+static const u8 rtw8814a_pwrtrk_type8_2g_cck_a_p[RTW_PWR_TRK_TBL_SZ] = {
+ 0, 0, 1, 2, 2, 3, 3, 4, 5, 5, 6, 7, 8, 9, 9, 10, 10,
+ 11, 11, 12, 12, 12, 13, 13, 13, 13, 13, 13, 13, 13
+};
+
+const struct rtw_pwr_track_tbl rtw8814a_rtw_pwrtrk_type8_tbl = {
+ .pwrtrk_5gd_n[RTW_PWR_TRK_5G_1] = rtw8814a_pwrtrk_type8_5gd_n[RTW_PWR_TRK_5G_1],
+ .pwrtrk_5gd_n[RTW_PWR_TRK_5G_2] = rtw8814a_pwrtrk_type8_5gd_n[RTW_PWR_TRK_5G_2],
+ .pwrtrk_5gd_n[RTW_PWR_TRK_5G_3] = rtw8814a_pwrtrk_type8_5gd_n[RTW_PWR_TRK_5G_3],
+ .pwrtrk_5gd_p[RTW_PWR_TRK_5G_1] = rtw8814a_pwrtrk_type8_5gd_p[RTW_PWR_TRK_5G_1],
+ .pwrtrk_5gd_p[RTW_PWR_TRK_5G_2] = rtw8814a_pwrtrk_type8_5gd_p[RTW_PWR_TRK_5G_2],
+ .pwrtrk_5gd_p[RTW_PWR_TRK_5G_3] = rtw8814a_pwrtrk_type8_5gd_p[RTW_PWR_TRK_5G_3],
+ .pwrtrk_5gc_n[RTW_PWR_TRK_5G_1] = rtw8814a_pwrtrk_type8_5gc_n[RTW_PWR_TRK_5G_1],
+ .pwrtrk_5gc_n[RTW_PWR_TRK_5G_2] = rtw8814a_pwrtrk_type8_5gc_n[RTW_PWR_TRK_5G_2],
+ .pwrtrk_5gc_n[RTW_PWR_TRK_5G_3] = rtw8814a_pwrtrk_type8_5gc_n[RTW_PWR_TRK_5G_3],
+ .pwrtrk_5gc_p[RTW_PWR_TRK_5G_1] = rtw8814a_pwrtrk_type8_5gc_p[RTW_PWR_TRK_5G_1],
+ .pwrtrk_5gc_p[RTW_PWR_TRK_5G_2] = rtw8814a_pwrtrk_type8_5gc_p[RTW_PWR_TRK_5G_2],
+ .pwrtrk_5gc_p[RTW_PWR_TRK_5G_3] = rtw8814a_pwrtrk_type8_5gc_p[RTW_PWR_TRK_5G_3],
+ .pwrtrk_5gb_n[RTW_PWR_TRK_5G_1] = rtw8814a_pwrtrk_type8_5gb_n[RTW_PWR_TRK_5G_1],
+ .pwrtrk_5gb_n[RTW_PWR_TRK_5G_2] = rtw8814a_pwrtrk_type8_5gb_n[RTW_PWR_TRK_5G_2],
+ .pwrtrk_5gb_n[RTW_PWR_TRK_5G_3] = rtw8814a_pwrtrk_type8_5gb_n[RTW_PWR_TRK_5G_3],
+ .pwrtrk_5gb_p[RTW_PWR_TRK_5G_1] = rtw8814a_pwrtrk_type8_5gb_p[RTW_PWR_TRK_5G_1],
+ .pwrtrk_5gb_p[RTW_PWR_TRK_5G_2] = rtw8814a_pwrtrk_type8_5gb_p[RTW_PWR_TRK_5G_2],
+ .pwrtrk_5gb_p[RTW_PWR_TRK_5G_3] = rtw8814a_pwrtrk_type8_5gb_p[RTW_PWR_TRK_5G_3],
+ .pwrtrk_5ga_n[RTW_PWR_TRK_5G_1] = rtw8814a_pwrtrk_type8_5ga_n[RTW_PWR_TRK_5G_1],
+ .pwrtrk_5ga_n[RTW_PWR_TRK_5G_2] = rtw8814a_pwrtrk_type8_5ga_n[RTW_PWR_TRK_5G_2],
+ .pwrtrk_5ga_n[RTW_PWR_TRK_5G_3] = rtw8814a_pwrtrk_type8_5ga_n[RTW_PWR_TRK_5G_3],
+ .pwrtrk_5ga_p[RTW_PWR_TRK_5G_1] = rtw8814a_pwrtrk_type8_5ga_p[RTW_PWR_TRK_5G_1],
+ .pwrtrk_5ga_p[RTW_PWR_TRK_5G_2] = rtw8814a_pwrtrk_type8_5ga_p[RTW_PWR_TRK_5G_2],
+ .pwrtrk_5ga_p[RTW_PWR_TRK_5G_3] = rtw8814a_pwrtrk_type8_5ga_p[RTW_PWR_TRK_5G_3],
+ .pwrtrk_2gd_n = rtw8814a_pwrtrk_type8_2gd_n,
+ .pwrtrk_2gd_p = rtw8814a_pwrtrk_type8_2gd_p,
+ .pwrtrk_2gc_n = rtw8814a_pwrtrk_type8_2gc_n,
+ .pwrtrk_2gc_p = rtw8814a_pwrtrk_type8_2gc_p,
+ .pwrtrk_2gb_n = rtw8814a_pwrtrk_type8_2gb_n,
+ .pwrtrk_2gb_p = rtw8814a_pwrtrk_type8_2gb_p,
+ .pwrtrk_2ga_n = rtw8814a_pwrtrk_type8_2ga_n,
+ .pwrtrk_2ga_p = rtw8814a_pwrtrk_type8_2ga_p,
+ .pwrtrk_2g_cckd_n = rtw8814a_pwrtrk_type8_2g_cck_d_n,
+ .pwrtrk_2g_cckd_p = rtw8814a_pwrtrk_type8_2g_cck_d_p,
+ .pwrtrk_2g_cckc_n = rtw8814a_pwrtrk_type8_2g_cck_c_n,
+ .pwrtrk_2g_cckc_p = rtw8814a_pwrtrk_type8_2g_cck_c_p,
+ .pwrtrk_2g_cckb_n = rtw8814a_pwrtrk_type8_2g_cck_b_n,
+ .pwrtrk_2g_cckb_p = rtw8814a_pwrtrk_type8_2g_cck_b_p,
+ .pwrtrk_2g_ccka_n = rtw8814a_pwrtrk_type8_2g_cck_a_n,
+ .pwrtrk_2g_ccka_p = rtw8814a_pwrtrk_type8_2g_cck_a_p,
+};
+
+static const struct rtw_pwr_seq_cmd init_power_on_8814a[] = {
+ {0x10c2,
+ RTW_PWR_CUT_ALL_MSK,
+ RTW_PWR_INTF_USB_MSK,
+ RTW_PWR_ADDR_MAC,
+ RTW_PWR_CMD_WRITE, BIT(1), BIT(1)},
+ {0xFFFF,
+ RTW_PWR_CUT_ALL_MSK,
+ RTW_PWR_INTF_ALL_MSK,
+ 0,
+ RTW_PWR_CMD_END, 0, 0},
+};
+
+static const struct rtw_pwr_seq_cmd trans_carddis_to_cardemu_8814a[] = {
+ {0x0012,
+ RTW_PWR_CUT_ALL_MSK,
+ RTW_PWR_INTF_ALL_MSK,
+ RTW_PWR_ADDR_MAC,
+ RTW_PWR_CMD_WRITE, BIT(6), BIT(6)},
+ {0x0015,
+ RTW_PWR_CUT_ALL_MSK,
+ RTW_PWR_INTF_ALL_MSK,
+ RTW_PWR_ADDR_MAC,
+ RTW_PWR_CMD_WRITE, BIT(5), 0},
+ {0x0015,
+ RTW_PWR_CUT_ALL_MSK,
+ RTW_PWR_INTF_ALL_MSK,
+ RTW_PWR_ADDR_MAC,
+ RTW_PWR_CMD_WRITE, BIT(6), 0},
+ {0x0023,
+ RTW_PWR_CUT_ALL_MSK,
+ RTW_PWR_INTF_ALL_MSK,
+ RTW_PWR_ADDR_MAC,
+ RTW_PWR_CMD_WRITE, BIT(4), 0},
+ {0x0046,
+ RTW_PWR_CUT_ALL_MSK,
+ RTW_PWR_INTF_ALL_MSK,
+ RTW_PWR_ADDR_MAC,
+ RTW_PWR_CMD_WRITE, 0xFF, 0x00},
+ {0x0062,
+ RTW_PWR_CUT_ALL_MSK,
+ RTW_PWR_INTF_ALL_MSK,
+ RTW_PWR_ADDR_MAC,
+ RTW_PWR_CMD_WRITE, 0xFF, 0x00},
+ {0x0005,
+ RTW_PWR_CUT_ALL_MSK,
+ RTW_PWR_INTF_PCI_MSK,
+ RTW_PWR_ADDR_MAC,
+ RTW_PWR_CMD_WRITE, BIT(2), 0},
+ {0x0005,
+ RTW_PWR_CUT_ALL_MSK,
+ RTW_PWR_INTF_ALL_MSK,
+ RTW_PWR_ADDR_MAC,
+ RTW_PWR_CMD_WRITE, BIT(3), 0},
+ {0x0301,
+ RTW_PWR_CUT_ALL_MSK,
+ RTW_PWR_INTF_PCI_MSK,
+ RTW_PWR_ADDR_MAC,
+ RTW_PWR_CMD_WRITE, 0xFF, 0},
+ {0x0071,
+ RTW_PWR_CUT_ALL_MSK,
+ RTW_PWR_INTF_PCI_MSK,
+ RTW_PWR_ADDR_MAC,
+ RTW_PWR_CMD_WRITE, BIT(2), 0},
+ {0xFFFF,
+ RTW_PWR_CUT_ALL_MSK,
+ RTW_PWR_INTF_ALL_MSK,
+ 0,
+ RTW_PWR_CMD_END, 0, 0},
+};
+
+static const struct rtw_pwr_seq_cmd trans_cardemu_to_act_8814a[] = {
+ {0x0005,
+ RTW_PWR_CUT_ALL_MSK,
+ RTW_PWR_INTF_ALL_MSK,
+ RTW_PWR_ADDR_MAC,
+ RTW_PWR_CMD_WRITE, BIT(2), 0},
+ {0x0006,
+ RTW_PWR_CUT_ALL_MSK,
+ RTW_PWR_INTF_ALL_MSK,
+ RTW_PWR_ADDR_MAC,
+ RTW_PWR_CMD_POLLING, BIT(1), BIT(1)},
+ {0x0005,
+ RTW_PWR_CUT_ALL_MSK,
+ RTW_PWR_INTF_ALL_MSK,
+ RTW_PWR_ADDR_MAC,
+ RTW_PWR_CMD_WRITE, BIT(3), 0},
+ {0x00F0,
+ RTW_PWR_CUT_ALL_MSK,
+ RTW_PWR_INTF_ALL_MSK,
+ RTW_PWR_ADDR_MAC,
+ RTW_PWR_CMD_WRITE, BIT(7), 0},
+ {0x0081,
+ RTW_PWR_CUT_ALL_MSK,
+ RTW_PWR_INTF_ALL_MSK,
+ RTW_PWR_ADDR_MAC,
+ RTW_PWR_CMD_WRITE, 0x30, 0x20},
+ {0x0005,
+ RTW_PWR_CUT_ALL_MSK,
+ RTW_PWR_INTF_ALL_MSK,
+ RTW_PWR_ADDR_MAC,
+ RTW_PWR_CMD_WRITE, BIT(0), BIT(0)},
+ {0x0005,
+ RTW_PWR_CUT_ALL_MSK,
+ RTW_PWR_INTF_ALL_MSK,
+ RTW_PWR_ADDR_MAC,
+ RTW_PWR_CMD_POLLING, BIT(0), 0},
+ {0xFFFF,
+ RTW_PWR_CUT_ALL_MSK,
+ RTW_PWR_INTF_ALL_MSK,
+ 0,
+ RTW_PWR_CMD_END, 0, 0},
+};
+
+static const struct rtw_pwr_seq_cmd trans_act_to_cardemu_8814a[] = {
+ {0x0c00,
+ RTW_PWR_CUT_ALL_MSK,
+ RTW_PWR_INTF_ALL_MSK,
+ RTW_PWR_ADDR_MAC,
+ RTW_PWR_CMD_WRITE, 0xFF, 0x04},
+ {0x0e00,
+ RTW_PWR_CUT_ALL_MSK,
+ RTW_PWR_INTF_ALL_MSK,
+ RTW_PWR_ADDR_MAC,
+ RTW_PWR_CMD_WRITE, 0xFF, 0x04},
+ {0x1002,
+ RTW_PWR_CUT_ALL_MSK,
+ RTW_PWR_INTF_ALL_MSK,
+ RTW_PWR_ADDR_MAC,
+ RTW_PWR_CMD_WRITE, BIT(0), 0},
+ {0x0002,
+ RTW_PWR_CUT_ALL_MSK,
+ RTW_PWR_INTF_ALL_MSK,
+ RTW_PWR_ADDR_MAC,
+ RTW_PWR_CMD_DELAY, 1, RTW_PWR_DELAY_US},
+ {0x1002,
+ RTW_PWR_CUT_ALL_MSK,
+ RTW_PWR_INTF_PCI_MSK,
+ RTW_PWR_ADDR_MAC,
+ RTW_PWR_CMD_WRITE, BIT(1), 0},
+ {0x001F,
+ RTW_PWR_CUT_ALL_MSK,
+ RTW_PWR_INTF_ALL_MSK,
+ RTW_PWR_ADDR_MAC,
+ RTW_PWR_CMD_WRITE, 0xFF, 0},
+ {0x0007,
+ RTW_PWR_CUT_ALL_MSK,
+ RTW_PWR_INTF_ALL_MSK,
+ RTW_PWR_ADDR_MAC,
+ RTW_PWR_CMD_WRITE, 0xFF, 0x28},
+ {0x0008,
+ RTW_PWR_CUT_ALL_MSK,
+ RTW_PWR_INTF_USB_MSK,
+ RTW_PWR_ADDR_MAC,
+ RTW_PWR_CMD_WRITE, 0x02, 0},
+ {0x0066,
+ RTW_PWR_CUT_ALL_MSK,
+ RTW_PWR_INTF_USB_MSK,
+ RTW_PWR_ADDR_MAC,
+ RTW_PWR_CMD_WRITE, BIT(7), 0},
+ {0x0041,
+ RTW_PWR_CUT_ALL_MSK,
+ RTW_PWR_INTF_USB_MSK,
+ RTW_PWR_ADDR_MAC,
+ RTW_PWR_CMD_WRITE, BIT(4), 0},
+ {0x0042,
+ RTW_PWR_CUT_ALL_MSK,
+ RTW_PWR_INTF_USB_MSK,
+ RTW_PWR_ADDR_MAC,
+ RTW_PWR_CMD_WRITE, BIT(1), 0},
+ {0x004e,
+ RTW_PWR_CUT_ALL_MSK,
+ RTW_PWR_INTF_USB_MSK,
+ RTW_PWR_ADDR_MAC,
+ RTW_PWR_CMD_WRITE, BIT(5), BIT(5)},
+ {0x0041,
+ RTW_PWR_CUT_ALL_MSK,
+ RTW_PWR_INTF_USB_MSK,
+ RTW_PWR_ADDR_MAC,
+ RTW_PWR_CMD_WRITE, BIT(0), 0},
+ {0x0005,
+ RTW_PWR_CUT_ALL_MSK,
+ RTW_PWR_INTF_ALL_MSK,
+ RTW_PWR_ADDR_MAC,
+ RTW_PWR_CMD_WRITE, BIT(1), BIT(1)},
+ {0x0005,
+ RTW_PWR_CUT_ALL_MSK,
+ RTW_PWR_INTF_ALL_MSK,
+ RTW_PWR_ADDR_MAC,
+ RTW_PWR_CMD_POLLING, BIT(1), 0},
+ {0xFFFF,
+ RTW_PWR_CUT_ALL_MSK,
+ RTW_PWR_INTF_ALL_MSK,
+ 0,
+ RTW_PWR_CMD_END, 0, 0},
+};
+
+static const struct rtw_pwr_seq_cmd trans_cardemu_to_carddis_8814a[] = {
+ {0x0003,
+ RTW_PWR_CUT_ALL_MSK,
+ RTW_PWR_INTF_ALL_MSK,
+ RTW_PWR_ADDR_MAC,
+ RTW_PWR_CMD_WRITE, BIT(2), 0},
+ {0x0080,
+ RTW_PWR_CUT_ALL_MSK,
+ RTW_PWR_INTF_ALL_MSK,
+ RTW_PWR_ADDR_MAC,
+ RTW_PWR_CMD_WRITE, 0xFF, 0x01},
+ {0x0081,
+ RTW_PWR_CUT_ALL_MSK,
+ RTW_PWR_INTF_ALL_MSK,
+ RTW_PWR_ADDR_MAC,
+ RTW_PWR_CMD_WRITE, 0xFF, 0x30},
+ {0x0045,
+ RTW_PWR_CUT_ALL_MSK,
+ RTW_PWR_INTF_ALL_MSK,
+ RTW_PWR_ADDR_MAC,
+ RTW_PWR_CMD_WRITE, 0xFF, 0x00},
+ {0x0046,
+ RTW_PWR_CUT_ALL_MSK,
+ RTW_PWR_INTF_ALL_MSK,
+ RTW_PWR_ADDR_MAC,
+ RTW_PWR_CMD_WRITE, 0xFF, 0xff},
+ {0x0047,
+ RTW_PWR_CUT_ALL_MSK,
+ RTW_PWR_INTF_ALL_MSK,
+ RTW_PWR_ADDR_MAC,
+ RTW_PWR_CMD_WRITE, 0xFF, 0},
+ {0x0015,
+ RTW_PWR_CUT_ALL_MSK,
+ RTW_PWR_INTF_ALL_MSK,
+ RTW_PWR_ADDR_MAC,
+ RTW_PWR_CMD_WRITE, BIT(6), BIT(6)},
+ {0x0015,
+ RTW_PWR_CUT_ALL_MSK,
+ RTW_PWR_INTF_ALL_MSK,
+ RTW_PWR_ADDR_MAC,
+ RTW_PWR_CMD_WRITE, BIT(5), BIT(5)},
+ {0x0012,
+ RTW_PWR_CUT_ALL_MSK,
+ RTW_PWR_INTF_ALL_MSK,
+ RTW_PWR_ADDR_MAC,
+ RTW_PWR_CMD_WRITE, BIT(6), 0},
+ {0x0023,
+ RTW_PWR_CUT_ALL_MSK,
+ RTW_PWR_INTF_ALL_MSK,
+ RTW_PWR_ADDR_MAC,
+ RTW_PWR_CMD_WRITE, BIT(4), BIT(4)},
+ {0x0008,
+ RTW_PWR_CUT_ALL_MSK,
+ RTW_PWR_INTF_ALL_MSK,
+ RTW_PWR_ADDR_MAC,
+ RTW_PWR_CMD_WRITE, BIT(1), 0},
+ {0x0007,
+ RTW_PWR_CUT_ALL_MSK,
+ RTW_PWR_INTF_ALL_MSK,
+ RTW_PWR_ADDR_MAC,
+ RTW_PWR_CMD_WRITE, 0xFF, 0x20},
+ {0x001f,
+ RTW_PWR_CUT_ALL_MSK,
+ RTW_PWR_INTF_ALL_MSK,
+ RTW_PWR_ADDR_MAC,
+ RTW_PWR_CMD_WRITE, BIT(1), 0},
+ {0x0020,
+ RTW_PWR_CUT_ALL_MSK,
+ RTW_PWR_INTF_ALL_MSK,
+ RTW_PWR_ADDR_MAC,
+ RTW_PWR_CMD_WRITE, BIT(1), 0},
+ {0x0021,
+ RTW_PWR_CUT_ALL_MSK,
+ RTW_PWR_INTF_ALL_MSK,
+ RTW_PWR_ADDR_MAC,
+ RTW_PWR_CMD_WRITE, BIT(1), 0},
+ {0x0076,
+ RTW_PWR_CUT_ALL_MSK,
+ RTW_PWR_INTF_ALL_MSK,
+ RTW_PWR_ADDR_MAC,
+ RTW_PWR_CMD_WRITE, BIT(1), 0},
+ {0x0091,
+ RTW_PWR_CUT_ALL_MSK,
+ RTW_PWR_INTF_ALL_MSK,
+ RTW_PWR_ADDR_MAC,
+ RTW_PWR_CMD_WRITE, 0xA0, 0xA0},
+ {0x0070,
+ RTW_PWR_CUT_ALL_MSK,
+ RTW_PWR_INTF_ALL_MSK,
+ RTW_PWR_ADDR_MAC,
+ RTW_PWR_CMD_WRITE, BIT(3), BIT(3)},
+ {0x0005,
+ RTW_PWR_CUT_ALL_MSK,
+ RTW_PWR_INTF_ALL_MSK,
+ RTW_PWR_ADDR_MAC,
+ RTW_PWR_CMD_WRITE, BIT(3), BIT(3)},
+ {0xFFFF,
+ RTW_PWR_CUT_ALL_MSK,
+ RTW_PWR_INTF_ALL_MSK,
+ 0,
+ RTW_PWR_CMD_END, 0, 0},
+};
+
+const struct rtw_pwr_seq_cmd * const card_enable_flow_8814a[] = {
+ init_power_on_8814a,
+ trans_carddis_to_cardemu_8814a,
+ trans_cardemu_to_act_8814a,
+ NULL
+};
+
+const struct rtw_pwr_seq_cmd * const card_disable_flow_8814a[] = {
+ trans_act_to_cardemu_8814a,
+ trans_cardemu_to_carddis_8814a,
+ NULL
+};
diff --git a/drivers/net/wireless/realtek/rtw88/rtw8814a_table.h b/drivers/net/wireless/realtek/rtw88/rtw8814a_table.h
new file mode 100644
index 000000000000..69fb87e36c48
--- /dev/null
+++ b/drivers/net/wireless/realtek/rtw88/rtw8814a_table.h
@@ -0,0 +1,40 @@
+/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
+/* Copyright(c) 2025 Realtek Corporation
+ */
+
+#ifndef __RTW8814A_TABLE_H__
+#define __RTW8814A_TABLE_H__
+
+extern const struct rtw_table rtw8814a_mac_tbl;
+extern const struct rtw_table rtw8814a_agc_tbl;
+extern const struct rtw_table rtw8814a_bb_tbl;
+extern const struct rtw_table rtw8814a_bb_pg_tbl;
+extern const struct rtw_table rtw8814a_bb_pg_type0_tbl;
+extern const struct rtw_table rtw8814a_bb_pg_type2_tbl;
+extern const struct rtw_table rtw8814a_bb_pg_type3_tbl;
+extern const struct rtw_table rtw8814a_bb_pg_type4_tbl;
+extern const struct rtw_table rtw8814a_bb_pg_type5_tbl;
+extern const struct rtw_table rtw8814a_bb_pg_type7_tbl;
+extern const struct rtw_table rtw8814a_bb_pg_type8_tbl;
+extern const struct rtw_table rtw8814a_rf_a_tbl;
+extern const struct rtw_table rtw8814a_rf_b_tbl;
+extern const struct rtw_table rtw8814a_rf_c_tbl;
+extern const struct rtw_table rtw8814a_rf_d_tbl;
+extern const struct rtw_table rtw8814a_txpwr_lmt_tbl;
+extern const struct rtw_table rtw8814a_txpwr_lmt_type0_tbl;
+extern const struct rtw_table rtw8814a_txpwr_lmt_type1_tbl;
+extern const struct rtw_table rtw8814a_txpwr_lmt_type2_tbl;
+extern const struct rtw_table rtw8814a_txpwr_lmt_type3_tbl;
+extern const struct rtw_table rtw8814a_txpwr_lmt_type5_tbl;
+extern const struct rtw_table rtw8814a_txpwr_lmt_type7_tbl;
+extern const struct rtw_table rtw8814a_txpwr_lmt_type8_tbl;
+extern const struct rtw_pwr_track_tbl rtw8814a_rtw_pwrtrk_tbl;
+extern const struct rtw_pwr_track_tbl rtw8814a_rtw_pwrtrk_type0_tbl;
+extern const struct rtw_pwr_track_tbl rtw8814a_rtw_pwrtrk_type2_tbl;
+extern const struct rtw_pwr_track_tbl rtw8814a_rtw_pwrtrk_type5_tbl;
+extern const struct rtw_pwr_track_tbl rtw8814a_rtw_pwrtrk_type7_tbl;
+extern const struct rtw_pwr_track_tbl rtw8814a_rtw_pwrtrk_type8_tbl;
+extern const struct rtw_pwr_seq_cmd * const card_disable_flow_8814a[];
+extern const struct rtw_pwr_seq_cmd * const card_enable_flow_8814a[];
+
+#endif
diff --git a/drivers/net/wireless/realtek/rtw88/rtw8814ae.c b/drivers/net/wireless/realtek/rtw88/rtw8814ae.c
new file mode 100644
index 000000000000..c7436f1c8d40
--- /dev/null
+++ b/drivers/net/wireless/realtek/rtw88/rtw8814ae.c
@@ -0,0 +1,31 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
+/* Copyright(c) 2025 Realtek Corporation
+ */
+
+#include <linux/module.h>
+#include <linux/pci.h>
+#include "pci.h"
+#include "rtw8814a.h"
+
+static const struct pci_device_id rtw_8814ae_id_table[] = {
+ {
+ PCI_DEVICE(PCI_VENDOR_ID_REALTEK, 0x8813),
+ .driver_data = (kernel_ulong_t)&rtw8814a_hw_spec
+ },
+ {}
+};
+MODULE_DEVICE_TABLE(pci, rtw_8814ae_id_table);
+
+static struct pci_driver rtw_8814ae_driver = {
+ .name = KBUILD_MODNAME,
+ .id_table = rtw_8814ae_id_table,
+ .probe = rtw_pci_probe,
+ .remove = rtw_pci_remove,
+ .driver.pm = &rtw_pm_ops,
+ .shutdown = rtw_pci_shutdown,
+};
+module_pci_driver(rtw_8814ae_driver);
+
+MODULE_AUTHOR("Bitterblue Smith <rtl8821cerfe2@gmail.com>");
+MODULE_DESCRIPTION("Realtek 802.11ac wireless 8814ae driver");
+MODULE_LICENSE("Dual BSD/GPL");
diff --git a/drivers/net/wireless/realtek/rtw88/rtw8814au.c b/drivers/net/wireless/realtek/rtw88/rtw8814au.c
new file mode 100644
index 000000000000..1a0886ec17dd
--- /dev/null
+++ b/drivers/net/wireless/realtek/rtw88/rtw8814au.c
@@ -0,0 +1,54 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
+/* Copyright(c) 2025 Realtek Corporation
+ */
+
+#include <linux/module.h>
+#include <linux/usb.h>
+#include "main.h"
+#include "rtw8814a.h"
+#include "usb.h"
+
+static const struct usb_device_id rtw_8814au_id_table[] = {
+ { USB_DEVICE_AND_INTERFACE_INFO(RTW_USB_VENDOR_ID_REALTEK, 0x8813, 0xff, 0xff, 0xff),
+ .driver_info = (kernel_ulong_t)&(rtw8814a_hw_spec) },
+ { USB_DEVICE_AND_INTERFACE_INFO(0x056e, 0x400b, 0xff, 0xff, 0xff),
+ .driver_info = (kernel_ulong_t)&(rtw8814a_hw_spec) },
+ { USB_DEVICE_AND_INTERFACE_INFO(0x056e, 0x400d, 0xff, 0xff, 0xff),
+ .driver_info = (kernel_ulong_t)&(rtw8814a_hw_spec) },
+ { USB_DEVICE_AND_INTERFACE_INFO(0x0846, 0x9054, 0xff, 0xff, 0xff),
+ .driver_info = (kernel_ulong_t)&(rtw8814a_hw_spec) },
+ { USB_DEVICE_AND_INTERFACE_INFO(0x0b05, 0x1817, 0xff, 0xff, 0xff),
+ .driver_info = (kernel_ulong_t)&(rtw8814a_hw_spec) },
+ { USB_DEVICE_AND_INTERFACE_INFO(0x0b05, 0x1852, 0xff, 0xff, 0xff),
+ .driver_info = (kernel_ulong_t)&(rtw8814a_hw_spec) },
+ { USB_DEVICE_AND_INTERFACE_INFO(0x0b05, 0x1853, 0xff, 0xff, 0xff),
+ .driver_info = (kernel_ulong_t)&(rtw8814a_hw_spec) },
+ { USB_DEVICE_AND_INTERFACE_INFO(0x0e66, 0x0026, 0xff, 0xff, 0xff),
+ .driver_info = (kernel_ulong_t)&(rtw8814a_hw_spec) },
+ { USB_DEVICE_AND_INTERFACE_INFO(0x2001, 0x331a, 0xff, 0xff, 0xff),
+ .driver_info = (kernel_ulong_t)&(rtw8814a_hw_spec) },
+ { USB_DEVICE_AND_INTERFACE_INFO(0x20f4, 0x809a, 0xff, 0xff, 0xff),
+ .driver_info = (kernel_ulong_t)&(rtw8814a_hw_spec) },
+ { USB_DEVICE_AND_INTERFACE_INFO(0x20f4, 0x809b, 0xff, 0xff, 0xff),
+ .driver_info = (kernel_ulong_t)&(rtw8814a_hw_spec) },
+ { USB_DEVICE_AND_INTERFACE_INFO(0x2357, 0x0106, 0xff, 0xff, 0xff),
+ .driver_info = (kernel_ulong_t)&(rtw8814a_hw_spec) },
+ { USB_DEVICE_AND_INTERFACE_INFO(0x7392, 0xa834, 0xff, 0xff, 0xff),
+ .driver_info = (kernel_ulong_t)&(rtw8814a_hw_spec) },
+ { USB_DEVICE_AND_INTERFACE_INFO(0x7392, 0xa833, 0xff, 0xff, 0xff),
+ .driver_info = (kernel_ulong_t)&(rtw8814a_hw_spec) },
+ {},
+};
+MODULE_DEVICE_TABLE(usb, rtw_8814au_id_table);
+
+static struct usb_driver rtw_8814au_driver = {
+ .name = KBUILD_MODNAME,
+ .id_table = rtw_8814au_id_table,
+ .probe = rtw_usb_probe,
+ .disconnect = rtw_usb_disconnect,
+};
+module_usb_driver(rtw_8814au_driver);
+
+MODULE_AUTHOR("Bitterblue Smith <rtl8821cerfe2@gmail.com>");
+MODULE_DESCRIPTION("Realtek 802.11ac wireless 8814au driver");
+MODULE_LICENSE("Dual BSD/GPL");
diff --git a/drivers/net/wireless/realtek/rtw88/rtw8821a.c b/drivers/net/wireless/realtek/rtw88/rtw8821a.c
index f68239b07319..413aec694c33 100644
--- a/drivers/net/wireless/realtek/rtw88/rtw8821a.c
+++ b/drivers/net/wireless/realtek/rtw88/rtw8821a.c
@@ -871,6 +871,7 @@ static const struct rtw_chip_ops rtw8821a_ops = {
.set_tx_power_index = rtw88xxa_set_tx_power_index,
.cfg_ldo25 = rtw8821a_cfg_ldo25,
.efuse_grant = rtw88xxa_efuse_grant,
+ .set_ampdu_factor = NULL,
.false_alarm_statistics = rtw88xxa_false_alarm_statistics,
.phy_calibration = rtw8821a_phy_calibration,
.cck_pd_set = rtw88xxa_phy_cck_pd_set,
@@ -1175,6 +1176,7 @@ const struct rtw_chip_info rtw8821a_hw_spec = {
.rfe_defs = rtw8821a_rfe_defs,
.rfe_defs_size = ARRAY_SIZE(rtw8821a_rfe_defs),
.rx_ldpc = false,
+ .amsdu_in_ampdu = true,
.hw_feature_report = false,
.c2h_ra_report_size = 4,
.old_datarate_fb_limit = true,
diff --git a/drivers/net/wireless/realtek/rtw88/rtw8821au.c b/drivers/net/wireless/realtek/rtw88/rtw8821au.c
index a01744b64e8d..28c281b32978 100644
--- a/drivers/net/wireless/realtek/rtw88/rtw8821au.c
+++ b/drivers/net/wireless/realtek/rtw88/rtw8821au.c
@@ -66,7 +66,7 @@ static const struct usb_device_id rtw_8821au_id_table[] = {
MODULE_DEVICE_TABLE(usb, rtw_8821au_id_table);
static struct usb_driver rtw_8821au_driver = {
- .name = "rtw_8821au",
+ .name = KBUILD_MODNAME,
.id_table = rtw_8821au_id_table,
.probe = rtw_usb_probe,
.disconnect = rtw_usb_disconnect,
diff --git a/drivers/net/wireless/realtek/rtw88/rtw8821c.c b/drivers/net/wireless/realtek/rtw88/rtw8821c.c
index eb7e34c545d0..413130a30ca9 100644
--- a/drivers/net/wireless/realtek/rtw88/rtw8821c.c
+++ b/drivers/net/wireless/realtek/rtw88/rtw8821c.c
@@ -680,11 +680,11 @@ static void query_phy_status(struct rtw_dev *rtwdev, u8 *phy_status,
}
static void
-rtw8821c_set_tx_power_index_by_rate(struct rtw_dev *rtwdev, u8 path, u8 rs)
+rtw8821c_set_tx_power_index_by_rate(struct rtw_dev *rtwdev, u8 path,
+ u8 rs, u32 *phy_pwr_idx)
{
struct rtw_hal *hal = &rtwdev->hal;
static const u32 offset_txagc[2] = {0x1d00, 0x1d80};
- static u32 phy_pwr_idx;
u8 rate, rate_idx, pwr_index, shift;
int j;
@@ -692,12 +692,12 @@ rtw8821c_set_tx_power_index_by_rate(struct rtw_dev *rtwdev, u8 path, u8 rs)
rate = rtw_rate_section[rs][j];
pwr_index = hal->tx_pwr_tbl[path][rate];
shift = rate & 0x3;
- phy_pwr_idx |= ((u32)pwr_index << (shift * 8));
+ *phy_pwr_idx |= ((u32)pwr_index << (shift * 8));
if (shift == 0x3 || rate == DESC_RATEVHT1SS_MCS9) {
rate_idx = rate & 0xfc;
rtw_write32(rtwdev, offset_txagc[path] + rate_idx,
- phy_pwr_idx);
- phy_pwr_idx = 0;
+ *phy_pwr_idx);
+ *phy_pwr_idx = 0;
}
}
}
@@ -705,14 +705,16 @@ rtw8821c_set_tx_power_index_by_rate(struct rtw_dev *rtwdev, u8 path, u8 rs)
static void rtw8821c_set_tx_power_index(struct rtw_dev *rtwdev)
{
struct rtw_hal *hal = &rtwdev->hal;
+ u32 phy_pwr_idx = 0;
int rs, path;
for (path = 0; path < hal->rf_path_num; path++) {
- for (rs = 0; rs < RTW_RATE_SECTION_MAX; rs++) {
+ for (rs = 0; rs <= __RTW_RATE_SECTION_2SS_MAX; rs++) {
if (rs == RTW_RATE_SECTION_HT_2S ||
rs == RTW_RATE_SECTION_VHT_2S)
continue;
- rtw8821c_set_tx_power_index_by_rate(rtwdev, path, rs);
+ rtw8821c_set_tx_power_index_by_rate(rtwdev, path, rs,
+ &phy_pwr_idx);
}
}
}
@@ -1666,6 +1668,7 @@ static const struct rtw_chip_ops rtw8821c_ops = {
.set_antenna = NULL,
.set_tx_power_index = rtw8821c_set_tx_power_index,
.cfg_ldo25 = rtw8821c_cfg_ldo25,
+ .set_ampdu_factor = NULL,
.false_alarm_statistics = rtw8821c_false_alarm_statistics,
.phy_calibration = rtw8821c_phy_calibration,
.cck_pd_set = rtw8821c_phy_cck_pd_set,
@@ -1988,6 +1991,7 @@ const struct rtw_chip_info rtw8821c_hw_spec = {
.band = RTW_BAND_2G | RTW_BAND_5G,
.page_size = TX_PAGE_SIZE,
.dig_min = 0x1c,
+ .amsdu_in_ampdu = true,
.usb_tx_agg_desc_num = 3,
.hw_feature_report = true,
.c2h_ra_report_size = 7,
diff --git a/drivers/net/wireless/realtek/rtw88/rtw8821ce.c b/drivers/net/wireless/realtek/rtw88/rtw8821ce.c
index f3d971feda04..40637c079d99 100644
--- a/drivers/net/wireless/realtek/rtw88/rtw8821ce.c
+++ b/drivers/net/wireless/realtek/rtw88/rtw8821ce.c
@@ -21,7 +21,7 @@ static const struct pci_device_id rtw_8821ce_id_table[] = {
MODULE_DEVICE_TABLE(pci, rtw_8821ce_id_table);
static struct pci_driver rtw_8821ce_driver = {
- .name = "rtw_8821ce",
+ .name = KBUILD_MODNAME,
.id_table = rtw_8821ce_id_table,
.probe = rtw_pci_probe,
.remove = rtw_pci_remove,
diff --git a/drivers/net/wireless/realtek/rtw88/rtw8821cs.c b/drivers/net/wireless/realtek/rtw88/rtw8821cs.c
index a359413369a4..6d94162213c6 100644
--- a/drivers/net/wireless/realtek/rtw88/rtw8821cs.c
+++ b/drivers/net/wireless/realtek/rtw88/rtw8821cs.c
@@ -20,7 +20,7 @@ static const struct sdio_device_id rtw_8821cs_id_table[] = {
MODULE_DEVICE_TABLE(sdio, rtw_8821cs_id_table);
static struct sdio_driver rtw_8821cs_driver = {
- .name = "rtw_8821cs",
+ .name = KBUILD_MODNAME,
.probe = rtw_sdio_probe,
.remove = rtw_sdio_remove,
.id_table = rtw_8821cs_id_table,
diff --git a/drivers/net/wireless/realtek/rtw88/rtw8821cu.c b/drivers/net/wireless/realtek/rtw88/rtw8821cu.c
index a019f4085e73..7a0fffc359e2 100644
--- a/drivers/net/wireless/realtek/rtw88/rtw8821cu.c
+++ b/drivers/net/wireless/realtek/rtw88/rtw8821cu.c
@@ -48,7 +48,7 @@ static int rtw_8821cu_probe(struct usb_interface *intf,
}
static struct usb_driver rtw_8821cu_driver = {
- .name = "rtw_8821cu",
+ .name = KBUILD_MODNAME,
.id_table = rtw_8821cu_id_table,
.probe = rtw_8821cu_probe,
.disconnect = rtw_usb_disconnect,
diff --git a/drivers/net/wireless/realtek/rtw88/rtw8822b.c b/drivers/net/wireless/realtek/rtw88/rtw8822b.c
index 7f03903ddf4b..ab199eaea3c7 100644
--- a/drivers/net/wireless/realtek/rtw88/rtw8822b.c
+++ b/drivers/net/wireless/realtek/rtw88/rtw8822b.c
@@ -935,11 +935,11 @@ static void query_phy_status(struct rtw_dev *rtwdev, u8 *phy_status,
}
static void
-rtw8822b_set_tx_power_index_by_rate(struct rtw_dev *rtwdev, u8 path, u8 rs)
+rtw8822b_set_tx_power_index_by_rate(struct rtw_dev *rtwdev, u8 path,
+ u8 rs, u32 *phy_pwr_idx)
{
struct rtw_hal *hal = &rtwdev->hal;
static const u32 offset_txagc[2] = {0x1d00, 0x1d80};
- static u32 phy_pwr_idx;
u8 rate, rate_idx, pwr_index, shift;
int j;
@@ -947,12 +947,12 @@ rtw8822b_set_tx_power_index_by_rate(struct rtw_dev *rtwdev, u8 path, u8 rs)
rate = rtw_rate_section[rs][j];
pwr_index = hal->tx_pwr_tbl[path][rate];
shift = rate & 0x3;
- phy_pwr_idx |= ((u32)pwr_index << (shift * 8));
+ *phy_pwr_idx |= ((u32)pwr_index << (shift * 8));
if (shift == 0x3) {
rate_idx = rate & 0xfc;
rtw_write32(rtwdev, offset_txagc[path] + rate_idx,
- phy_pwr_idx);
- phy_pwr_idx = 0;
+ *phy_pwr_idx);
+ *phy_pwr_idx = 0;
}
}
}
@@ -960,11 +960,13 @@ rtw8822b_set_tx_power_index_by_rate(struct rtw_dev *rtwdev, u8 path, u8 rs)
static void rtw8822b_set_tx_power_index(struct rtw_dev *rtwdev)
{
struct rtw_hal *hal = &rtwdev->hal;
+ u32 phy_pwr_idx = 0;
int rs, path;
for (path = 0; path < hal->rf_path_num; path++) {
- for (rs = 0; rs < RTW_RATE_SECTION_MAX; rs++)
- rtw8822b_set_tx_power_index_by_rate(rtwdev, path, rs);
+ for (rs = 0; rs <= __RTW_RATE_SECTION_2SS_MAX; rs++)
+ rtw8822b_set_tx_power_index_by_rate(rtwdev, path, rs,
+ &phy_pwr_idx);
}
}
@@ -2156,6 +2158,7 @@ static const struct rtw_chip_ops rtw8822b_ops = {
.set_tx_power_index = rtw8822b_set_tx_power_index,
.set_antenna = rtw8822b_set_antenna,
.cfg_ldo25 = rtw8822b_cfg_ldo25,
+ .set_ampdu_factor = NULL,
.false_alarm_statistics = rtw8822b_false_alarm_statistics,
.phy_calibration = rtw8822b_phy_calibration,
.pwr_track = rtw8822b_pwr_track,
@@ -2529,6 +2532,7 @@ const struct rtw_chip_info rtw8822b_hw_spec = {
.band = RTW_BAND_2G | RTW_BAND_5G,
.page_size = TX_PAGE_SIZE,
.dig_min = 0x1c,
+ .amsdu_in_ampdu = true,
.usb_tx_agg_desc_num = 3,
.hw_feature_report = true,
.c2h_ra_report_size = 7,
diff --git a/drivers/net/wireless/realtek/rtw88/rtw8822be.c b/drivers/net/wireless/realtek/rtw88/rtw8822be.c
index 4994950776cd..0bb9f70e7920 100644
--- a/drivers/net/wireless/realtek/rtw88/rtw8822be.c
+++ b/drivers/net/wireless/realtek/rtw88/rtw8822be.c
@@ -17,7 +17,7 @@ static const struct pci_device_id rtw_8822be_id_table[] = {
MODULE_DEVICE_TABLE(pci, rtw_8822be_id_table);
static struct pci_driver rtw_8822be_driver = {
- .name = "rtw_8822be",
+ .name = KBUILD_MODNAME,
.id_table = rtw_8822be_id_table,
.probe = rtw_pci_probe,
.remove = rtw_pci_remove,
diff --git a/drivers/net/wireless/realtek/rtw88/rtw8822bs.c b/drivers/net/wireless/realtek/rtw88/rtw8822bs.c
index 31d8645f83bd..744781dcb419 100644
--- a/drivers/net/wireless/realtek/rtw88/rtw8822bs.c
+++ b/drivers/net/wireless/realtek/rtw88/rtw8822bs.c
@@ -20,7 +20,7 @@ static const struct sdio_device_id rtw_8822bs_id_table[] = {
MODULE_DEVICE_TABLE(sdio, rtw_8822bs_id_table);
static struct sdio_driver rtw_8822bs_driver = {
- .name = "rtw_8822bs",
+ .name = KBUILD_MODNAME,
.probe = rtw_sdio_probe,
.remove = rtw_sdio_remove,
.id_table = rtw_8822bs_id_table,
diff --git a/drivers/net/wireless/realtek/rtw88/rtw8822bu.c b/drivers/net/wireless/realtek/rtw88/rtw8822bu.c
index 8883300fc6ad..44e28e583964 100644
--- a/drivers/net/wireless/realtek/rtw88/rtw8822bu.c
+++ b/drivers/net/wireless/realtek/rtw88/rtw8822bu.c
@@ -73,6 +73,12 @@ static const struct usb_device_id rtw_8822bu_id_table[] = {
.driver_info = (kernel_ulong_t)&(rtw8822b_hw_spec) }, /* ELECOM WDB-867DU3S */
{ USB_DEVICE_AND_INTERFACE_INFO(0x2c4e, 0x0107, 0xff, 0xff, 0xff),
.driver_info = (kernel_ulong_t)&(rtw8822b_hw_spec) }, /* Mercusys MA30H */
+ { USB_DEVICE_AND_INTERFACE_INFO(0x2c4e, 0x010a, 0xff, 0xff, 0xff),
+ .driver_info = (kernel_ulong_t)&(rtw8822b_hw_spec) }, /* Mercusys MA30N */
+ { USB_DEVICE_AND_INTERFACE_INFO(0x2001, 0x3322, 0xff, 0xff, 0xff),
+ .driver_info = (kernel_ulong_t)&(rtw8822b_hw_spec) }, /* D-Link DWA-T185 rev. A1 */
+ { USB_DEVICE_AND_INTERFACE_INFO(0x0411, 0x03d1, 0xff, 0xff, 0xff),
+ .driver_info = (kernel_ulong_t)&(rtw8822b_hw_spec) }, /* BUFFALO WI-U2-866DM */
{},
};
MODULE_DEVICE_TABLE(usb, rtw_8822bu_id_table);
@@ -84,7 +90,7 @@ static int rtw8822bu_probe(struct usb_interface *intf,
}
static struct usb_driver rtw_8822bu_driver = {
- .name = "rtw_8822bu",
+ .name = KBUILD_MODNAME,
.id_table = rtw_8822bu_id_table,
.probe = rtw8822bu_probe,
.disconnect = rtw_usb_disconnect,
diff --git a/drivers/net/wireless/realtek/rtw88/rtw8822c.c b/drivers/net/wireless/realtek/rtw88/rtw8822c.c
index ec362a817f5f..017d959de3ce 100644
--- a/drivers/net/wireless/realtek/rtw88/rtw8822c.c
+++ b/drivers/net/wireless/realtek/rtw88/rtw8822c.c
@@ -2746,7 +2746,7 @@ static void rtw8822c_set_tx_power_index(struct rtw_dev *rtwdev)
s8 diff_idx[4];
rtw8822c_set_write_tx_power_ref(rtwdev, pwr_ref_cck, pwr_ref_ofdm);
- for (rs = 0; rs < RTW_RATE_SECTION_MAX; rs++) {
+ for (rs = 0; rs <= __RTW_RATE_SECTION_2SS_MAX; rs++) {
for (j = 0; j < rtw_rate_size[rs]; j++) {
rate = rtw_rate_section[rs][j];
pwr_a = hal->tx_pwr_tbl[RF_PATH_A][rate];
@@ -3951,7 +3951,8 @@ static void rtw8822c_dpk_cal_coef1(struct rtw_dev *rtwdev)
rtw_write32(rtwdev, REG_NCTL0, 0x00001148);
rtw_write32(rtwdev, REG_NCTL0, 0x00001149);
- check_hw_ready(rtwdev, 0x2d9c, MASKBYTE0, 0x55);
+ if (!check_hw_ready(rtwdev, 0x2d9c, MASKBYTE0, 0x55))
+ rtw_warn(rtwdev, "DPK stuck, performance may be suboptimal");
rtw_write8(rtwdev, 0x1b10, 0x0);
rtw_write32_mask(rtwdev, REG_NCTL0, BIT_SUBPAGE, 0x0000000c);
@@ -4968,6 +4969,7 @@ static const struct rtw_chip_ops rtw8822c_ops = {
.set_tx_power_index = rtw8822c_set_tx_power_index,
.set_antenna = rtw8822c_set_antenna,
.cfg_ldo25 = rtw8822c_cfg_ldo25,
+ .set_ampdu_factor = NULL,
.false_alarm_statistics = rtw8822c_false_alarm_statistics,
.dpk_track = rtw8822c_dpk_track,
.phy_calibration = rtw8822c_phy_calibration,
@@ -5349,6 +5351,7 @@ const struct rtw_chip_info rtw8822c_hw_spec = {
.band = RTW_BAND_2G | RTW_BAND_5G,
.page_size = TX_PAGE_SIZE,
.dig_min = 0x20,
+ .amsdu_in_ampdu = true,
.usb_tx_agg_desc_num = 3,
.hw_feature_report = true,
.c2h_ra_report_size = 7,
diff --git a/drivers/net/wireless/realtek/rtw88/rtw8822ce.c b/drivers/net/wireless/realtek/rtw88/rtw8822ce.c
index e26c6bc82936..9def732480af 100644
--- a/drivers/net/wireless/realtek/rtw88/rtw8822ce.c
+++ b/drivers/net/wireless/realtek/rtw88/rtw8822ce.c
@@ -21,7 +21,7 @@ static const struct pci_device_id rtw_8822ce_id_table[] = {
MODULE_DEVICE_TABLE(pci, rtw_8822ce_id_table);
static struct pci_driver rtw_8822ce_driver = {
- .name = "rtw_8822ce",
+ .name = KBUILD_MODNAME,
.id_table = rtw_8822ce_id_table,
.probe = rtw_pci_probe,
.remove = rtw_pci_remove,
diff --git a/drivers/net/wireless/realtek/rtw88/rtw8822cs.c b/drivers/net/wireless/realtek/rtw88/rtw8822cs.c
index 975e81c824f2..322281e07eb8 100644
--- a/drivers/net/wireless/realtek/rtw88/rtw8822cs.c
+++ b/drivers/net/wireless/realtek/rtw88/rtw8822cs.c
@@ -20,7 +20,7 @@ static const struct sdio_device_id rtw_8822cs_id_table[] = {
MODULE_DEVICE_TABLE(sdio, rtw_8822cs_id_table);
static struct sdio_driver rtw_8822cs_driver = {
- .name = "rtw_8822cs",
+ .name = KBUILD_MODNAME,
.probe = rtw_sdio_probe,
.remove = rtw_sdio_remove,
.id_table = rtw_8822cs_id_table,
diff --git a/drivers/net/wireless/realtek/rtw88/rtw8822cu.c b/drivers/net/wireless/realtek/rtw88/rtw8822cu.c
index 157d5102a4b1..324fd5c8bfd4 100644
--- a/drivers/net/wireless/realtek/rtw88/rtw8822cu.c
+++ b/drivers/net/wireless/realtek/rtw88/rtw8822cu.c
@@ -32,7 +32,7 @@ static int rtw8822cu_probe(struct usb_interface *intf,
}
static struct usb_driver rtw_8822cu_driver = {
- .name = "rtw_8822cu",
+ .name = KBUILD_MODNAME,
.id_table = rtw_8822cu_id_table,
.probe = rtw8822cu_probe,
.disconnect = rtw_usb_disconnect,
diff --git a/drivers/net/wireless/realtek/rtw88/rtw88xxa.c b/drivers/net/wireless/realtek/rtw88/rtw88xxa.c
index 71e61b9c0bec..0fa943271fb6 100644
--- a/drivers/net/wireless/realtek/rtw88/rtw88xxa.c
+++ b/drivers/net/wireless/realtek/rtw88/rtw88xxa.c
@@ -1637,7 +1637,7 @@ void rtw88xxa_set_tx_power_index(struct rtw_dev *rtwdev)
int rs, path;
for (path = 0; path < hal->rf_path_num; path++) {
- for (rs = 0; rs < RTW_RATE_SECTION_MAX; rs++) {
+ for (rs = 0; rs <= __RTW_RATE_SECTION_2SS_MAX; rs++) {
if (hal->rf_path_num == 1 &&
(rs == RTW_RATE_SECTION_HT_2S ||
rs == RTW_RATE_SECTION_VHT_2S))
diff --git a/drivers/net/wireless/realtek/rtw88/rx.c b/drivers/net/wireless/realtek/rtw88/rx.c
index 90fc8a5fa89e..8b0afaaffaa0 100644
--- a/drivers/net/wireless/realtek/rtw88/rx.c
+++ b/drivers/net/wireless/realtek/rtw88/rx.c
@@ -73,6 +73,12 @@ static void rtw_rx_phy_stat(struct rtw_dev *rtwdev,
rate_ss_evm = 2;
evm_id = RTW_EVM_2SS_A;
break;
+ case DESC_RATEMCS16...DESC_RATEMCS23:
+ case DESC_RATEVHT3SS_MCS0...DESC_RATEVHT3SS_MCS9:
+ rate_ss = 3;
+ rate_ss_evm = 3;
+ evm_id = RTW_EVM_3SS_A;
+ break;
default:
rtw_warn(rtwdev, "unknown pkt rate = %d\n", pkt_stat->rate);
return;
diff --git a/drivers/net/wireless/realtek/rtw88/sar.c b/drivers/net/wireless/realtek/rtw88/sar.c
index c472f1502b82..50b9c2412bb1 100644
--- a/drivers/net/wireless/realtek/rtw88/sar.c
+++ b/drivers/net/wireless/realtek/rtw88/sar.c
@@ -97,7 +97,7 @@ int rtw_set_sar_specs(struct rtw_dev *rtwdev,
power, BIT(RTW_COMMON_SAR_FCT));
for (j = 0; j < RTW_RF_PATH_MAX; j++) {
- for (k = 0; k < RTW_RATE_SECTION_MAX; k++) {
+ for (k = 0; k < RTW_RATE_SECTION_NUM; k++) {
arg = (struct rtw_sar_arg){
.sar_band = idx,
.path = j,
diff --git a/drivers/net/wireless/realtek/rtw88/sdio.c b/drivers/net/wireless/realtek/rtw88/sdio.c
index e024061bdbf7..e733ed846123 100644
--- a/drivers/net/wireless/realtek/rtw88/sdio.c
+++ b/drivers/net/wireless/realtek/rtw88/sdio.c
@@ -10,6 +10,7 @@
#include <linux/mmc/host.h>
#include <linux/mmc/sdio_func.h>
#include "main.h"
+#include "mac.h"
#include "debug.h"
#include "fw.h"
#include "ps.h"
@@ -677,12 +678,22 @@ static void rtw_sdio_enable_rx_aggregation(struct rtw_dev *rtwdev)
{
u8 size, timeout;
- if (rtw_chip_wcpu_11n(rtwdev)) {
+ switch (rtwdev->chip->id) {
+ case RTW_CHIP_TYPE_8703B:
+ case RTW_CHIP_TYPE_8821A:
+ case RTW_CHIP_TYPE_8812A:
size = 0x6;
timeout = 0x6;
- } else {
+ break;
+ case RTW_CHIP_TYPE_8723D:
+ size = 0xa;
+ timeout = 0x3;
+ rtw_write8_set(rtwdev, REG_RXDMA_AGG_PG_TH + 3, BIT(7));
+ break;
+ default:
size = 0xff;
timeout = 0x1;
+ break;
}
/* Make the firmware honor the size limit configured below */
@@ -718,10 +729,7 @@ static u8 rtw_sdio_get_tx_qsel(struct rtw_dev *rtwdev, struct sk_buff *skb,
case RTW_TX_QUEUE_H2C:
return TX_DESC_QSEL_H2C;
case RTW_TX_QUEUE_MGMT:
- if (rtw_chip_wcpu_11n(rtwdev))
- return TX_DESC_QSEL_HIGH;
- else
- return TX_DESC_QSEL_MGMT;
+ return TX_DESC_QSEL_MGMT;
case RTW_TX_QUEUE_HI0:
return TX_DESC_QSEL_HIGH;
default:
@@ -1147,7 +1155,7 @@ static void rtw_sdio_declaim(struct rtw_dev *rtwdev,
sdio_release_host(sdio_func);
}
-static struct rtw_hci_ops rtw_sdio_ops = {
+static const struct rtw_hci_ops rtw_sdio_ops = {
.tx_write = rtw_sdio_tx_write,
.tx_kick_off = rtw_sdio_tx_kick_off,
.setup = rtw_sdio_setup,
@@ -1157,6 +1165,7 @@ static struct rtw_hci_ops rtw_sdio_ops = {
.link_ps = rtw_sdio_link_ps,
.interface_cfg = rtw_sdio_interface_cfg,
.dynamic_rx_agg = NULL,
+ .write_firmware_page = rtw_write_firmware_page,
.read8 = rtw_sdio_read8,
.read16 = rtw_sdio_read16,
@@ -1227,10 +1236,7 @@ static void rtw_sdio_process_tx_queue(struct rtw_dev *rtwdev,
return;
}
- if (queue <= RTW_TX_QUEUE_VO)
- rtw_sdio_indicate_tx_status(rtwdev, skb);
- else
- dev_kfree_skb_any(skb);
+ rtw_sdio_indicate_tx_status(rtwdev, skb);
}
static void rtw_sdio_tx_handler(struct work_struct *work)
@@ -1298,7 +1304,6 @@ static void rtw_sdio_deinit_tx(struct rtw_dev *rtwdev)
struct rtw_sdio *rtwsdio = (struct rtw_sdio *)rtwdev->priv;
int i;
- flush_workqueue(rtwsdio->txwq);
destroy_workqueue(rtwsdio->txwq);
kfree(rtwsdio->tx_handler_data);
diff --git a/drivers/net/wireless/realtek/rtw88/usb.c b/drivers/net/wireless/realtek/rtw88/usb.c
index c4908db4ff0e..3b5126ffc81a 100644
--- a/drivers/net/wireless/realtek/rtw88/usb.c
+++ b/drivers/net/wireless/realtek/rtw88/usb.c
@@ -139,7 +139,7 @@ static void rtw_usb_write(struct rtw_dev *rtwdev, u32 addr, u32 val, int len)
ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
RTW_USB_CMD_REQ, RTW_USB_CMD_WRITE,
- addr, 0, data, len, 30000);
+ addr, 0, data, len, 500);
if (ret < 0 && ret != -ENODEV && count++ < 4)
rtw_err(rtwdev, "write register 0x%x failed with %d\n",
addr, ret);
@@ -165,6 +165,60 @@ static void rtw_usb_write32(struct rtw_dev *rtwdev, u32 addr, u32 val)
rtw_usb_write(rtwdev, addr, val, 4);
}
+static void rtw_usb_write_firmware_page(struct rtw_dev *rtwdev, u32 page,
+ const u8 *data, u32 size)
+{
+ struct rtw_usb *rtwusb = rtw_get_usb_priv(rtwdev);
+ struct usb_device *udev = rtwusb->udev;
+ u32 addr = FW_START_ADDR_LEGACY;
+ u8 *data_dup, *buf;
+ u32 n, block_size;
+ int ret;
+
+ switch (rtwdev->chip->id) {
+ case RTW_CHIP_TYPE_8723D:
+ block_size = 254;
+ break;
+ default:
+ block_size = 196;
+ break;
+ }
+
+ data_dup = kmemdup(data, size, GFP_KERNEL);
+ if (!data_dup)
+ return;
+
+ buf = data_dup;
+
+ rtw_write32_mask(rtwdev, REG_MCUFW_CTRL, BIT_ROM_PGE, page);
+
+ while (size > 0) {
+ if (size >= block_size)
+ n = block_size;
+ else if (size >= 8)
+ n = 8;
+ else
+ n = 1;
+
+ ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+ RTW_USB_CMD_REQ, RTW_USB_CMD_WRITE,
+ addr, 0, buf, n, 500);
+ if (ret != n) {
+ if (ret != -ENODEV)
+ rtw_err(rtwdev,
+ "write 0x%x len %d failed: %d\n",
+ addr, n, ret);
+ break;
+ }
+
+ addr += n;
+ buf += n;
+ size -= n;
+ }
+
+ kfree(data_dup);
+}
+
static int dma_mapping_to_ep(enum rtw_dma_mapping dma_mapping)
{
switch (dma_mapping) {
@@ -866,6 +920,7 @@ static void rtw_usb_dynamic_rx_agg(struct rtw_dev *rtwdev, bool enable)
case RTW_CHIP_TYPE_8822C:
case RTW_CHIP_TYPE_8822B:
case RTW_CHIP_TYPE_8821C:
+ case RTW_CHIP_TYPE_8814A:
rtw_usb_dynamic_rx_agg_v1(rtwdev, enable);
break;
case RTW_CHIP_TYPE_8821A:
@@ -881,7 +936,7 @@ static void rtw_usb_dynamic_rx_agg(struct rtw_dev *rtwdev, bool enable)
}
}
-static struct rtw_hci_ops rtw_usb_ops = {
+static const struct rtw_hci_ops rtw_usb_ops = {
.tx_write = rtw_usb_tx_write,
.tx_kick_off = rtw_usb_tx_kick_off,
.setup = rtw_usb_setup,
@@ -891,6 +946,7 @@ static struct rtw_hci_ops rtw_usb_ops = {
.link_ps = rtw_usb_link_ps,
.interface_cfg = rtw_usb_interface_cfg,
.dynamic_rx_agg = rtw_usb_dynamic_rx_agg,
+ .write_firmware_page = rtw_usb_write_firmware_page,
.write8 = rtw_usb_write8,
.write16 = rtw_usb_write16,
@@ -948,7 +1004,6 @@ static void rtw_usb_deinit_rx(struct rtw_dev *rtwdev)
skb_queue_purge(&rtwusb->rx_queue);
- flush_workqueue(rtwusb->rxwq);
destroy_workqueue(rtwusb->rxwq);
skb_queue_purge(&rtwusb->rx_free_queue);
@@ -977,7 +1032,6 @@ static void rtw_usb_deinit_tx(struct rtw_dev *rtwdev)
{
struct rtw_usb *rtwusb = rtw_get_usb_priv(rtwdev);
- flush_workqueue(rtwusb->txwq);
destroy_workqueue(rtwusb->txwq);
rtw_usb_tx_queue_purge(rtwusb);
}
@@ -1094,7 +1148,8 @@ static int rtw_usb_switch_mode_new(struct rtw_dev *rtwdev)
static bool rtw_usb3_chip_old(u8 chip_id)
{
- return chip_id == RTW_CHIP_TYPE_8812A;
+ return chip_id == RTW_CHIP_TYPE_8812A ||
+ chip_id == RTW_CHIP_TYPE_8814A;
}
static bool rtw_usb3_chip_new(u8 chip_id)
diff --git a/drivers/net/wireless/realtek/rtw88/util.c b/drivers/net/wireless/realtek/rtw88/util.c
index e222d3c01a77..66819f694405 100644
--- a/drivers/net/wireless/realtek/rtw88/util.c
+++ b/drivers/net/wireless/realtek/rtw88/util.c
@@ -101,7 +101,8 @@ void rtw_desc_to_mcsrate(u16 rate, u8 *mcs, u8 *nss)
*nss = 4;
*mcs = rate - DESC_RATEVHT4SS_MCS0;
} else if (rate >= DESC_RATEMCS0 &&
- rate <= DESC_RATEMCS15) {
+ rate <= DESC_RATEMCS31) {
+ *nss = 0;
*mcs = rate - DESC_RATEMCS0;
}
}
diff --git a/drivers/net/wireless/realtek/rtw89/Kconfig b/drivers/net/wireless/realtek/rtw89/Kconfig
index b1c86cdd9c0e..205d7ecca7d7 100644
--- a/drivers/net/wireless/realtek/rtw89/Kconfig
+++ b/drivers/net/wireless/realtek/rtw89/Kconfig
@@ -123,7 +123,7 @@ config RTW89_DEBUGMSG
config RTW89_DEBUGFS
bool "Realtek rtw89 debugfs support"
- depends on RTW89_CORE
+ depends on RTW89_CORE && CFG80211_DEBUGFS
select RTW89_DEBUG
help
Enable debugfs support
diff --git a/drivers/net/wireless/realtek/rtw89/acpi.c b/drivers/net/wireless/realtek/rtw89/acpi.c
index f5dedb12c129..581d6d4154d3 100644
--- a/drivers/net/wireless/realtek/rtw89/acpi.c
+++ b/drivers/net/wireless/realtek/rtw89/acpi.c
@@ -12,6 +12,121 @@ static const guid_t rtw89_guid = GUID_INIT(0xD2A8C3E8, 0x4B69, 0x4F00,
0x82, 0xBD, 0xFE, 0x86,
0x07, 0x80, 0x3A, 0xA7);
+static u32 rtw89_acpi_traversal_object(struct rtw89_dev *rtwdev,
+ const union acpi_object *obj, u8 *pos)
+{
+ const union acpi_object *elm;
+ unsigned int i;
+ u32 sub_len;
+ u32 len = 0;
+ u8 *tmp;
+
+ switch (obj->type) {
+ case ACPI_TYPE_INTEGER:
+ if (pos)
+ pos[len] = obj->integer.value;
+
+ len++;
+ break;
+ case ACPI_TYPE_BUFFER:
+ if (unlikely(obj->buffer.length == 0)) {
+ rtw89_debug(rtwdev, RTW89_DBG_ACPI,
+ "%s: invalid buffer type\n", __func__);
+ goto err;
+ }
+
+ if (pos)
+ memcpy(pos, obj->buffer.pointer, obj->buffer.length);
+
+ len += obj->buffer.length;
+ break;
+ case ACPI_TYPE_PACKAGE:
+ if (unlikely(obj->package.count == 0)) {
+ rtw89_debug(rtwdev, RTW89_DBG_ACPI,
+ "%s: invalid package type\n", __func__);
+ goto err;
+ }
+
+ for (i = 0; i < obj->package.count; i++) {
+ elm = &obj->package.elements[i];
+ tmp = pos ? pos + len : NULL;
+
+ sub_len = rtw89_acpi_traversal_object(rtwdev, elm, tmp);
+ if (unlikely(sub_len == 0))
+ goto err;
+
+ len += sub_len;
+ }
+ break;
+ default:
+ rtw89_debug(rtwdev, RTW89_DBG_ACPI, "%s: unhandled type: %d\n",
+ __func__, obj->type);
+ goto err;
+ }
+
+ return len;
+
+err:
+ return 0;
+}
+
+static u32 rtw89_acpi_calculate_object_length(struct rtw89_dev *rtwdev,
+ const union acpi_object *obj)
+{
+ return rtw89_acpi_traversal_object(rtwdev, obj, NULL);
+}
+
+static struct rtw89_acpi_data *
+rtw89_acpi_evaluate_method(struct rtw89_dev *rtwdev, const char *method)
+{
+ struct acpi_buffer buf = {ACPI_ALLOCATE_BUFFER, NULL};
+ struct rtw89_acpi_data *data = NULL;
+ acpi_handle root, handle;
+ union acpi_object *obj;
+ acpi_status status;
+ u32 len;
+
+ root = ACPI_HANDLE(rtwdev->dev);
+ if (!root) {
+ rtw89_debug(rtwdev, RTW89_DBG_ACPI,
+ "acpi (%s): failed to get root\n", method);
+ return NULL;
+ }
+
+ status = acpi_get_handle(root, (acpi_string)method, &handle);
+ if (ACPI_FAILURE(status)) {
+ rtw89_debug(rtwdev, RTW89_DBG_ACPI,
+ "acpi (%s): failed to get handle\n", method);
+ return NULL;
+ }
+
+ status = acpi_evaluate_object(handle, NULL, NULL, &buf);
+ if (ACPI_FAILURE(status)) {
+ rtw89_debug(rtwdev, RTW89_DBG_ACPI,
+ "acpi (%s): failed to evaluate object\n", method);
+ return NULL;
+ }
+
+ obj = buf.pointer;
+ len = rtw89_acpi_calculate_object_length(rtwdev, obj);
+ if (unlikely(len == 0)) {
+ rtw89_debug(rtwdev, RTW89_DBG_ACPI,
+ "acpi (%s): failed to traversal obj len\n", method);
+ goto out;
+ }
+
+ data = kzalloc(struct_size(data, buf, len), GFP_KERNEL);
+ if (!data)
+ goto out;
+
+ data->len = len;
+ rtw89_acpi_traversal_object(rtwdev, obj, data->buf);
+
+out:
+ ACPI_FREE(obj);
+ return data;
+}
+
static
int rtw89_acpi_dsm_get_value(struct rtw89_dev *rtwdev, union acpi_object *obj,
u8 *value)
@@ -121,6 +236,49 @@ int rtw89_acpi_dsm_get_policy_6ghz_sp(struct rtw89_dev *rtwdev,
return 0;
}
+static bool chk_acpi_policy_tas_sig(const struct rtw89_acpi_policy_tas *p)
+{
+ return p->signature[0] == 0x52 &&
+ p->signature[1] == 0x54 &&
+ p->signature[2] == 0x4B &&
+ p->signature[3] == 0x05;
+}
+
+static int rtw89_acpi_dsm_get_policy_tas(struct rtw89_dev *rtwdev,
+ union acpi_object *obj,
+ struct rtw89_acpi_policy_tas **policy)
+{
+ const struct rtw89_acpi_policy_tas *ptr;
+ u32 buf_len;
+
+ if (obj->type != ACPI_TYPE_BUFFER) {
+ rtw89_debug(rtwdev, RTW89_DBG_ACPI,
+ "acpi: expect buffer but type: %d\n", obj->type);
+ return -EINVAL;
+ }
+
+ buf_len = obj->buffer.length;
+ if (buf_len < sizeof(*ptr)) {
+ rtw89_debug(rtwdev, RTW89_DBG_ACPI, "%s: invalid buffer length: %u\n",
+ __func__, buf_len);
+ return -EINVAL;
+ }
+
+ ptr = (typeof(ptr))obj->buffer.pointer;
+ if (!chk_acpi_policy_tas_sig(ptr)) {
+ rtw89_debug(rtwdev, RTW89_DBG_ACPI, "%s: bad signature\n", __func__);
+ return -EINVAL;
+ }
+
+ *policy = kmemdup(ptr, sizeof(*ptr), GFP_KERNEL);
+ if (!*policy)
+ return -ENOMEM;
+
+ rtw89_hex_dump(rtwdev, RTW89_DBG_ACPI, "policy_tas: ", *policy,
+ sizeof(*ptr));
+ return 0;
+}
+
int rtw89_acpi_evaluate_dsm(struct rtw89_dev *rtwdev,
enum rtw89_acpi_dsm_func func,
struct rtw89_acpi_dsm_result *res)
@@ -142,6 +300,8 @@ int rtw89_acpi_evaluate_dsm(struct rtw89_dev *rtwdev,
else if (func == RTW89_ACPI_DSM_FUNC_6GHZ_SP_SUP)
ret = rtw89_acpi_dsm_get_policy_6ghz_sp(rtwdev, obj,
&res->u.policy_6ghz_sp);
+ else if (func == RTW89_ACPI_DSM_FUNC_TAS_EN)
+ ret = rtw89_acpi_dsm_get_policy_tas(rtwdev, obj, &res->u.policy_tas);
else
ret = rtw89_acpi_dsm_get_value(rtwdev, obj, &res->u.value);
@@ -152,46 +312,875 @@ int rtw89_acpi_evaluate_dsm(struct rtw89_dev *rtwdev,
int rtw89_acpi_evaluate_rtag(struct rtw89_dev *rtwdev,
struct rtw89_acpi_rtag_result *res)
{
- struct acpi_buffer buf = {ACPI_ALLOCATE_BUFFER, NULL};
- acpi_handle root, handle;
- union acpi_object *obj;
- acpi_status status;
+ const struct rtw89_acpi_data *data;
u32 buf_len;
int ret = 0;
- root = ACPI_HANDLE(rtwdev->dev);
- if (!root)
- return -EOPNOTSUPP;
-
- status = acpi_get_handle(root, (acpi_string)"RTAG", &handle);
- if (ACPI_FAILURE(status))
+ data = rtw89_acpi_evaluate_method(rtwdev, "RTAG");
+ if (!data)
return -EIO;
- status = acpi_evaluate_object(handle, NULL, NULL, &buf);
- if (ACPI_FAILURE(status))
- return -EIO;
+ buf_len = data->len;
+ if (buf_len != sizeof(*res)) {
+ rtw89_debug(rtwdev, RTW89_DBG_ACPI, "%s: invalid buffer length: %u\n",
+ __func__, buf_len);
+ ret = -EINVAL;
+ goto out;
+ }
- obj = buf.pointer;
- if (obj->type != ACPI_TYPE_BUFFER) {
+ *res = *(struct rtw89_acpi_rtag_result *)data->buf;
+
+ rtw89_hex_dump(rtwdev, RTW89_DBG_ACPI, "antenna_gain: ", res, sizeof(*res));
+
+out:
+ kfree(data);
+ return ret;
+}
+
+enum rtw89_acpi_sar_subband rtw89_acpi_sar_get_subband(struct rtw89_dev *rtwdev,
+ u32 center_freq)
+{
+ switch (center_freq) {
+ default:
rtw89_debug(rtwdev, RTW89_DBG_ACPI,
- "acpi: expect buffer but type: %d\n", obj->type);
- ret = -EINVAL;
+ "center freq %u to ACPI SAR subband is unhandled\n",
+ center_freq);
+ fallthrough;
+ case 2412 ... 2484:
+ return RTW89_ACPI_SAR_2GHZ_SUBBAND;
+ case 5180 ... 5240:
+ return RTW89_ACPI_SAR_5GHZ_SUBBAND_1;
+ case 5250 ... 5320:
+ return RTW89_ACPI_SAR_5GHZ_SUBBAND_2;
+ case 5500 ... 5720:
+ return RTW89_ACPI_SAR_5GHZ_SUBBAND_2E;
+ case 5745 ... 5885:
+ return RTW89_ACPI_SAR_5GHZ_SUBBAND_3_4;
+ case 5955 ... 6155:
+ return RTW89_ACPI_SAR_6GHZ_SUBBAND_5_L;
+ case 6175 ... 6415:
+ return RTW89_ACPI_SAR_6GHZ_SUBBAND_5_H;
+ case 6435 ... 6515:
+ return RTW89_ACPI_SAR_6GHZ_SUBBAND_6;
+ case 6535 ... 6695:
+ return RTW89_ACPI_SAR_6GHZ_SUBBAND_7_L;
+ case 6715 ... 6855:
+ return RTW89_ACPI_SAR_6GHZ_SUBBAND_7_H;
+
+ /* freq 6875 (ch 185, 20MHz) spans RTW89_ACPI_SAR_6GHZ_SUBBAND_7_H
+ * and RTW89_ACPI_SAR_6GHZ_SUBBAND_8, so directly describe it with
+ * struct rtw89_6ghz_span.
+ */
+
+ case 6895 ... 7115:
+ return RTW89_ACPI_SAR_6GHZ_SUBBAND_8;
+ }
+}
+
+enum rtw89_band rtw89_acpi_sar_subband_to_band(struct rtw89_dev *rtwdev,
+ enum rtw89_acpi_sar_subband subband)
+{
+ switch (subband) {
+ default:
+ rtw89_debug(rtwdev, RTW89_DBG_ACPI,
+ "ACPI SAR subband %u to band is unhandled\n", subband);
+ fallthrough;
+ case RTW89_ACPI_SAR_2GHZ_SUBBAND:
+ return RTW89_BAND_2G;
+ case RTW89_ACPI_SAR_5GHZ_SUBBAND_1:
+ return RTW89_BAND_5G;
+ case RTW89_ACPI_SAR_5GHZ_SUBBAND_2:
+ return RTW89_BAND_5G;
+ case RTW89_ACPI_SAR_5GHZ_SUBBAND_2E:
+ return RTW89_BAND_5G;
+ case RTW89_ACPI_SAR_5GHZ_SUBBAND_3_4:
+ return RTW89_BAND_5G;
+ case RTW89_ACPI_SAR_6GHZ_SUBBAND_5_L:
+ return RTW89_BAND_6G;
+ case RTW89_ACPI_SAR_6GHZ_SUBBAND_5_H:
+ return RTW89_BAND_6G;
+ case RTW89_ACPI_SAR_6GHZ_SUBBAND_6:
+ return RTW89_BAND_6G;
+ case RTW89_ACPI_SAR_6GHZ_SUBBAND_7_L:
+ return RTW89_BAND_6G;
+ case RTW89_ACPI_SAR_6GHZ_SUBBAND_7_H:
+ return RTW89_BAND_6G;
+ case RTW89_ACPI_SAR_6GHZ_SUBBAND_8:
+ return RTW89_BAND_6G;
+ }
+}
+
+static u8 rtw89_acpi_sar_rfpath_to_hp_antidx(enum rtw89_rf_path rfpath)
+{
+ switch (rfpath) {
+ default:
+ case RF_PATH_B:
+ return 0;
+ case RF_PATH_A:
+ return 1;
+ }
+}
+
+static u8 rtw89_acpi_sar_rfpath_to_rt_antidx(enum rtw89_rf_path rfpath)
+{
+ switch (rfpath) {
+ default:
+ case RF_PATH_A:
+ return 0;
+ case RF_PATH_B:
+ return 1;
+ }
+}
+
+static s16 rtw89_acpi_sar_normalize_hp_val(u8 v)
+{
+ static const u8 bias = 10;
+ static const u8 fct = 1;
+ u16 res;
+
+ BUILD_BUG_ON(fct > TXPWR_FACTOR_OF_RTW89_ACPI_SAR);
+
+ res = (bias << TXPWR_FACTOR_OF_RTW89_ACPI_SAR) +
+ (v << (TXPWR_FACTOR_OF_RTW89_ACPI_SAR - fct));
+
+ return min_t(s32, res, MAX_VAL_OF_RTW89_ACPI_SAR);
+}
+
+static s16 rtw89_acpi_sar_normalize_rt_val(u8 v)
+{
+ static const u8 fct = 3;
+ u16 res;
+
+ BUILD_BUG_ON(fct > TXPWR_FACTOR_OF_RTW89_ACPI_SAR);
+
+ res = v << (TXPWR_FACTOR_OF_RTW89_ACPI_SAR - fct);
+
+ return min_t(s32, res, MAX_VAL_OF_RTW89_ACPI_SAR);
+}
+
+static
+void rtw89_acpi_sar_load_std_legacy(struct rtw89_dev *rtwdev,
+ const struct rtw89_acpi_sar_recognition *rec,
+ const void *content,
+ struct rtw89_sar_entry_from_acpi *ent)
+{
+ const struct rtw89_acpi_sar_std_legacy *ptr = content;
+ enum rtw89_acpi_sar_subband subband;
+ enum rtw89_rf_path path;
+
+ for (subband = 0; subband < NUM_OF_RTW89_ACPI_SAR_SUBBAND; subband++) {
+ for (path = 0; path < NUM_OF_RTW89_ACPI_SAR_RF_PATH; path++) {
+ u8 antidx = rec->rfpath_to_antidx(path);
+
+ if (subband < RTW89_ACPI_SAR_SUBBAND_NR_LEGACY)
+ ent->v[subband][path] =
+ rec->normalize(ptr->v[antidx][subband]);
+ else
+ ent->v[subband][path] = MAX_VAL_OF_RTW89_ACPI_SAR;
+ }
+ }
+}
+
+static
+void rtw89_acpi_sar_load_std_has_6ghz(struct rtw89_dev *rtwdev,
+ const struct rtw89_acpi_sar_recognition *rec,
+ const void *content,
+ struct rtw89_sar_entry_from_acpi *ent)
+{
+ const struct rtw89_acpi_sar_std_has_6ghz *ptr = content;
+ enum rtw89_acpi_sar_subband subband;
+ enum rtw89_rf_path path;
+
+ BUILD_BUG_ON(RTW89_ACPI_SAR_SUBBAND_NR_HAS_6GHZ != NUM_OF_RTW89_ACPI_SAR_SUBBAND);
+
+ for (subband = 0; subband < NUM_OF_RTW89_ACPI_SAR_SUBBAND; subband++) {
+ for (path = 0; path < NUM_OF_RTW89_ACPI_SAR_RF_PATH; path++) {
+ u8 antidx = rec->rfpath_to_antidx(path);
+
+ ent->v[subband][path] = rec->normalize(ptr->v[antidx][subband]);
+ }
+ }
+}
+
+static
+void rtw89_acpi_sar_load_sml_legacy(struct rtw89_dev *rtwdev,
+ const struct rtw89_acpi_sar_recognition *rec,
+ const void *content,
+ struct rtw89_sar_entry_from_acpi *ent)
+{
+ const struct rtw89_acpi_sar_sml_legacy *ptr = content;
+ enum rtw89_acpi_sar_subband subband;
+ enum rtw89_rf_path path;
+
+ for (subband = 0; subband < NUM_OF_RTW89_ACPI_SAR_SUBBAND; subband++) {
+ for (path = 0; path < NUM_OF_RTW89_ACPI_SAR_RF_PATH; path++) {
+ u8 antidx = rec->rfpath_to_antidx(path);
+
+ if (subband < RTW89_ACPI_SAR_SUBBAND_NR_LEGACY)
+ ent->v[subband][path] =
+ rec->normalize(ptr->v[antidx][subband]);
+ else
+ ent->v[subband][path] = MAX_VAL_OF_RTW89_ACPI_SAR;
+ }
+ }
+}
+
+static
+void rtw89_acpi_sar_load_sml_has_6ghz(struct rtw89_dev *rtwdev,
+ const struct rtw89_acpi_sar_recognition *rec,
+ const void *content,
+ struct rtw89_sar_entry_from_acpi *ent)
+{
+ const struct rtw89_acpi_sar_sml_has_6ghz *ptr = content;
+ enum rtw89_acpi_sar_subband subband;
+ enum rtw89_rf_path path;
+
+ BUILD_BUG_ON(RTW89_ACPI_SAR_SUBBAND_NR_HAS_6GHZ != NUM_OF_RTW89_ACPI_SAR_SUBBAND);
+
+ for (subband = 0; subband < NUM_OF_RTW89_ACPI_SAR_SUBBAND; subband++) {
+ for (path = 0; path < NUM_OF_RTW89_ACPI_SAR_RF_PATH; path++) {
+ u8 antidx = rec->rfpath_to_antidx(path);
+
+ ent->v[subband][path] = rec->normalize(ptr->v[antidx][subband]);
+ }
+ }
+}
+
+static s16 rtw89_acpi_geo_sar_normalize_delta(s8 delta)
+{
+ static const u8 fct = 1;
+
+ BUILD_BUG_ON(fct > TXPWR_FACTOR_OF_RTW89_ACPI_SAR);
+
+ return delta << (TXPWR_FACTOR_OF_RTW89_ACPI_SAR - fct);
+}
+
+static enum rtw89_acpi_geo_sar_regd_hp
+rtw89_acpi_geo_sar_regd_convert_hp_idx(enum rtw89_regulation_type regd)
+{
+ switch (regd) {
+ case RTW89_FCC:
+ case RTW89_IC:
+ case RTW89_NCC:
+ case RTW89_CHILE:
+ case RTW89_MEXICO:
+ return RTW89_ACPI_GEO_SAR_REGD_HP_FCC;
+ case RTW89_ETSI:
+ case RTW89_MKK:
+ case RTW89_ACMA:
+ return RTW89_ACPI_GEO_SAR_REGD_HP_ETSI;
+ default:
+ case RTW89_WW:
+ case RTW89_NA:
+ case RTW89_KCC:
+ return RTW89_ACPI_GEO_SAR_REGD_HP_WW;
+ }
+}
+
+static enum rtw89_acpi_geo_sar_regd_rt
+rtw89_acpi_geo_sar_regd_convert_rt_idx(enum rtw89_regulation_type regd)
+{
+ switch (regd) {
+ case RTW89_FCC:
+ case RTW89_NCC:
+ case RTW89_CHILE:
+ case RTW89_MEXICO:
+ return RTW89_ACPI_GEO_SAR_REGD_RT_FCC;
+ case RTW89_ETSI:
+ case RTW89_ACMA:
+ return RTW89_ACPI_GEO_SAR_REGD_RT_ETSI;
+ case RTW89_MKK:
+ return RTW89_ACPI_GEO_SAR_REGD_RT_MKK;
+ case RTW89_IC:
+ return RTW89_ACPI_GEO_SAR_REGD_RT_IC;
+ case RTW89_KCC:
+ return RTW89_ACPI_GEO_SAR_REGD_RT_KCC;
+ default:
+ case RTW89_WW:
+ case RTW89_NA:
+ return RTW89_ACPI_GEO_SAR_REGD_RT_WW;
+ }
+}
+
+static
+void rtw89_acpi_geo_sar_load_by_hp(struct rtw89_dev *rtwdev,
+ const struct rtw89_acpi_geo_sar_hp_val *ptr,
+ enum rtw89_rf_path path, s16 *val)
+{
+ u8 antidx = rtw89_acpi_sar_rfpath_to_hp_antidx(path);
+ s16 delta = rtw89_acpi_geo_sar_normalize_delta(ptr->delta[antidx]);
+ s16 max = rtw89_acpi_sar_normalize_hp_val(ptr->max);
+
+ *val = clamp_t(s32, (*val) + delta, MIN_VAL_OF_RTW89_ACPI_SAR, max);
+}
+
+static
+void rtw89_acpi_geo_sar_load_by_rt(struct rtw89_dev *rtwdev,
+ const struct rtw89_acpi_geo_sar_rt_val *ptr,
+ s16 *val)
+{
+ s16 delta = rtw89_acpi_geo_sar_normalize_delta(ptr->delta);
+ s16 max = rtw89_acpi_sar_normalize_rt_val(ptr->max);
+
+ *val = clamp_t(s32, (*val) + delta, MIN_VAL_OF_RTW89_ACPI_SAR, max);
+}
+
+static
+void rtw89_acpi_geo_sar_load_hp_legacy(struct rtw89_dev *rtwdev,
+ const void *content,
+ enum rtw89_regulation_type regd,
+ struct rtw89_sar_entry_from_acpi *ent)
+{
+ const struct rtw89_acpi_geo_sar_hp_legacy *ptr = content;
+ const struct rtw89_acpi_geo_sar_hp_legacy_entry *ptr_ent;
+ const struct rtw89_acpi_geo_sar_hp_val *ptr_ent_val;
+ enum rtw89_acpi_geo_sar_regd_hp geo_idx =
+ rtw89_acpi_geo_sar_regd_convert_hp_idx(regd);
+ enum rtw89_acpi_sar_subband subband;
+ enum rtw89_rf_path path;
+ enum rtw89_band band;
+
+ ptr_ent = &ptr->entries[geo_idx];
+
+ for (subband = 0; subband < NUM_OF_RTW89_ACPI_SAR_SUBBAND; subband++) {
+ band = rtw89_acpi_sar_subband_to_band(rtwdev, subband);
+ switch (band) {
+ case RTW89_BAND_2G:
+ ptr_ent_val = &ptr_ent->val_2ghz;
+ break;
+ case RTW89_BAND_5G:
+ ptr_ent_val = &ptr_ent->val_5ghz;
+ break;
+ default:
+ case RTW89_BAND_6G:
+ ptr_ent_val = NULL;
+ break;
+ }
+
+ if (!ptr_ent_val)
+ continue;
+
+ for (path = 0; path < NUM_OF_RTW89_ACPI_SAR_RF_PATH; path++)
+ rtw89_acpi_geo_sar_load_by_hp(rtwdev, ptr_ent_val, path,
+ &ent->v[subband][path]);
+ }
+}
+
+static
+void rtw89_acpi_geo_sar_load_hp_has_6ghz(struct rtw89_dev *rtwdev,
+ const void *content,
+ enum rtw89_regulation_type regd,
+ struct rtw89_sar_entry_from_acpi *ent)
+{
+ const struct rtw89_acpi_geo_sar_hp_has_6ghz *ptr = content;
+ const struct rtw89_acpi_geo_sar_hp_has_6ghz_entry *ptr_ent;
+ const struct rtw89_acpi_geo_sar_hp_val *ptr_ent_val;
+ enum rtw89_acpi_geo_sar_regd_hp geo_idx =
+ rtw89_acpi_geo_sar_regd_convert_hp_idx(regd);
+ enum rtw89_acpi_sar_subband subband;
+ enum rtw89_rf_path path;
+ enum rtw89_band band;
+
+ ptr_ent = &ptr->entries[geo_idx];
+
+ for (subband = 0; subband < NUM_OF_RTW89_ACPI_SAR_SUBBAND; subband++) {
+ band = rtw89_acpi_sar_subband_to_band(rtwdev, subband);
+ switch (band) {
+ case RTW89_BAND_2G:
+ ptr_ent_val = &ptr_ent->val_2ghz;
+ break;
+ case RTW89_BAND_5G:
+ ptr_ent_val = &ptr_ent->val_5ghz;
+ break;
+ case RTW89_BAND_6G:
+ ptr_ent_val = &ptr_ent->val_6ghz;
+ break;
+ default:
+ ptr_ent_val = NULL;
+ break;
+ }
+
+ if (!ptr_ent_val)
+ continue;
+
+ for (path = 0; path < NUM_OF_RTW89_ACPI_SAR_RF_PATH; path++)
+ rtw89_acpi_geo_sar_load_by_hp(rtwdev, ptr_ent_val, path,
+ &ent->v[subband][path]);
+ }
+}
+
+static
+void rtw89_acpi_geo_sar_load_rt_legacy(struct rtw89_dev *rtwdev,
+ const void *content,
+ enum rtw89_regulation_type regd,
+ struct rtw89_sar_entry_from_acpi *ent)
+{
+ const struct rtw89_acpi_geo_sar_rt_legacy *ptr = content;
+ const struct rtw89_acpi_geo_sar_rt_legacy_entry *ptr_ent;
+ const struct rtw89_acpi_geo_sar_rt_val *ptr_ent_val;
+ enum rtw89_acpi_geo_sar_regd_rt geo_idx =
+ rtw89_acpi_geo_sar_regd_convert_rt_idx(regd);
+ enum rtw89_acpi_sar_subband subband;
+ enum rtw89_rf_path path;
+ enum rtw89_band band;
+
+ ptr_ent = &ptr->entries[geo_idx];
+
+ for (subband = 0; subband < NUM_OF_RTW89_ACPI_SAR_SUBBAND; subband++) {
+ band = rtw89_acpi_sar_subband_to_band(rtwdev, subband);
+ switch (band) {
+ case RTW89_BAND_2G:
+ ptr_ent_val = &ptr_ent->val_2ghz;
+ break;
+ case RTW89_BAND_5G:
+ ptr_ent_val = &ptr_ent->val_5ghz;
+ break;
+ default:
+ case RTW89_BAND_6G:
+ ptr_ent_val = NULL;
+ break;
+ }
+
+ if (!ptr_ent_val)
+ continue;
+
+ for (path = 0; path < NUM_OF_RTW89_ACPI_SAR_RF_PATH; path++)
+ rtw89_acpi_geo_sar_load_by_rt(rtwdev, ptr_ent_val,
+ &ent->v[subband][path]);
+ }
+}
+
+static
+void rtw89_acpi_geo_sar_load_rt_has_6ghz(struct rtw89_dev *rtwdev,
+ const void *content,
+ enum rtw89_regulation_type regd,
+ struct rtw89_sar_entry_from_acpi *ent)
+{
+ const struct rtw89_acpi_geo_sar_rt_has_6ghz *ptr = content;
+ const struct rtw89_acpi_geo_sar_rt_has_6ghz_entry *ptr_ent;
+ const struct rtw89_acpi_geo_sar_rt_val *ptr_ent_val;
+ enum rtw89_acpi_geo_sar_regd_rt geo_idx =
+ rtw89_acpi_geo_sar_regd_convert_rt_idx(regd);
+ enum rtw89_acpi_sar_subband subband;
+ enum rtw89_rf_path path;
+ enum rtw89_band band;
+
+ ptr_ent = &ptr->entries[geo_idx];
+
+ for (subband = 0; subband < NUM_OF_RTW89_ACPI_SAR_SUBBAND; subband++) {
+ band = rtw89_acpi_sar_subband_to_band(rtwdev, subband);
+ switch (band) {
+ case RTW89_BAND_2G:
+ ptr_ent_val = &ptr_ent->val_2ghz;
+ break;
+ case RTW89_BAND_5G:
+ ptr_ent_val = &ptr_ent->val_5ghz;
+ break;
+ case RTW89_BAND_6G:
+ ptr_ent_val = &ptr_ent->val_6ghz;
+ break;
+ default:
+ ptr_ent_val = NULL;
+ break;
+ }
+
+ if (!ptr_ent_val)
+ continue;
+
+ for (path = 0; path < NUM_OF_RTW89_ACPI_SAR_RF_PATH; path++)
+ rtw89_acpi_geo_sar_load_by_rt(rtwdev, ptr_ent_val,
+ &ent->v[subband][path]);
+ }
+}
+
+#define RTW89_ACPI_GEO_SAR_DECL_HANDLER(type) \
+static const struct rtw89_acpi_geo_sar_handler \
+rtw89_acpi_geo_sar_handler_ ## type = { \
+ .data_size = RTW89_ACPI_GEO_SAR_SIZE_OF(type), \
+ .load = rtw89_acpi_geo_sar_load_ ## type, \
+}
+
+RTW89_ACPI_GEO_SAR_DECL_HANDLER(hp_legacy);
+RTW89_ACPI_GEO_SAR_DECL_HANDLER(hp_has_6ghz);
+RTW89_ACPI_GEO_SAR_DECL_HANDLER(rt_legacy);
+RTW89_ACPI_GEO_SAR_DECL_HANDLER(rt_has_6ghz);
+
+static const struct rtw89_acpi_sar_recognition rtw89_acpi_sar_recs[] = {
+ {
+ .id = {
+ .cid = RTW89_ACPI_SAR_CID_HP,
+ .rev = RTW89_ACPI_SAR_REV_LEGACY,
+ .size = RTW89_ACPI_SAR_SIZE_OF(std_legacy),
+ },
+ .geo = &rtw89_acpi_geo_sar_handler_hp_legacy,
+
+ .rfpath_to_antidx = rtw89_acpi_sar_rfpath_to_hp_antidx,
+ .normalize = rtw89_acpi_sar_normalize_hp_val,
+ .load = rtw89_acpi_sar_load_std_legacy,
+ },
+ {
+ .id = {
+ .cid = RTW89_ACPI_SAR_CID_HP,
+ .rev = RTW89_ACPI_SAR_REV_HAS_6GHZ,
+ .size = RTW89_ACPI_SAR_SIZE_OF(std_has_6ghz),
+ },
+ .geo = &rtw89_acpi_geo_sar_handler_hp_has_6ghz,
+
+ .rfpath_to_antidx = rtw89_acpi_sar_rfpath_to_hp_antidx,
+ .normalize = rtw89_acpi_sar_normalize_hp_val,
+ .load = rtw89_acpi_sar_load_std_has_6ghz,
+ },
+ {
+ .id = {
+ .cid = RTW89_ACPI_SAR_CID_RT,
+ .rev = RTW89_ACPI_SAR_REV_LEGACY,
+ .size = RTW89_ACPI_SAR_SIZE_OF(std_legacy),
+ },
+ .geo = &rtw89_acpi_geo_sar_handler_rt_legacy,
+
+ .rfpath_to_antidx = rtw89_acpi_sar_rfpath_to_rt_antidx,
+ .normalize = rtw89_acpi_sar_normalize_rt_val,
+ .load = rtw89_acpi_sar_load_std_legacy,
+ },
+ {
+ .id = {
+ .cid = RTW89_ACPI_SAR_CID_RT,
+ .rev = RTW89_ACPI_SAR_REV_HAS_6GHZ,
+ .size = RTW89_ACPI_SAR_SIZE_OF(std_has_6ghz),
+ },
+ .geo = &rtw89_acpi_geo_sar_handler_rt_has_6ghz,
+
+ .rfpath_to_antidx = rtw89_acpi_sar_rfpath_to_rt_antidx,
+ .normalize = rtw89_acpi_sar_normalize_rt_val,
+ .load = rtw89_acpi_sar_load_std_has_6ghz,
+ },
+ {
+ .id = {
+ .cid = RTW89_ACPI_SAR_CID_RT,
+ .rev = RTW89_ACPI_SAR_REV_LEGACY,
+ .size = RTW89_ACPI_SAR_SIZE_OF(sml_legacy),
+ },
+ .geo = &rtw89_acpi_geo_sar_handler_rt_legacy,
+
+ .rfpath_to_antidx = rtw89_acpi_sar_rfpath_to_rt_antidx,
+ .normalize = rtw89_acpi_sar_normalize_rt_val,
+ .load = rtw89_acpi_sar_load_sml_legacy,
+ },
+ {
+ .id = {
+ .cid = RTW89_ACPI_SAR_CID_RT,
+ .rev = RTW89_ACPI_SAR_REV_HAS_6GHZ,
+ .size = RTW89_ACPI_SAR_SIZE_OF(sml_has_6ghz),
+ },
+ .geo = &rtw89_acpi_geo_sar_handler_rt_has_6ghz,
+
+ .rfpath_to_antidx = rtw89_acpi_sar_rfpath_to_rt_antidx,
+ .normalize = rtw89_acpi_sar_normalize_rt_val,
+ .load = rtw89_acpi_sar_load_sml_has_6ghz,
+ },
+};
+
+struct rtw89_acpi_sar_rec_parm {
+ u32 pld_len;
+ u8 tbl_cnt;
+ u16 cid;
+ u8 rev;
+};
+
+static const struct rtw89_acpi_sar_recognition *
+rtw89_acpi_sar_recognize(struct rtw89_dev *rtwdev,
+ const struct rtw89_acpi_sar_rec_parm *parm)
+{
+ const u32 tbl_len = parm->pld_len / parm->tbl_cnt;
+ const struct rtw89_acpi_sar_recognition *rec;
+ struct rtw89_acpi_sar_identifier id = {};
+
+ rtw89_debug(rtwdev, RTW89_DBG_ACPI,
+ "%s: cid %u, rev %u, tbl len %u, tbl cnt %u\n",
+ __func__, parm->cid, parm->rev, tbl_len, parm->tbl_cnt);
+
+ if (unlikely(parm->pld_len % parm->tbl_cnt)) {
+ rtw89_debug(rtwdev, RTW89_DBG_ACPI, "invalid pld len %u\n",
+ parm->pld_len);
+ return NULL;
+ }
+
+ if (unlikely(tbl_len > RTW89_ACPI_SAR_SIZE_MAX)) {
+ rtw89_debug(rtwdev, RTW89_DBG_ACPI, "invalid tbl len %u\n",
+ tbl_len);
+ return NULL;
+ }
+
+ if (unlikely(parm->tbl_cnt > MAX_NUM_OF_RTW89_ACPI_SAR_TBL)) {
+ rtw89_debug(rtwdev, RTW89_DBG_ACPI, "invalid tbl cnt %u\n",
+ parm->tbl_cnt);
+ return NULL;
+ }
+
+ switch (parm->cid) {
+ case RTW89_ACPI_SAR_CID_HP:
+ case RTW89_ACPI_SAR_CID_RT:
+ id.cid = parm->cid;
+ break;
+ default:
+ rtw89_debug(rtwdev, RTW89_DBG_ACPI, "invalid cid 0x%x\n",
+ parm->cid);
+ return NULL;
+ }
+
+ switch (parm->rev) {
+ case RTW89_ACPI_SAR_REV_LEGACY:
+ case RTW89_ACPI_SAR_REV_HAS_6GHZ:
+ id.rev = parm->rev;
+ break;
+ default:
+ rtw89_debug(rtwdev, RTW89_DBG_ACPI, "invalid rev %u\n",
+ parm->rev);
+ return NULL;
+ }
+
+ id.size = tbl_len;
+ for (unsigned int i = 0; i < ARRAY_SIZE(rtw89_acpi_sar_recs); i++) {
+ rec = &rtw89_acpi_sar_recs[i];
+ if (memcmp(&rec->id, &id, sizeof(rec->id)) == 0)
+ return rec;
+ }
+
+ rtw89_debug(rtwdev, RTW89_DBG_ACPI, "failed to recognize\n");
+ return NULL;
+}
+
+static const struct rtw89_acpi_sar_recognition *
+rtw89_acpi_evaluate_static_sar(struct rtw89_dev *rtwdev,
+ struct rtw89_sar_cfg_acpi *cfg)
+{
+ const struct rtw89_acpi_sar_recognition *rec = NULL;
+ const struct rtw89_acpi_static_sar_hdr *hdr;
+ struct rtw89_sar_entry_from_acpi tmp = {};
+ struct rtw89_acpi_sar_rec_parm parm = {};
+ struct rtw89_sar_table_from_acpi *tbl;
+ const struct rtw89_acpi_data *data;
+ u32 len;
+
+ data = rtw89_acpi_evaluate_method(rtwdev, RTW89_ACPI_METHOD_STATIC_SAR);
+ if (!data)
+ return NULL;
+
+ rtw89_debug(rtwdev, RTW89_DBG_ACPI, "acpi load static sar\n");
+
+ len = data->len;
+ if (len <= sizeof(*hdr)) {
+ rtw89_debug(rtwdev, RTW89_DBG_ACPI, "invalid buf len %u\n", len);
goto out;
}
- buf_len = obj->buffer.length;
- if (buf_len != sizeof(*res)) {
- rtw89_debug(rtwdev, RTW89_DBG_ACPI, "%s: invalid buffer length: %u\n",
- __func__, buf_len);
+ hdr = (typeof(hdr))data->buf;
+
+ parm.cid = le16_to_cpu(hdr->cid);
+ parm.rev = hdr->rev;
+ parm.tbl_cnt = 1;
+ parm.pld_len = len - sizeof(*hdr);
+
+ rec = rtw89_acpi_sar_recognize(rtwdev, &parm);
+ if (!rec)
+ goto out;
+
+ rec->load(rtwdev, rec, hdr->content, &tmp);
+
+ tbl = &cfg->tables[0];
+ for (u8 regd = 0; regd < RTW89_REGD_NUM; regd++)
+ tbl->entries[regd] = tmp;
+
+ cfg->valid_num = 1;
+
+out:
+ kfree(data);
+ return rec;
+}
+
+static const struct rtw89_acpi_sar_recognition *
+rtw89_acpi_evaluate_dynamic_sar(struct rtw89_dev *rtwdev,
+ struct rtw89_sar_cfg_acpi *cfg)
+{
+ const struct rtw89_acpi_sar_recognition *rec = NULL;
+ const struct rtw89_acpi_dynamic_sar_hdr *hdr;
+ struct rtw89_acpi_sar_rec_parm parm = {};
+ struct rtw89_sar_table_from_acpi *tbl;
+ const struct rtw89_acpi_data *data;
+ u32 len;
+
+ data = rtw89_acpi_evaluate_method(rtwdev, RTW89_ACPI_METHOD_DYNAMIC_SAR);
+ if (!data)
+ return NULL;
+
+ rtw89_debug(rtwdev, RTW89_DBG_ACPI, "acpi load dynamic sar\n");
+
+ len = data->len;
+ if (len <= sizeof(*hdr)) {
+ rtw89_debug(rtwdev, RTW89_DBG_ACPI, "invalid buf len %u\n", len);
+ goto out;
+ }
+
+ hdr = (typeof(hdr))data->buf;
+
+ parm.cid = le16_to_cpu(hdr->cid);
+ parm.rev = hdr->rev;
+ parm.tbl_cnt = hdr->cnt;
+ parm.pld_len = len - sizeof(*hdr);
+
+ rec = rtw89_acpi_sar_recognize(rtwdev, &parm);
+ if (!rec)
+ goto out;
+
+ for (unsigned int i = 0; i < hdr->cnt; i++) {
+ const u8 *content = hdr->content + rec->id.size * i;
+ struct rtw89_sar_entry_from_acpi tmp = {};
+
+ rec->load(rtwdev, rec, content, &tmp);
+
+ tbl = &cfg->tables[i];
+ for (u8 regd = 0; regd < RTW89_REGD_NUM; regd++)
+ tbl->entries[regd] = tmp;
+ }
+
+ cfg->valid_num = hdr->cnt;
+
+out:
+ kfree(data);
+ return rec;
+}
+
+int rtw89_acpi_evaluate_dynamic_sar_indicator(struct rtw89_dev *rtwdev,
+ struct rtw89_sar_cfg_acpi *cfg,
+ bool *poll_changed)
+{
+ struct rtw89_sar_indicator_from_acpi *ind = &cfg->indicator;
+ struct rtw89_sar_indicator_from_acpi tmp = *ind;
+ const struct rtw89_acpi_data *data;
+ const u8 *tbl_base1_by_ant;
+ enum rtw89_rf_path path;
+ int ret = 0;
+ u32 len;
+
+ data = rtw89_acpi_evaluate_method(rtwdev, RTW89_ACPI_METHOD_DYNAMIC_SAR_INDICATOR);
+ if (!data)
+ return -EFAULT;
+
+ if (!poll_changed)
+ rtw89_debug(rtwdev, RTW89_DBG_ACPI, "acpi load dynamic sar indicator\n");
+
+ len = data->len;
+ if (len != ind->fields) {
+ rtw89_debug(rtwdev, RTW89_DBG_ACPI, "invalid buf len %u\n", len);
ret = -EINVAL;
goto out;
}
- *res = *(struct rtw89_acpi_rtag_result *)obj->buffer.pointer;
+ tbl_base1_by_ant = data->buf;
- rtw89_hex_dump(rtwdev, RTW89_DBG_ACPI, "antenna_gain: ", res, sizeof(*res));
+ for (path = 0; path < NUM_OF_RTW89_ACPI_SAR_RF_PATH; path++) {
+ u8 antidx = ind->rfpath_to_antidx(path);
+ u8 sel;
+
+ if (antidx >= ind->fields)
+ antidx = 0;
+
+ /* convert the table index from 1-based to 0-based */
+ sel = tbl_base1_by_ant[antidx] - 1;
+ if (sel >= cfg->valid_num)
+ sel = 0;
+
+ tmp.tblsel[path] = sel;
+ }
+
+ if (memcmp(ind, &tmp, sizeof(*ind)) == 0) {
+ if (poll_changed)
+ *poll_changed = false;
+ } else {
+ if (poll_changed)
+ *poll_changed = true;
+
+ *ind = tmp;
+ }
out:
- ACPI_FREE(obj);
+ kfree(data);
return ret;
}
+
+static
+void rtw89_acpi_evaluate_geo_sar(struct rtw89_dev *rtwdev,
+ const struct rtw89_acpi_geo_sar_handler *hdl,
+ struct rtw89_sar_cfg_acpi *cfg)
+{
+ const struct rtw89_acpi_data *data;
+ u32 len;
+
+ data = rtw89_acpi_evaluate_method(rtwdev, RTW89_ACPI_METHOD_GEO_SAR);
+ if (!data)
+ return;
+
+ rtw89_debug(rtwdev, RTW89_DBG_ACPI, "acpi load geo sar\n");
+
+ len = data->len;
+ if (len != hdl->data_size) {
+ rtw89_debug(rtwdev, RTW89_DBG_ACPI, "invalid buf len %u (expected %u)\n",
+ len, hdl->data_size);
+ goto out;
+ }
+
+ for (unsigned int i = 0; i < cfg->valid_num; i++)
+ for (u8 regd = 0; regd < RTW89_REGD_NUM; regd++)
+ hdl->load(rtwdev, data->buf, regd, &cfg->tables[i].entries[regd]);
+
+out:
+ kfree(data);
+}
+
+int rtw89_acpi_evaluate_sar(struct rtw89_dev *rtwdev,
+ struct rtw89_sar_cfg_acpi *cfg)
+{
+ struct rtw89_sar_indicator_from_acpi *ind = &cfg->indicator;
+ const struct rtw89_acpi_sar_recognition *rec;
+ bool fetch_indicator = false;
+ int ret;
+
+ rec = rtw89_acpi_evaluate_static_sar(rtwdev, cfg);
+ if (rec)
+ goto recognized;
+
+ rec = rtw89_acpi_evaluate_dynamic_sar(rtwdev, cfg);
+ if (!rec)
+ return -ENOENT;
+
+ fetch_indicator = true;
+
+recognized:
+ rtw89_acpi_evaluate_geo_sar(rtwdev, rec->geo, cfg);
+
+ switch (rec->id.cid) {
+ case RTW89_ACPI_SAR_CID_HP:
+ cfg->downgrade_2tx = 3 << TXPWR_FACTOR_OF_RTW89_ACPI_SAR;
+ ind->fields = RTW89_ACPI_SAR_ANT_NR_STD;
+ break;
+ case RTW89_ACPI_SAR_CID_RT:
+ cfg->downgrade_2tx = 0;
+ ind->fields = 1;
+ break;
+ default:
+ return -EFAULT;
+ }
+
+ if (fetch_indicator) {
+ ind->rfpath_to_antidx = rec->rfpath_to_antidx;
+ ret = rtw89_acpi_evaluate_dynamic_sar_indicator(rtwdev, cfg, NULL);
+ if (ret)
+ fetch_indicator = false;
+ }
+
+ if (!fetch_indicator)
+ memset(ind->tblsel, 0, sizeof(ind->tblsel));
+
+ ind->enable_sync = fetch_indicator;
+ return 0;
+}
diff --git a/drivers/net/wireless/realtek/rtw89/acpi.h b/drivers/net/wireless/realtek/rtw89/acpi.h
index b43ab106e44d..8c918ee02d2e 100644
--- a/drivers/net/wireless/realtek/rtw89/acpi.h
+++ b/drivers/net/wireless/realtek/rtw89/acpi.h
@@ -7,6 +7,11 @@
#include "core.h"
+struct rtw89_acpi_data {
+ u32 len;
+ u8 buf[] __counted_by(len);
+};
+
enum rtw89_acpi_dsm_func {
RTW89_ACPI_DSM_FUNC_IDN_BAND_SUP = 2,
RTW89_ACPI_DSM_FUNC_6G_DIS = 3,
@@ -26,6 +31,13 @@ enum rtw89_acpi_policy_mode {
RTW89_ACPI_POLICY_ALLOW = 1,
};
+enum rtw89_acpi_conf_tas {
+ RTW89_ACPI_CONF_TAS_US = BIT(0),
+ RTW89_ACPI_CONF_TAS_CA = BIT(1),
+ RTW89_ACPI_CONF_TAS_KR = BIT(2),
+ RTW89_ACPI_CONF_TAS_OTHERS = BIT(7),
+};
+
struct rtw89_acpi_country_code {
/* below are allowed:
* * ISO alpha2 country code
@@ -54,12 +66,21 @@ struct rtw89_acpi_policy_6ghz_sp {
u8 rsvd;
} __packed;
+struct rtw89_acpi_policy_tas {
+ u8 signature[4];
+ u8 revision;
+ u8 enable;
+ u8 enabled_countries;
+ u8 rsvd[3];
+} __packed;
+
struct rtw89_acpi_dsm_result {
union {
u8 value;
/* caller needs to free it after using */
struct rtw89_acpi_policy_6ghz *policy_6ghz;
struct rtw89_acpi_policy_6ghz_sp *policy_6ghz_sp;
+ struct rtw89_acpi_policy_tas *policy_tas;
} u;
};
@@ -70,10 +91,179 @@ struct rtw89_acpi_rtag_result {
u8 ant_gain_table[RTW89_ANT_GAIN_CHAIN_NUM][RTW89_ANT_GAIN_SUBBAND_NR];
} __packed;
+enum rtw89_acpi_sar_cid {
+ RTW89_ACPI_SAR_CID_HP = 0x5048,
+ RTW89_ACPI_SAR_CID_RT = 0x5452,
+};
+
+enum rtw89_acpi_sar_rev {
+ RTW89_ACPI_SAR_REV_LEGACY = 1,
+ RTW89_ACPI_SAR_REV_HAS_6GHZ = 2,
+};
+
+#define RTW89_ACPI_SAR_ANT_NR_STD 4
+#define RTW89_ACPI_SAR_ANT_NR_SML 2
+
+#define RTW89_ACPI_METHOD_STATIC_SAR "WRDS"
+#define RTW89_ACPI_METHOD_DYNAMIC_SAR "RWRD"
+#define RTW89_ACPI_METHOD_DYNAMIC_SAR_INDICATOR "RWSI"
+#define RTW89_ACPI_METHOD_GEO_SAR "RWGS"
+
+struct rtw89_acpi_sar_std_legacy {
+ u8 v[RTW89_ACPI_SAR_ANT_NR_STD][RTW89_ACPI_SAR_SUBBAND_NR_LEGACY];
+} __packed;
+
+struct rtw89_acpi_sar_std_has_6ghz {
+ u8 v[RTW89_ACPI_SAR_ANT_NR_STD][RTW89_ACPI_SAR_SUBBAND_NR_HAS_6GHZ];
+} __packed;
+
+struct rtw89_acpi_sar_sml_legacy {
+ u8 v[RTW89_ACPI_SAR_ANT_NR_SML][RTW89_ACPI_SAR_SUBBAND_NR_LEGACY];
+} __packed;
+
+struct rtw89_acpi_sar_sml_has_6ghz {
+ u8 v[RTW89_ACPI_SAR_ANT_NR_SML][RTW89_ACPI_SAR_SUBBAND_NR_HAS_6GHZ];
+} __packed;
+
+struct rtw89_acpi_static_sar_hdr {
+ __le16 cid;
+ u8 rev;
+ u8 content[];
+} __packed;
+
+struct rtw89_acpi_dynamic_sar_hdr {
+ __le16 cid;
+ u8 rev;
+ u8 cnt;
+ u8 content[];
+} __packed;
+
+struct rtw89_acpi_sar_identifier {
+ enum rtw89_acpi_sar_cid cid;
+ enum rtw89_acpi_sar_rev rev;
+ u8 size;
+};
+
+/* for rtw89_acpi_sar_identifier::size */
+#define RTW89_ACPI_SAR_SIZE_MAX U8_MAX
+#define RTW89_ACPI_SAR_SIZE_OF(type) \
+ (BUILD_BUG_ON_ZERO(sizeof(struct rtw89_acpi_sar_ ## type) > \
+ RTW89_ACPI_SAR_SIZE_MAX) + \
+ sizeof(struct rtw89_acpi_sar_ ## type))
+
+struct rtw89_acpi_sar_recognition {
+ struct rtw89_acpi_sar_identifier id;
+ const struct rtw89_acpi_geo_sar_handler *geo;
+
+ u8 (*rfpath_to_antidx)(enum rtw89_rf_path rfpath);
+ s16 (*normalize)(u8 v);
+ void (*load)(struct rtw89_dev *rtwdev,
+ const struct rtw89_acpi_sar_recognition *rec,
+ const void *content,
+ struct rtw89_sar_entry_from_acpi *ent);
+};
+
+struct rtw89_acpi_geo_sar_hp_val {
+ u8 max;
+ s8 delta[RTW89_ACPI_SAR_ANT_NR_STD];
+} __packed;
+
+struct rtw89_acpi_geo_sar_hp_legacy_entry {
+ struct rtw89_acpi_geo_sar_hp_val val_2ghz;
+ struct rtw89_acpi_geo_sar_hp_val val_5ghz;
+} __packed;
+
+struct rtw89_acpi_geo_sar_hp_has_6ghz_entry {
+ struct rtw89_acpi_geo_sar_hp_val val_2ghz;
+ struct rtw89_acpi_geo_sar_hp_val val_5ghz;
+ struct rtw89_acpi_geo_sar_hp_val val_6ghz;
+} __packed;
+
+enum rtw89_acpi_geo_sar_regd_hp {
+ RTW89_ACPI_GEO_SAR_REGD_HP_FCC = 0,
+ RTW89_ACPI_GEO_SAR_REGD_HP_ETSI = 1,
+ RTW89_ACPI_GEO_SAR_REGD_HP_WW = 2,
+
+ RTW89_ACPI_GEO_SAR_REGD_NR_HP,
+};
+
+struct rtw89_acpi_geo_sar_hp_legacy {
+ struct rtw89_acpi_geo_sar_hp_legacy_entry
+ entries[RTW89_ACPI_GEO_SAR_REGD_NR_HP];
+} __packed;
+
+struct rtw89_acpi_geo_sar_hp_has_6ghz {
+ struct rtw89_acpi_geo_sar_hp_has_6ghz_entry
+ entries[RTW89_ACPI_GEO_SAR_REGD_NR_HP];
+} __packed;
+
+struct rtw89_acpi_geo_sar_rt_val {
+ u8 max;
+ s8 delta;
+} __packed;
+
+struct rtw89_acpi_geo_sar_rt_legacy_entry {
+ struct rtw89_acpi_geo_sar_rt_val val_2ghz;
+ struct rtw89_acpi_geo_sar_rt_val val_5ghz;
+} __packed;
+
+struct rtw89_acpi_geo_sar_rt_has_6ghz_entry {
+ struct rtw89_acpi_geo_sar_rt_val val_2ghz;
+ struct rtw89_acpi_geo_sar_rt_val val_5ghz;
+ struct rtw89_acpi_geo_sar_rt_val val_6ghz;
+} __packed;
+
+enum rtw89_acpi_geo_sar_regd_rt {
+ RTW89_ACPI_GEO_SAR_REGD_RT_FCC = 0,
+ RTW89_ACPI_GEO_SAR_REGD_RT_ETSI = 1,
+ RTW89_ACPI_GEO_SAR_REGD_RT_MKK = 2,
+ RTW89_ACPI_GEO_SAR_REGD_RT_IC = 3,
+ RTW89_ACPI_GEO_SAR_REGD_RT_KCC = 4,
+ RTW89_ACPI_GEO_SAR_REGD_RT_WW = 5,
+
+ RTW89_ACPI_GEO_SAR_REGD_NR_RT,
+};
+
+struct rtw89_acpi_geo_sar_rt_legacy {
+ struct rtw89_acpi_geo_sar_rt_legacy_entry
+ entries[RTW89_ACPI_GEO_SAR_REGD_NR_RT];
+} __packed;
+
+struct rtw89_acpi_geo_sar_rt_has_6ghz {
+ struct rtw89_acpi_geo_sar_rt_has_6ghz_entry
+ entries[RTW89_ACPI_GEO_SAR_REGD_NR_RT];
+} __packed;
+
+struct rtw89_acpi_geo_sar_handler {
+ u8 data_size;
+
+ void (*load)(struct rtw89_dev *rtwdev,
+ const void *content,
+ enum rtw89_regulation_type regd,
+ struct rtw89_sar_entry_from_acpi *ent);
+};
+
+/* for rtw89_acpi_geo_sar_handler::data_size */
+#define RTW89_ACPI_GEO_SAR_SIZE_MAX U8_MAX
+#define RTW89_ACPI_GEO_SAR_SIZE_OF(type) \
+ (BUILD_BUG_ON_ZERO(sizeof(struct rtw89_acpi_geo_sar_ ## type) > \
+ RTW89_ACPI_GEO_SAR_SIZE_MAX) + \
+ sizeof(struct rtw89_acpi_geo_sar_ ## type))
+
+enum rtw89_acpi_sar_subband rtw89_acpi_sar_get_subband(struct rtw89_dev *rtwdev,
+ u32 center_freq);
+enum rtw89_band rtw89_acpi_sar_subband_to_band(struct rtw89_dev *rtwdev,
+ enum rtw89_acpi_sar_subband subband);
+
int rtw89_acpi_evaluate_dsm(struct rtw89_dev *rtwdev,
enum rtw89_acpi_dsm_func func,
struct rtw89_acpi_dsm_result *res);
int rtw89_acpi_evaluate_rtag(struct rtw89_dev *rtwdev,
struct rtw89_acpi_rtag_result *res);
+int rtw89_acpi_evaluate_sar(struct rtw89_dev *rtwdev,
+ struct rtw89_sar_cfg_acpi *cfg);
+int rtw89_acpi_evaluate_dynamic_sar_indicator(struct rtw89_dev *rtwdev,
+ struct rtw89_sar_cfg_acpi *cfg,
+ bool *changed);
#endif
diff --git a/drivers/net/wireless/realtek/rtw89/cam.c b/drivers/net/wireless/realtek/rtw89/cam.c
index 8fa1e6c1ce13..385a238fe5cc 100644
--- a/drivers/net/wireless/realtek/rtw89/cam.c
+++ b/drivers/net/wireless/realtek/rtw89/cam.c
@@ -6,6 +6,7 @@
#include "debug.h"
#include "fw.h"
#include "mac.h"
+#include "ps.h"
static struct sk_buff *
rtw89_cam_get_sec_key_cmd(struct rtw89_dev *rtwdev,
@@ -469,13 +470,25 @@ int rtw89_cam_sec_key_add(struct rtw89_dev *rtwdev,
bool ext_key = false;
int ret;
+ if (ieee80211_vif_is_mld(vif) && !chip->hw_mlo_bmc_crypto &&
+ !(key->flags & IEEE80211_KEY_FLAG_PAIRWISE))
+ return -EOPNOTSUPP;
+
switch (key->cipher) {
case WLAN_CIPHER_SUITE_WEP40:
+ rtw89_leave_ips_by_hwflags(rtwdev);
hw_key_type = RTW89_SEC_KEY_TYPE_WEP40;
break;
case WLAN_CIPHER_SUITE_WEP104:
+ rtw89_leave_ips_by_hwflags(rtwdev);
hw_key_type = RTW89_SEC_KEY_TYPE_WEP104;
break;
+ case WLAN_CIPHER_SUITE_TKIP:
+ if (!chip->hw_tkip_crypto)
+ return -EOPNOTSUPP;
+ hw_key_type = RTW89_SEC_KEY_TYPE_TKIP;
+ key->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIC;
+ break;
case WLAN_CIPHER_SUITE_CCMP:
hw_key_type = RTW89_SEC_KEY_TYPE_CCMP128;
if (!chip->hw_mgmt_tx_encrypt)
diff --git a/drivers/net/wireless/realtek/rtw89/chan.c b/drivers/net/wireless/realtek/rtw89/chan.c
index 4df4e04c3e67..806f42429a29 100644
--- a/drivers/net/wireless/realtek/rtw89/chan.c
+++ b/drivers/net/wireless/realtek/rtw89/chan.c
@@ -8,6 +8,7 @@
#include "fw.h"
#include "mac.h"
#include "ps.h"
+#include "sar.h"
#include "util.h"
static void rtw89_swap_chanctx(struct rtw89_dev *rtwdev,
@@ -155,7 +156,7 @@ int rtw89_iterate_entity_chan(struct rtw89_dev *rtwdev,
int ret;
u8 idx;
- lockdep_assert_held(&rtwdev->mutex);
+ lockdep_assert_wiphy(rtwdev->hw->wiphy);
for_each_set_bit(idx, hal->entity_map, NUM_OF_RTW89_CHANCTX) {
chan = rtw89_chan_get(rtwdev, idx);
@@ -188,9 +189,10 @@ void rtw89_config_entity_chandef(struct rtw89_dev *rtwdev,
}
void rtw89_config_roc_chandef(struct rtw89_dev *rtwdev,
- enum rtw89_chanctx_idx idx,
+ struct rtw89_vif_link *rtwvif_link,
const struct cfg80211_chan_def *chandef)
{
+ enum rtw89_chanctx_idx idx = rtwvif_link->chanctx_idx;
struct rtw89_hal *hal = &rtwdev->hal;
enum rtw89_chanctx_idx cur;
@@ -204,6 +206,7 @@ void rtw89_config_roc_chandef(struct rtw89_dev *rtwdev,
}
hal->roc_chandef = *chandef;
+ hal->roc_link_index = rtw89_vif_link_inst_get_index(rtwvif_link);
} else {
cur = atomic_cmpxchg(&hal->roc_chanctx_idx, idx,
RTW89_CHANCTX_IDLE);
@@ -310,7 +313,7 @@ const struct rtw89_chan *__rtw89_mgnt_chan_get(struct rtw89_dev *rtwdev,
enum rtw89_entity_mode mode;
u8 role_index;
- lockdep_assert_held(&rtwdev->mutex);
+ lockdep_assert_wiphy(rtwdev->hw->wiphy);
if (unlikely(link_index >= __RTW89_MLD_MAX_LINK_NUM)) {
WARN(1, "link index %u is invalid (max link inst num: %d)\n",
@@ -338,11 +341,10 @@ const struct rtw89_chan *__rtw89_mgnt_chan_get(struct rtw89_dev *rtwdev,
roc_idx = atomic_read(&hal->roc_chanctx_idx);
if (roc_idx != RTW89_CHANCTX_IDLE) {
- /* ROC is ongoing (given ROC runs on RTW89_ROC_BY_LINK_INDEX).
- * If @link_index is the same as RTW89_ROC_BY_LINK_INDEX, get
- * the ongoing ROC chanctx.
+ /* ROC is ongoing (given ROC runs on @hal->roc_link_index).
+ * If @link_index is the same, get the ongoing ROC chanctx.
*/
- if (link_index == RTW89_ROC_BY_LINK_INDEX)
+ if (link_index == hal->roc_link_index)
chanctx_idx = roc_idx;
}
@@ -357,16 +359,45 @@ dflt:
}
EXPORT_SYMBOL(__rtw89_mgnt_chan_get);
+static enum rtw89_mlo_dbcc_mode
+rtw89_entity_sel_mlo_dbcc_mode(struct rtw89_dev *rtwdev, u8 active_hws)
+{
+ if (rtwdev->chip->chip_gen != RTW89_CHIP_BE)
+ return MLO_DBCC_NOT_SUPPORT;
+
+ switch (active_hws) {
+ case BIT(0):
+ return MLO_2_PLUS_0_1RF;
+ case BIT(1):
+ return MLO_0_PLUS_2_1RF;
+ case BIT(0) | BIT(1):
+ default:
+ return MLO_1_PLUS_1_1RF;
+ }
+}
+
+static
+void rtw89_entity_recalc_mlo_dbcc_mode(struct rtw89_dev *rtwdev, u8 active_hws)
+{
+ enum rtw89_mlo_dbcc_mode mode;
+
+ mode = rtw89_entity_sel_mlo_dbcc_mode(rtwdev, active_hws);
+ rtwdev->mlo_dbcc_mode = mode;
+
+ rtw89_debug(rtwdev, RTW89_DBG_STATE, "recalc mlo dbcc mode to %d\n", mode);
+}
+
static void rtw89_entity_recalc_mgnt_roles(struct rtw89_dev *rtwdev)
{
struct rtw89_hal *hal = &rtwdev->hal;
struct rtw89_entity_mgnt *mgnt = &hal->entity_mgnt;
struct rtw89_vif_link *link;
struct rtw89_vif *role;
+ u8 active_hws = 0;
u8 pos = 0;
int i, j;
- lockdep_assert_held(&rtwdev->mutex);
+ lockdep_assert_wiphy(rtwdev->hw->wiphy);
for (i = 0; i < RTW89_MAX_INTERFACE_NUM; i++)
mgnt->active_roles[i] = NULL;
@@ -411,10 +442,13 @@ fill:
continue;
mgnt->chanctx_tbl[pos][i] = link->chanctx_idx;
+ active_hws |= BIT(i);
}
mgnt->active_roles[pos++] = role;
}
+
+ rtw89_entity_recalc_mlo_dbcc_mode(rtwdev, active_hws);
}
enum rtw89_entity_mode rtw89_entity_recalc(struct rtw89_dev *rtwdev)
@@ -427,7 +461,7 @@ enum rtw89_entity_mode rtw89_entity_recalc(struct rtw89_dev *rtwdev)
struct rtw89_chan chan;
u8 idx;
- lockdep_assert_held(&rtwdev->mutex);
+ lockdep_assert_wiphy(rtwdev->hw->wiphy);
bitmap_copy(recalc_map, hal->entity_map, NUM_OF_RTW89_CHANCTX);
@@ -556,7 +590,9 @@ static u32 rtw89_mcc_get_tbtt_ofst(struct rtw89_dev *rtwdev,
u64 sync_tsf = READ_ONCE(rtwvif_link->sync_bcn_tsf);
u32 remainder;
- if (tsf < sync_tsf) {
+ if (role->is_go) {
+ sync_tsf = 0;
+ } else if (tsf < sync_tsf) {
rtw89_debug(rtwdev, RTW89_DBG_CHAN,
"MCC get tbtt ofst: tsf might not update yet\n");
sync_tsf = 0;
@@ -690,19 +726,13 @@ static void rtw89_mcc_role_macid_sta_iter(void *data, struct ieee80211_sta *sta)
struct rtw89_vif *target = mcc_role->rtwvif_link->rtwvif;
struct rtw89_sta *rtwsta = sta_to_rtwsta(sta);
struct rtw89_vif *rtwvif = rtwsta->rtwvif;
- struct rtw89_dev *rtwdev = rtwsta->rtwdev;
- struct rtw89_sta_link *rtwsta_link;
+ u8 macid;
if (rtwvif != target)
return;
- rtwsta_link = rtw89_sta_get_link_inst(rtwsta, 0);
- if (unlikely(!rtwsta_link)) {
- rtw89_err(rtwdev, "mcc sta macid: find no link on HW-0\n");
- return;
- }
-
- rtw89_mcc_role_fw_macid_bitmap_set_bit(mcc_role, rtwsta_link->mac_id);
+ macid = rtw89_sta_get_main_macid(rtwsta);
+ rtw89_mcc_role_fw_macid_bitmap_set_bit(mcc_role, macid);
}
static void rtw89_mcc_fill_role_macid_bitmap(struct rtw89_dev *rtwdev,
@@ -746,9 +776,11 @@ static void rtw89_mcc_fill_role_limit(struct rtw89_dev *rtwdev,
int ret;
int i;
- if (!mcc_role->is_go && !mcc_role->is_gc)
+ if (!mcc_role->is_gc)
return;
+ rtw89_p2p_noa_once_recalc(rtwvif_link);
+
rcu_read_lock();
bss_conf = rtw89_vif_rcu_dereference_link(rtwvif_link, true);
@@ -784,6 +816,9 @@ fill:
}
tsf_lmt = (tsf & GENMASK_ULL(63, 32)) | start_time;
+ if (tsf_lmt < tsf)
+ tsf_lmt += roundup_u64(tsf - tsf_lmt, interval);
+
max_toa_us = rtw89_mcc_get_tbtt_ofst(rtwdev, mcc_role, tsf_lmt);
max_dur_us = interval - duration;
max_tob_us = max_dur_us - max_toa_us;
@@ -928,6 +963,15 @@ static int rtw89_mcc_fill_all_roles(struct rtw89_dev *rtwdev)
return 0;
}
+static bool rtw89_mcc_can_courtesy(const struct rtw89_mcc_role *provider,
+ const struct rtw89_mcc_role *receiver)
+{
+ if (provider->is_go || receiver->is_gc)
+ return false;
+
+ return true;
+}
+
static void rtw89_mcc_assign_pattern(struct rtw89_dev *rtwdev,
const struct rtw89_mcc_pattern *new)
{
@@ -936,37 +980,44 @@ static void rtw89_mcc_assign_pattern(struct rtw89_dev *rtwdev,
struct rtw89_mcc_role *aux = &mcc->role_aux;
struct rtw89_mcc_config *config = &mcc->config;
struct rtw89_mcc_pattern *pattern = &config->pattern;
+ struct rtw89_mcc_courtesy_cfg *crtz;
rtw89_debug(rtwdev, RTW89_DBG_CHAN,
"MCC assign pattern: ref {%d | %d}, aux {%d | %d}\n",
new->tob_ref, new->toa_ref, new->tob_aux, new->toa_aux);
+ rtw89_debug(rtwdev, RTW89_DBG_CHAN, "MCC pattern plan: %d\n", new->plan);
+
*pattern = *new;
memset(&pattern->courtesy, 0, sizeof(pattern->courtesy));
- if (pattern->tob_aux <= 0 || pattern->toa_aux <= 0) {
- pattern->courtesy.macid_tgt = aux->rtwvif_link->mac_id;
- pattern->courtesy.macid_src = ref->rtwvif_link->mac_id;
- pattern->courtesy.slot_num = RTW89_MCC_DFLT_COURTESY_SLOT;
- pattern->courtesy.enable = true;
- } else if (pattern->tob_ref <= 0 || pattern->toa_ref <= 0) {
- pattern->courtesy.macid_tgt = ref->rtwvif_link->mac_id;
- pattern->courtesy.macid_src = aux->rtwvif_link->mac_id;
- pattern->courtesy.slot_num = RTW89_MCC_DFLT_COURTESY_SLOT;
- pattern->courtesy.enable = true;
+ if (RTW89_MCC_REQ_COURTESY(pattern, aux) && rtw89_mcc_can_courtesy(ref, aux)) {
+ crtz = &pattern->courtesy.ref;
+ ref->crtz = crtz;
+
+ crtz->macid_tgt = aux->rtwvif_link->mac_id;
+ crtz->slot_num = RTW89_MCC_DFLT_COURTESY_SLOT;
+
+ rtw89_debug(rtwdev, RTW89_DBG_CHAN,
+ "MCC courtesy ref: tgt %d, slot %d\n",
+ crtz->macid_tgt, crtz->slot_num);
+ } else {
+ ref->crtz = NULL;
}
- rtw89_debug(rtwdev, RTW89_DBG_CHAN,
- "MCC pattern flags: plan %d, courtesy_en %d\n",
- pattern->plan, pattern->courtesy.enable);
+ if (RTW89_MCC_REQ_COURTESY(pattern, ref) && rtw89_mcc_can_courtesy(aux, ref)) {
+ crtz = &pattern->courtesy.aux;
+ aux->crtz = crtz;
- if (!pattern->courtesy.enable)
- return;
+ crtz->macid_tgt = ref->rtwvif_link->mac_id;
+ crtz->slot_num = RTW89_MCC_DFLT_COURTESY_SLOT;
- rtw89_debug(rtwdev, RTW89_DBG_CHAN,
- "MCC pattern courtesy: tgt %d, src %d, slot %d\n",
- pattern->courtesy.macid_tgt, pattern->courtesy.macid_src,
- pattern->courtesy.slot_num);
+ rtw89_debug(rtwdev, RTW89_DBG_CHAN,
+ "MCC courtesy aux: tgt %d, slot %d\n",
+ crtz->macid_tgt, crtz->slot_num);
+ } else {
+ aux->crtz = NULL;
+ }
}
/* The follow-up roughly shows the relationship between the parameters
@@ -991,6 +1042,7 @@ static void __rtw89_mcc_calc_pattern_loose(struct rtw89_dev *rtwdev,
struct rtw89_mcc_role *ref = &mcc->role_ref;
struct rtw89_mcc_role *aux = &mcc->role_aux;
struct rtw89_mcc_config *config = &mcc->config;
+ u16 mcc_intvl = config->mcc_interval;
u16 bcn_ofst = config->beacon_offset;
u16 bt_dur_in_mid = 0;
u16 max_bcn_ofst;
@@ -1024,7 +1076,7 @@ calc:
res = bcn_ofst - bt_dur_in_mid;
upper = min_t(s16, ref->duration, res);
- lower = 0;
+ lower = max_t(s16, 0, ref->duration - (mcc_intvl - bcn_ofst));
if (ref->limit.enable) {
upper = min_t(s16, upper, ref->limit.max_toa);
@@ -1135,6 +1187,107 @@ static int __rtw89_mcc_calc_pattern_strict(struct rtw89_dev *rtwdev,
return 0;
}
+static void __rtw89_mcc_fill_ptrn_anchor_ref(struct rtw89_dev *rtwdev,
+ struct rtw89_mcc_pattern *ptrn,
+ bool small_bcn_ofst)
+{
+ struct rtw89_mcc_info *mcc = &rtwdev->mcc;
+ struct rtw89_mcc_role *ref = &mcc->role_ref;
+ struct rtw89_mcc_role *aux = &mcc->role_aux;
+ struct rtw89_mcc_config *config = &mcc->config;
+ u16 bcn_ofst = config->beacon_offset;
+ u16 ref_tob;
+ u16 ref_toa;
+
+ if (ref->limit.enable) {
+ ref_tob = ref->limit.max_tob;
+ ref_toa = ref->limit.max_toa;
+ } else {
+ ref_tob = ref->duration / 2;
+ ref_toa = ref->duration / 2;
+ }
+
+ if (small_bcn_ofst) {
+ ptrn->toa_ref = ref_toa;
+ ptrn->tob_ref = ref->duration - ptrn->toa_ref;
+ } else {
+ ptrn->tob_ref = ref_tob;
+ ptrn->toa_ref = ref->duration - ptrn->tob_ref;
+ }
+
+ ptrn->tob_aux = bcn_ofst - ptrn->toa_ref;
+ ptrn->toa_aux = aux->duration - ptrn->tob_aux;
+}
+
+static void __rtw89_mcc_fill_ptrn_anchor_aux(struct rtw89_dev *rtwdev,
+ struct rtw89_mcc_pattern *ptrn,
+ bool small_bcn_ofst)
+{
+ struct rtw89_mcc_info *mcc = &rtwdev->mcc;
+ struct rtw89_mcc_role *ref = &mcc->role_ref;
+ struct rtw89_mcc_role *aux = &mcc->role_aux;
+ struct rtw89_mcc_config *config = &mcc->config;
+ u16 bcn_ofst = config->beacon_offset;
+ u16 aux_tob;
+ u16 aux_toa;
+
+ if (aux->limit.enable) {
+ aux_tob = aux->limit.max_tob;
+ aux_toa = aux->limit.max_toa;
+ } else {
+ aux_tob = aux->duration / 2;
+ aux_toa = aux->duration / 2;
+ }
+
+ if (small_bcn_ofst) {
+ ptrn->tob_aux = aux_tob;
+ ptrn->toa_aux = aux->duration - ptrn->tob_aux;
+ } else {
+ ptrn->toa_aux = aux_toa;
+ ptrn->tob_aux = aux->duration - ptrn->toa_aux;
+ }
+
+ ptrn->toa_ref = bcn_ofst - ptrn->tob_aux;
+ ptrn->tob_ref = ref->duration - ptrn->toa_ref;
+}
+
+static int __rtw89_mcc_calc_pattern_anchor(struct rtw89_dev *rtwdev,
+ struct rtw89_mcc_pattern *ptrn,
+ bool hdl_bt)
+{
+ struct rtw89_mcc_info *mcc = &rtwdev->mcc;
+ struct rtw89_mcc_role *ref = &mcc->role_ref;
+ struct rtw89_mcc_role *aux = &mcc->role_aux;
+ struct rtw89_mcc_config *config = &mcc->config;
+ u16 mcc_intvl = config->mcc_interval;
+ u16 bcn_ofst = config->beacon_offset;
+ bool small_bcn_ofst;
+
+ if (bcn_ofst < RTW89_MCC_MIN_RX_BCN_TIME)
+ small_bcn_ofst = true;
+ else if (mcc_intvl - bcn_ofst < RTW89_MCC_MIN_RX_BCN_TIME)
+ small_bcn_ofst = false;
+ else
+ return -EPERM;
+
+ *ptrn = (typeof(*ptrn)){
+ .plan = hdl_bt ? RTW89_MCC_PLAN_TAIL_BT : RTW89_MCC_PLAN_NO_BT,
+ };
+
+ rtw89_debug(rtwdev, RTW89_DBG_CHAN,
+ "MCC calc ptrn_ac: plan %d, bcn_ofst %d\n",
+ ptrn->plan, bcn_ofst);
+
+ if (ref->is_go || ref->is_gc)
+ __rtw89_mcc_fill_ptrn_anchor_ref(rtwdev, ptrn, small_bcn_ofst);
+ else if (aux->is_go || aux->is_gc)
+ __rtw89_mcc_fill_ptrn_anchor_aux(rtwdev, ptrn, small_bcn_ofst);
+ else
+ __rtw89_mcc_fill_ptrn_anchor_ref(rtwdev, ptrn, small_bcn_ofst);
+
+ return 0;
+}
+
static int rtw89_mcc_calc_pattern(struct rtw89_dev *rtwdev, bool hdl_bt)
{
struct rtw89_mcc_info *mcc = &rtwdev->mcc;
@@ -1188,6 +1341,10 @@ static int rtw89_mcc_calc_pattern(struct rtw89_dev *rtwdev, bool hdl_bt)
goto done;
}
+ ret = __rtw89_mcc_calc_pattern_anchor(rtwdev, &ptrn, hdl_bt);
+ if (!ret)
+ goto done;
+
__rtw89_mcc_calc_pattern_loose(rtwdev, &ptrn, hdl_bt);
done:
@@ -1438,88 +1595,41 @@ static bool rtw89_mcc_duration_decision_on_bt(struct rtw89_dev *rtwdev)
return false;
}
-static void rtw89_mcc_sync_tbtt(struct rtw89_dev *rtwdev,
- struct rtw89_mcc_role *tgt,
- struct rtw89_mcc_role *src,
- bool ref_is_src)
-{
- struct rtw89_mcc_info *mcc = &rtwdev->mcc;
- struct rtw89_mcc_config *config = &mcc->config;
- u16 beacon_offset_us = ieee80211_tu_to_usec(config->beacon_offset);
- u32 bcn_intvl_src_us = ieee80211_tu_to_usec(src->beacon_interval);
- u32 cur_tbtt_ofst_src;
- u32 tsf_ofst_tgt;
- u32 remainder;
- u64 tbtt_tgt;
- u64 tsf_src;
- int ret;
-
- ret = rtw89_mac_port_get_tsf(rtwdev, src->rtwvif_link, &tsf_src);
- if (ret) {
- rtw89_warn(rtwdev, "MCC failed to get port tsf: %d\n", ret);
- return;
- }
-
- cur_tbtt_ofst_src = rtw89_mcc_get_tbtt_ofst(rtwdev, src, tsf_src);
-
- if (ref_is_src)
- tbtt_tgt = tsf_src - cur_tbtt_ofst_src + beacon_offset_us;
- else
- tbtt_tgt = tsf_src - cur_tbtt_ofst_src +
- (bcn_intvl_src_us - beacon_offset_us);
-
- div_u64_rem(tbtt_tgt, bcn_intvl_src_us, &remainder);
- tsf_ofst_tgt = bcn_intvl_src_us - remainder;
-
- config->sync.macid_tgt = tgt->rtwvif_link->mac_id;
- config->sync.band_tgt = tgt->rtwvif_link->mac_idx;
- config->sync.port_tgt = tgt->rtwvif_link->port;
- config->sync.macid_src = src->rtwvif_link->mac_id;
- config->sync.band_src = src->rtwvif_link->mac_idx;
- config->sync.port_src = src->rtwvif_link->port;
- config->sync.offset = tsf_ofst_tgt / 1024;
- config->sync.enable = true;
-
- rtw89_debug(rtwdev, RTW89_DBG_CHAN,
- "MCC sync tbtt: tgt %d, src %d, offset %d\n",
- config->sync.macid_tgt, config->sync.macid_src,
- config->sync.offset);
-
- rtw89_mac_port_tsf_sync(rtwdev, tgt->rtwvif_link, src->rtwvif_link,
- config->sync.offset);
-}
-
static int rtw89_mcc_fill_start_tsf(struct rtw89_dev *rtwdev)
{
struct rtw89_mcc_info *mcc = &rtwdev->mcc;
struct rtw89_mcc_role *ref = &mcc->role_ref;
+ struct rtw89_mcc_role *aux = &mcc->role_aux;
struct rtw89_mcc_config *config = &mcc->config;
u32 bcn_intvl_ref_us = ieee80211_tu_to_usec(ref->beacon_interval);
- u32 tob_ref_us = ieee80211_tu_to_usec(config->pattern.tob_ref);
- struct rtw89_vif_link *rtwvif_link = ref->rtwvif_link;
+ s32 tob_ref_us = ieee80211_tu_to_usec(config->pattern.tob_ref);
u64 tsf, start_tsf;
u32 cur_tbtt_ofst;
u64 min_time;
+ u64 tsf_aux;
int ret;
- ret = rtw89_mac_port_get_tsf(rtwdev, rtwvif_link, &tsf);
- if (ret) {
- rtw89_warn(rtwdev, "MCC failed to get port tsf: %d\n", ret);
+ if (rtw89_concurrent_via_mrc(rtwdev))
+ ret = __mrc_fw_req_tsf(rtwdev, &tsf, &tsf_aux);
+ else
+ ret = __mcc_fw_req_tsf(rtwdev, &tsf, &tsf_aux);
+
+ if (ret)
return ret;
- }
min_time = tsf;
- if (ref->is_go)
+ if (ref->is_go || aux->is_go)
min_time += ieee80211_tu_to_usec(RTW89_MCC_SHORT_TRIGGER_TIME);
else
min_time += ieee80211_tu_to_usec(RTW89_MCC_LONG_TRIGGER_TIME);
cur_tbtt_ofst = rtw89_mcc_get_tbtt_ofst(rtwdev, ref, tsf);
start_tsf = tsf - cur_tbtt_ofst + bcn_intvl_ref_us - tob_ref_us;
- while (start_tsf < min_time)
- start_tsf += bcn_intvl_ref_us;
+ if (start_tsf < min_time)
+ start_tsf += roundup_u64(min_time - start_tsf, bcn_intvl_ref_us);
config->start_tsf = start_tsf;
+ config->start_tsf_in_aux_domain = tsf_aux + start_tsf - tsf;
return 0;
}
@@ -1536,13 +1646,11 @@ static int rtw89_mcc_fill_config(struct rtw89_dev *rtwdev)
switch (mcc->mode) {
case RTW89_MCC_MODE_GO_STA:
- config->beacon_offset = RTW89_MCC_DFLT_BCN_OFST_TIME;
+ config->beacon_offset = rtw89_mcc_get_bcn_ofst(rtwdev);
if (ref->is_go) {
- rtw89_mcc_sync_tbtt(rtwdev, ref, aux, false);
config->mcc_interval = ref->beacon_interval;
rtw89_mcc_set_duration_go_sta(rtwdev, ref, aux);
} else {
- rtw89_mcc_sync_tbtt(rtwdev, aux, ref, true);
config->mcc_interval = aux->beacon_interval;
rtw89_mcc_set_duration_go_sta(rtwdev, aux, ref);
}
@@ -1572,10 +1680,8 @@ bottom:
static int __mcc_fw_add_role(struct rtw89_dev *rtwdev, struct rtw89_mcc_role *role)
{
+ const struct rtw89_mcc_courtesy_cfg *crtz = role->crtz;
struct rtw89_mcc_info *mcc = &rtwdev->mcc;
- struct rtw89_mcc_config *config = &mcc->config;
- struct rtw89_mcc_pattern *pattern = &config->pattern;
- struct rtw89_mcc_courtesy *courtesy = &pattern->courtesy;
struct rtw89_mcc_policy *policy = &role->policy;
struct rtw89_fw_mcc_add_req req = {};
const struct rtw89_chan *chan;
@@ -1598,9 +1704,9 @@ static int __mcc_fw_add_role(struct rtw89_dev *rtwdev, struct rtw89_mcc_role *ro
req.duration = role->duration;
req.btc_in_2g = false;
- if (courtesy->enable && courtesy->macid_src == req.macid) {
- req.courtesy_target = courtesy->macid_tgt;
- req.courtesy_num = courtesy->slot_num;
+ if (crtz) {
+ req.courtesy_target = crtz->macid_tgt;
+ req.courtesy_num = crtz->slot_num;
req.courtesy_en = true;
}
@@ -1780,26 +1886,23 @@ static void __mrc_fw_add_courtesy(struct rtw89_dev *rtwdev,
struct rtw89_mcc_info *mcc = &rtwdev->mcc;
struct rtw89_mcc_role *ref = &mcc->role_ref;
struct rtw89_mcc_role *aux = &mcc->role_aux;
- struct rtw89_mcc_config *config = &mcc->config;
- struct rtw89_mcc_pattern *pattern = &config->pattern;
- struct rtw89_mcc_courtesy *courtesy = &pattern->courtesy;
struct rtw89_fw_mrc_add_slot_arg *slot_arg_src;
- u8 slot_idx_tgt;
- if (!courtesy->enable)
- return;
-
- if (courtesy->macid_src == ref->rtwvif_link->mac_id) {
+ if (ref->crtz) {
slot_arg_src = &arg->slots[ref->slot_idx];
- slot_idx_tgt = aux->slot_idx;
- } else {
- slot_arg_src = &arg->slots[aux->slot_idx];
- slot_idx_tgt = ref->slot_idx;
+
+ slot_arg_src->courtesy_target = aux->slot_idx;
+ slot_arg_src->courtesy_period = ref->crtz->slot_num;
+ slot_arg_src->courtesy_en = true;
}
- slot_arg_src->courtesy_target = slot_idx_tgt;
- slot_arg_src->courtesy_period = courtesy->slot_num;
- slot_arg_src->courtesy_en = true;
+ if (aux->crtz) {
+ slot_arg_src = &arg->slots[aux->slot_idx];
+
+ slot_arg_src->courtesy_target = ref->slot_idx;
+ slot_arg_src->courtesy_period = aux->crtz->slot_num;
+ slot_arg_src->courtesy_en = true;
+ }
}
static int __mrc_fw_start(struct rtw89_dev *rtwdev, bool replace)
@@ -1999,30 +2102,24 @@ static void rtw89_mcc_handle_beacon_noa(struct rtw89_dev *rtwdev, bool enable)
struct rtw89_mcc_role *ref = &mcc->role_ref;
struct rtw89_mcc_role *aux = &mcc->role_aux;
struct rtw89_mcc_config *config = &mcc->config;
- struct rtw89_mcc_pattern *pattern = &config->pattern;
- struct rtw89_mcc_sync *sync = &config->sync;
struct ieee80211_p2p_noa_desc noa_desc = {};
- u64 start_time = config->start_tsf;
u32 interval = config->mcc_interval;
struct rtw89_vif_link *rtwvif_go;
+ u64 start_time;
u32 duration;
if (mcc->mode != RTW89_MCC_MODE_GO_STA)
return;
if (ref->is_go) {
+ start_time = config->start_tsf;
rtwvif_go = ref->rtwvif_link;
start_time += ieee80211_tu_to_usec(ref->duration);
duration = config->mcc_interval - ref->duration;
} else if (aux->is_go) {
+ start_time = config->start_tsf_in_aux_domain;
rtwvif_go = aux->rtwvif_link;
- start_time += ieee80211_tu_to_usec(pattern->tob_ref) +
- ieee80211_tu_to_usec(config->beacon_offset) +
- ieee80211_tu_to_usec(pattern->toa_aux);
duration = config->mcc_interval - aux->duration;
-
- /* convert time domain from sta(ref) to GO(aux) */
- start_time += ieee80211_tu_to_usec(sync->offset);
} else {
rtw89_debug(rtwdev, RTW89_DBG_CHAN,
"MCC find no GO: skip updating beacon NoA\n");
@@ -2126,6 +2223,12 @@ static int rtw89_mcc_start(struct rtw89_dev *rtwdev)
}
struct rtw89_mcc_stop_sel {
+ struct {
+ const struct rtw89_vif_link *target;
+ } hint;
+
+ /* selection content */
+ bool filled;
u8 mac_id;
u8 slot_idx;
};
@@ -2135,6 +2238,7 @@ static void rtw89_mcc_stop_sel_fill(struct rtw89_mcc_stop_sel *sel,
{
sel->mac_id = mcc_role->rtwvif_link->mac_id;
sel->slot_idx = mcc_role->slot_idx;
+ sel->filled = true;
}
static int rtw89_mcc_stop_sel_iterator(struct rtw89_dev *rtwdev,
@@ -2144,23 +2248,41 @@ static int rtw89_mcc_stop_sel_iterator(struct rtw89_dev *rtwdev,
{
struct rtw89_mcc_stop_sel *sel = data;
+ if (mcc_role->rtwvif_link == sel->hint.target) {
+ rtw89_mcc_stop_sel_fill(sel, mcc_role);
+ return 1; /* break iteration */
+ }
+
+ if (sel->filled)
+ return 0;
+
if (!mcc_role->rtwvif_link->chanctx_assigned)
return 0;
rtw89_mcc_stop_sel_fill(sel, mcc_role);
- return 1; /* break iteration */
+ return 0;
}
-static void rtw89_mcc_stop(struct rtw89_dev *rtwdev)
+static void rtw89_mcc_stop(struct rtw89_dev *rtwdev,
+ const struct rtw89_chanctx_pause_parm *pause)
{
+ struct rtw89_hal *hal = &rtwdev->hal;
struct rtw89_mcc_info *mcc = &rtwdev->mcc;
struct rtw89_mcc_role *ref = &mcc->role_ref;
- struct rtw89_mcc_stop_sel sel;
+ struct rtw89_mcc_stop_sel sel = {
+ .hint.target = pause ? pause->trigger : NULL,
+ };
int ret;
+ if (!pause) {
+ wiphy_delayed_work_cancel(rtwdev->hw->wiphy, &rtwdev->chanctx_work);
+ bitmap_zero(hal->changes, NUM_OF_RTW89_CHANCTX_CHANGES);
+ }
+
/* by default, stop at ref */
- rtw89_mcc_stop_sel_fill(&sel, ref);
rtw89_iterate_mcc_roles(rtwdev, rtw89_mcc_stop_sel_iterator, &sel);
+ if (!sel.filled)
+ rtw89_mcc_stop_sel_fill(&sel, ref);
rtw89_debug(rtwdev, RTW89_DBG_CHAN, "MCC stop at <macid %d>\n", sel.mac_id);
@@ -2192,6 +2314,7 @@ static int rtw89_mcc_update(struct rtw89_dev *rtwdev)
struct rtw89_mcc_info *mcc = &rtwdev->mcc;
struct rtw89_mcc_config *config = &mcc->config;
struct rtw89_mcc_config old_cfg = *config;
+ bool courtesy_changed;
bool sync_changed;
int ret;
@@ -2204,8 +2327,15 @@ static int rtw89_mcc_update(struct rtw89_dev *rtwdev)
if (ret)
return ret;
+ if (memcmp(&old_cfg.pattern.courtesy, &config->pattern.courtesy,
+ sizeof(old_cfg.pattern.courtesy)) == 0)
+ courtesy_changed = false;
+ else
+ courtesy_changed = true;
+
if (old_cfg.pattern.plan != RTW89_MCC_PLAN_NO_BT ||
- config->pattern.plan != RTW89_MCC_PLAN_NO_BT) {
+ config->pattern.plan != RTW89_MCC_PLAN_NO_BT ||
+ courtesy_changed) {
if (rtw89_concurrent_via_mrc(rtwdev))
ret = __mrc_fw_start(rtwdev, true);
else
@@ -2237,7 +2367,7 @@ static void rtw89_mcc_track(struct rtw89_dev *rtwdev)
struct rtw89_mcc_info *mcc = &rtwdev->mcc;
struct rtw89_mcc_config *config = &mcc->config;
struct rtw89_mcc_pattern *pattern = &config->pattern;
- s16 tolerance;
+ u16 tolerance;
u16 bcn_ofst;
u16 diff;
@@ -2245,18 +2375,25 @@ static void rtw89_mcc_track(struct rtw89_dev *rtwdev)
return;
bcn_ofst = rtw89_mcc_get_bcn_ofst(rtwdev);
+ if (bcn_ofst == config->beacon_offset)
+ return;
+
if (bcn_ofst > config->beacon_offset) {
diff = bcn_ofst - config->beacon_offset;
if (pattern->tob_aux < 0)
tolerance = -pattern->tob_aux;
- else
+ else if (pattern->toa_aux > 0)
tolerance = pattern->toa_aux;
+ else
+ return; /* no chance to improve */
} else {
diff = config->beacon_offset - bcn_ofst;
if (pattern->toa_aux < 0)
tolerance = -pattern->toa_aux;
- else
+ else if (pattern->tob_aux > 0)
tolerance = pattern->tob_aux;
+ else
+ return; /* no chance to improve */
}
if (diff <= tolerance)
@@ -2390,7 +2527,7 @@ static void rtw89_mcc_update_limit(struct rtw89_dev *rtwdev)
rtw89_iterate_mcc_roles(rtwdev, rtw89_mcc_upd_lmt_iterator, NULL);
}
-void rtw89_chanctx_work(struct work_struct *work)
+void rtw89_chanctx_work(struct wiphy *wiphy, struct wiphy_work *work)
{
struct rtw89_dev *rtwdev = container_of(work, struct rtw89_dev,
chanctx_work.work);
@@ -2401,12 +2538,10 @@ void rtw89_chanctx_work(struct work_struct *work)
int ret;
int i;
- mutex_lock(&rtwdev->mutex);
+ lockdep_assert_wiphy(wiphy);
- if (hal->entity_pause) {
- mutex_unlock(&rtwdev->mutex);
+ if (hal->entity_pause)
return;
- }
for (i = 0; i < NUM_OF_RTW89_CHANCTX_CHANGES; i++) {
if (test_and_clear_bit(i, hal->changes))
@@ -2445,8 +2580,6 @@ void rtw89_chanctx_work(struct work_struct *work)
default:
break;
}
-
- mutex_unlock(&rtwdev->mutex);
}
void rtw89_queue_chanctx_change(struct rtw89_dev *rtwdev,
@@ -2477,8 +2610,8 @@ void rtw89_queue_chanctx_change(struct rtw89_dev *rtwdev,
rtw89_debug(rtwdev, RTW89_DBG_CHAN,
"queue chanctx work for mode %d with delay %d us\n",
mode, delay);
- ieee80211_queue_delayed_work(rtwdev->hw, &rtwdev->chanctx_work,
- usecs_to_jiffies(delay));
+ wiphy_delayed_work_queue(rtwdev->hw->wiphy, &rtwdev->chanctx_work,
+ usecs_to_jiffies(delay));
}
void rtw89_queue_chanctx_work(struct rtw89_dev *rtwdev)
@@ -2491,7 +2624,7 @@ void rtw89_chanctx_track(struct rtw89_dev *rtwdev)
struct rtw89_hal *hal = &rtwdev->hal;
enum rtw89_entity_mode mode;
- lockdep_assert_held(&rtwdev->mutex);
+ lockdep_assert_wiphy(rtwdev->hw->wiphy);
if (hal->entity_pause)
return;
@@ -2507,22 +2640,22 @@ void rtw89_chanctx_track(struct rtw89_dev *rtwdev)
}
void rtw89_chanctx_pause(struct rtw89_dev *rtwdev,
- enum rtw89_chanctx_pause_reasons rsn)
+ const struct rtw89_chanctx_pause_parm *pause_parm)
{
struct rtw89_hal *hal = &rtwdev->hal;
enum rtw89_entity_mode mode;
- lockdep_assert_held(&rtwdev->mutex);
+ lockdep_assert_wiphy(rtwdev->hw->wiphy);
if (hal->entity_pause)
return;
- rtw89_debug(rtwdev, RTW89_DBG_CHAN, "chanctx pause (rsn: %d)\n", rsn);
+ rtw89_debug(rtwdev, RTW89_DBG_CHAN, "chanctx pause (rsn: %d)\n", pause_parm->rsn);
mode = rtw89_get_entity_mode(rtwdev);
switch (mode) {
case RTW89_ENTITY_MODE_MCC:
- rtw89_mcc_stop(rtwdev);
+ rtw89_mcc_stop(rtwdev, pause_parm);
break;
default:
break;
@@ -2555,7 +2688,7 @@ void rtw89_chanctx_proceed(struct rtw89_dev *rtwdev,
enum rtw89_entity_mode mode;
int ret;
- lockdep_assert_held(&rtwdev->mutex);
+ lockdep_assert_wiphy(rtwdev->hw->wiphy);
if (unlikely(!hal->entity_pause)) {
rtw89_chanctx_proceed_cb(rtwdev, cb_parm);
@@ -2677,6 +2810,7 @@ int rtw89_chanctx_ops_assign_vif(struct rtw89_dev *rtwdev,
struct rtw89_hal *hal = &rtwdev->hal;
struct rtw89_entity_mgnt *mgnt = &hal->entity_mgnt;
struct rtw89_entity_weight w = {};
+ int ret;
rtwvif_link->chanctx_idx = cfg->idx;
rtwvif_link->chanctx_assigned = true;
@@ -2696,7 +2830,13 @@ int rtw89_chanctx_ops_assign_vif(struct rtw89_dev *rtwdev,
rtw89_swap_chanctx(rtwdev, cfg->idx, RTW89_CHANCTX_0);
out:
- return rtw89_set_channel(rtwdev);
+ ret = rtw89_set_channel(rtwdev);
+ if (ret)
+ return ret;
+
+ rtw89_tas_reset(rtwdev, true);
+
+ return 0;
}
void rtw89_chanctx_ops_unassign_vif(struct rtw89_dev *rtwdev,
@@ -2740,7 +2880,7 @@ out:
cur = rtw89_get_entity_mode(rtwdev);
switch (cur) {
case RTW89_ENTITY_MODE_MCC:
- rtw89_mcc_stop(rtwdev);
+ rtw89_mcc_stop(rtwdev, NULL);
break;
default:
break;
diff --git a/drivers/net/wireless/realtek/rtw89/chan.h b/drivers/net/wireless/realtek/rtw89/chan.h
index 092a6f676894..2a25563593af 100644
--- a/drivers/net/wireless/realtek/rtw89/chan.h
+++ b/drivers/net/wireless/realtek/rtw89/chan.h
@@ -31,6 +31,14 @@
#define RTW89_MCC_DFLT_TX_NULL_EARLY 3
#define RTW89_MCC_DFLT_COURTESY_SLOT 3
+#define RTW89_MCC_REQ_COURTESY_TIME 5
+#define RTW89_MCC_REQ_COURTESY(pattern, role) \
+({ \
+ const struct rtw89_mcc_pattern *p = pattern; \
+ p->tob_ ## role <= RTW89_MCC_REQ_COURTESY_TIME || \
+ p->toa_ ## role <= RTW89_MCC_REQ_COURTESY_TIME; \
+})
+
#define NUM_OF_RTW89_MCC_ROLES 2
enum rtw89_chanctx_pause_reasons {
@@ -38,6 +46,11 @@ enum rtw89_chanctx_pause_reasons {
RTW89_CHANCTX_PAUSE_REASON_ROC,
};
+struct rtw89_chanctx_pause_parm {
+ const struct rtw89_vif_link *trigger;
+ enum rtw89_chanctx_pause_reasons rsn;
+};
+
struct rtw89_chanctx_cb_parm {
int (*cb)(struct rtw89_dev *rtwdev, void *data);
void *data;
@@ -95,17 +108,17 @@ void rtw89_config_entity_chandef(struct rtw89_dev *rtwdev,
enum rtw89_chanctx_idx idx,
const struct cfg80211_chan_def *chandef);
void rtw89_config_roc_chandef(struct rtw89_dev *rtwdev,
- enum rtw89_chanctx_idx idx,
+ struct rtw89_vif_link *rtwvif_link,
const struct cfg80211_chan_def *chandef);
void rtw89_entity_init(struct rtw89_dev *rtwdev);
enum rtw89_entity_mode rtw89_entity_recalc(struct rtw89_dev *rtwdev);
-void rtw89_chanctx_work(struct work_struct *work);
+void rtw89_chanctx_work(struct wiphy *wiphy, struct wiphy_work *work);
void rtw89_queue_chanctx_work(struct rtw89_dev *rtwdev);
void rtw89_queue_chanctx_change(struct rtw89_dev *rtwdev,
enum rtw89_chanctx_changes change);
void rtw89_chanctx_track(struct rtw89_dev *rtwdev);
void rtw89_chanctx_pause(struct rtw89_dev *rtwdev,
- enum rtw89_chanctx_pause_reasons rsn);
+ const struct rtw89_chanctx_pause_parm *parm);
void rtw89_chanctx_proceed(struct rtw89_dev *rtwdev,
const struct rtw89_chanctx_cb_parm *cb_parm);
diff --git a/drivers/net/wireless/realtek/rtw89/coex.c b/drivers/net/wireless/realtek/rtw89/coex.c
index 68316d44b204..5ccf0cbaed2f 100644
--- a/drivers/net/wireless/realtek/rtw89/coex.c
+++ b/drivers/net/wireless/realtek/rtw89/coex.c
@@ -10,7 +10,7 @@
#include "ps.h"
#include "reg.h"
-#define RTW89_COEX_VERSION 0x07000113
+#define RTW89_COEX_VERSION 0x07000413
#define FCXDEF_STEP 50 /* MUST <= FCXMAX_STEP and match with wl fw*/
#define BTC_E2G_LIMIT_DEF 80
@@ -89,10 +89,10 @@ static const struct rtw89_btc_fbtc_slot s_def[] = {
[CXST_B4] = __DEF_FBTC_SLOT(50, 0xe5555555, SLOT_MIX),
[CXST_LK] = __DEF_FBTC_SLOT(20, 0xea5a5a5a, SLOT_ISO),
[CXST_BLK] = __DEF_FBTC_SLOT(500, 0x55555555, SLOT_MIX),
- [CXST_E2G] = __DEF_FBTC_SLOT(0, 0xea5a5a5a, SLOT_MIX),
- [CXST_E5G] = __DEF_FBTC_SLOT(0, 0xffffffff, SLOT_ISO),
+ [CXST_E2G] = __DEF_FBTC_SLOT(5, 0xea5a5a5a, SLOT_MIX),
+ [CXST_E5G] = __DEF_FBTC_SLOT(5, 0xffffffff, SLOT_ISO),
[CXST_EBT] = __DEF_FBTC_SLOT(5, 0xe5555555, SLOT_MIX),
- [CXST_ENULL] = __DEF_FBTC_SLOT(0, 0xaaaaaaaa, SLOT_ISO),
+ [CXST_ENULL] = __DEF_FBTC_SLOT(5, 0xaaaaaaaa, SLOT_ISO),
[CXST_WLK] = __DEF_FBTC_SLOT(250, 0xea5a5a5a, SLOT_MIX),
[CXST_W1FDD] = __DEF_FBTC_SLOT(50, 0xffffffff, SLOT_ISO),
[CXST_B1FDD] = __DEF_FBTC_SLOT(50, 0xffffdfff, SLOT_ISO),
@@ -132,6 +132,14 @@ static const u32 cxtbl[] = {
static const struct rtw89_btc_ver rtw89_btc_ver_defs[] = {
/* firmware version must be in decreasing order for each chip */
+ {RTL8852BT, RTW89_FW_VER_CODE(0, 29, 122, 0),
+ .fcxbtcrpt = 8, .fcxtdma = 7, .fcxslots = 7, .fcxcysta = 7,
+ .fcxstep = 7, .fcxnullsta = 7, .fcxmreg = 7, .fcxgpiodbg = 7,
+ .fcxbtver = 7, .fcxbtscan = 7, .fcxbtafh = 7, .fcxbtdevinfo = 7,
+ .fwlrole = 7, .frptmap = 3, .fcxctrl = 7, .fcxinit = 7,
+ .fwevntrptl = 1, .fwc2hfunc = 2, .drvinfo_type = 1, .info_buf = 1800,
+ .max_role_num = 6,
+ },
{RTL8852BT, RTW89_FW_VER_CODE(0, 29, 90, 0),
.fcxbtcrpt = 7, .fcxtdma = 7, .fcxslots = 7, .fcxcysta = 7,
.fcxstep = 7, .fcxnullsta = 7, .fcxmreg = 7, .fcxgpiodbg = 7,
@@ -1372,11 +1380,9 @@ static u32 _chk_btc_report(struct rtw89_dev *rtwdev,
} else if (ver->fcxbtcrpt == 8) {
pfinfo = &pfwinfo->rpt_ctrl.finfo.v8;
pcinfo->req_len = sizeof(pfwinfo->rpt_ctrl.finfo.v8);
- break;
} else if (ver->fcxbtcrpt == 7) {
pfinfo = &pfwinfo->rpt_ctrl.finfo.v7;
pcinfo->req_len = sizeof(pfwinfo->rpt_ctrl.finfo.v7);
- break;
} else {
goto err;
}
@@ -1534,6 +1540,9 @@ static u32 _chk_btc_report(struct rtw89_dev *rtwdev,
} else if (ver->fcxbtafh == 2) {
pfinfo = &pfwinfo->rpt_fbtc_btafh.finfo.v2;
pcinfo->req_len = sizeof(pfwinfo->rpt_fbtc_btafh.finfo.v2);
+ } else if (ver->fcxbtafh == 7) {
+ pfinfo = &pfwinfo->rpt_fbtc_btafh.finfo.v7;
+ pcinfo->req_len = sizeof(pfwinfo->rpt_fbtc_btafh.finfo.v7);
} else {
goto err;
}
@@ -1602,7 +1611,7 @@ static u32 _chk_btc_report(struct rtw89_dev *rtwdev,
wl->ver_info.fw = le32_to_cpu(prpt->v4.wl_fw_info.fw_ver);
dm->wl_fw_cx_offload = !!le32_to_cpu(prpt->v4.wl_fw_info.cx_offload);
- for (i = RTW89_PHY_0; i < RTW89_PHY_MAX; i++)
+ for (i = RTW89_PHY_0; i < RTW89_PHY_NUM; i++)
memcpy(&dm->gnt.band[i], &prpt->v4.gnt_val[i],
sizeof(dm->gnt.band[i]));
@@ -1634,7 +1643,7 @@ static u32 _chk_btc_report(struct rtw89_dev *rtwdev,
wl->ver_info.fw = le32_to_cpu(prpt->v5.rpt_info.fw_ver);
dm->wl_fw_cx_offload = 0;
- for (i = RTW89_PHY_0; i < RTW89_PHY_MAX; i++)
+ for (i = RTW89_PHY_0; i < RTW89_PHY_NUM; i++)
memcpy(&dm->gnt.band[i], &prpt->v5.gnt_val[i][0],
sizeof(dm->gnt.band[i]));
@@ -1661,7 +1670,7 @@ static u32 _chk_btc_report(struct rtw89_dev *rtwdev,
wl->ver_info.fw = le32_to_cpu(prpt->v105.rpt_info.fw_ver);
dm->wl_fw_cx_offload = 0;
- for (i = RTW89_PHY_0; i < RTW89_PHY_MAX; i++)
+ for (i = RTW89_PHY_0; i < RTW89_PHY_NUM; i++)
memcpy(&dm->gnt.band[i], &prpt->v105.gnt_val[i][0],
sizeof(dm->gnt.band[i]));
@@ -1687,7 +1696,7 @@ static u32 _chk_btc_report(struct rtw89_dev *rtwdev,
wl->ver_info.fw_coex = le32_to_cpu(prpt->v7.rpt_info.cx_ver);
wl->ver_info.fw = le32_to_cpu(prpt->v7.rpt_info.fw_ver);
- for (i = RTW89_PHY_0; i < RTW89_PHY_MAX; i++)
+ for (i = RTW89_PHY_0; i < RTW89_PHY_NUM; i++)
memcpy(&dm->gnt.band[i], &prpt->v7.gnt_val[i][0],
sizeof(dm->gnt.band[i]));
@@ -1719,7 +1728,7 @@ static u32 _chk_btc_report(struct rtw89_dev *rtwdev,
wl->ver_info.fw_coex = le32_to_cpu(prpt->v8.rpt_info.cx_ver);
wl->ver_info.fw = le32_to_cpu(prpt->v8.rpt_info.fw_ver);
- for (i = RTW89_PHY_0; i < RTW89_PHY_MAX; i++)
+ for (i = RTW89_PHY_0; i < RTW89_PHY_NUM; i++)
memcpy(&dm->gnt.band[i], &prpt->v8.gnt_val[i][0],
sizeof(dm->gnt.band[i]));
@@ -2706,7 +2715,7 @@ static void _set_gnt(struct rtw89_dev *rtwdev, u8 phy_map, u8 wl_state, u8 bt_st
if (phy_map > BTC_PHY_ALL)
return;
- for (i = 0; i < RTW89_PHY_MAX; i++) {
+ for (i = 0; i < RTW89_PHY_NUM; i++) {
if (!(phy_map & BIT(i)))
continue;
@@ -2755,7 +2764,7 @@ static void _set_gnt_v1(struct rtw89_dev *rtwdev, u8 phy_map,
if (phy_map > BTC_PHY_ALL)
return;
- for (i = 0; i < RTW89_PHY_MAX; i++) {
+ for (i = 0; i < RTW89_PHY_NUM; i++) {
if (!(phy_map & BIT(i)))
continue;
@@ -2955,7 +2964,7 @@ static void _set_rf_trx_para(struct rtw89_dev *rtwdev)
if (ver->fwlrole == 0) {
link_mode = wl->role_info.link_mode;
- for (i = 0; i < RTW89_PHY_MAX; i++) {
+ for (i = 0; i < RTW89_PHY_NUM; i++) {
if (wl->dbcc_info.real_band[i] == RTW89_BAND_2G)
dbcc_2g_phy = i;
}
@@ -4240,7 +4249,7 @@ static void _set_ant_v0(struct rtw89_dev *rtwdev, bool force_exec,
case BTC_ANT_W2G:
rtw89_chip_cfg_ctrl_path(rtwdev, BTC_CTRL_BY_WL);
if (rtwdev->dbcc_en) {
- for (i = 0; i < RTW89_PHY_MAX; i++) {
+ for (i = 0; i < RTW89_PHY_NUM; i++) {
b2g = (wl_dinfo->real_band[i] == RTW89_BAND_2G);
gnt_wl_ctrl = b2g ? BTC_GNT_HW : BTC_GNT_SW_HI;
@@ -4583,23 +4592,16 @@ static void _action_bt_hid(struct rtw89_dev *rtwdev)
static void _action_bt_a2dp(struct rtw89_dev *rtwdev)
{
struct rtw89_btc *btc = &rtwdev->btc;
- struct rtw89_btc_bt_link_info *bt_linfo = &btc->cx.bt.link_info;
- struct rtw89_btc_bt_a2dp_desc a2dp = bt_linfo->a2dp_desc;
struct rtw89_btc_dm *dm = &btc->dm;
_set_ant(rtwdev, NM_EXEC, BTC_PHY_ALL, BTC_ANT_W2G);
+ dm->slot_dur[CXST_W1] = 20;
+ dm->slot_dur[CXST_B1] = BTC_B1_MAX;
+
switch (btc->cx.state_map) {
case BTC_WBUSY_BNOSCAN: /* wl-busy + bt-A2DP */
- if (a2dp.vendor_id == 0x4c || dm->leak_ap) {
- dm->slot_dur[CXST_W1] = 40;
- dm->slot_dur[CXST_B1] = 200;
- _set_policy(rtwdev,
- BTC_CXP_PAUTO_TDW1B1, BTC_ACT_BT_A2DP);
- } else {
- _set_policy(rtwdev,
- BTC_CXP_PAUTO_TD50B1, BTC_ACT_BT_A2DP);
- }
+ _set_policy(rtwdev, BTC_CXP_PAUTO_TDW1B1, BTC_ACT_BT_A2DP);
break;
case BTC_WBUSY_BSCAN: /* wl-busy + bt-inq + bt-A2DP */
_set_policy(rtwdev, BTC_CXP_PAUTO2_TD3050, BTC_ACT_BT_A2DP);
@@ -4609,15 +4611,10 @@ static void _action_bt_a2dp(struct rtw89_dev *rtwdev)
break;
case BTC_WSCAN_BNOSCAN: /* wl-scan + bt-A2DP */
case BTC_WLINKING: /* wl-connecting + bt-A2DP */
- if (a2dp.vendor_id == 0x4c || dm->leak_ap) {
- dm->slot_dur[CXST_W1] = 40;
- dm->slot_dur[CXST_B1] = 200;
- _set_policy(rtwdev, BTC_CXP_AUTO_TDW1B1,
- BTC_ACT_BT_A2DP);
- } else {
- _set_policy(rtwdev, BTC_CXP_AUTO_TD50B1,
- BTC_ACT_BT_A2DP);
- }
+ if (btc->cx.wl.rfk_info.con_rfk)
+ _set_policy(rtwdev, BTC_CXP_OFF_BT, BTC_ACT_BT_A2DP);
+ else
+ _set_policy(rtwdev, BTC_CXP_AUTO_TDW1B1, BTC_ACT_BT_A2DP);
break;
case BTC_WIDLE: /* wl-idle + bt-A2DP */
_set_policy(rtwdev, BTC_CXP_AUTO_TD20B1, BTC_ACT_BT_A2DP);
@@ -4645,7 +4642,10 @@ static void _action_bt_a2dpsink(struct rtw89_dev *rtwdev)
_set_policy(rtwdev, BTC_CXP_FIX_TD2060, BTC_ACT_BT_A2DPSINK);
break;
case BTC_WLINKING: /* wl-connecting + bt-A2dp_Sink */
- _set_policy(rtwdev, BTC_CXP_FIX_TD3030, BTC_ACT_BT_A2DPSINK);
+ if (btc->cx.wl.rfk_info.con_rfk)
+ _set_policy(rtwdev, BTC_CXP_OFF_BT, BTC_ACT_BT_A2DPSINK);
+ else
+ _set_policy(rtwdev, BTC_CXP_FIX_TD3030, BTC_ACT_BT_A2DPSINK);
break;
case BTC_WIDLE: /* wl-idle + bt-A2dp_Sink */
_set_policy(rtwdev, BTC_CXP_FIX_TD2080, BTC_ACT_BT_A2DPSINK);
@@ -4693,27 +4693,20 @@ static void _action_bt_pan(struct rtw89_dev *rtwdev)
static void _action_bt_a2dp_hid(struct rtw89_dev *rtwdev)
{
struct rtw89_btc *btc = &rtwdev->btc;
- struct rtw89_btc_bt_link_info *bt_linfo = &btc->cx.bt.link_info;
- struct rtw89_btc_bt_a2dp_desc a2dp = bt_linfo->a2dp_desc;
struct rtw89_btc_dm *dm = &btc->dm;
_set_ant(rtwdev, NM_EXEC, BTC_PHY_ALL, BTC_ANT_W2G);
+ dm->slot_dur[CXST_W1] = 20;
+ dm->slot_dur[CXST_B1] = BTC_B1_MAX;
+
switch (btc->cx.state_map) {
case BTC_WBUSY_BNOSCAN: /* wl-busy + bt-A2DP+HID */
case BTC_WIDLE: /* wl-idle + bt-A2DP */
- if (a2dp.vendor_id == 0x4c || dm->leak_ap) {
- dm->slot_dur[CXST_W1] = 40;
- dm->slot_dur[CXST_B1] = 200;
- _set_policy(rtwdev,
- BTC_CXP_PAUTO_TDW1B1, BTC_ACT_BT_A2DP_HID);
- } else {
- _set_policy(rtwdev,
- BTC_CXP_PAUTO_TD50B1, BTC_ACT_BT_A2DP_HID);
- }
+ _set_policy(rtwdev, BTC_CXP_PAUTO_TDW1B1, BTC_ACT_BT_A2DP_HID);
break;
case BTC_WBUSY_BSCAN: /* wl-busy + bt-inq + bt-A2DP+HID */
- _set_policy(rtwdev, BTC_CXP_PAUTO2_TD3050, BTC_ACT_BT_A2DP_HID);
+ _set_policy(rtwdev, BTC_CXP_PAUTO2_TD3070, BTC_ACT_BT_A2DP_HID);
break;
case BTC_WSCAN_BSCAN: /* wl-scan + bt-inq + bt-A2DP+HID */
@@ -4721,15 +4714,10 @@ static void _action_bt_a2dp_hid(struct rtw89_dev *rtwdev)
break;
case BTC_WSCAN_BNOSCAN: /* wl-scan + bt-A2DP+HID */
case BTC_WLINKING: /* wl-connecting + bt-A2DP+HID */
- if (a2dp.vendor_id == 0x4c || dm->leak_ap) {
- dm->slot_dur[CXST_W1] = 40;
- dm->slot_dur[CXST_B1] = 200;
- _set_policy(rtwdev, BTC_CXP_AUTO_TDW1B1,
- BTC_ACT_BT_A2DP_HID);
- } else {
- _set_policy(rtwdev, BTC_CXP_AUTO_TD50B1,
- BTC_ACT_BT_A2DP_HID);
- }
+ if (btc->cx.wl.rfk_info.con_rfk)
+ _set_policy(rtwdev, BTC_CXP_OFF_BT, BTC_ACT_BT_A2DP_HID);
+ else
+ _set_policy(rtwdev, BTC_CXP_AUTO_TDW1B1, BTC_ACT_BT_A2DP_HID);
break;
}
}
@@ -4905,9 +4893,9 @@ static void _set_btg_ctrl(struct rtw89_dev *rtwdev)
if (rtwdev->dbcc_en) {
if (ver->fwlrole == 0) {
- wl_rinfo.dbcc_2g_phy = RTW89_PHY_MAX;
+ wl_rinfo.dbcc_2g_phy = RTW89_PHY_NUM;
- for (i = 0; i < RTW89_PHY_MAX; i++) {
+ for (i = 0; i < RTW89_PHY_NUM; i++) {
if (wl_dinfo->real_band[i] == RTW89_BAND_2G)
wl_rinfo.dbcc_2g_phy = i;
}
@@ -5408,7 +5396,8 @@ static void _action_wl_scan(struct rtw89_dev *rtwdev)
struct rtw89_btc_wl_info *wl = &btc->cx.wl;
struct rtw89_btc_wl_dbcc_info *wl_dinfo = &wl->dbcc_info;
- if (RTW89_CHK_FW_FEATURE(SCAN_OFFLOAD, &rtwdev->fw)) {
+ if (btc->cx.state_map != BTC_WLINKING &&
+ RTW89_CHK_FW_FEATURE(SCAN_OFFLOAD, &rtwdev->fw)) {
_action_wl_25g_mcc(rtwdev);
rtw89_debug(rtwdev, RTW89_DBG_BTC, "[BTC], Scan offload!\n");
} else if (rtwdev->dbcc_en) {
@@ -5798,7 +5787,7 @@ static void _update_wl_info(struct rtw89_dev *rtwdev)
phy = wl_linfo[i].phy;
/* check dbcc role */
- if (rtwdev->dbcc_en && phy < RTW89_PHY_MAX) {
+ if (rtwdev->dbcc_en && phy < RTW89_PHY_NUM) {
wl_dinfo->role[phy] = wl_linfo[i].role;
wl_dinfo->op_band[phy] = wl_linfo[i].band;
_update_dbcc_band(rtwdev, phy);
@@ -5948,7 +5937,7 @@ static void _update_wl_info_v1(struct rtw89_dev *rtwdev)
phy = wl_linfo[i].phy;
- if (rtwdev->dbcc_en && phy < RTW89_PHY_MAX) {
+ if (rtwdev->dbcc_en && phy < RTW89_PHY_NUM) {
wl_dinfo->role[phy] = wl_linfo[i].role;
wl_dinfo->op_band[phy] = wl_linfo[i].band;
_update_dbcc_band(rtwdev, phy);
@@ -6098,7 +6087,7 @@ static void _update_wl_info_v2(struct rtw89_dev *rtwdev)
phy = wl_linfo[i].phy;
- if (rtwdev->dbcc_en && phy < RTW89_PHY_MAX) {
+ if (rtwdev->dbcc_en && phy < RTW89_PHY_NUM) {
wl_dinfo->role[phy] = wl_linfo[i].role;
wl_dinfo->op_band[phy] = wl_linfo[i].band;
_update_dbcc_band(rtwdev, phy);
@@ -6389,7 +6378,7 @@ static void _update_wl_info_v7(struct rtw89_dev *rtwdev, u8 rid)
struct rtw89_btc_wl_dbcc_info *wl_dinfo = &wl->dbcc_info;
struct rtw89_btc_wl_link_info *wl_linfo = wl->link_info;
struct rtw89_btc_wl_active_role_v7 *act_role = NULL;
- u8 i, mode, cnt = 0, cnt_2g = 0, cnt_5g = 0, phy_now = RTW89_PHY_MAX, phy_dbcc;
+ u8 i, mode, cnt = 0, cnt_2g = 0, cnt_5g = 0, phy_now = RTW89_PHY_NUM, phy_dbcc;
bool b2g = false, b5g = false, client_joined = false, client_inc_2g = false;
u8 client_cnt_last[RTW89_BE_BTC_WL_MAX_ROLE_NUMBER] = {};
u8 cid_role[RTW89_BE_BTC_WL_MAX_ROLE_NUMBER] = {};
@@ -6400,7 +6389,7 @@ static void _update_wl_info_v7(struct rtw89_dev *rtwdev, u8 rid)
memset(wl_rinfo, 0, sizeof(*wl_rinfo));
for (i = 0; i < RTW89_PORT_NUM; i++) {
- if (!wl_linfo[i].active || wl_linfo[i].phy >= RTW89_PHY_MAX)
+ if (!wl_linfo[i].active || wl_linfo[i].phy >= RTW89_PHY_NUM)
continue;
act_role = &wl_rinfo->active_role[i];
@@ -6494,7 +6483,7 @@ static void _update_wl_info_v7(struct rtw89_dev *rtwdev, u8 rid)
mode = _chk_dbcc(rtwdev, cid_ch, cid_phy, cid_role, &dbcc_2g_phy);
/* correct 2G-located PHY band for gnt ctrl */
- if (dbcc_2g_phy < RTW89_PHY_MAX)
+ if (dbcc_2g_phy < RTW89_PHY_NUM)
wl_dinfo->op_band[dbcc_2g_phy] = RTW89_BAND_2G;
} else if (b2g && b5g && cnt == 2) {
mode = BTC_WLINK_25G_MCC;
@@ -6720,7 +6709,7 @@ static void _update_wl_info_v8(struct rtw89_dev *rtwdev, u8 role_id, u8 rlink_id
_fw_set_drv_info(rtwdev, CXDRVINFO_ROLE);
}
-void rtw89_coex_act1_work(struct work_struct *work)
+void rtw89_coex_act1_work(struct wiphy *wiphy, struct wiphy_work *work)
{
struct rtw89_dev *rtwdev = container_of(work, struct rtw89_dev,
coex_act1_work.work);
@@ -6729,7 +6718,8 @@ void rtw89_coex_act1_work(struct work_struct *work)
struct rtw89_btc_cx *cx = &btc->cx;
struct rtw89_btc_wl_info *wl = &cx->wl;
- mutex_lock(&rtwdev->mutex);
+ lockdep_assert_wiphy(wiphy);
+
rtw89_debug(rtwdev, RTW89_DBG_BTC, "[BTC], %s(): enter\n", __func__);
dm->cnt_notify[BTC_NCNT_TIMER]++;
if (wl->status.map._4way)
@@ -6738,10 +6728,9 @@ void rtw89_coex_act1_work(struct work_struct *work)
wl->status.map.connecting = false;
_run_coex(rtwdev, BTC_RSN_ACT1_WORK);
- mutex_unlock(&rtwdev->mutex);
}
-void rtw89_coex_bt_devinfo_work(struct work_struct *work)
+void rtw89_coex_bt_devinfo_work(struct wiphy *wiphy, struct wiphy_work *work)
{
struct rtw89_dev *rtwdev = container_of(work, struct rtw89_dev,
coex_bt_devinfo_work.work);
@@ -6749,15 +6738,15 @@ void rtw89_coex_bt_devinfo_work(struct work_struct *work)
struct rtw89_btc_dm *dm = &rtwdev->btc.dm;
struct rtw89_btc_bt_a2dp_desc *a2dp = &btc->cx.bt.link_info.a2dp_desc;
- mutex_lock(&rtwdev->mutex);
+ lockdep_assert_wiphy(wiphy);
+
rtw89_debug(rtwdev, RTW89_DBG_BTC, "[BTC], %s(): enter\n", __func__);
dm->cnt_notify[BTC_NCNT_TIMER]++;
a2dp->play_latency = 0;
_run_coex(rtwdev, BTC_RSN_BT_DEVINFO_WORK);
- mutex_unlock(&rtwdev->mutex);
}
-void rtw89_coex_rfk_chk_work(struct work_struct *work)
+void rtw89_coex_rfk_chk_work(struct wiphy *wiphy, struct wiphy_work *work)
{
struct rtw89_dev *rtwdev = container_of(work, struct rtw89_dev,
coex_rfk_chk_work.work);
@@ -6766,7 +6755,8 @@ void rtw89_coex_rfk_chk_work(struct work_struct *work)
struct rtw89_btc_cx *cx = &btc->cx;
struct rtw89_btc_wl_info *wl = &cx->wl;
- mutex_lock(&rtwdev->mutex);
+ lockdep_assert_wiphy(wiphy);
+
rtw89_debug(rtwdev, RTW89_DBG_BTC, "[BTC], %s(): enter\n", __func__);
dm->cnt_notify[BTC_NCNT_TIMER]++;
if (wl->rfk_info.state != BTC_WRFK_STOP) {
@@ -6778,7 +6768,6 @@ void rtw89_coex_rfk_chk_work(struct work_struct *work)
_write_scbd(rtwdev, BTC_WSCB_WLRFK, false);
_run_coex(rtwdev, BTC_RSN_RFK_CHK_WORK);
}
- mutex_unlock(&rtwdev->mutex);
}
static void _update_bt_scbd(struct rtw89_dev *rtwdev, bool only_update)
@@ -6882,7 +6871,7 @@ void _run_coex(struct rtw89_dev *rtwdev, enum btc_reason_and_action reason)
struct rtw89_btc_wl_role_info_v8 *wl_rinfo_v8 = &wl->role_info_v8;
u8 mode, igno_bt, always_freerun;
- lockdep_assert_held(&rtwdev->mutex);
+ lockdep_assert_wiphy(rtwdev->hw->wiphy);
dm->run_reason = reason;
_update_dm_step(rtwdev, reason);
@@ -7002,7 +6991,7 @@ void _run_coex(struct rtw89_dev *rtwdev, enum btc_reason_and_action reason)
goto exit;
}
- if (wl->status.val & btc_scanning_map.val) {
+ if (wl->status.val & btc_scanning_map.val && !wl->rfk_info.con_rfk) {
_action_wl_scan(rtwdev);
bt->scan_rx_low_pri = true;
goto exit;
@@ -7187,7 +7176,7 @@ void rtw89_btc_ntfy_scan_start(struct rtw89_dev *rtwdev, u8 phy_idx, u8 band)
"[BTC], %s(): phy_idx=%d, band=%d\n",
__func__, phy_idx, band);
- if (phy_idx >= RTW89_PHY_MAX)
+ if (phy_idx >= RTW89_PHY_NUM)
return;
btc->dm.cnt_notify[BTC_NCNT_SCAN_START]++;
@@ -7223,6 +7212,8 @@ void rtw89_btc_ntfy_scan_finish(struct rtw89_dev *rtwdev, u8 phy_idx)
_fw_set_drv_info(rtwdev, CXDRVINFO_DBCC);
}
+ btc->dm.tdma_instant_excute = 1;
+
_run_coex(rtwdev, BTC_RSN_NTFY_SCAN_FINISH);
}
@@ -7235,7 +7226,7 @@ void rtw89_btc_ntfy_switch_band(struct rtw89_dev *rtwdev, u8 phy_idx, u8 band)
"[BTC], %s(): phy_idx=%d, band=%d\n",
__func__, phy_idx, band);
- if (phy_idx >= RTW89_PHY_MAX)
+ if (phy_idx >= RTW89_PHY_NUM)
return;
btc->dm.cnt_notify[BTC_NCNT_SWITCH_BAND]++;
@@ -7284,7 +7275,7 @@ void rtw89_btc_ntfy_specific_packet(struct rtw89_dev *rtwdev,
"[BTC], %s(): EAPOL_End cnt=%d\n",
__func__, cnt);
wl->status.map._4way = false;
- cancel_delayed_work(&rtwdev->coex_act1_work);
+ wiphy_delayed_work_cancel(rtwdev->hw->wiphy, &rtwdev->coex_act1_work);
break;
case PACKET_ARP:
cnt = ++cx->cnt_wl[BTC_WCNT_ARP];
@@ -7303,56 +7294,56 @@ void rtw89_btc_ntfy_specific_packet(struct rtw89_dev *rtwdev,
}
if (delay_work) {
- cancel_delayed_work(&rtwdev->coex_act1_work);
- ieee80211_queue_delayed_work(rtwdev->hw,
- &rtwdev->coex_act1_work, delay);
+ wiphy_delayed_work_cancel(rtwdev->hw->wiphy, &rtwdev->coex_act1_work);
+ wiphy_delayed_work_queue(rtwdev->hw->wiphy,
+ &rtwdev->coex_act1_work, delay);
}
btc->dm.cnt_notify[BTC_NCNT_SPECIAL_PACKET]++;
_run_coex(rtwdev, BTC_RSN_NTFY_SPECIFIC_PACKET);
}
-void rtw89_btc_ntfy_eapol_packet_work(struct work_struct *work)
+void rtw89_btc_ntfy_eapol_packet_work(struct wiphy *wiphy, struct wiphy_work *work)
{
struct rtw89_dev *rtwdev = container_of(work, struct rtw89_dev,
btc.eapol_notify_work);
- mutex_lock(&rtwdev->mutex);
+ lockdep_assert_wiphy(wiphy);
+
rtw89_leave_ps_mode(rtwdev);
rtw89_btc_ntfy_specific_packet(rtwdev, PACKET_EAPOL);
- mutex_unlock(&rtwdev->mutex);
}
-void rtw89_btc_ntfy_arp_packet_work(struct work_struct *work)
+void rtw89_btc_ntfy_arp_packet_work(struct wiphy *wiphy, struct wiphy_work *work)
{
struct rtw89_dev *rtwdev = container_of(work, struct rtw89_dev,
btc.arp_notify_work);
- mutex_lock(&rtwdev->mutex);
+ lockdep_assert_wiphy(wiphy);
+
rtw89_btc_ntfy_specific_packet(rtwdev, PACKET_ARP);
- mutex_unlock(&rtwdev->mutex);
}
-void rtw89_btc_ntfy_dhcp_packet_work(struct work_struct *work)
+void rtw89_btc_ntfy_dhcp_packet_work(struct wiphy *wiphy, struct wiphy_work *work)
{
struct rtw89_dev *rtwdev = container_of(work, struct rtw89_dev,
btc.dhcp_notify_work);
- mutex_lock(&rtwdev->mutex);
+ lockdep_assert_wiphy(wiphy);
+
rtw89_leave_ps_mode(rtwdev);
rtw89_btc_ntfy_specific_packet(rtwdev, PACKET_DHCP);
- mutex_unlock(&rtwdev->mutex);
}
-void rtw89_btc_ntfy_icmp_packet_work(struct work_struct *work)
+void rtw89_btc_ntfy_icmp_packet_work(struct wiphy *wiphy, struct wiphy_work *work)
{
struct rtw89_dev *rtwdev = container_of(work, struct rtw89_dev,
btc.icmp_notify_work);
- mutex_lock(&rtwdev->mutex);
+ lockdep_assert_wiphy(wiphy);
+
rtw89_leave_ps_mode(rtwdev);
rtw89_btc_ntfy_specific_packet(rtwdev, PACKET_ICMP);
- mutex_unlock(&rtwdev->mutex);
}
static u8 _update_bt_rssi_level(struct rtw89_dev *rtwdev, u8 rssi)
@@ -7531,9 +7522,9 @@ static void _update_bt_info(struct rtw89_dev *rtwdev, u8 *buf, u32 len)
a2dp->vendor_id = 0;
a2dp->flush_time = 0;
a2dp->play_latency = 1;
- ieee80211_queue_delayed_work(rtwdev->hw,
- &rtwdev->coex_bt_devinfo_work,
- RTW89_COEX_BT_DEVINFO_WORK_PERIOD);
+ wiphy_delayed_work_queue(rtwdev->hw->wiphy,
+ &rtwdev->coex_bt_devinfo_work,
+ RTW89_COEX_BT_DEVINFO_WORK_PERIOD);
}
_run_coex(rtwdev, BTC_RSN_UPDATE_BT_INFO);
@@ -7671,7 +7662,8 @@ void rtw89_btc_ntfy_role_info(struct rtw89_dev *rtwdev,
else
wl->status.map.connecting = 0;
- if (state == BTC_ROLE_MSTS_STA_DIS_CONN)
+ if (state == BTC_ROLE_MSTS_STA_DIS_CONN ||
+ state == BTC_ROLE_MSTS_STA_CONN_END)
wl->status.map._4way = false;
_run_coex(rtwdev, BTC_RSN_NTFY_ROLE_INFO);
@@ -7781,7 +7773,7 @@ static bool _ntfy_wl_rfk(struct rtw89_dev *rtwdev, u8 phy_path,
wl->rfk_info.state = BTC_WRFK_STOP;
_write_scbd(rtwdev, BTC_WSCB_WLRFK, false);
- cancel_delayed_work(&rtwdev->coex_rfk_chk_work);
+ wiphy_delayed_work_cancel(rtwdev->hw->wiphy, &rtwdev->coex_rfk_chk_work);
break;
default:
rtw89_debug(rtwdev, RTW89_DBG_BTC,
@@ -7795,9 +7787,9 @@ static bool _ntfy_wl_rfk(struct rtw89_dev *rtwdev, u8 phy_path,
_run_coex(rtwdev, BTC_RSN_NTFY_WL_RFK);
if (wl->rfk_info.state == BTC_WRFK_START)
- ieee80211_queue_delayed_work(rtwdev->hw,
- &rtwdev->coex_rfk_chk_work,
- RTW89_COEX_RFK_CHK_WORK_PERIOD);
+ wiphy_delayed_work_queue(rtwdev->hw->wiphy,
+ &rtwdev->coex_rfk_chk_work,
+ RTW89_COEX_RFK_CHK_WORK_PERIOD);
}
rtw89_debug(rtwdev, RTW89_DBG_BTC,
@@ -7815,6 +7807,8 @@ void rtw89_btc_ntfy_wl_rfk(struct rtw89_dev *rtwdev, u8 phy_map,
bool allow;
int ret;
+ lockdep_assert_wiphy(rtwdev->hw->wiphy);
+
band = FIELD_GET(BTC_RFK_BAND_MAP, phy_map);
rtw89_debug(rtwdev, RTW89_DBG_RFK,
@@ -8115,6 +8109,7 @@ void rtw89_btc_c2h_handle(struct rtw89_dev *rtwdev, struct sk_buff *skb,
return;
func = rtw89_btc_c2h_get_index_by_ver(rtwdev, func);
+ pfwinfo->cnt_c2h++;
switch (func) {
case BTF_EVNT_BUF_OVERFLOW:
@@ -8156,7 +8151,7 @@ void rtw89_btc_c2h_handle(struct rtw89_dev *rtwdev, struct sk_buff *skb,
#define BTC_CX_FW_OFFLOAD 0
-static void _show_cx_info(struct rtw89_dev *rtwdev, struct seq_file *m)
+static int _show_cx_info(struct rtw89_dev *rtwdev, char *buf, size_t bufsz)
{
union rtw89_btc_module_info *md = &rtwdev->btc.mdinfo;
const struct rtw89_chip_info *chip = rtwdev->chip;
@@ -8168,40 +8163,43 @@ static void _show_cx_info(struct rtw89_dev *rtwdev, struct seq_file *m)
struct rtw89_btc_wl_info *wl = &btc->cx.wl;
u32 ver_main = 0, ver_sub = 0, ver_hotfix = 0, id_branch = 0;
u8 cv, rfe, iso, ant_num, ant_single_pos;
+ char *p = buf, *end = buf + bufsz;
if (!(dm->coex_info_map & BTC_COEX_INFO_CX))
- return;
+ return 0;
dm->cnt_notify[BTC_NCNT_SHOW_COEX_INFO]++;
- seq_printf(m, "========== [BTC COEX INFO (%d)] ==========\n",
- chip->chip_id);
+ p += scnprintf(p, end - p,
+ "========== [BTC COEX INFO (%d)] ==========\n",
+ chip->chip_id);
ver_main = FIELD_GET(GENMASK(31, 24), RTW89_COEX_VERSION);
ver_sub = FIELD_GET(GENMASK(23, 16), RTW89_COEX_VERSION);
ver_hotfix = FIELD_GET(GENMASK(15, 8), RTW89_COEX_VERSION);
id_branch = FIELD_GET(GENMASK(7, 0), RTW89_COEX_VERSION);
- seq_printf(m, " %-15s : Coex:%d.%d.%d(branch:%d), ",
- "[coex_version]", ver_main, ver_sub, ver_hotfix, id_branch);
+ p += scnprintf(p, end - p, " %-15s : Coex:%d.%d.%d(branch:%d), ",
+ "[coex_version]", ver_main, ver_sub, ver_hotfix,
+ id_branch);
ver_main = FIELD_GET(GENMASK(31, 24), wl->ver_info.fw_coex);
ver_sub = FIELD_GET(GENMASK(23, 16), wl->ver_info.fw_coex);
ver_hotfix = FIELD_GET(GENMASK(15, 8), wl->ver_info.fw_coex);
id_branch = FIELD_GET(GENMASK(7, 0), wl->ver_info.fw_coex);
- seq_printf(m, "WL_FW_coex:%d.%d.%d(branch:%d)",
- ver_main, ver_sub, ver_hotfix, id_branch);
+ p += scnprintf(p, end - p, "WL_FW_coex:%d.%d.%d(branch:%d)",
+ ver_main, ver_sub, ver_hotfix, id_branch);
ver_main = FIELD_GET(GENMASK(31, 24), chip->wlcx_desired);
ver_sub = FIELD_GET(GENMASK(23, 16), chip->wlcx_desired);
ver_hotfix = FIELD_GET(GENMASK(15, 8), chip->wlcx_desired);
- seq_printf(m, "(%s, desired:%d.%d.%d), ",
- (wl->ver_info.fw_coex >= chip->wlcx_desired ?
- "Match" : "Mismatch"), ver_main, ver_sub, ver_hotfix);
+ p += scnprintf(p, end - p, "(%s, desired:%d.%d.%d), ",
+ (wl->ver_info.fw_coex >= chip->wlcx_desired ?
+ "Match" : "Mismatch"), ver_main, ver_sub, ver_hotfix);
- seq_printf(m, "BT_FW_coex:%d(%s, desired:%d)\n",
- bt->ver_info.fw_coex,
- (bt->ver_info.fw_coex >= chip->btcx_desired ?
- "Match" : "Mismatch"), chip->btcx_desired);
+ p += scnprintf(p, end - p, "BT_FW_coex:%d(%s, desired:%d)\n",
+ bt->ver_info.fw_coex,
+ (bt->ver_info.fw_coex >= chip->btcx_desired ?
+ "Match" : "Mismatch"), chip->btcx_desired);
if (bt->enable.now && bt->ver_info.fw == 0)
rtw89_btc_fw_en_rpt(rtwdev, RPT_EN_BT_VER_INFO, true);
@@ -8212,10 +8210,11 @@ static void _show_cx_info(struct rtw89_dev *rtwdev, struct seq_file *m)
ver_sub = FIELD_GET(GENMASK(23, 16), wl->ver_info.fw);
ver_hotfix = FIELD_GET(GENMASK(15, 8), wl->ver_info.fw);
id_branch = FIELD_GET(GENMASK(7, 0), wl->ver_info.fw);
- seq_printf(m, " %-15s : WL_FW:%d.%d.%d.%d, BT_FW:0x%x(%s)\n",
- "[sub_module]",
- ver_main, ver_sub, ver_hotfix, id_branch,
- bt->ver_info.fw, bt->run_patch_code ? "patch" : "ROM");
+ p += scnprintf(p, end - p,
+ " %-15s : WL_FW:%d.%d.%d.%d, BT_FW:0x%x(%s)\n",
+ "[sub_module]",
+ ver_main, ver_sub, ver_hotfix, id_branch,
+ bt->ver_info.fw, bt->run_patch_code ? "patch" : "ROM");
if (ver->fcxinit == 7) {
cv = md->md_v7.kt_ver;
@@ -8231,36 +8230,41 @@ static void _show_cx_info(struct rtw89_dev *rtwdev, struct seq_file *m)
ant_single_pos = md->md.ant.single_pos;
}
- seq_printf(m, " %-15s : cv:%x, rfe_type:0x%x, ant_iso:%d, ant_pg:%d, %s",
- "[hw_info]", cv, rfe, iso, ant_num,
- ant_num > 1 ? "" :
- ant_single_pos ? "1Ant_Pos:S1, " : "1Ant_Pos:S0, ");
+ p += scnprintf(p, end - p,
+ " %-15s : cv:%x, rfe_type:0x%x, ant_iso:%d, ant_pg:%d, %s",
+ "[hw_info]", cv, rfe, iso, ant_num,
+ ant_num > 1 ? "" :
+ ant_single_pos ? "1Ant_Pos:S1, " : "1Ant_Pos:S0, ");
- seq_printf(m, "3rd_coex:%d, dbcc:%d, tx_num:%d, rx_num:%d\n",
- btc->cx.other.type, rtwdev->dbcc_en, hal->tx_nss,
- hal->rx_nss);
+ p += scnprintf(p, end - p,
+ "3rd_coex:%d, dbcc:%d, tx_num:%d, rx_num:%d\n",
+ btc->cx.other.type, rtwdev->dbcc_en, hal->tx_nss,
+ hal->rx_nss);
+
+ return p - buf;
}
-static void _show_wl_role_info(struct rtw89_dev *rtwdev, struct seq_file *m)
+static int _show_wl_role_info(struct rtw89_dev *rtwdev, char *buf, size_t bufsz)
{
struct rtw89_btc *btc = &rtwdev->btc;
struct rtw89_btc_wl_link_info *plink = NULL;
struct rtw89_btc_wl_info *wl = &btc->cx.wl;
struct rtw89_btc_wl_dbcc_info *wl_dinfo = &wl->dbcc_info;
struct rtw89_traffic_stats *t;
+ char *p = buf, *end = buf + bufsz;
u8 i;
if (rtwdev->dbcc_en) {
- seq_printf(m,
- " %-15s : PHY0_band(op:%d/scan:%d/real:%d), ",
- "[dbcc_info]", wl_dinfo->op_band[RTW89_PHY_0],
- wl_dinfo->scan_band[RTW89_PHY_0],
- wl_dinfo->real_band[RTW89_PHY_0]);
- seq_printf(m,
- "PHY1_band(op:%d/scan:%d/real:%d)\n",
- wl_dinfo->op_band[RTW89_PHY_1],
- wl_dinfo->scan_band[RTW89_PHY_1],
- wl_dinfo->real_band[RTW89_PHY_1]);
+ p += scnprintf(p, end - p,
+ " %-15s : PHY0_band(op:%d/scan:%d/real:%d), ",
+ "[dbcc_info]", wl_dinfo->op_band[RTW89_PHY_0],
+ wl_dinfo->scan_band[RTW89_PHY_0],
+ wl_dinfo->real_band[RTW89_PHY_0]);
+ p += scnprintf(p, end - p,
+ "PHY1_band(op:%d/scan:%d/real:%d)\n",
+ wl_dinfo->op_band[RTW89_PHY_1],
+ wl_dinfo->scan_band[RTW89_PHY_1],
+ wl_dinfo->real_band[RTW89_PHY_1]);
}
for (i = 0; i < RTW89_PORT_NUM; i++) {
@@ -8272,38 +8276,41 @@ static void _show_wl_role_info(struct rtw89_dev *rtwdev, struct seq_file *m)
if (!plink->active)
continue;
- seq_printf(m,
- " [port_%d] : role=%d(phy-%d), connect=%d(client_cnt=%d), mode=%d, center_ch=%d, bw=%d",
- plink->pid, (u32)plink->role, plink->phy,
- (u32)plink->connected, plink->client_cnt - 1,
- (u32)plink->mode, plink->ch, (u32)plink->bw);
+ p += scnprintf(p, end - p,
+ " [port_%d] : role=%d(phy-%d), connect=%d(client_cnt=%d), mode=%d, center_ch=%d, bw=%d",
+ plink->pid, (u32)plink->role, plink->phy,
+ (u32)plink->connected, plink->client_cnt - 1,
+ (u32)plink->mode, plink->ch, (u32)plink->bw);
if (plink->connected == MLME_NO_LINK)
continue;
- seq_printf(m,
- ", mac_id=%d, max_tx_time=%dus, max_tx_retry=%d\n",
- plink->mac_id, plink->tx_time, plink->tx_retry);
+ p += scnprintf(p, end - p,
+ ", mac_id=%d, max_tx_time=%dus, max_tx_retry=%d\n",
+ plink->mac_id, plink->tx_time, plink->tx_retry);
- seq_printf(m,
- " [port_%d] : rssi=-%ddBm(%d), busy=%d, dir=%s, ",
- plink->pid, 110 - plink->stat.rssi,
- plink->stat.rssi, plink->busy,
- plink->dir == RTW89_TFC_UL ? "UL" : "DL");
+ p += scnprintf(p, end - p,
+ " [port_%d] : rssi=-%ddBm(%d), busy=%d, dir=%s, ",
+ plink->pid, 110 - plink->stat.rssi,
+ plink->stat.rssi, plink->busy,
+ plink->dir == RTW89_TFC_UL ? "UL" : "DL");
t = &plink->stat.traffic;
- seq_printf(m,
- "tx[rate:%d/busy_level:%d], ",
- (u32)t->tx_rate, t->tx_tfc_lv);
+ p += scnprintf(p, end - p,
+ "tx[rate:%d/busy_level:%d], ",
+ (u32)t->tx_rate, t->tx_tfc_lv);
- seq_printf(m, "rx[rate:%d/busy_level:%d/drop:%d]\n",
- (u32)t->rx_rate,
- t->rx_tfc_lv, plink->rx_rate_drop_cnt);
+ p += scnprintf(p, end - p,
+ "rx[rate:%d/busy_level:%d/drop:%d]\n",
+ (u32)t->rx_rate,
+ t->rx_tfc_lv, plink->rx_rate_drop_cnt);
}
+
+ return p - buf;
}
-static void _show_wl_info(struct rtw89_dev *rtwdev, struct seq_file *m)
+static int _show_wl_info(struct rtw89_dev *rtwdev, char *buf, size_t bufsz)
{
struct rtw89_btc *btc = &rtwdev->btc;
const struct rtw89_btc_ver *ver = btc->ver;
@@ -8314,12 +8321,13 @@ static void _show_wl_info(struct rtw89_dev *rtwdev, struct seq_file *m)
struct rtw89_btc_wl_role_info_v2 *wl_rinfo_v2 = &wl->role_info_v2;
struct rtw89_btc_wl_role_info_v7 *wl_rinfo_v7 = &wl->role_info_v7;
struct rtw89_btc_wl_role_info_v8 *wl_rinfo_v8 = &wl->role_info_v8;
+ char *p = buf, *end = buf + bufsz;
u8 mode;
if (!(btc->dm.coex_info_map & BTC_COEX_INFO_WL))
- return;
+ return 0;
- seq_puts(m, "========== [WL Status] ==========\n");
+ p += scnprintf(p, end - p, "========== [WL Status] ==========\n");
if (ver->fwlrole == 0)
mode = wl_rinfo->link_mode;
@@ -8332,24 +8340,28 @@ static void _show_wl_info(struct rtw89_dev *rtwdev, struct seq_file *m)
else if (ver->fwlrole == 8)
mode = wl_rinfo_v8->link_mode;
else
- return;
+ goto out;
- seq_printf(m, " %-15s : link_mode:%d, ", "[status]", mode);
+ p += scnprintf(p, end - p, " %-15s : link_mode:%d, ", "[status]",
+ mode);
- seq_printf(m,
- "rf_off:%d, power_save:%d, scan:%s(band:%d/phy_map:0x%x), ",
- wl->status.map.rf_off, wl->status.map.lps,
- wl->status.map.scan ? "Y" : "N",
- wl->scan_info.band[RTW89_PHY_0], wl->scan_info.phy_map);
+ p += scnprintf(p, end - p,
+ "rf_off:%d, power_save:%d, scan:%s(band:%d/phy_map:0x%x), ",
+ wl->status.map.rf_off, wl->status.map.lps,
+ wl->status.map.scan ? "Y" : "N",
+ wl->scan_info.band[RTW89_PHY_0], wl->scan_info.phy_map);
- seq_printf(m,
- "connecting:%s, roam:%s, 4way:%s, init_ok:%s\n",
- wl->status.map.connecting ? "Y" : "N",
- wl->status.map.roaming ? "Y" : "N",
- wl->status.map._4way ? "Y" : "N",
- wl->status.map.init_ok ? "Y" : "N");
+ p += scnprintf(p, end - p,
+ "connecting:%s, roam:%s, 4way:%s, init_ok:%s\n",
+ wl->status.map.connecting ? "Y" : "N",
+ wl->status.map.roaming ? "Y" : "N",
+ wl->status.map._4way ? "Y" : "N",
+ wl->status.map.init_ok ? "Y" : "N");
- _show_wl_role_info(rtwdev, m);
+ p += _show_wl_role_info(rtwdev, p, end - p);
+
+out:
+ return p - buf;
}
enum btc_bt_a2dp_type {
@@ -8358,7 +8370,7 @@ enum btc_bt_a2dp_type {
BTC_A2DP_TWS_RELAY = 2,
};
-static void _show_bt_profile_info(struct rtw89_dev *rtwdev, struct seq_file *m)
+static int _show_bt_profile_info(struct rtw89_dev *rtwdev, char *buf, size_t bufsz)
{
struct rtw89_btc *btc = &rtwdev->btc;
struct rtw89_btc_bt_link_info *bt_linfo = &btc->cx.bt.link_info;
@@ -8366,50 +8378,55 @@ static void _show_bt_profile_info(struct rtw89_dev *rtwdev, struct seq_file *m)
struct rtw89_btc_bt_hid_desc hid = bt_linfo->hid_desc;
struct rtw89_btc_bt_a2dp_desc a2dp = bt_linfo->a2dp_desc;
struct rtw89_btc_bt_pan_desc pan = bt_linfo->pan_desc;
+ char *p = buf, *end = buf + bufsz;
if (hfp.exist) {
- seq_printf(m, " %-15s : type:%s, sut_pwr:%d, golden-rx:%d",
- "[HFP]", (hfp.type == 0 ? "SCO" : "eSCO"),
- bt_linfo->sut_pwr_level[0],
- bt_linfo->golden_rx_shift[0]);
+ p += scnprintf(p, end - p,
+ " %-15s : type:%s, sut_pwr:%d, golden-rx:%d",
+ "[HFP]", (hfp.type == 0 ? "SCO" : "eSCO"),
+ bt_linfo->sut_pwr_level[0],
+ bt_linfo->golden_rx_shift[0]);
}
if (hid.exist) {
- seq_printf(m,
- "\n\r %-15s : type:%s%s%s%s%s pair-cnt:%d, sut_pwr:%d, golden-rx:%d\n",
- "[HID]",
- hid.type & BTC_HID_218 ? "2/18," : "",
- hid.type & BTC_HID_418 ? "4/18," : "",
- hid.type & BTC_HID_BLE ? "BLE," : "",
- hid.type & BTC_HID_RCU ? "RCU," : "",
- hid.type & BTC_HID_RCU_VOICE ? "RCU-Voice," : "",
- hid.pair_cnt, bt_linfo->sut_pwr_level[1],
- bt_linfo->golden_rx_shift[1]);
+ p += scnprintf(p, end - p,
+ "\n\r %-15s : type:%s%s%s%s%s pair-cnt:%d, sut_pwr:%d, golden-rx:%d\n",
+ "[HID]",
+ hid.type & BTC_HID_218 ? "2/18," : "",
+ hid.type & BTC_HID_418 ? "4/18," : "",
+ hid.type & BTC_HID_BLE ? "BLE," : "",
+ hid.type & BTC_HID_RCU ? "RCU," : "",
+ hid.type & BTC_HID_RCU_VOICE ? "RCU-Voice," : "",
+ hid.pair_cnt, bt_linfo->sut_pwr_level[1],
+ bt_linfo->golden_rx_shift[1]);
}
if (a2dp.exist) {
- seq_printf(m,
- " %-15s : type:%s, bit-pool:%d, flush-time:%d, ",
- "[A2DP]",
- a2dp.type == BTC_A2DP_LEGACY ? "Legacy" : "TWS",
- a2dp.bitpool, a2dp.flush_time);
+ p += scnprintf(p, end - p,
+ " %-15s : type:%s, bit-pool:%d, flush-time:%d, ",
+ "[A2DP]",
+ a2dp.type == BTC_A2DP_LEGACY ? "Legacy" : "TWS",
+ a2dp.bitpool, a2dp.flush_time);
- seq_printf(m,
- "vid:0x%x, Dev-name:0x%x, sut_pwr:%d, golden-rx:%d\n",
- a2dp.vendor_id, a2dp.device_name,
- bt_linfo->sut_pwr_level[2],
- bt_linfo->golden_rx_shift[2]);
+ p += scnprintf(p, end - p,
+ "vid:0x%x, Dev-name:0x%x, sut_pwr:%d, golden-rx:%d\n",
+ a2dp.vendor_id, a2dp.device_name,
+ bt_linfo->sut_pwr_level[2],
+ bt_linfo->golden_rx_shift[2]);
}
if (pan.exist) {
- seq_printf(m, " %-15s : sut_pwr:%d, golden-rx:%d\n",
- "[PAN]",
- bt_linfo->sut_pwr_level[3],
- bt_linfo->golden_rx_shift[3]);
+ p += scnprintf(p, end - p,
+ " %-15s : sut_pwr:%d, golden-rx:%d\n",
+ "[PAN]",
+ bt_linfo->sut_pwr_level[3],
+ bt_linfo->golden_rx_shift[3]);
}
+
+ return p - buf;
}
-static void _show_bt_info(struct rtw89_dev *rtwdev, struct seq_file *m)
+static int _show_bt_info(struct rtw89_dev *rtwdev, char *buf, size_t bufsz)
{
struct rtw89_btc *btc = &rtwdev->btc;
const struct rtw89_btc_ver *ver = btc->ver;
@@ -8418,129 +8435,136 @@ static void _show_bt_info(struct rtw89_dev *rtwdev, struct seq_file *m)
struct rtw89_btc_wl_info *wl = &cx->wl;
struct rtw89_btc_bt_link_info *bt_linfo = &bt->link_info;
union rtw89_btc_module_info *md = &btc->mdinfo;
+ char *p = buf, *end = buf + bufsz;
u8 *afh = bt_linfo->afh_map;
u8 *afh_le = bt_linfo->afh_map_le;
u8 bt_pos;
if (!(btc->dm.coex_info_map & BTC_COEX_INFO_BT))
- return;
+ return 0;
if (ver->fcxinit == 7)
bt_pos = md->md_v7.bt_pos;
else
bt_pos = md->md.bt_pos;
- seq_puts(m, "========== [BT Status] ==========\n");
-
- seq_printf(m, " %-15s : enable:%s, btg:%s%s, connect:%s, ",
- "[status]", bt->enable.now ? "Y" : "N",
- bt->btg_type ? "Y" : "N",
- (bt->enable.now && (bt->btg_type != bt_pos) ?
- "(efuse-mismatch!!)" : ""),
- (bt_linfo->status.map.connect ? "Y" : "N"));
-
- seq_printf(m, "igno_wl:%s, mailbox_avl:%s, rfk_state:0x%x\n",
- bt->igno_wl ? "Y" : "N",
- bt->mbx_avl ? "Y" : "N", bt->rfk_info.val);
-
- seq_printf(m, " %-15s : profile:%s%s%s%s%s ",
- "[profile]",
- (bt_linfo->profile_cnt.now == 0) ? "None," : "",
- bt_linfo->hfp_desc.exist ? "HFP," : "",
- bt_linfo->hid_desc.exist ? "HID," : "",
- bt_linfo->a2dp_desc.exist ?
- (bt_linfo->a2dp_desc.sink ? "A2DP_sink," : "A2DP,") : "",
- bt_linfo->pan_desc.exist ? "PAN," : "");
-
- seq_printf(m,
- "multi-link:%s, role:%s, ble-connect:%s, CQDDR:%s, A2DP_active:%s, PAN_active:%s\n",
- bt_linfo->multi_link.now ? "Y" : "N",
- bt_linfo->slave_role ? "Slave" : "Master",
- bt_linfo->status.map.ble_connect ? "Y" : "N",
- bt_linfo->cqddr ? "Y" : "N",
- bt_linfo->a2dp_desc.active ? "Y" : "N",
- bt_linfo->pan_desc.active ? "Y" : "N");
-
- seq_printf(m,
- " %-15s : rssi:%ddBm(lvl:%d), tx_rate:%dM, %s%s%s",
- "[link]", bt_linfo->rssi - 100,
- bt->rssi_level,
- bt_linfo->tx_3m ? 3 : 2,
- bt_linfo->status.map.inq_pag ? " inq-page!!" : "",
- bt_linfo->status.map.acl_busy ? " acl_busy!!" : "",
- bt_linfo->status.map.mesh_busy ? " mesh_busy!!" : "");
-
- seq_printf(m,
- "%s afh_map[%02x%02x_%02x%02x_%02x%02x_%02x%02x_%02x%02x], ",
- bt_linfo->relink.now ? " ReLink!!" : "",
- afh[0], afh[1], afh[2], afh[3], afh[4],
- afh[5], afh[6], afh[7], afh[8], afh[9]);
+ p += scnprintf(p, end - p, "========== [BT Status] ==========\n");
+
+ p += scnprintf(p, end - p,
+ " %-15s : enable:%s, btg:%s%s, connect:%s, ",
+ "[status]", bt->enable.now ? "Y" : "N",
+ bt->btg_type ? "Y" : "N",
+ (bt->enable.now && (bt->btg_type != bt_pos) ?
+ "(efuse-mismatch!!)" : ""),
+ (bt_linfo->status.map.connect ? "Y" : "N"));
+
+ p += scnprintf(p, end - p,
+ "igno_wl:%s, mailbox_avl:%s, rfk_state:0x%x\n",
+ bt->igno_wl ? "Y" : "N",
+ bt->mbx_avl ? "Y" : "N", bt->rfk_info.val);
+
+ p += scnprintf(p, end - p, " %-15s : profile:%s%s%s%s%s ",
+ "[profile]",
+ (bt_linfo->profile_cnt.now == 0) ? "None," : "",
+ bt_linfo->hfp_desc.exist ? "HFP," : "",
+ bt_linfo->hid_desc.exist ? "HID," : "",
+ bt_linfo->a2dp_desc.exist ?
+ (bt_linfo->a2dp_desc.sink ? "A2DP_sink," : "A2DP,") : "",
+ bt_linfo->pan_desc.exist ? "PAN," : "");
+
+ p += scnprintf(p, end - p,
+ "multi-link:%s, role:%s, ble-connect:%s, CQDDR:%s, A2DP_active:%s, PAN_active:%s\n",
+ bt_linfo->multi_link.now ? "Y" : "N",
+ bt_linfo->slave_role ? "Slave" : "Master",
+ bt_linfo->status.map.ble_connect ? "Y" : "N",
+ bt_linfo->cqddr ? "Y" : "N",
+ bt_linfo->a2dp_desc.active ? "Y" : "N",
+ bt_linfo->pan_desc.active ? "Y" : "N");
+
+ p += scnprintf(p, end - p,
+ " %-15s : rssi:%ddBm(lvl:%d), tx_rate:%dM, %s%s%s",
+ "[link]", bt_linfo->rssi - 100,
+ bt->rssi_level,
+ bt_linfo->tx_3m ? 3 : 2,
+ bt_linfo->status.map.inq_pag ? " inq-page!!" : "",
+ bt_linfo->status.map.acl_busy ? " acl_busy!!" : "",
+ bt_linfo->status.map.mesh_busy ? " mesh_busy!!" : "");
+
+ p += scnprintf(p, end - p,
+ "%s afh_map[%02x%02x_%02x%02x_%02x%02x_%02x%02x_%02x%02x], ",
+ bt_linfo->relink.now ? " ReLink!!" : "",
+ afh[0], afh[1], afh[2], afh[3], afh[4],
+ afh[5], afh[6], afh[7], afh[8], afh[9]);
if (ver->fcxbtafh == 2 && bt_linfo->status.map.ble_connect)
- seq_printf(m,
- "LE[%02x%02x_%02x_%02x%02x]",
- afh_le[0], afh_le[1], afh_le[2],
- afh_le[3], afh_le[4]);
-
- seq_printf(m, "wl_ch_map[en:%d/ch:%d/bw:%d]\n",
- wl->afh_info.en, wl->afh_info.ch, wl->afh_info.bw);
-
- seq_printf(m,
- " %-15s : retry:%d, relink:%d, rate_chg:%d, reinit:%d, reenable:%d, ",
- "[stat_cnt]", cx->cnt_bt[BTC_BCNT_RETRY],
- cx->cnt_bt[BTC_BCNT_RELINK], cx->cnt_bt[BTC_BCNT_RATECHG],
- cx->cnt_bt[BTC_BCNT_REINIT], cx->cnt_bt[BTC_BCNT_REENABLE]);
-
- seq_printf(m,
- "role-switch:%d, afh:%d, inq_page:%d(inq:%d/page:%d), igno_wl:%d\n",
- cx->cnt_bt[BTC_BCNT_ROLESW], cx->cnt_bt[BTC_BCNT_AFH],
- cx->cnt_bt[BTC_BCNT_INQPAG], cx->cnt_bt[BTC_BCNT_INQ],
- cx->cnt_bt[BTC_BCNT_PAGE], cx->cnt_bt[BTC_BCNT_IGNOWL]);
-
- _show_bt_profile_info(rtwdev, m);
-
- seq_printf(m,
- " %-15s : raw_data[%02x %02x %02x %02x %02x %02x] (type:%s/cnt:%d/same:%d)\n",
- "[bt_info]", bt->raw_info[2], bt->raw_info[3],
- bt->raw_info[4], bt->raw_info[5], bt->raw_info[6],
- bt->raw_info[7],
- bt->raw_info[0] == BTC_BTINFO_AUTO ? "auto" : "reply",
- cx->cnt_bt[BTC_BCNT_INFOUPDATE],
- cx->cnt_bt[BTC_BCNT_INFOSAME]);
-
- seq_printf(m,
- " %-15s : Hi-rx = %d, Hi-tx = %d, Lo-rx = %d, Lo-tx = %d (bt_polut_wl_tx = %d)",
- "[trx_req_cnt]", cx->cnt_bt[BTC_BCNT_HIPRI_RX],
- cx->cnt_bt[BTC_BCNT_HIPRI_TX], cx->cnt_bt[BTC_BCNT_LOPRI_RX],
- cx->cnt_bt[BTC_BCNT_LOPRI_TX], cx->cnt_bt[BTC_BCNT_POLUT]);
+ p += scnprintf(p, end - p,
+ "LE[%02x%02x_%02x_%02x%02x]",
+ afh_le[0], afh_le[1], afh_le[2],
+ afh_le[3], afh_le[4]);
+
+ p += scnprintf(p, end - p, "wl_ch_map[en:%d/ch:%d/bw:%d]\n",
+ wl->afh_info.en, wl->afh_info.ch, wl->afh_info.bw);
+
+ p += scnprintf(p, end - p,
+ " %-15s : retry:%d, relink:%d, rate_chg:%d, reinit:%d, reenable:%d, ",
+ "[stat_cnt]", cx->cnt_bt[BTC_BCNT_RETRY],
+ cx->cnt_bt[BTC_BCNT_RELINK],
+ cx->cnt_bt[BTC_BCNT_RATECHG],
+ cx->cnt_bt[BTC_BCNT_REINIT],
+ cx->cnt_bt[BTC_BCNT_REENABLE]);
+
+ p += scnprintf(p, end - p,
+ "role-switch:%d, afh:%d, inq_page:%d(inq:%d/page:%d), igno_wl:%d\n",
+ cx->cnt_bt[BTC_BCNT_ROLESW], cx->cnt_bt[BTC_BCNT_AFH],
+ cx->cnt_bt[BTC_BCNT_INQPAG], cx->cnt_bt[BTC_BCNT_INQ],
+ cx->cnt_bt[BTC_BCNT_PAGE], cx->cnt_bt[BTC_BCNT_IGNOWL]);
+
+ p += _show_bt_profile_info(rtwdev, p, end - p);
+
+ p += scnprintf(p, end - p,
+ " %-15s : raw_data[%02x %02x %02x %02x %02x %02x] (type:%s/cnt:%d/same:%d)\n",
+ "[bt_info]", bt->raw_info[2], bt->raw_info[3],
+ bt->raw_info[4], bt->raw_info[5], bt->raw_info[6],
+ bt->raw_info[7],
+ bt->raw_info[0] == BTC_BTINFO_AUTO ? "auto" : "reply",
+ cx->cnt_bt[BTC_BCNT_INFOUPDATE],
+ cx->cnt_bt[BTC_BCNT_INFOSAME]);
+
+ p += scnprintf(p, end - p,
+ " %-15s : Hi-rx = %d, Hi-tx = %d, Lo-rx = %d, Lo-tx = %d (bt_polut_wl_tx = %d)",
+ "[trx_req_cnt]", cx->cnt_bt[BTC_BCNT_HIPRI_RX],
+ cx->cnt_bt[BTC_BCNT_HIPRI_TX],
+ cx->cnt_bt[BTC_BCNT_LOPRI_RX],
+ cx->cnt_bt[BTC_BCNT_LOPRI_TX],
+ cx->cnt_bt[BTC_BCNT_POLUT]);
if (!bt->scan_info_update) {
rtw89_btc_fw_en_rpt(rtwdev, RPT_EN_BT_SCAN_INFO, true);
- seq_puts(m, "\n");
+ p += scnprintf(p, end - p, "\n");
} else {
rtw89_btc_fw_en_rpt(rtwdev, RPT_EN_BT_SCAN_INFO, false);
if (ver->fcxbtscan == 1) {
- seq_printf(m,
- "(INQ:%d-%d/PAGE:%d-%d/LE:%d-%d/INIT:%d-%d)",
- le16_to_cpu(bt->scan_info_v1[BTC_SCAN_INQ].win),
- le16_to_cpu(bt->scan_info_v1[BTC_SCAN_INQ].intvl),
- le16_to_cpu(bt->scan_info_v1[BTC_SCAN_PAGE].win),
- le16_to_cpu(bt->scan_info_v1[BTC_SCAN_PAGE].intvl),
- le16_to_cpu(bt->scan_info_v1[BTC_SCAN_BLE].win),
- le16_to_cpu(bt->scan_info_v1[BTC_SCAN_BLE].intvl),
- le16_to_cpu(bt->scan_info_v1[BTC_SCAN_INIT].win),
- le16_to_cpu(bt->scan_info_v1[BTC_SCAN_INIT].intvl));
+ p += scnprintf(p, end - p,
+ "(INQ:%d-%d/PAGE:%d-%d/LE:%d-%d/INIT:%d-%d)",
+ le16_to_cpu(bt->scan_info_v1[BTC_SCAN_INQ].win),
+ le16_to_cpu(bt->scan_info_v1[BTC_SCAN_INQ].intvl),
+ le16_to_cpu(bt->scan_info_v1[BTC_SCAN_PAGE].win),
+ le16_to_cpu(bt->scan_info_v1[BTC_SCAN_PAGE].intvl),
+ le16_to_cpu(bt->scan_info_v1[BTC_SCAN_BLE].win),
+ le16_to_cpu(bt->scan_info_v1[BTC_SCAN_BLE].intvl),
+ le16_to_cpu(bt->scan_info_v1[BTC_SCAN_INIT].win),
+ le16_to_cpu(bt->scan_info_v1[BTC_SCAN_INIT].intvl));
} else if (ver->fcxbtscan == 2) {
- seq_printf(m,
- "(BG:%d-%d/INIT:%d-%d/LE:%d-%d)",
- le16_to_cpu(bt->scan_info_v2[CXSCAN_BG].win),
- le16_to_cpu(bt->scan_info_v2[CXSCAN_BG].intvl),
- le16_to_cpu(bt->scan_info_v2[CXSCAN_INIT].win),
- le16_to_cpu(bt->scan_info_v2[CXSCAN_INIT].intvl),
- le16_to_cpu(bt->scan_info_v2[CXSCAN_LE].win),
- le16_to_cpu(bt->scan_info_v2[CXSCAN_LE].intvl));
+ p += scnprintf(p, end - p,
+ "(BG:%d-%d/INIT:%d-%d/LE:%d-%d)",
+ le16_to_cpu(bt->scan_info_v2[CXSCAN_BG].win),
+ le16_to_cpu(bt->scan_info_v2[CXSCAN_BG].intvl),
+ le16_to_cpu(bt->scan_info_v2[CXSCAN_INIT].win),
+ le16_to_cpu(bt->scan_info_v2[CXSCAN_INIT].intvl),
+ le16_to_cpu(bt->scan_info_v2[CXSCAN_LE].win),
+ le16_to_cpu(bt->scan_info_v2[CXSCAN_LE].intvl));
}
- seq_puts(m, "\n");
+ p += scnprintf(p, end - p, "\n");
}
if (bt_linfo->profile_cnt.now || bt_linfo->status.map.ble_connect)
@@ -8560,6 +8584,8 @@ static void _show_bt_info(struct rtw89_dev *rtwdev, struct seq_file *m)
rtw89_btc_fw_en_rpt(rtwdev, RPT_EN_BT_DEVICE_INFO, true);
else
rtw89_btc_fw_en_rpt(rtwdev, RPT_EN_BT_DEVICE_INFO, false);
+
+ return p - buf;
}
#define CASE_BTC_RSN_STR(e) case BTC_RSN_ ## e: return #e
@@ -8853,114 +8879,132 @@ static const char *id_to_ant(u32 id)
}
static
-void seq_print_segment(struct seq_file *m, const char *prefix, u16 *data,
- u8 len, u8 seg_len, u8 start_idx, u8 ring_len)
+int scnprintf_segment(char *buf, size_t bufsz, const char *prefix, const u16 *data,
+ u8 len, u8 seg_len, u8 start_idx, u8 ring_len)
{
- u8 i;
+ char *p = buf, *end = buf + bufsz;
u8 cur_index;
+ u8 i;
for (i = 0; i < len ; i++) {
if ((i % seg_len) == 0)
- seq_printf(m, " %-15s : ", prefix);
+ p += scnprintf(p, end - p, " %-15s : ", prefix);
cur_index = (start_idx + i) % ring_len;
if (i % 3 == 0)
- seq_printf(m, "-> %-20s",
- steps_to_str(*(data + cur_index)));
+ p += scnprintf(p, end - p, "-> %-20s",
+ steps_to_str(*(data + cur_index)));
else if (i % 3 == 1)
- seq_printf(m, "-> %-15s",
- steps_to_str(*(data + cur_index)));
+ p += scnprintf(p, end - p, "-> %-15s",
+ steps_to_str(*(data + cur_index)));
else
- seq_printf(m, "-> %-13s",
- steps_to_str(*(data + cur_index)));
+ p += scnprintf(p, end - p, "-> %-13s",
+ steps_to_str(*(data + cur_index)));
if (i == (len - 1) || (i % seg_len) == (seg_len - 1))
- seq_puts(m, "\n");
+ p += scnprintf(p, end - p, "\n");
}
+
+ return p - buf;
}
-static void _show_dm_step(struct rtw89_dev *rtwdev, struct seq_file *m)
+static int _show_dm_step(struct rtw89_dev *rtwdev, char *buf, size_t bufsz)
{
struct rtw89_btc *btc = &rtwdev->btc;
struct rtw89_btc_dm *dm = &btc->dm;
+ char *p = buf, *end = buf + bufsz;
u8 start_idx;
u8 len;
len = dm->dm_step.step_ov ? RTW89_BTC_DM_MAXSTEP : dm->dm_step.step_pos;
start_idx = dm->dm_step.step_ov ? dm->dm_step.step_pos : 0;
- seq_print_segment(m, "[dm_steps]", dm->dm_step.step, len, 6, start_idx,
- ARRAY_SIZE(dm->dm_step.step));
+ p += scnprintf_segment(p, end - p, "[dm_steps]", dm->dm_step.step, len,
+ 6, start_idx, ARRAY_SIZE(dm->dm_step.step));
+
+ return p - buf;
}
-static void _show_dm_info(struct rtw89_dev *rtwdev, struct seq_file *m)
+static int _show_dm_info(struct rtw89_dev *rtwdev, char *buf, size_t bufsz)
{
struct rtw89_btc *btc = &rtwdev->btc;
const struct rtw89_btc_ver *ver = btc->ver;
struct rtw89_btc_dm *dm = &btc->dm;
struct rtw89_btc_wl_info *wl = &btc->cx.wl;
struct rtw89_btc_bt_info *bt = &btc->cx.bt;
+ char *p = buf, *end = buf + bufsz;
u8 igno_bt;
if (!(dm->coex_info_map & BTC_COEX_INFO_DM))
- return;
+ return 0;
- seq_printf(m, "========== [Mechanism Status %s] ==========\n",
- (btc->manual_ctrl ? "(Manual)" : "(Auto)"));
+ p += scnprintf(p, end - p,
+ "========== [Mechanism Status %s] ==========\n",
+ (btc->manual_ctrl ? "(Manual)" : "(Auto)"));
- seq_printf(m,
- " %-15s : type:%s, reason:%s(), action:%s(), ant_path:%s, init_mode:%s, run_cnt:%d\n",
- "[status]",
- btc->ant_type == BTC_ANT_SHARED ? "shared" : "dedicated",
- steps_to_str(dm->run_reason),
- steps_to_str(dm->run_action | BTC_ACT_EXT_BIT),
- id_to_ant(FIELD_GET(GENMASK(7, 0), dm->set_ant_path)),
- id_to_mode(wl->coex_mode),
- dm->cnt_dm[BTC_DCNT_RUN]);
+ p += scnprintf(p, end - p,
+ " %-15s : type:%s, reason:%s(), action:%s(), ant_path:%s, init_mode:%s, run_cnt:%d\n",
+ "[status]",
+ btc->ant_type == BTC_ANT_SHARED ? "shared" : "dedicated",
+ steps_to_str(dm->run_reason),
+ steps_to_str(dm->run_action | BTC_ACT_EXT_BIT),
+ id_to_ant(FIELD_GET(GENMASK(7, 0), dm->set_ant_path)),
+ id_to_mode(wl->coex_mode),
+ dm->cnt_dm[BTC_DCNT_RUN]);
- _show_dm_step(rtwdev, m);
+ p += _show_dm_step(rtwdev, p, end - p);
if (ver->fcxctrl == 7)
igno_bt = btc->ctrl.ctrl_v7.igno_bt;
else
igno_bt = btc->ctrl.ctrl.igno_bt;
- seq_printf(m, " %-15s : wl_only:%d, bt_only:%d, igno_bt:%d, free_run:%d, wl_ps_ctrl:%d, wl_mimo_ps:%d, ",
- "[dm_flag]", dm->wl_only, dm->bt_only, igno_bt,
- dm->freerun, btc->lps, dm->wl_mimo_ps);
+ p += scnprintf(p, end - p,
+ " %-15s : wl_only:%d, bt_only:%d, igno_bt:%d, free_run:%d, wl_ps_ctrl:%d, wl_mimo_ps:%d, ",
+ "[dm_flag]", dm->wl_only, dm->bt_only, igno_bt,
+ dm->freerun, btc->lps, dm->wl_mimo_ps);
- seq_printf(m, "leak_ap:%d, fw_offload:%s%s\n", dm->leak_ap,
- (BTC_CX_FW_OFFLOAD ? "Y" : "N"),
- (dm->wl_fw_cx_offload == BTC_CX_FW_OFFLOAD ?
- "" : "(Mismatch!!)"));
+ p += scnprintf(p, end - p, "leak_ap:%d, fw_offload:%s%s\n",
+ dm->leak_ap,
+ (BTC_CX_FW_OFFLOAD ? "Y" : "N"),
+ (dm->wl_fw_cx_offload == BTC_CX_FW_OFFLOAD ?
+ "" : "(Mismatch!!)"));
if (dm->rf_trx_para.wl_tx_power == 0xff)
- seq_printf(m,
- " %-15s : wl_rssi_lvl:%d, para_lvl:%d, wl_tx_pwr:orig, ",
- "[trx_ctrl]", wl->rssi_level, dm->trx_para_level);
+ p += scnprintf(p, end - p,
+ " %-15s : wl_rssi_lvl:%d, para_lvl:%d, wl_tx_pwr:orig, ",
+ "[trx_ctrl]", wl->rssi_level,
+ dm->trx_para_level);
else
- seq_printf(m,
- " %-15s : wl_rssi_lvl:%d, para_lvl:%d, wl_tx_pwr:%d, ",
- "[trx_ctrl]", wl->rssi_level, dm->trx_para_level,
- dm->rf_trx_para.wl_tx_power);
+ p += scnprintf(p, end - p,
+ " %-15s : wl_rssi_lvl:%d, para_lvl:%d, wl_tx_pwr:%d, ",
+ "[trx_ctrl]", wl->rssi_level,
+ dm->trx_para_level,
+ dm->rf_trx_para.wl_tx_power);
- seq_printf(m,
- "wl_rx_lvl:%d, bt_tx_pwr_dec:%d, bt_rx_lna:%d(%s-tbl), wl_btg_rx:%d\n",
- dm->rf_trx_para.wl_rx_gain, dm->rf_trx_para.bt_tx_power,
- dm->rf_trx_para.bt_rx_gain,
- (bt->hi_lna_rx ? "Hi" : "Ori"), dm->wl_btg_rx);
+ p += scnprintf(p, end - p,
+ "wl_rx_lvl:%d, bt_tx_pwr_dec:%d, bt_rx_lna:%d(%s-tbl), wl_btg_rx:%d\n",
+ dm->rf_trx_para.wl_rx_gain,
+ dm->rf_trx_para.bt_tx_power,
+ dm->rf_trx_para.bt_rx_gain,
+ (bt->hi_lna_rx ? "Hi" : "Ori"), dm->wl_btg_rx);
- seq_printf(m,
- " %-15s : wl_tx_limit[en:%d/max_t:%dus/max_retry:%d], bt_slot_reg:%d-TU, bt_scan_rx_low_pri:%d\n",
- "[dm_ctrl]", dm->wl_tx_limit.enable, dm->wl_tx_limit.tx_time,
- dm->wl_tx_limit.tx_retry, btc->bt_req_len, bt->scan_rx_low_pri);
+ p += scnprintf(p, end - p,
+ " %-15s : wl_tx_limit[en:%d/max_t:%dus/max_retry:%d], bt_slot_reg:%d-TU, bt_scan_rx_low_pri:%d\n",
+ "[dm_ctrl]", dm->wl_tx_limit.enable,
+ dm->wl_tx_limit.tx_time,
+ dm->wl_tx_limit.tx_retry, btc->bt_req_len,
+ bt->scan_rx_low_pri);
+
+ return p - buf;
}
-static void _show_error(struct rtw89_dev *rtwdev, struct seq_file *m)
+static int _show_error(struct rtw89_dev *rtwdev, char *buf, size_t bufsz)
{
struct rtw89_btc *btc = &rtwdev->btc;
const struct rtw89_btc_ver *ver = btc->ver;
struct rtw89_btc_btf_fwinfo *pfwinfo = &btc->fwinfo;
union rtw89_btc_fbtc_cysta_info *pcysta;
+ char *p = buf, *end = buf + bufsz;
u32 except_cnt, exception_map;
pcysta = &pfwinfo->rpt_fbtc_cysta.finfo;
@@ -8985,81 +9029,87 @@ static void _show_error(struct rtw89_dev *rtwdev, struct seq_file *m)
except_cnt = pcysta->v7.except_cnt;
exception_map = le32_to_cpu(pcysta->v7.except_map);
} else {
- return;
+ return 0;
}
if (pfwinfo->event[BTF_EVNT_BUF_OVERFLOW] == 0 && except_cnt == 0 &&
!pfwinfo->len_mismch && !pfwinfo->fver_mismch)
- return;
+ return 0;
- seq_printf(m, " %-15s : ", "[error]");
+ p += scnprintf(p, end - p, " %-15s : ", "[error]");
if (pfwinfo->event[BTF_EVNT_BUF_OVERFLOW]) {
- seq_printf(m,
- "overflow-cnt: %d, ",
- pfwinfo->event[BTF_EVNT_BUF_OVERFLOW]);
+ p += scnprintf(p, end - p,
+ "overflow-cnt: %d, ",
+ pfwinfo->event[BTF_EVNT_BUF_OVERFLOW]);
}
if (pfwinfo->len_mismch) {
- seq_printf(m,
- "len-mismatch: 0x%x, ",
- pfwinfo->len_mismch);
+ p += scnprintf(p, end - p,
+ "len-mismatch: 0x%x, ",
+ pfwinfo->len_mismch);
}
if (pfwinfo->fver_mismch) {
- seq_printf(m,
- "fver-mismatch: 0x%x, ",
- pfwinfo->fver_mismch);
+ p += scnprintf(p, end - p,
+ "fver-mismatch: 0x%x, ",
+ pfwinfo->fver_mismch);
}
/* cycle statistics exceptions */
if (exception_map || except_cnt) {
- seq_printf(m,
- "exception-type: 0x%x, exception-cnt = %d",
- exception_map, except_cnt);
+ p += scnprintf(p, end - p,
+ "exception-type: 0x%x, exception-cnt = %d",
+ exception_map, except_cnt);
}
- seq_puts(m, "\n");
+ p += scnprintf(p, end - p, "\n");
+
+ return p - buf;
}
-static void _show_fbtc_tdma(struct rtw89_dev *rtwdev, struct seq_file *m)
+static int _show_fbtc_tdma(struct rtw89_dev *rtwdev, char *buf, size_t bufsz)
{
struct rtw89_btc *btc = &rtwdev->btc;
const struct rtw89_btc_ver *ver = btc->ver;
struct rtw89_btc_btf_fwinfo *pfwinfo = &btc->fwinfo;
struct rtw89_btc_rpt_cmn_info *pcinfo = NULL;
struct rtw89_btc_fbtc_tdma *t = NULL;
+ char *p = buf, *end = buf + bufsz;
pcinfo = &pfwinfo->rpt_fbtc_tdma.cinfo;
if (!pcinfo->valid)
- return;
+ return 0;
if (ver->fcxtdma == 1)
t = &pfwinfo->rpt_fbtc_tdma.finfo.v1;
else
t = &pfwinfo->rpt_fbtc_tdma.finfo.v3.tdma;
- seq_printf(m,
- " %-15s : ", "[tdma_policy]");
- seq_printf(m,
- "type:%d, rx_flow_ctrl:%d, tx_pause:%d, ",
- (u32)t->type,
- t->rxflctrl, t->txpause);
+ p += scnprintf(p, end - p,
+ " %-15s : ", "[tdma_policy]");
+ p += scnprintf(p, end - p,
+ "type:%d, rx_flow_ctrl:%d, tx_pause:%d, ",
+ (u32)t->type,
+ t->rxflctrl, t->txpause);
- seq_printf(m,
- "wl_toggle_n:%d, leak_n:%d, ext_ctrl:%d, ",
- t->wtgle_n, t->leak_n, t->ext_ctrl);
+ p += scnprintf(p, end - p,
+ "wl_toggle_n:%d, leak_n:%d, ext_ctrl:%d, ",
+ t->wtgle_n, t->leak_n, t->ext_ctrl);
- seq_printf(m,
- "policy_type:%d",
- (u32)btc->policy_type);
+ p += scnprintf(p, end - p,
+ "policy_type:%d",
+ (u32)btc->policy_type);
- seq_puts(m, "\n");
+ p += scnprintf(p, end - p, "\n");
+
+ return p - buf;
}
-static void _show_fbtc_slots(struct rtw89_dev *rtwdev, struct seq_file *m)
+static int _show_fbtc_slots(struct rtw89_dev *rtwdev, char *buf, size_t bufsz)
{
struct rtw89_btc *btc = &rtwdev->btc;
struct rtw89_btc_dm *dm = &btc->dm;
+ char *p = buf, *end = buf + bufsz;
u16 dur, cxtype;
u32 tbl;
u8 i = 0;
@@ -9074,28 +9124,30 @@ static void _show_fbtc_slots(struct rtw89_dev *rtwdev, struct seq_file *m)
tbl = le32_to_cpu(dm->slot_now.v7[i].cxtbl);
cxtype = le16_to_cpu(dm->slot_now.v7[i].cxtype);
} else {
- return;
+ return 0;
}
if (i % 5 == 0)
- seq_printf(m,
- " %-15s : %5s[%03d/0x%x/%d]",
- "[slot_list]",
- id_to_slot((u32)i),
- dur, tbl, cxtype);
+ p += scnprintf(p, end - p,
+ " %-15s : %5s[%03d/0x%x/%d]",
+ "[slot_list]",
+ id_to_slot((u32)i),
+ dur, tbl, cxtype);
else
- seq_printf(m,
- ", %5s[%03d/0x%x/%d]",
- id_to_slot((u32)i),
- dur, tbl, cxtype);
+ p += scnprintf(p, end - p,
+ ", %5s[%03d/0x%x/%d]",
+ id_to_slot((u32)i),
+ dur, tbl, cxtype);
if (i % 5 == 4)
- seq_puts(m, "\n");
+ p += scnprintf(p, end - p, "\n");
}
- seq_puts(m, "\n");
+ p += scnprintf(p, end - p, "\n");
+
+ return p - buf;
}
-static void _show_fbtc_cysta_v2(struct rtw89_dev *rtwdev, struct seq_file *m)
+static int _show_fbtc_cysta_v2(struct rtw89_dev *rtwdev, char *buf, size_t bufsz)
{
struct rtw89_btc *btc = &rtwdev->btc;
struct rtw89_btc_btf_fwinfo *pfwinfo = &btc->fwinfo;
@@ -9104,63 +9156,64 @@ static void _show_fbtc_cysta_v2(struct rtw89_dev *rtwdev, struct seq_file *m)
struct rtw89_btc_rpt_cmn_info *pcinfo = NULL;
struct rtw89_btc_fbtc_cysta_v2 *pcysta_le32 = NULL;
union rtw89_btc_fbtc_rxflct r;
- u8 i, cnt = 0, slot_pair;
u16 cycle, c_begin, c_end, store_index;
+ char *p = buf, *end = buf + bufsz;
+ u8 i, cnt = 0, slot_pair;
pcinfo = &pfwinfo->rpt_fbtc_cysta.cinfo;
if (!pcinfo->valid)
- return;
+ return 0;
pcysta_le32 = &pfwinfo->rpt_fbtc_cysta.finfo.v2;
- seq_printf(m,
- " %-15s : cycle:%d, bcn[all:%d/all_ok:%d/bt:%d/bt_ok:%d]",
- "[cycle_cnt]",
- le16_to_cpu(pcysta_le32->cycles),
- le32_to_cpu(pcysta_le32->bcn_cnt[CXBCN_ALL]),
- le32_to_cpu(pcysta_le32->bcn_cnt[CXBCN_ALL_OK]),
- le32_to_cpu(pcysta_le32->bcn_cnt[CXBCN_BT_SLOT]),
- le32_to_cpu(pcysta_le32->bcn_cnt[CXBCN_BT_OK]));
+ p += scnprintf(p, end - p,
+ " %-15s : cycle:%d, bcn[all:%d/all_ok:%d/bt:%d/bt_ok:%d]",
+ "[cycle_cnt]",
+ le16_to_cpu(pcysta_le32->cycles),
+ le32_to_cpu(pcysta_le32->bcn_cnt[CXBCN_ALL]),
+ le32_to_cpu(pcysta_le32->bcn_cnt[CXBCN_ALL_OK]),
+ le32_to_cpu(pcysta_le32->bcn_cnt[CXBCN_BT_SLOT]),
+ le32_to_cpu(pcysta_le32->bcn_cnt[CXBCN_BT_OK]));
for (i = 0; i < CXST_MAX; i++) {
if (!le32_to_cpu(pcysta_le32->slot_cnt[i]))
continue;
- seq_printf(m, ", %s:%d", id_to_slot((u32)i),
- le32_to_cpu(pcysta_le32->slot_cnt[i]));
+ p += scnprintf(p, end - p, ", %s:%d", id_to_slot((u32)i),
+ le32_to_cpu(pcysta_le32->slot_cnt[i]));
}
if (dm->tdma_now.rxflctrl) {
- seq_printf(m, ", leak_rx:%d",
- le32_to_cpu(pcysta_le32->leakrx_cnt));
+ p += scnprintf(p, end - p, ", leak_rx:%d",
+ le32_to_cpu(pcysta_le32->leakrx_cnt));
}
if (le32_to_cpu(pcysta_le32->collision_cnt)) {
- seq_printf(m, ", collision:%d",
- le32_to_cpu(pcysta_le32->collision_cnt));
+ p += scnprintf(p, end - p, ", collision:%d",
+ le32_to_cpu(pcysta_le32->collision_cnt));
}
if (le32_to_cpu(pcysta_le32->skip_cnt)) {
- seq_printf(m, ", skip:%d",
- le32_to_cpu(pcysta_le32->skip_cnt));
- }
- seq_puts(m, "\n");
-
- seq_printf(m, " %-15s : avg_t[wl:%d/bt:%d/lk:%d.%03d]",
- "[cycle_time]",
- le16_to_cpu(pcysta_le32->tavg_cycle[CXT_WL]),
- le16_to_cpu(pcysta_le32->tavg_cycle[CXT_BT]),
- le16_to_cpu(pcysta_le32->tavg_lk) / 1000,
- le16_to_cpu(pcysta_le32->tavg_lk) % 1000);
- seq_printf(m, ", max_t[wl:%d/bt:%d/lk:%d.%03d]",
- le16_to_cpu(pcysta_le32->tmax_cycle[CXT_WL]),
- le16_to_cpu(pcysta_le32->tmax_cycle[CXT_BT]),
- le16_to_cpu(pcysta_le32->tmax_lk) / 1000,
- le16_to_cpu(pcysta_le32->tmax_lk) % 1000);
- seq_printf(m, ", maxdiff_t[wl:%d/bt:%d]\n",
- le16_to_cpu(pcysta_le32->tmaxdiff_cycle[CXT_WL]),
- le16_to_cpu(pcysta_le32->tmaxdiff_cycle[CXT_BT]));
+ p += scnprintf(p, end - p, ", skip:%d",
+ le32_to_cpu(pcysta_le32->skip_cnt));
+ }
+ p += scnprintf(p, end - p, "\n");
+
+ p += scnprintf(p, end - p, " %-15s : avg_t[wl:%d/bt:%d/lk:%d.%03d]",
+ "[cycle_time]",
+ le16_to_cpu(pcysta_le32->tavg_cycle[CXT_WL]),
+ le16_to_cpu(pcysta_le32->tavg_cycle[CXT_BT]),
+ le16_to_cpu(pcysta_le32->tavg_lk) / 1000,
+ le16_to_cpu(pcysta_le32->tavg_lk) % 1000);
+ p += scnprintf(p, end - p, ", max_t[wl:%d/bt:%d/lk:%d.%03d]",
+ le16_to_cpu(pcysta_le32->tmax_cycle[CXT_WL]),
+ le16_to_cpu(pcysta_le32->tmax_cycle[CXT_BT]),
+ le16_to_cpu(pcysta_le32->tmax_lk) / 1000,
+ le16_to_cpu(pcysta_le32->tmax_lk) % 1000);
+ p += scnprintf(p, end - p, ", maxdiff_t[wl:%d/bt:%d]\n",
+ le16_to_cpu(pcysta_le32->tmaxdiff_cycle[CXT_WL]),
+ le16_to_cpu(pcysta_le32->tmaxdiff_cycle[CXT_BT]));
if (le16_to_cpu(pcysta_le32->cycles) <= 1)
- return;
+ goto out;
/* 1 cycle record 1 wl-slot and 1 bt-slot */
slot_pair = BTC_CYCLE_SLOT_MAX / 2;
@@ -9177,53 +9230,57 @@ static void _show_fbtc_cysta_v2(struct rtw89_dev *rtwdev, struct seq_file *m)
store_index = ((cycle - 1) % slot_pair) * 2;
if (cnt % (BTC_CYCLE_SLOT_MAX / 4) == 1)
- seq_printf(m,
- " %-15s : ->b%02d->w%02d", "[cycle_step]",
- le16_to_cpu(pcysta_le32->tslot_cycle[store_index]),
- le16_to_cpu(pcysta_le32->tslot_cycle[store_index + 1]));
+ p += scnprintf(p, end - p,
+ " %-15s : ->b%02d->w%02d",
+ "[cycle_step]",
+ le16_to_cpu(pcysta_le32->tslot_cycle[store_index]),
+ le16_to_cpu(pcysta_le32->tslot_cycle[store_index + 1]));
else
- seq_printf(m,
- "->b%02d->w%02d",
- le16_to_cpu(pcysta_le32->tslot_cycle[store_index]),
- le16_to_cpu(pcysta_le32->tslot_cycle[store_index + 1]));
+ p += scnprintf(p, end - p,
+ "->b%02d->w%02d",
+ le16_to_cpu(pcysta_le32->tslot_cycle[store_index]),
+ le16_to_cpu(pcysta_le32->tslot_cycle[store_index + 1]));
if (cnt % (BTC_CYCLE_SLOT_MAX / 4) == 0 || cnt == c_end)
- seq_puts(m, "\n");
+ p += scnprintf(p, end - p, "\n");
}
if (a2dp->exist) {
- seq_printf(m,
- " %-15s : a2dp_ept:%d, a2dp_late:%d",
- "[a2dp_t_sta]",
- le16_to_cpu(pcysta_le32->a2dpept),
- le16_to_cpu(pcysta_le32->a2dpeptto));
-
- seq_printf(m,
- ", avg_t:%d, max_t:%d",
- le16_to_cpu(pcysta_le32->tavg_a2dpept),
- le16_to_cpu(pcysta_le32->tmax_a2dpept));
+ p += scnprintf(p, end - p,
+ " %-15s : a2dp_ept:%d, a2dp_late:%d",
+ "[a2dp_t_sta]",
+ le16_to_cpu(pcysta_le32->a2dpept),
+ le16_to_cpu(pcysta_le32->a2dpeptto));
+
+ p += scnprintf(p, end - p,
+ ", avg_t:%d, max_t:%d",
+ le16_to_cpu(pcysta_le32->tavg_a2dpept),
+ le16_to_cpu(pcysta_le32->tmax_a2dpept));
r.val = dm->tdma_now.rxflctrl;
if (r.type && r.tgln_n) {
- seq_printf(m,
- ", cycle[PSTDMA:%d/TDMA:%d], ",
- le16_to_cpu(pcysta_le32->cycles_a2dp[CXT_FLCTRL_ON]),
- le16_to_cpu(pcysta_le32->cycles_a2dp[CXT_FLCTRL_OFF]));
-
- seq_printf(m,
- "avg_t[PSTDMA:%d/TDMA:%d], ",
- le16_to_cpu(pcysta_le32->tavg_a2dp[CXT_FLCTRL_ON]),
- le16_to_cpu(pcysta_le32->tavg_a2dp[CXT_FLCTRL_OFF]));
-
- seq_printf(m,
- "max_t[PSTDMA:%d/TDMA:%d]",
- le16_to_cpu(pcysta_le32->tmax_a2dp[CXT_FLCTRL_ON]),
- le16_to_cpu(pcysta_le32->tmax_a2dp[CXT_FLCTRL_OFF]));
+ p += scnprintf(p, end - p,
+ ", cycle[PSTDMA:%d/TDMA:%d], ",
+ le16_to_cpu(pcysta_le32->cycles_a2dp[CXT_FLCTRL_ON]),
+ le16_to_cpu(pcysta_le32->cycles_a2dp[CXT_FLCTRL_OFF]));
+
+ p += scnprintf(p, end - p,
+ "avg_t[PSTDMA:%d/TDMA:%d], ",
+ le16_to_cpu(pcysta_le32->tavg_a2dp[CXT_FLCTRL_ON]),
+ le16_to_cpu(pcysta_le32->tavg_a2dp[CXT_FLCTRL_OFF]));
+
+ p += scnprintf(p, end - p,
+ "max_t[PSTDMA:%d/TDMA:%d]",
+ le16_to_cpu(pcysta_le32->tmax_a2dp[CXT_FLCTRL_ON]),
+ le16_to_cpu(pcysta_le32->tmax_a2dp[CXT_FLCTRL_OFF]));
}
- seq_puts(m, "\n");
+ p += scnprintf(p, end - p, "\n");
}
+
+out:
+ return p - buf;
}
-static void _show_fbtc_cysta_v3(struct rtw89_dev *rtwdev, struct seq_file *m)
+static int _show_fbtc_cysta_v3(struct rtw89_dev *rtwdev, char *buf, size_t bufsz)
{
struct rtw89_btc *btc = &rtwdev->btc;
struct rtw89_btc_bt_a2dp_desc *a2dp = &btc->cx.bt.link_info.a2dp_desc;
@@ -9234,60 +9291,64 @@ static void _show_fbtc_cysta_v3(struct rtw89_dev *rtwdev, struct seq_file *m)
struct rtw89_btc_rpt_cmn_info *pcinfo;
u8 i, cnt = 0, slot_pair, divide_cnt;
u16 cycle, c_begin, c_end, store_index;
+ char *p = buf, *end = buf + bufsz;
pcinfo = &pfwinfo->rpt_fbtc_cysta.cinfo;
if (!pcinfo->valid)
- return;
+ return 0;
pcysta = &pfwinfo->rpt_fbtc_cysta.finfo.v3;
- seq_printf(m,
- " %-15s : cycle:%d, bcn[all:%d/all_ok:%d/bt:%d/bt_ok:%d]",
- "[cycle_cnt]",
- le16_to_cpu(pcysta->cycles),
- le32_to_cpu(pcysta->bcn_cnt[CXBCN_ALL]),
- le32_to_cpu(pcysta->bcn_cnt[CXBCN_ALL_OK]),
- le32_to_cpu(pcysta->bcn_cnt[CXBCN_BT_SLOT]),
- le32_to_cpu(pcysta->bcn_cnt[CXBCN_BT_OK]));
+ p += scnprintf(p, end - p,
+ " %-15s : cycle:%d, bcn[all:%d/all_ok:%d/bt:%d/bt_ok:%d]",
+ "[cycle_cnt]",
+ le16_to_cpu(pcysta->cycles),
+ le32_to_cpu(pcysta->bcn_cnt[CXBCN_ALL]),
+ le32_to_cpu(pcysta->bcn_cnt[CXBCN_ALL_OK]),
+ le32_to_cpu(pcysta->bcn_cnt[CXBCN_BT_SLOT]),
+ le32_to_cpu(pcysta->bcn_cnt[CXBCN_BT_OK]));
for (i = 0; i < CXST_MAX; i++) {
if (!le32_to_cpu(pcysta->slot_cnt[i]))
continue;
- seq_printf(m, ", %s:%d", id_to_slot(i),
- le32_to_cpu(pcysta->slot_cnt[i]));
+ p += scnprintf(p, end - p, ", %s:%d", id_to_slot(i),
+ le32_to_cpu(pcysta->slot_cnt[i]));
}
if (dm->tdma_now.rxflctrl)
- seq_printf(m, ", leak_rx:%d", le32_to_cpu(pcysta->leak_slot.cnt_rximr));
+ p += scnprintf(p, end - p, ", leak_rx:%d",
+ le32_to_cpu(pcysta->leak_slot.cnt_rximr));
if (le32_to_cpu(pcysta->collision_cnt))
- seq_printf(m, ", collision:%d", le32_to_cpu(pcysta->collision_cnt));
+ p += scnprintf(p, end - p, ", collision:%d",
+ le32_to_cpu(pcysta->collision_cnt));
if (le32_to_cpu(pcysta->skip_cnt))
- seq_printf(m, ", skip:%d", le32_to_cpu(pcysta->skip_cnt));
-
- seq_puts(m, "\n");
-
- seq_printf(m, " %-15s : avg_t[wl:%d/bt:%d/lk:%d.%03d]",
- "[cycle_time]",
- le16_to_cpu(pcysta->cycle_time.tavg[CXT_WL]),
- le16_to_cpu(pcysta->cycle_time.tavg[CXT_BT]),
- le16_to_cpu(pcysta->leak_slot.tavg) / 1000,
- le16_to_cpu(pcysta->leak_slot.tavg) % 1000);
- seq_printf(m,
- ", max_t[wl:%d/bt:%d/lk:%d.%03d]",
- le16_to_cpu(pcysta->cycle_time.tmax[CXT_WL]),
- le16_to_cpu(pcysta->cycle_time.tmax[CXT_BT]),
- le16_to_cpu(pcysta->leak_slot.tmax) / 1000,
- le16_to_cpu(pcysta->leak_slot.tmax) % 1000);
- seq_printf(m,
- ", maxdiff_t[wl:%d/bt:%d]\n",
- le16_to_cpu(pcysta->cycle_time.tmaxdiff[CXT_WL]),
- le16_to_cpu(pcysta->cycle_time.tmaxdiff[CXT_BT]));
+ p += scnprintf(p, end - p, ", skip:%d",
+ le32_to_cpu(pcysta->skip_cnt));
+
+ p += scnprintf(p, end - p, "\n");
+
+ p += scnprintf(p, end - p, " %-15s : avg_t[wl:%d/bt:%d/lk:%d.%03d]",
+ "[cycle_time]",
+ le16_to_cpu(pcysta->cycle_time.tavg[CXT_WL]),
+ le16_to_cpu(pcysta->cycle_time.tavg[CXT_BT]),
+ le16_to_cpu(pcysta->leak_slot.tavg) / 1000,
+ le16_to_cpu(pcysta->leak_slot.tavg) % 1000);
+ p += scnprintf(p, end - p,
+ ", max_t[wl:%d/bt:%d/lk:%d.%03d]",
+ le16_to_cpu(pcysta->cycle_time.tmax[CXT_WL]),
+ le16_to_cpu(pcysta->cycle_time.tmax[CXT_BT]),
+ le16_to_cpu(pcysta->leak_slot.tmax) / 1000,
+ le16_to_cpu(pcysta->leak_slot.tmax) % 1000);
+ p += scnprintf(p, end - p,
+ ", maxdiff_t[wl:%d/bt:%d]\n",
+ le16_to_cpu(pcysta->cycle_time.tmaxdiff[CXT_WL]),
+ le16_to_cpu(pcysta->cycle_time.tmaxdiff[CXT_BT]));
cycle = le16_to_cpu(pcysta->cycles);
if (cycle <= 1)
- return;
+ goto out;
/* 1 cycle record 1 wl-slot and 1 bt-slot */
slot_pair = BTC_CYCLE_SLOT_MAX / 2;
@@ -9309,51 +9370,56 @@ static void _show_fbtc_cysta_v3(struct rtw89_dev *rtwdev, struct seq_file *m)
store_index = ((cycle - 1) % slot_pair) * 2;
if (cnt % divide_cnt == 1)
- seq_printf(m, " %-15s : ", "[cycle_step]");
+ p += scnprintf(p, end - p, " %-15s : ",
+ "[cycle_step]");
- seq_printf(m, "->b%02d",
- le16_to_cpu(pcysta->slot_step_time[store_index]));
+ p += scnprintf(p, end - p, "->b%02d",
+ le16_to_cpu(pcysta->slot_step_time[store_index]));
if (a2dp->exist) {
a2dp_trx = &pcysta->a2dp_trx[store_index];
- seq_printf(m, "(%d/%d/%dM/%d/%d/%d)",
- a2dp_trx->empty_cnt,
- a2dp_trx->retry_cnt,
- a2dp_trx->tx_rate ? 3 : 2,
- a2dp_trx->tx_cnt,
- a2dp_trx->ack_cnt,
- a2dp_trx->nack_cnt);
+ p += scnprintf(p, end - p, "(%d/%d/%dM/%d/%d/%d)",
+ a2dp_trx->empty_cnt,
+ a2dp_trx->retry_cnt,
+ a2dp_trx->tx_rate ? 3 : 2,
+ a2dp_trx->tx_cnt,
+ a2dp_trx->ack_cnt,
+ a2dp_trx->nack_cnt);
}
- seq_printf(m, "->w%02d",
- le16_to_cpu(pcysta->slot_step_time[store_index + 1]));
+ p += scnprintf(p, end - p, "->w%02d",
+ le16_to_cpu(pcysta->slot_step_time[store_index + 1]));
if (a2dp->exist) {
a2dp_trx = &pcysta->a2dp_trx[store_index + 1];
- seq_printf(m, "(%d/%d/%dM/%d/%d/%d)",
- a2dp_trx->empty_cnt,
- a2dp_trx->retry_cnt,
- a2dp_trx->tx_rate ? 3 : 2,
- a2dp_trx->tx_cnt,
- a2dp_trx->ack_cnt,
- a2dp_trx->nack_cnt);
+ p += scnprintf(p, end - p, "(%d/%d/%dM/%d/%d/%d)",
+ a2dp_trx->empty_cnt,
+ a2dp_trx->retry_cnt,
+ a2dp_trx->tx_rate ? 3 : 2,
+ a2dp_trx->tx_cnt,
+ a2dp_trx->ack_cnt,
+ a2dp_trx->nack_cnt);
}
if (cnt % divide_cnt == 0 || cnt == c_end)
- seq_puts(m, "\n");
+ p += scnprintf(p, end - p, "\n");
}
if (a2dp->exist) {
- seq_printf(m, " %-15s : a2dp_ept:%d, a2dp_late:%d",
- "[a2dp_t_sta]",
- le16_to_cpu(pcysta->a2dp_ept.cnt),
- le16_to_cpu(pcysta->a2dp_ept.cnt_timeout));
+ p += scnprintf(p, end - p,
+ " %-15s : a2dp_ept:%d, a2dp_late:%d",
+ "[a2dp_t_sta]",
+ le16_to_cpu(pcysta->a2dp_ept.cnt),
+ le16_to_cpu(pcysta->a2dp_ept.cnt_timeout));
- seq_printf(m, ", avg_t:%d, max_t:%d",
- le16_to_cpu(pcysta->a2dp_ept.tavg),
- le16_to_cpu(pcysta->a2dp_ept.tmax));
+ p += scnprintf(p, end - p, ", avg_t:%d, max_t:%d",
+ le16_to_cpu(pcysta->a2dp_ept.tavg),
+ le16_to_cpu(pcysta->a2dp_ept.tmax));
- seq_puts(m, "\n");
+ p += scnprintf(p, end - p, "\n");
}
+
+out:
+ return p - buf;
}
-static void _show_fbtc_cysta_v4(struct rtw89_dev *rtwdev, struct seq_file *m)
+static int _show_fbtc_cysta_v4(struct rtw89_dev *rtwdev, char *buf, size_t bufsz)
{
struct rtw89_btc *btc = &rtwdev->btc;
struct rtw89_btc_bt_a2dp_desc *a2dp = &btc->cx.bt.link_info.a2dp_desc;
@@ -9364,62 +9430,64 @@ static void _show_fbtc_cysta_v4(struct rtw89_dev *rtwdev, struct seq_file *m)
struct rtw89_btc_rpt_cmn_info *pcinfo;
u8 i, cnt = 0, slot_pair, divide_cnt;
u16 cycle, c_begin, c_end, store_index;
+ char *p = buf, *end = buf + bufsz;
pcinfo = &pfwinfo->rpt_fbtc_cysta.cinfo;
if (!pcinfo->valid)
- return;
+ return 0;
pcysta = &pfwinfo->rpt_fbtc_cysta.finfo.v4;
- seq_printf(m,
- " %-15s : cycle:%d, bcn[all:%d/all_ok:%d/bt:%d/bt_ok:%d]",
- "[cycle_cnt]",
- le16_to_cpu(pcysta->cycles),
- le16_to_cpu(pcysta->bcn_cnt[CXBCN_ALL]),
- le16_to_cpu(pcysta->bcn_cnt[CXBCN_ALL_OK]),
- le16_to_cpu(pcysta->bcn_cnt[CXBCN_BT_SLOT]),
- le16_to_cpu(pcysta->bcn_cnt[CXBCN_BT_OK]));
+ p += scnprintf(p, end - p,
+ " %-15s : cycle:%d, bcn[all:%d/all_ok:%d/bt:%d/bt_ok:%d]",
+ "[cycle_cnt]",
+ le16_to_cpu(pcysta->cycles),
+ le16_to_cpu(pcysta->bcn_cnt[CXBCN_ALL]),
+ le16_to_cpu(pcysta->bcn_cnt[CXBCN_ALL_OK]),
+ le16_to_cpu(pcysta->bcn_cnt[CXBCN_BT_SLOT]),
+ le16_to_cpu(pcysta->bcn_cnt[CXBCN_BT_OK]));
for (i = 0; i < CXST_MAX; i++) {
if (!le16_to_cpu(pcysta->slot_cnt[i]))
continue;
- seq_printf(m, ", %s:%d", id_to_slot(i),
- le16_to_cpu(pcysta->slot_cnt[i]));
+ p += scnprintf(p, end - p, ", %s:%d", id_to_slot(i),
+ le16_to_cpu(pcysta->slot_cnt[i]));
}
if (dm->tdma_now.rxflctrl)
- seq_printf(m, ", leak_rx:%d",
- le32_to_cpu(pcysta->leak_slot.cnt_rximr));
+ p += scnprintf(p, end - p, ", leak_rx:%d",
+ le32_to_cpu(pcysta->leak_slot.cnt_rximr));
if (pcysta->collision_cnt)
- seq_printf(m, ", collision:%d", pcysta->collision_cnt);
+ p += scnprintf(p, end - p, ", collision:%d",
+ pcysta->collision_cnt);
if (le16_to_cpu(pcysta->skip_cnt))
- seq_printf(m, ", skip:%d",
- le16_to_cpu(pcysta->skip_cnt));
-
- seq_puts(m, "\n");
-
- seq_printf(m, " %-15s : avg_t[wl:%d/bt:%d/lk:%d.%03d]",
- "[cycle_time]",
- le16_to_cpu(pcysta->cycle_time.tavg[CXT_WL]),
- le16_to_cpu(pcysta->cycle_time.tavg[CXT_BT]),
- le16_to_cpu(pcysta->leak_slot.tavg) / 1000,
- le16_to_cpu(pcysta->leak_slot.tavg) % 1000);
- seq_printf(m,
- ", max_t[wl:%d/bt:%d/lk:%d.%03d]",
- le16_to_cpu(pcysta->cycle_time.tmax[CXT_WL]),
- le16_to_cpu(pcysta->cycle_time.tmax[CXT_BT]),
- le16_to_cpu(pcysta->leak_slot.tmax) / 1000,
- le16_to_cpu(pcysta->leak_slot.tmax) % 1000);
- seq_printf(m,
- ", maxdiff_t[wl:%d/bt:%d]\n",
- le16_to_cpu(pcysta->cycle_time.tmaxdiff[CXT_WL]),
- le16_to_cpu(pcysta->cycle_time.tmaxdiff[CXT_BT]));
+ p += scnprintf(p, end - p, ", skip:%d",
+ le16_to_cpu(pcysta->skip_cnt));
+
+ p += scnprintf(p, end - p, "\n");
+
+ p += scnprintf(p, end - p, " %-15s : avg_t[wl:%d/bt:%d/lk:%d.%03d]",
+ "[cycle_time]",
+ le16_to_cpu(pcysta->cycle_time.tavg[CXT_WL]),
+ le16_to_cpu(pcysta->cycle_time.tavg[CXT_BT]),
+ le16_to_cpu(pcysta->leak_slot.tavg) / 1000,
+ le16_to_cpu(pcysta->leak_slot.tavg) % 1000);
+ p += scnprintf(p, end - p,
+ ", max_t[wl:%d/bt:%d/lk:%d.%03d]",
+ le16_to_cpu(pcysta->cycle_time.tmax[CXT_WL]),
+ le16_to_cpu(pcysta->cycle_time.tmax[CXT_BT]),
+ le16_to_cpu(pcysta->leak_slot.tmax) / 1000,
+ le16_to_cpu(pcysta->leak_slot.tmax) % 1000);
+ p += scnprintf(p, end - p,
+ ", maxdiff_t[wl:%d/bt:%d]\n",
+ le16_to_cpu(pcysta->cycle_time.tmaxdiff[CXT_WL]),
+ le16_to_cpu(pcysta->cycle_time.tmaxdiff[CXT_BT]));
cycle = le16_to_cpu(pcysta->cycles);
if (cycle <= 1)
- return;
+ goto out;
/* 1 cycle record 1 wl-slot and 1 bt-slot */
slot_pair = BTC_CYCLE_SLOT_MAX / 2;
@@ -9441,51 +9509,56 @@ static void _show_fbtc_cysta_v4(struct rtw89_dev *rtwdev, struct seq_file *m)
store_index = ((cycle - 1) % slot_pair) * 2;
if (cnt % divide_cnt == 1)
- seq_printf(m, " %-15s : ", "[cycle_step]");
+ p += scnprintf(p, end - p, " %-15s : ",
+ "[cycle_step]");
- seq_printf(m, "->b%02d",
- le16_to_cpu(pcysta->slot_step_time[store_index]));
+ p += scnprintf(p, end - p, "->b%02d",
+ le16_to_cpu(pcysta->slot_step_time[store_index]));
if (a2dp->exist) {
a2dp_trx = &pcysta->a2dp_trx[store_index];
- seq_printf(m, "(%d/%d/%dM/%d/%d/%d)",
- a2dp_trx->empty_cnt,
- a2dp_trx->retry_cnt,
- a2dp_trx->tx_rate ? 3 : 2,
- a2dp_trx->tx_cnt,
- a2dp_trx->ack_cnt,
- a2dp_trx->nack_cnt);
+ p += scnprintf(p, end - p, "(%d/%d/%dM/%d/%d/%d)",
+ a2dp_trx->empty_cnt,
+ a2dp_trx->retry_cnt,
+ a2dp_trx->tx_rate ? 3 : 2,
+ a2dp_trx->tx_cnt,
+ a2dp_trx->ack_cnt,
+ a2dp_trx->nack_cnt);
}
- seq_printf(m, "->w%02d",
- le16_to_cpu(pcysta->slot_step_time[store_index + 1]));
+ p += scnprintf(p, end - p, "->w%02d",
+ le16_to_cpu(pcysta->slot_step_time[store_index + 1]));
if (a2dp->exist) {
a2dp_trx = &pcysta->a2dp_trx[store_index + 1];
- seq_printf(m, "(%d/%d/%dM/%d/%d/%d)",
- a2dp_trx->empty_cnt,
- a2dp_trx->retry_cnt,
- a2dp_trx->tx_rate ? 3 : 2,
- a2dp_trx->tx_cnt,
- a2dp_trx->ack_cnt,
- a2dp_trx->nack_cnt);
+ p += scnprintf(p, end - p, "(%d/%d/%dM/%d/%d/%d)",
+ a2dp_trx->empty_cnt,
+ a2dp_trx->retry_cnt,
+ a2dp_trx->tx_rate ? 3 : 2,
+ a2dp_trx->tx_cnt,
+ a2dp_trx->ack_cnt,
+ a2dp_trx->nack_cnt);
}
if (cnt % divide_cnt == 0 || cnt == c_end)
- seq_puts(m, "\n");
+ p += scnprintf(p, end - p, "\n");
}
if (a2dp->exist) {
- seq_printf(m, " %-15s : a2dp_ept:%d, a2dp_late:%d",
- "[a2dp_t_sta]",
- le16_to_cpu(pcysta->a2dp_ept.cnt),
- le16_to_cpu(pcysta->a2dp_ept.cnt_timeout));
+ p += scnprintf(p, end - p,
+ " %-15s : a2dp_ept:%d, a2dp_late:%d",
+ "[a2dp_t_sta]",
+ le16_to_cpu(pcysta->a2dp_ept.cnt),
+ le16_to_cpu(pcysta->a2dp_ept.cnt_timeout));
- seq_printf(m, ", avg_t:%d, max_t:%d",
- le16_to_cpu(pcysta->a2dp_ept.tavg),
- le16_to_cpu(pcysta->a2dp_ept.tmax));
+ p += scnprintf(p, end - p, ", avg_t:%d, max_t:%d",
+ le16_to_cpu(pcysta->a2dp_ept.tavg),
+ le16_to_cpu(pcysta->a2dp_ept.tmax));
- seq_puts(m, "\n");
+ p += scnprintf(p, end - p, "\n");
}
+
+out:
+ return p - buf;
}
-static void _show_fbtc_cysta_v5(struct rtw89_dev *rtwdev, struct seq_file *m)
+static int _show_fbtc_cysta_v5(struct rtw89_dev *rtwdev, char *buf, size_t bufsz)
{
struct rtw89_btc *btc = &rtwdev->btc;
struct rtw89_btc_bt_a2dp_desc *a2dp = &btc->cx.bt.link_info.a2dp_desc;
@@ -9496,58 +9569,60 @@ static void _show_fbtc_cysta_v5(struct rtw89_dev *rtwdev, struct seq_file *m)
struct rtw89_btc_rpt_cmn_info *pcinfo;
u8 i, cnt = 0, slot_pair, divide_cnt;
u16 cycle, c_begin, c_end, store_index;
+ char *p = buf, *end = buf + bufsz;
pcinfo = &pfwinfo->rpt_fbtc_cysta.cinfo;
if (!pcinfo->valid)
- return;
+ return 0;
pcysta = &pfwinfo->rpt_fbtc_cysta.finfo.v5;
- seq_printf(m,
- " %-15s : cycle:%d, bcn[all:%d/all_ok:%d/bt:%d/bt_ok:%d]",
- "[cycle_cnt]",
- le16_to_cpu(pcysta->cycles),
- le16_to_cpu(pcysta->bcn_cnt[CXBCN_ALL]),
- le16_to_cpu(pcysta->bcn_cnt[CXBCN_ALL_OK]),
- le16_to_cpu(pcysta->bcn_cnt[CXBCN_BT_SLOT]),
- le16_to_cpu(pcysta->bcn_cnt[CXBCN_BT_OK]));
+ p += scnprintf(p, end - p,
+ " %-15s : cycle:%d, bcn[all:%d/all_ok:%d/bt:%d/bt_ok:%d]",
+ "[cycle_cnt]",
+ le16_to_cpu(pcysta->cycles),
+ le16_to_cpu(pcysta->bcn_cnt[CXBCN_ALL]),
+ le16_to_cpu(pcysta->bcn_cnt[CXBCN_ALL_OK]),
+ le16_to_cpu(pcysta->bcn_cnt[CXBCN_BT_SLOT]),
+ le16_to_cpu(pcysta->bcn_cnt[CXBCN_BT_OK]));
for (i = 0; i < CXST_MAX; i++) {
if (!le16_to_cpu(pcysta->slot_cnt[i]))
continue;
- seq_printf(m, ", %s:%d", id_to_slot(i),
- le16_to_cpu(pcysta->slot_cnt[i]));
+ p += scnprintf(p, end - p, ", %s:%d", id_to_slot(i),
+ le16_to_cpu(pcysta->slot_cnt[i]));
}
if (dm->tdma_now.rxflctrl)
- seq_printf(m, ", leak_rx:%d",
- le32_to_cpu(pcysta->leak_slot.cnt_rximr));
+ p += scnprintf(p, end - p, ", leak_rx:%d",
+ le32_to_cpu(pcysta->leak_slot.cnt_rximr));
if (pcysta->collision_cnt)
- seq_printf(m, ", collision:%d", pcysta->collision_cnt);
+ p += scnprintf(p, end - p, ", collision:%d",
+ pcysta->collision_cnt);
if (le16_to_cpu(pcysta->skip_cnt))
- seq_printf(m, ", skip:%d",
- le16_to_cpu(pcysta->skip_cnt));
-
- seq_puts(m, "\n");
-
- seq_printf(m, " %-15s : avg_t[wl:%d/bt:%d/lk:%d.%03d]",
- "[cycle_time]",
- le16_to_cpu(pcysta->cycle_time.tavg[CXT_WL]),
- le16_to_cpu(pcysta->cycle_time.tavg[CXT_BT]),
- le16_to_cpu(pcysta->leak_slot.tavg) / 1000,
- le16_to_cpu(pcysta->leak_slot.tavg) % 1000);
- seq_printf(m,
- ", max_t[wl:%d/bt:%d/lk:%d.%03d]\n",
- le16_to_cpu(pcysta->cycle_time.tmax[CXT_WL]),
- le16_to_cpu(pcysta->cycle_time.tmax[CXT_BT]),
- le16_to_cpu(pcysta->leak_slot.tmax) / 1000,
- le16_to_cpu(pcysta->leak_slot.tmax) % 1000);
+ p += scnprintf(p, end - p, ", skip:%d",
+ le16_to_cpu(pcysta->skip_cnt));
+
+ p += scnprintf(p, end - p, "\n");
+
+ p += scnprintf(p, end - p, " %-15s : avg_t[wl:%d/bt:%d/lk:%d.%03d]",
+ "[cycle_time]",
+ le16_to_cpu(pcysta->cycle_time.tavg[CXT_WL]),
+ le16_to_cpu(pcysta->cycle_time.tavg[CXT_BT]),
+ le16_to_cpu(pcysta->leak_slot.tavg) / 1000,
+ le16_to_cpu(pcysta->leak_slot.tavg) % 1000);
+ p += scnprintf(p, end - p,
+ ", max_t[wl:%d/bt:%d/lk:%d.%03d]\n",
+ le16_to_cpu(pcysta->cycle_time.tmax[CXT_WL]),
+ le16_to_cpu(pcysta->cycle_time.tmax[CXT_BT]),
+ le16_to_cpu(pcysta->leak_slot.tmax) / 1000,
+ le16_to_cpu(pcysta->leak_slot.tmax) % 1000);
cycle = le16_to_cpu(pcysta->cycles);
if (cycle <= 1)
- return;
+ goto out;
/* 1 cycle record 1 wl-slot and 1 bt-slot */
slot_pair = BTC_CYCLE_SLOT_MAX / 2;
@@ -9565,58 +9640,63 @@ static void _show_fbtc_cysta_v5(struct rtw89_dev *rtwdev, struct seq_file *m)
divide_cnt = BTC_CYCLE_SLOT_MAX / 4;
if (c_begin > c_end)
- return;
+ goto out;
for (cycle = c_begin; cycle <= c_end; cycle++) {
cnt++;
store_index = ((cycle - 1) % slot_pair) * 2;
if (cnt % divide_cnt == 1)
- seq_printf(m, " %-15s : ", "[cycle_step]");
+ p += scnprintf(p, end - p, " %-15s : ",
+ "[cycle_step]");
- seq_printf(m, "->b%02d",
- le16_to_cpu(pcysta->slot_step_time[store_index]));
+ p += scnprintf(p, end - p, "->b%02d",
+ le16_to_cpu(pcysta->slot_step_time[store_index]));
if (a2dp->exist) {
a2dp_trx = &pcysta->a2dp_trx[store_index];
- seq_printf(m, "(%d/%d/%dM/%d/%d/%d)",
- a2dp_trx->empty_cnt,
- a2dp_trx->retry_cnt,
- a2dp_trx->tx_rate ? 3 : 2,
- a2dp_trx->tx_cnt,
- a2dp_trx->ack_cnt,
- a2dp_trx->nack_cnt);
+ p += scnprintf(p, end - p, "(%d/%d/%dM/%d/%d/%d)",
+ a2dp_trx->empty_cnt,
+ a2dp_trx->retry_cnt,
+ a2dp_trx->tx_rate ? 3 : 2,
+ a2dp_trx->tx_cnt,
+ a2dp_trx->ack_cnt,
+ a2dp_trx->nack_cnt);
}
- seq_printf(m, "->w%02d",
- le16_to_cpu(pcysta->slot_step_time[store_index + 1]));
+ p += scnprintf(p, end - p, "->w%02d",
+ le16_to_cpu(pcysta->slot_step_time[store_index + 1]));
if (a2dp->exist) {
a2dp_trx = &pcysta->a2dp_trx[store_index + 1];
- seq_printf(m, "(%d/%d/%dM/%d/%d/%d)",
- a2dp_trx->empty_cnt,
- a2dp_trx->retry_cnt,
- a2dp_trx->tx_rate ? 3 : 2,
- a2dp_trx->tx_cnt,
- a2dp_trx->ack_cnt,
- a2dp_trx->nack_cnt);
+ p += scnprintf(p, end - p, "(%d/%d/%dM/%d/%d/%d)",
+ a2dp_trx->empty_cnt,
+ a2dp_trx->retry_cnt,
+ a2dp_trx->tx_rate ? 3 : 2,
+ a2dp_trx->tx_cnt,
+ a2dp_trx->ack_cnt,
+ a2dp_trx->nack_cnt);
}
if (cnt % divide_cnt == 0 || cnt == c_end)
- seq_puts(m, "\n");
+ p += scnprintf(p, end - p, "\n");
}
if (a2dp->exist) {
- seq_printf(m, " %-15s : a2dp_ept:%d, a2dp_late:%d",
- "[a2dp_t_sta]",
- le16_to_cpu(pcysta->a2dp_ept.cnt),
- le16_to_cpu(pcysta->a2dp_ept.cnt_timeout));
+ p += scnprintf(p, end - p,
+ " %-15s : a2dp_ept:%d, a2dp_late:%d",
+ "[a2dp_t_sta]",
+ le16_to_cpu(pcysta->a2dp_ept.cnt),
+ le16_to_cpu(pcysta->a2dp_ept.cnt_timeout));
- seq_printf(m, ", avg_t:%d, max_t:%d",
- le16_to_cpu(pcysta->a2dp_ept.tavg),
- le16_to_cpu(pcysta->a2dp_ept.tmax));
+ p += scnprintf(p, end - p, ", avg_t:%d, max_t:%d",
+ le16_to_cpu(pcysta->a2dp_ept.tavg),
+ le16_to_cpu(pcysta->a2dp_ept.tmax));
- seq_puts(m, "\n");
+ p += scnprintf(p, end - p, "\n");
}
+
+out:
+ return p - buf;
}
-static void _show_fbtc_cysta_v7(struct rtw89_dev *rtwdev, struct seq_file *m)
+static int _show_fbtc_cysta_v7(struct rtw89_dev *rtwdev, char *buf, size_t bufsz)
{
struct rtw89_btc_bt_info *bt = &rtwdev->btc.cx.bt;
struct rtw89_btc_bt_a2dp_desc *a2dp = &bt->link_info.a2dp_desc;
@@ -9624,68 +9704,75 @@ static void _show_fbtc_cysta_v7(struct rtw89_dev *rtwdev, struct seq_file *m)
struct rtw89_btc_fbtc_cysta_v7 *pcysta = NULL;
struct rtw89_btc_dm *dm = &rtwdev->btc.dm;
struct rtw89_btc_rpt_cmn_info *pcinfo;
+ char *p = buf, *end = buf + bufsz;
u16 cycle, c_begin, c_end, s_id;
u8 i, cnt = 0, divide_cnt;
u8 slot_pair;
pcinfo = &pfwinfo->rpt_fbtc_cysta.cinfo;
if (!pcinfo->valid)
- return;
+ return 0;
pcysta = &pfwinfo->rpt_fbtc_cysta.finfo.v7;
- seq_printf(m, "\n\r %-15s : cycle:%d", "[slot_stat]",
- le16_to_cpu(pcysta->cycles));
+ p += scnprintf(p, end - p, "\n\r %-15s : cycle:%d", "[slot_stat]",
+ le16_to_cpu(pcysta->cycles));
for (i = 0; i < CXST_MAX; i++) {
if (!le16_to_cpu(pcysta->slot_cnt[i]))
continue;
- seq_printf(m, ", %s:%d",
- id_to_slot(i), le16_to_cpu(pcysta->slot_cnt[i]));
+ p += scnprintf(p, end - p, ", %s:%d",
+ id_to_slot(i),
+ le16_to_cpu(pcysta->slot_cnt[i]));
}
if (dm->tdma_now.rxflctrl)
- seq_printf(m, ", leak_rx:%d",
- le32_to_cpu(pcysta->leak_slot.cnt_rximr));
+ p += scnprintf(p, end - p, ", leak_rx:%d",
+ le32_to_cpu(pcysta->leak_slot.cnt_rximr));
if (pcysta->collision_cnt)
- seq_printf(m, ", collision:%d", pcysta->collision_cnt);
+ p += scnprintf(p, end - p, ", collision:%d",
+ pcysta->collision_cnt);
if (pcysta->skip_cnt)
- seq_printf(m, ", skip:%d", le16_to_cpu(pcysta->skip_cnt));
-
- seq_printf(m, "\n\r %-15s : avg_t[wl:%d/bt:%d/lk:%d.%03d]",
- "[cycle_stat]",
- le16_to_cpu(pcysta->cycle_time.tavg[CXT_WL]),
- le16_to_cpu(pcysta->cycle_time.tavg[CXT_BT]),
- le16_to_cpu(pcysta->leak_slot.tavg) / 1000,
- le16_to_cpu(pcysta->leak_slot.tavg) % 1000);
- seq_printf(m, ", max_t[wl:%d/bt:%d(>%dms:%d)/lk:%d.%03d]",
- le16_to_cpu(pcysta->cycle_time.tmax[CXT_WL]),
- le16_to_cpu(pcysta->cycle_time.tmax[CXT_BT]),
- dm->bt_slot_flood, dm->cnt_dm[BTC_DCNT_BT_SLOT_FLOOD],
- le16_to_cpu(pcysta->leak_slot.tamx) / 1000,
- le16_to_cpu(pcysta->leak_slot.tamx) % 1000);
- seq_printf(m, ", bcn[all:%d/ok:%d/in_bt:%d/in_bt_ok:%d]",
- le16_to_cpu(pcysta->bcn_cnt[CXBCN_ALL]),
- le16_to_cpu(pcysta->bcn_cnt[CXBCN_ALL_OK]),
- le16_to_cpu(pcysta->bcn_cnt[CXBCN_BT_SLOT]),
- le16_to_cpu(pcysta->bcn_cnt[CXBCN_BT_OK]));
+ p += scnprintf(p, end - p, ", skip:%d",
+ le16_to_cpu(pcysta->skip_cnt));
+
+ p += scnprintf(p, end - p,
+ "\n\r %-15s : avg_t[wl:%d/bt:%d/lk:%d.%03d]",
+ "[cycle_stat]",
+ le16_to_cpu(pcysta->cycle_time.tavg[CXT_WL]),
+ le16_to_cpu(pcysta->cycle_time.tavg[CXT_BT]),
+ le16_to_cpu(pcysta->leak_slot.tavg) / 1000,
+ le16_to_cpu(pcysta->leak_slot.tavg) % 1000);
+ p += scnprintf(p, end - p,
+ ", max_t[wl:%d/bt:%d(>%dms:%d)/lk:%d.%03d]",
+ le16_to_cpu(pcysta->cycle_time.tmax[CXT_WL]),
+ le16_to_cpu(pcysta->cycle_time.tmax[CXT_BT]),
+ dm->bt_slot_flood, dm->cnt_dm[BTC_DCNT_BT_SLOT_FLOOD],
+ le16_to_cpu(pcysta->leak_slot.tamx) / 1000,
+ le16_to_cpu(pcysta->leak_slot.tamx) % 1000);
+ p += scnprintf(p, end - p, ", bcn[all:%d/ok:%d/in_bt:%d/in_bt_ok:%d]",
+ le16_to_cpu(pcysta->bcn_cnt[CXBCN_ALL]),
+ le16_to_cpu(pcysta->bcn_cnt[CXBCN_ALL_OK]),
+ le16_to_cpu(pcysta->bcn_cnt[CXBCN_BT_SLOT]),
+ le16_to_cpu(pcysta->bcn_cnt[CXBCN_BT_OK]));
if (a2dp->exist) {
- seq_printf(m,
- "\n\r %-15s : a2dp_ept:%d, a2dp_late:%d(streak 2S:%d/max:%d)",
- "[a2dp_stat]",
- le16_to_cpu(pcysta->a2dp_ept.cnt),
- le16_to_cpu(pcysta->a2dp_ept.cnt_timeout),
- a2dp->no_empty_streak_2s, a2dp->no_empty_streak_max);
+ p += scnprintf(p, end - p,
+ "\n\r %-15s : a2dp_ept:%d, a2dp_late:%d(streak 2S:%d/max:%d)",
+ "[a2dp_stat]",
+ le16_to_cpu(pcysta->a2dp_ept.cnt),
+ le16_to_cpu(pcysta->a2dp_ept.cnt_timeout),
+ a2dp->no_empty_streak_2s,
+ a2dp->no_empty_streak_max);
- seq_printf(m, ", avg_t:%d, max_t:%d",
- le16_to_cpu(pcysta->a2dp_ept.tavg),
- le16_to_cpu(pcysta->a2dp_ept.tmax));
+ p += scnprintf(p, end - p, ", avg_t:%d, max_t:%d",
+ le16_to_cpu(pcysta->a2dp_ept.tavg),
+ le16_to_cpu(pcysta->a2dp_ept.tmax));
}
if (le16_to_cpu(pcysta->cycles) <= 1)
- return;
+ goto out;
/* 1 cycle = 1 wl-slot + 1 bt-slot */
slot_pair = BTC_CYCLE_SLOT_MAX / 2;
@@ -9703,7 +9790,7 @@ static void _show_fbtc_cysta_v7(struct rtw89_dev *rtwdev, struct seq_file *m)
divide_cnt = 6;
if (c_begin > c_end)
- return;
+ goto out;
for (cycle = c_begin; cycle <= c_end; cycle++) {
cnt++;
@@ -9711,129 +9798,142 @@ static void _show_fbtc_cysta_v7(struct rtw89_dev *rtwdev, struct seq_file *m)
if (cnt % divide_cnt == 1) {
if (a2dp->exist)
- seq_printf(m, "\n\r %-15s : ", "[slotT_wermtan]");
+ p += scnprintf(p, end - p, "\n\r %-15s : ",
+ "[slotT_wermtan]");
else
- seq_printf(m, "\n\r %-15s : ", "[slotT_rxerr]");
+ p += scnprintf(p, end - p, "\n\r %-15s : ",
+ "[slotT_rxerr]");
}
- seq_printf(m, "->b%d", le16_to_cpu(pcysta->slot_step_time[s_id]));
+ p += scnprintf(p, end - p, "->b%d",
+ le16_to_cpu(pcysta->slot_step_time[s_id]));
if (a2dp->exist)
- seq_printf(m, "(%d/%d/%d/%dM/%d/%d/%d)",
- pcysta->wl_rx_err_ratio[s_id],
- pcysta->a2dp_trx[s_id].empty_cnt,
- pcysta->a2dp_trx[s_id].retry_cnt,
- (pcysta->a2dp_trx[s_id].tx_rate ? 3 : 2),
- pcysta->a2dp_trx[s_id].tx_cnt,
- pcysta->a2dp_trx[s_id].ack_cnt,
- pcysta->a2dp_trx[s_id].nack_cnt);
+ p += scnprintf(p, end - p, "(%d/%d/%d/%dM/%d/%d/%d)",
+ pcysta->wl_rx_err_ratio[s_id],
+ pcysta->a2dp_trx[s_id].empty_cnt,
+ pcysta->a2dp_trx[s_id].retry_cnt,
+ (pcysta->a2dp_trx[s_id].tx_rate ? 3 : 2),
+ pcysta->a2dp_trx[s_id].tx_cnt,
+ pcysta->a2dp_trx[s_id].ack_cnt,
+ pcysta->a2dp_trx[s_id].nack_cnt);
else
- seq_printf(m, "(%d)", pcysta->wl_rx_err_ratio[s_id]);
+ p += scnprintf(p, end - p, "(%d)",
+ pcysta->wl_rx_err_ratio[s_id]);
- seq_printf(m, "->w%d", le16_to_cpu(pcysta->slot_step_time[s_id + 1]));
+ p += scnprintf(p, end - p, "->w%d",
+ le16_to_cpu(pcysta->slot_step_time[s_id + 1]));
if (a2dp->exist)
- seq_printf(m, "(%d/%d/%d/%dM/%d/%d/%d)",
- pcysta->wl_rx_err_ratio[s_id + 1],
- pcysta->a2dp_trx[s_id + 1].empty_cnt,
- pcysta->a2dp_trx[s_id + 1].retry_cnt,
- (pcysta->a2dp_trx[s_id + 1].tx_rate ? 3 : 2),
- pcysta->a2dp_trx[s_id + 1].tx_cnt,
- pcysta->a2dp_trx[s_id + 1].ack_cnt,
- pcysta->a2dp_trx[s_id + 1].nack_cnt);
+ p += scnprintf(p, end - p, "(%d/%d/%d/%dM/%d/%d/%d)",
+ pcysta->wl_rx_err_ratio[s_id + 1],
+ pcysta->a2dp_trx[s_id + 1].empty_cnt,
+ pcysta->a2dp_trx[s_id + 1].retry_cnt,
+ (pcysta->a2dp_trx[s_id + 1].tx_rate ? 3 : 2),
+ pcysta->a2dp_trx[s_id + 1].tx_cnt,
+ pcysta->a2dp_trx[s_id + 1].ack_cnt,
+ pcysta->a2dp_trx[s_id + 1].nack_cnt);
else
- seq_printf(m, "(%d)", pcysta->wl_rx_err_ratio[s_id + 1]);
+ p += scnprintf(p, end - p, "(%d)",
+ pcysta->wl_rx_err_ratio[s_id + 1]);
}
+
+out:
+ return p - buf;
}
-static void _show_fbtc_nullsta(struct rtw89_dev *rtwdev, struct seq_file *m)
+static int _show_fbtc_nullsta(struct rtw89_dev *rtwdev, char *buf, size_t bufsz)
{
struct rtw89_btc *btc = &rtwdev->btc;
const struct rtw89_btc_ver *ver = btc->ver;
struct rtw89_btc_btf_fwinfo *pfwinfo = &btc->fwinfo;
struct rtw89_btc_rpt_cmn_info *pcinfo;
union rtw89_btc_fbtc_cynullsta_info *ns;
+ char *p = buf, *end = buf + bufsz;
u8 i = 0;
if (!btc->dm.tdma_now.rxflctrl)
- return;
+ return 0;
pcinfo = &pfwinfo->rpt_fbtc_nullsta.cinfo;
if (!pcinfo->valid)
- return;
+ return 0;
ns = &pfwinfo->rpt_fbtc_nullsta.finfo;
if (ver->fcxnullsta == 1) {
for (i = 0; i < 2; i++) {
- seq_printf(m, " %-15s : ", "[NULL-STA]");
- seq_printf(m, "null-%d", i);
- seq_printf(m, "[ok:%d/",
- le32_to_cpu(ns->v1.result[i][1]));
- seq_printf(m, "fail:%d/",
- le32_to_cpu(ns->v1.result[i][0]));
- seq_printf(m, "on_time:%d/",
- le32_to_cpu(ns->v1.result[i][2]));
- seq_printf(m, "retry:%d/",
- le32_to_cpu(ns->v1.result[i][3]));
- seq_printf(m, "avg_t:%d.%03d/",
- le32_to_cpu(ns->v1.avg_t[i]) / 1000,
- le32_to_cpu(ns->v1.avg_t[i]) % 1000);
- seq_printf(m, "max_t:%d.%03d]\n",
- le32_to_cpu(ns->v1.max_t[i]) / 1000,
- le32_to_cpu(ns->v1.max_t[i]) % 1000);
+ p += scnprintf(p, end - p, " %-15s : ", "[NULL-STA]");
+ p += scnprintf(p, end - p, "null-%d", i);
+ p += scnprintf(p, end - p, "[ok:%d/",
+ le32_to_cpu(ns->v1.result[i][1]));
+ p += scnprintf(p, end - p, "fail:%d/",
+ le32_to_cpu(ns->v1.result[i][0]));
+ p += scnprintf(p, end - p, "on_time:%d/",
+ le32_to_cpu(ns->v1.result[i][2]));
+ p += scnprintf(p, end - p, "retry:%d/",
+ le32_to_cpu(ns->v1.result[i][3]));
+ p += scnprintf(p, end - p, "avg_t:%d.%03d/",
+ le32_to_cpu(ns->v1.avg_t[i]) / 1000,
+ le32_to_cpu(ns->v1.avg_t[i]) % 1000);
+ p += scnprintf(p, end - p, "max_t:%d.%03d]\n",
+ le32_to_cpu(ns->v1.max_t[i]) / 1000,
+ le32_to_cpu(ns->v1.max_t[i]) % 1000);
}
} else if (ver->fcxnullsta == 7) {
for (i = 0; i < 2; i++) {
- seq_printf(m, " %-15s : ", "[NULL-STA]");
- seq_printf(m, "null-%d", i);
- seq_printf(m, "[Tx:%d/",
- le32_to_cpu(ns->v7.result[i][4]));
- seq_printf(m, "[ok:%d/",
- le32_to_cpu(ns->v7.result[i][1]));
- seq_printf(m, "fail:%d/",
- le32_to_cpu(ns->v7.result[i][0]));
- seq_printf(m, "on_time:%d/",
- le32_to_cpu(ns->v7.result[i][2]));
- seq_printf(m, "retry:%d/",
- le32_to_cpu(ns->v7.result[i][3]));
- seq_printf(m, "avg_t:%d.%03d/",
- le32_to_cpu(ns->v7.tavg[i]) / 1000,
- le32_to_cpu(ns->v7.tavg[i]) % 1000);
- seq_printf(m, "max_t:%d.%03d]\n",
- le32_to_cpu(ns->v7.tmax[i]) / 1000,
- le32_to_cpu(ns->v7.tmax[i]) % 1000);
+ p += scnprintf(p, end - p, " %-15s : ", "[NULL-STA]");
+ p += scnprintf(p, end - p, "null-%d", i);
+ p += scnprintf(p, end - p, "[Tx:%d/",
+ le32_to_cpu(ns->v7.result[i][4]));
+ p += scnprintf(p, end - p, "[ok:%d/",
+ le32_to_cpu(ns->v7.result[i][1]));
+ p += scnprintf(p, end - p, "fail:%d/",
+ le32_to_cpu(ns->v7.result[i][0]));
+ p += scnprintf(p, end - p, "on_time:%d/",
+ le32_to_cpu(ns->v7.result[i][2]));
+ p += scnprintf(p, end - p, "retry:%d/",
+ le32_to_cpu(ns->v7.result[i][3]));
+ p += scnprintf(p, end - p, "avg_t:%d.%03d/",
+ le32_to_cpu(ns->v7.tavg[i]) / 1000,
+ le32_to_cpu(ns->v7.tavg[i]) % 1000);
+ p += scnprintf(p, end - p, "max_t:%d.%03d]\n",
+ le32_to_cpu(ns->v7.tmax[i]) / 1000,
+ le32_to_cpu(ns->v7.tmax[i]) % 1000);
}
} else {
for (i = 0; i < 2; i++) {
- seq_printf(m, " %-15s : ", "[NULL-STA]");
- seq_printf(m, "null-%d", i);
- seq_printf(m, "[Tx:%d/",
- le32_to_cpu(ns->v2.result[i][4]));
- seq_printf(m, "[ok:%d/",
- le32_to_cpu(ns->v2.result[i][1]));
- seq_printf(m, "fail:%d/",
- le32_to_cpu(ns->v2.result[i][0]));
- seq_printf(m, "on_time:%d/",
- le32_to_cpu(ns->v2.result[i][2]));
- seq_printf(m, "retry:%d/",
- le32_to_cpu(ns->v2.result[i][3]));
- seq_printf(m, "avg_t:%d.%03d/",
- le32_to_cpu(ns->v2.avg_t[i]) / 1000,
- le32_to_cpu(ns->v2.avg_t[i]) % 1000);
- seq_printf(m, "max_t:%d.%03d]\n",
- le32_to_cpu(ns->v2.max_t[i]) / 1000,
- le32_to_cpu(ns->v2.max_t[i]) % 1000);
+ p += scnprintf(p, end - p, " %-15s : ", "[NULL-STA]");
+ p += scnprintf(p, end - p, "null-%d", i);
+ p += scnprintf(p, end - p, "[Tx:%d/",
+ le32_to_cpu(ns->v2.result[i][4]));
+ p += scnprintf(p, end - p, "[ok:%d/",
+ le32_to_cpu(ns->v2.result[i][1]));
+ p += scnprintf(p, end - p, "fail:%d/",
+ le32_to_cpu(ns->v2.result[i][0]));
+ p += scnprintf(p, end - p, "on_time:%d/",
+ le32_to_cpu(ns->v2.result[i][2]));
+ p += scnprintf(p, end - p, "retry:%d/",
+ le32_to_cpu(ns->v2.result[i][3]));
+ p += scnprintf(p, end - p, "avg_t:%d.%03d/",
+ le32_to_cpu(ns->v2.avg_t[i]) / 1000,
+ le32_to_cpu(ns->v2.avg_t[i]) % 1000);
+ p += scnprintf(p, end - p, "max_t:%d.%03d]\n",
+ le32_to_cpu(ns->v2.max_t[i]) / 1000,
+ le32_to_cpu(ns->v2.max_t[i]) % 1000);
}
}
+
+ return p - buf;
}
-static void _show_fbtc_step_v2(struct rtw89_dev *rtwdev, struct seq_file *m)
+static int _show_fbtc_step_v2(struct rtw89_dev *rtwdev, char *buf, size_t bufsz)
{
struct rtw89_btc *btc = &rtwdev->btc;
struct rtw89_btc_btf_fwinfo *pfwinfo = &btc->fwinfo;
struct rtw89_btc_rpt_cmn_info *pcinfo = NULL;
struct rtw89_btc_fbtc_steps_v2 *pstep = NULL;
const struct rtw89_btc_ver *ver = btc->ver;
+ char *p = buf, *end = buf + bufsz;
u8 type, val, cnt = 0, state = 0;
bool outloop = false;
u16 i, diff_t, n_start = 0, n_stop = 0;
@@ -9841,14 +9941,14 @@ static void _show_fbtc_step_v2(struct rtw89_dev *rtwdev, struct seq_file *m)
pcinfo = &pfwinfo->rpt_fbtc_step.cinfo;
if (!pcinfo->valid)
- return;
+ return 0;
pstep = &pfwinfo->rpt_fbtc_step.finfo.v2;
pos_old = le16_to_cpu(pstep->pos_old);
pos_new = le16_to_cpu(pstep->pos_new);
if (pcinfo->req_fver != pstep->fver)
- return;
+ return 0;
/* store step info by using ring instead of FIFO*/
do {
@@ -9877,13 +9977,15 @@ static void _show_fbtc_step_v2(struct rtw89_dev *rtwdev, struct seq_file *m)
continue;
if (cnt % 10 == 0)
- seq_printf(m, " %-15s : ", "[steps]");
+ p += scnprintf(p, end - p,
+ " %-15s : ", "[steps]");
- seq_printf(m, "-> %s(%02d)(%02d)",
- (type == CXSTEP_SLOT ? "SLT" :
- "EVT"), (u32)val, diff_t);
+ p += scnprintf(p, end - p,
+ "-> %s(%02d)(%02d)",
+ (type == CXSTEP_SLOT ? "SLT" :
+ "EVT"), (u32)val, diff_t);
if (cnt % 10 == 9)
- seq_puts(m, "\n");
+ p += scnprintf(p, end - p, "\n");
cnt++;
}
@@ -9900,29 +10002,32 @@ static void _show_fbtc_step_v2(struct rtw89_dev *rtwdev, struct seq_file *m)
break;
}
} while (!outloop);
+
+ return p - buf;
}
-static void _show_fbtc_step_v3(struct rtw89_dev *rtwdev, struct seq_file *m)
+static int _show_fbtc_step_v3(struct rtw89_dev *rtwdev, char *buf, size_t bufsz)
{
struct rtw89_btc *btc = &rtwdev->btc;
struct rtw89_btc_btf_fwinfo *pfwinfo = &btc->fwinfo;
struct rtw89_btc_rpt_cmn_info *pcinfo;
struct rtw89_btc_fbtc_steps_v3 *pstep;
u32 i, n_begin, n_end, array_idx, cnt = 0;
+ char *p = buf, *end = buf + bufsz;
u8 type, val;
u16 diff_t;
if ((pfwinfo->rpt_en_map &
rtw89_btc_fw_rpt_ver(rtwdev, RPT_EN_FW_STEP_INFO)) == 0)
- return;
+ return 0;
pcinfo = &pfwinfo->rpt_fbtc_step.cinfo;
if (!pcinfo->valid)
- return;
+ return 0;
pstep = &pfwinfo->rpt_fbtc_step.finfo.v3;
if (pcinfo->req_fver != pstep->fver)
- return;
+ return 0;
if (le32_to_cpu(pstep->cnt) <= FCXDEF_STEP)
n_begin = 1;
@@ -9932,7 +10037,7 @@ static void _show_fbtc_step_v3(struct rtw89_dev *rtwdev, struct seq_file *m)
n_end = le32_to_cpu(pstep->cnt);
if (n_begin > n_end)
- return;
+ return 0;
/* restore step info by using ring instead of FIFO */
for (i = n_begin; i <= n_end; i++) {
@@ -9945,50 +10050,55 @@ static void _show_fbtc_step_v3(struct rtw89_dev *rtwdev, struct seq_file *m)
continue;
if (cnt % 10 == 0)
- seq_printf(m, " %-15s : ", "[steps]");
+ p += scnprintf(p, end - p, " %-15s : ", "[steps]");
- seq_printf(m, "-> %s(%02d)",
- (type == CXSTEP_SLOT ?
- id_to_slot((u32)val) :
- id_to_evt((u32)val)), diff_t);
+ p += scnprintf(p, end - p, "-> %s(%02d)",
+ (type == CXSTEP_SLOT ?
+ id_to_slot((u32)val) :
+ id_to_evt((u32)val)), diff_t);
if (cnt % 10 == 9)
- seq_puts(m, "\n");
+ p += scnprintf(p, end - p, "\n");
cnt++;
}
+
+ return p - buf;
}
-static void _show_fw_dm_msg(struct rtw89_dev *rtwdev, struct seq_file *m)
+static int _show_fw_dm_msg(struct rtw89_dev *rtwdev, char *buf, size_t bufsz)
{
struct rtw89_btc *btc = &rtwdev->btc;
const struct rtw89_btc_ver *ver = btc->ver;
+ char *p = buf, *end = buf + bufsz;
if (!(btc->dm.coex_info_map & BTC_COEX_INFO_DM))
- return;
+ goto out;
- _show_error(rtwdev, m);
- _show_fbtc_tdma(rtwdev, m);
- _show_fbtc_slots(rtwdev, m);
+ p += _show_error(rtwdev, p, end - p);
+ p += _show_fbtc_tdma(rtwdev, p, end - p);
+ p += _show_fbtc_slots(rtwdev, p, end - p);
if (ver->fcxcysta == 2)
- _show_fbtc_cysta_v2(rtwdev, m);
+ p += _show_fbtc_cysta_v2(rtwdev, p, end - p);
else if (ver->fcxcysta == 3)
- _show_fbtc_cysta_v3(rtwdev, m);
+ p += _show_fbtc_cysta_v3(rtwdev, p, end - p);
else if (ver->fcxcysta == 4)
- _show_fbtc_cysta_v4(rtwdev, m);
+ p += _show_fbtc_cysta_v4(rtwdev, p, end - p);
else if (ver->fcxcysta == 5)
- _show_fbtc_cysta_v5(rtwdev, m);
+ p += _show_fbtc_cysta_v5(rtwdev, p, end - p);
else if (ver->fcxcysta == 7)
- _show_fbtc_cysta_v7(rtwdev, m);
+ p += _show_fbtc_cysta_v7(rtwdev, p, end - p);
- _show_fbtc_nullsta(rtwdev, m);
+ p += _show_fbtc_nullsta(rtwdev, p, end - p);
if (ver->fcxstep == 2)
- _show_fbtc_step_v2(rtwdev, m);
+ p += _show_fbtc_step_v2(rtwdev, p, end - p);
else if (ver->fcxstep == 3)
- _show_fbtc_step_v3(rtwdev, m);
+ p += _show_fbtc_step_v3(rtwdev, p, end - p);
+out:
+ return p - buf;
}
static void _get_gnt(struct rtw89_dev *rtwdev, struct rtw89_mac_ax_coex_gnt *gnt_cfg)
@@ -10033,12 +10143,13 @@ static void _get_gnt(struct rtw89_dev *rtwdev, struct rtw89_mac_ax_coex_gnt *gnt
}
}
-static void _show_gpio_dbg(struct rtw89_dev *rtwdev, struct seq_file *m)
+static int _show_gpio_dbg(struct rtw89_dev *rtwdev, char *buf, size_t bufsz)
{
struct rtw89_btc_btf_fwinfo *pfwinfo = &rtwdev->btc.fwinfo;
const struct rtw89_btc_ver *ver = rtwdev->btc.ver;
struct rtw89_btc_rpt_cmn_info *pcinfo = NULL;
union rtw89_btc_fbtc_gpio_dbg *gdbg = NULL;
+ char *p = buf, *end = buf + bufsz;
u8 *gpio_map, i;
u32 en_map;
@@ -10048,8 +10159,8 @@ static void _show_gpio_dbg(struct rtw89_dev *rtwdev, struct seq_file *m)
rtw89_debug(rtwdev, RTW89_DBG_BTC,
"[BTC], %s(): stop due rpt_fbtc_gpio_dbg.cinfo\n",
__func__);
- seq_puts(m, "\n");
- return;
+ p += scnprintf(p, end - p, "\n");
+ goto out;
}
if (ver->fcxgpiodbg == 7) {
@@ -10061,20 +10172,24 @@ static void _show_gpio_dbg(struct rtw89_dev *rtwdev, struct seq_file *m)
}
if (!en_map)
- return;
+ goto out;
- seq_printf(m, " %-15s : enable_map:0x%08x",
- "[gpio_dbg]", en_map);
+ p += scnprintf(p, end - p, " %-15s : enable_map:0x%08x",
+ "[gpio_dbg]", en_map);
for (i = 0; i < BTC_DBG_MAX1; i++) {
if (!(en_map & BIT(i)))
continue;
- seq_printf(m, ", %s->GPIO%d", id_to_gdbg(i), gpio_map[i]);
+ p += scnprintf(p, end - p, ", %s->GPIO%d", id_to_gdbg(i),
+ gpio_map[i]);
}
- seq_puts(m, "\n");
+ p += scnprintf(p, end - p, "\n");
+
+out:
+ return p - buf;
}
-static void _show_mreg_v1(struct rtw89_dev *rtwdev, struct seq_file *m)
+static int _show_mreg_v1(struct rtw89_dev *rtwdev, char *buf, size_t bufsz)
{
const struct rtw89_chip_info *chip = rtwdev->chip;
struct rtw89_btc *btc = &rtwdev->btc;
@@ -10086,45 +10201,47 @@ static void _show_mreg_v1(struct rtw89_dev *rtwdev, struct seq_file *m)
struct rtw89_btc_bt_info *bt = &btc->cx.bt;
struct rtw89_mac_ax_coex_gnt gnt_cfg = {};
struct rtw89_mac_ax_gnt gnt;
+ char *p = buf, *end = buf + bufsz;
u8 i = 0, type = 0, cnt = 0;
u32 val, offset;
if (!(btc->dm.coex_info_map & BTC_COEX_INFO_MREG))
- return;
+ return 0;
- seq_puts(m, "========== [HW Status] ==========\n");
+ p += scnprintf(p, end - p, "========== [HW Status] ==========\n");
- seq_printf(m,
- " %-15s : WL->BT:0x%08x(cnt:%d), BT->WL:0x%08x(total:%d, bt_update:%d)\n",
- "[scoreboard]", wl->scbd, cx->cnt_wl[BTC_WCNT_SCBDUPDATE],
- bt->scbd, cx->cnt_bt[BTC_BCNT_SCBDREAD],
- cx->cnt_bt[BTC_BCNT_SCBDUPDATE]);
+ p += scnprintf(p, end - p,
+ " %-15s : WL->BT:0x%08x(cnt:%d), BT->WL:0x%08x(total:%d, bt_update:%d)\n",
+ "[scoreboard]", wl->scbd,
+ cx->cnt_wl[BTC_WCNT_SCBDUPDATE],
+ bt->scbd, cx->cnt_bt[BTC_BCNT_SCBDREAD],
+ cx->cnt_bt[BTC_BCNT_SCBDUPDATE]);
btc->dm.pta_owner = rtw89_mac_get_ctrl_path(rtwdev);
_get_gnt(rtwdev, &gnt_cfg);
gnt = gnt_cfg.band[0];
- seq_printf(m,
- " %-15s : pta_owner:%s, phy-0[gnt_wl:%s-%d/gnt_bt:%s-%d], ",
- "[gnt_status]",
- chip->chip_id == RTL8852C ? "HW" :
- btc->dm.pta_owner == BTC_CTRL_BY_WL ? "WL" : "BT",
- gnt.gnt_wl_sw_en ? "SW" : "HW", gnt.gnt_wl,
- gnt.gnt_bt_sw_en ? "SW" : "HW", gnt.gnt_bt);
+ p += scnprintf(p, end - p,
+ " %-15s : pta_owner:%s, phy-0[gnt_wl:%s-%d/gnt_bt:%s-%d], ",
+ "[gnt_status]",
+ chip->chip_id == RTL8852C ? "HW" :
+ btc->dm.pta_owner == BTC_CTRL_BY_WL ? "WL" : "BT",
+ gnt.gnt_wl_sw_en ? "SW" : "HW", gnt.gnt_wl,
+ gnt.gnt_bt_sw_en ? "SW" : "HW", gnt.gnt_bt);
gnt = gnt_cfg.band[1];
- seq_printf(m, "phy-1[gnt_wl:%s-%d/gnt_bt:%s-%d]\n",
- gnt.gnt_wl_sw_en ? "SW" : "HW",
- gnt.gnt_wl,
- gnt.gnt_bt_sw_en ? "SW" : "HW",
- gnt.gnt_bt);
+ p += scnprintf(p, end - p, "phy-1[gnt_wl:%s-%d/gnt_bt:%s-%d]\n",
+ gnt.gnt_wl_sw_en ? "SW" : "HW",
+ gnt.gnt_wl,
+ gnt.gnt_bt_sw_en ? "SW" : "HW",
+ gnt.gnt_bt);
pcinfo = &pfwinfo->rpt_fbtc_mregval.cinfo;
if (!pcinfo->valid) {
rtw89_debug(rtwdev, RTW89_DBG_BTC,
"[BTC], %s(): stop due rpt_fbtc_mregval.cinfo\n",
__func__);
- return;
+ goto out;
}
pmreg = &pfwinfo->rpt_fbtc_mregval.finfo.v1;
@@ -10138,21 +10255,26 @@ static void _show_mreg_v1(struct rtw89_dev *rtwdev, struct seq_file *m)
val = le32_to_cpu(pmreg->mreg_val[i]);
if (cnt % 6 == 0)
- seq_printf(m, " %-15s : %d_0x%04x=0x%08x",
- "[reg]", (u32)type, offset, val);
+ p += scnprintf(p, end - p,
+ " %-15s : %d_0x%04x=0x%08x",
+ "[reg]", (u32)type, offset, val);
else
- seq_printf(m, ", %d_0x%04x=0x%08x", (u32)type,
- offset, val);
+ p += scnprintf(p, end - p, ", %d_0x%04x=0x%08x",
+ (u32)type,
+ offset, val);
if (cnt % 6 == 5)
- seq_puts(m, "\n");
+ p += scnprintf(p, end - p, "\n");
cnt++;
if (i >= pmreg->reg_num)
- seq_puts(m, "\n");
+ p += scnprintf(p, end - p, "\n");
}
+
+out:
+ return p - buf;
}
-static void _show_mreg_v2(struct rtw89_dev *rtwdev, struct seq_file *m)
+static int _show_mreg_v2(struct rtw89_dev *rtwdev, char *buf, size_t bufsz)
{
const struct rtw89_chip_info *chip = rtwdev->chip;
struct rtw89_btc *btc = &rtwdev->btc;
@@ -10164,46 +10286,48 @@ static void _show_mreg_v2(struct rtw89_dev *rtwdev, struct seq_file *m)
struct rtw89_btc_bt_info *bt = &btc->cx.bt;
struct rtw89_mac_ax_coex_gnt gnt_cfg = {};
struct rtw89_mac_ax_gnt gnt;
+ char *p = buf, *end = buf + bufsz;
u8 i = 0, type = 0, cnt = 0;
u32 val, offset;
if (!(btc->dm.coex_info_map & BTC_COEX_INFO_MREG))
- return;
+ return 0;
- seq_puts(m, "========== [HW Status] ==========\n");
+ p += scnprintf(p, end - p, "========== [HW Status] ==========\n");
- seq_printf(m,
- " %-15s : WL->BT:0x%08x(cnt:%d), BT->WL:0x%08x(total:%d, bt_update:%d)\n",
- "[scoreboard]", wl->scbd, cx->cnt_wl[BTC_WCNT_SCBDUPDATE],
- bt->scbd, cx->cnt_bt[BTC_BCNT_SCBDREAD],
- cx->cnt_bt[BTC_BCNT_SCBDUPDATE]);
+ p += scnprintf(p, end - p,
+ " %-15s : WL->BT:0x%08x(cnt:%d), BT->WL:0x%08x(total:%d, bt_update:%d)\n",
+ "[scoreboard]", wl->scbd,
+ cx->cnt_wl[BTC_WCNT_SCBDUPDATE],
+ bt->scbd, cx->cnt_bt[BTC_BCNT_SCBDREAD],
+ cx->cnt_bt[BTC_BCNT_SCBDUPDATE]);
btc->dm.pta_owner = rtw89_mac_get_ctrl_path(rtwdev);
_get_gnt(rtwdev, &gnt_cfg);
gnt = gnt_cfg.band[0];
- seq_printf(m,
- " %-15s : pta_owner:%s, phy-0[gnt_wl:%s-%d/gnt_bt:%s-%d], polut_type:%s",
- "[gnt_status]",
- chip->chip_id == RTL8852C ? "HW" :
- btc->dm.pta_owner == BTC_CTRL_BY_WL ? "WL" : "BT",
- gnt.gnt_wl_sw_en ? "SW" : "HW", gnt.gnt_wl,
- gnt.gnt_bt_sw_en ? "SW" : "HW", gnt.gnt_bt,
- id_to_polut(wl->bt_polut_type[wl->pta_req_mac]));
+ p += scnprintf(p, end - p,
+ " %-15s : pta_owner:%s, phy-0[gnt_wl:%s-%d/gnt_bt:%s-%d], polut_type:%s",
+ "[gnt_status]",
+ chip->chip_id == RTL8852C ? "HW" :
+ btc->dm.pta_owner == BTC_CTRL_BY_WL ? "WL" : "BT",
+ gnt.gnt_wl_sw_en ? "SW" : "HW", gnt.gnt_wl,
+ gnt.gnt_bt_sw_en ? "SW" : "HW", gnt.gnt_bt,
+ id_to_polut(wl->bt_polut_type[wl->pta_req_mac]));
gnt = gnt_cfg.band[1];
- seq_printf(m, "phy-1[gnt_wl:%s-%d/gnt_bt:%s-%d]\n",
- gnt.gnt_wl_sw_en ? "SW" : "HW",
- gnt.gnt_wl,
- gnt.gnt_bt_sw_en ? "SW" : "HW",
- gnt.gnt_bt);
+ p += scnprintf(p, end - p, "phy-1[gnt_wl:%s-%d/gnt_bt:%s-%d]\n",
+ gnt.gnt_wl_sw_en ? "SW" : "HW",
+ gnt.gnt_wl,
+ gnt.gnt_bt_sw_en ? "SW" : "HW",
+ gnt.gnt_bt);
pcinfo = &pfwinfo->rpt_fbtc_mregval.cinfo;
if (!pcinfo->valid) {
rtw89_debug(rtwdev, RTW89_DBG_BTC,
"[BTC], %s(): stop due rpt_fbtc_mregval.cinfo\n",
__func__);
- return;
+ goto out;
}
pmreg = &pfwinfo->rpt_fbtc_mregval.finfo.v2;
@@ -10217,21 +10341,26 @@ static void _show_mreg_v2(struct rtw89_dev *rtwdev, struct seq_file *m)
val = le32_to_cpu(pmreg->mreg_val[i]);
if (cnt % 6 == 0)
- seq_printf(m, " %-15s : %d_0x%04x=0x%08x",
- "[reg]", (u32)type, offset, val);
+ p += scnprintf(p, end - p,
+ " %-15s : %d_0x%04x=0x%08x",
+ "[reg]", (u32)type, offset, val);
else
- seq_printf(m, ", %d_0x%04x=0x%08x", (u32)type,
- offset, val);
+ p += scnprintf(p, end - p, ", %d_0x%04x=0x%08x",
+ (u32)type,
+ offset, val);
if (cnt % 6 == 5)
- seq_puts(m, "\n");
+ p += scnprintf(p, end - p, "\n");
cnt++;
if (i >= pmreg->reg_num)
- seq_puts(m, "\n");
+ p += scnprintf(p, end - p, "\n");
}
+
+out:
+ return p - buf;
}
-static void _show_mreg_v7(struct rtw89_dev *rtwdev, struct seq_file *m)
+static int _show_mreg_v7(struct rtw89_dev *rtwdev, char *buf, size_t bufsz)
{
struct rtw89_btc *btc = &rtwdev->btc;
struct rtw89_btc_btf_fwinfo *pfwinfo = &btc->fwinfo;
@@ -10242,46 +10371,50 @@ static void _show_mreg_v7(struct rtw89_dev *rtwdev, struct seq_file *m)
struct rtw89_btc_bt_info *bt = &cx->bt;
struct rtw89_mac_ax_gnt *gnt = NULL;
struct rtw89_btc_dm *dm = &btc->dm;
+ char *p = buf, *end = buf + bufsz;
u8 i, type, cnt = 0;
u32 val, offset;
if (!(dm->coex_info_map & BTC_COEX_INFO_MREG))
- return;
+ return 0;
- seq_puts(m, "\n\r========== [HW Status] ==========");
+ p += scnprintf(p, end - p, "\n\r========== [HW Status] ==========");
- seq_printf(m,
- "\n\r %-15s : WL->BT:0x%08x(cnt:%d), BT->WL:0x%08x(total:%d, bt_update:%d)",
- "[scoreboard]", wl->scbd, cx->cnt_wl[BTC_WCNT_SCBDUPDATE],
- bt->scbd, cx->cnt_bt[BTC_BCNT_SCBDREAD],
- cx->cnt_bt[BTC_BCNT_SCBDUPDATE]);
+ p += scnprintf(p, end - p,
+ "\n\r %-15s : WL->BT:0x%08x(cnt:%d), BT->WL:0x%08x(total:%d, bt_update:%d)",
+ "[scoreboard]", wl->scbd,
+ cx->cnt_wl[BTC_WCNT_SCBDUPDATE],
+ bt->scbd, cx->cnt_bt[BTC_BCNT_SCBDREAD],
+ cx->cnt_bt[BTC_BCNT_SCBDUPDATE]);
/* To avoid I/O if WL LPS or power-off */
dm->pta_owner = rtw89_mac_get_ctrl_path(rtwdev);
- seq_printf(m,
- "\n\r %-15s : pta_owner:%s, pta_req_mac:MAC%d, rf_gnt_source: polut_type:%s",
- "[gnt_status]",
- rtwdev->chip->para_ver & BTC_FEAT_PTA_ONOFF_CTRL ? "HW" :
- dm->pta_owner == BTC_CTRL_BY_WL ? "WL" : "BT",
- wl->pta_req_mac, id_to_polut(wl->bt_polut_type[wl->pta_req_mac]));
+ p += scnprintf(p, end - p,
+ "\n\r %-15s : pta_owner:%s, pta_req_mac:MAC%d, rf_gnt_source: polut_type:%s",
+ "[gnt_status]",
+ rtwdev->chip->para_ver & BTC_FEAT_PTA_ONOFF_CTRL ? "HW" :
+ dm->pta_owner == BTC_CTRL_BY_WL ? "WL" : "BT",
+ wl->pta_req_mac,
+ id_to_polut(wl->bt_polut_type[wl->pta_req_mac]));
gnt = &dm->gnt.band[RTW89_PHY_0];
- seq_printf(m, ", phy-0[gnt_wl:%s-%d/gnt_bt:%s-%d]",
- gnt->gnt_wl_sw_en ? "SW" : "HW", gnt->gnt_wl,
- gnt->gnt_bt_sw_en ? "SW" : "HW", gnt->gnt_bt);
+ p += scnprintf(p, end - p, ", phy-0[gnt_wl:%s-%d/gnt_bt:%s-%d]",
+ gnt->gnt_wl_sw_en ? "SW" : "HW", gnt->gnt_wl,
+ gnt->gnt_bt_sw_en ? "SW" : "HW", gnt->gnt_bt);
if (rtwdev->dbcc_en) {
gnt = &dm->gnt.band[RTW89_PHY_1];
- seq_printf(m, ", phy-1[gnt_wl:%s-%d/gnt_bt:%s-%d]",
- gnt->gnt_wl_sw_en ? "SW" : "HW", gnt->gnt_wl,
- gnt->gnt_bt_sw_en ? "SW" : "HW", gnt->gnt_bt);
+ p += scnprintf(p, end - p,
+ ", phy-1[gnt_wl:%s-%d/gnt_bt:%s-%d]",
+ gnt->gnt_wl_sw_en ? "SW" : "HW", gnt->gnt_wl,
+ gnt->gnt_bt_sw_en ? "SW" : "HW", gnt->gnt_bt);
}
pcinfo = &pfwinfo->rpt_fbtc_mregval.cinfo;
if (!pcinfo->valid)
- return;
+ goto out;
pmreg = &pfwinfo->rpt_fbtc_mregval.finfo.v7;
@@ -10291,17 +10424,21 @@ static void _show_mreg_v7(struct rtw89_dev *rtwdev, struct seq_file *m)
val = le32_to_cpu(pmreg->mreg_val[i]);
if (cnt % 6 == 0)
- seq_printf(m, "\n\r %-15s : %s_0x%x=0x%x", "[reg]",
- id_to_regtype(type), offset, val);
+ p += scnprintf(p, end - p,
+ "\n\r %-15s : %s_0x%x=0x%x", "[reg]",
+ id_to_regtype(type), offset, val);
else
- seq_printf(m, ", %s_0x%x=0x%x",
- id_to_regtype(type), offset, val);
+ p += scnprintf(p, end - p, ", %s_0x%x=0x%x",
+ id_to_regtype(type), offset, val);
cnt++;
}
- seq_puts(m, "\n");
+ p += scnprintf(p, end - p, "\n");
+
+out:
+ return p - buf;
}
-static void _show_summary_v1(struct rtw89_dev *rtwdev, struct seq_file *m)
+static int _show_summary_v1(struct rtw89_dev *rtwdev, char *buf, size_t bufsz)
{
struct rtw89_btc *btc = &rtwdev->btc;
struct rtw89_btc_btf_fwinfo *pfwinfo = &btc->fwinfo;
@@ -10312,56 +10449,59 @@ static void _show_summary_v1(struct rtw89_dev *rtwdev, struct seq_file *m)
struct rtw89_btc_wl_info *wl = &cx->wl;
struct rtw89_btc_bt_info *bt = &cx->bt;
u32 cnt_sum = 0, *cnt = btc->dm.cnt_notify;
+ char *p = buf, *end = buf + bufsz;
u8 i;
if (!(dm->coex_info_map & BTC_COEX_INFO_SUMMARY))
- return;
+ return 0;
- seq_puts(m, "========== [Statistics] ==========\n");
+ p += scnprintf(p, end - p, "========== [Statistics] ==========\n");
pcinfo = &pfwinfo->rpt_ctrl.cinfo;
if (pcinfo->valid && !wl->status.map.lps && !wl->status.map.rf_off) {
prptctrl = &pfwinfo->rpt_ctrl.finfo.v1;
- seq_printf(m,
- " %-15s : h2c_cnt=%d(fail:%d, fw_recv:%d), c2h_cnt=%d(fw_send:%d), ",
- "[summary]", pfwinfo->cnt_h2c,
- pfwinfo->cnt_h2c_fail, prptctrl->h2c_cnt,
- pfwinfo->cnt_c2h, prptctrl->c2h_cnt);
+ p += scnprintf(p, end - p,
+ " %-15s : h2c_cnt=%d(fail:%d, fw_recv:%d), c2h_cnt=%d(fw_send:%d), ",
+ "[summary]", pfwinfo->cnt_h2c,
+ pfwinfo->cnt_h2c_fail, prptctrl->h2c_cnt,
+ pfwinfo->cnt_c2h, prptctrl->c2h_cnt);
- seq_printf(m,
- "rpt_cnt=%d(fw_send:%d), rpt_map=0x%x, dm_error_map:0x%x",
- pfwinfo->event[BTF_EVNT_RPT], prptctrl->rpt_cnt,
- prptctrl->rpt_enable, dm->error.val);
+ p += scnprintf(p, end - p,
+ "rpt_cnt=%d(fw_send:%d), rpt_map=0x%x, dm_error_map:0x%x",
+ pfwinfo->event[BTF_EVNT_RPT],
+ prptctrl->rpt_cnt,
+ prptctrl->rpt_enable, dm->error.val);
if (dm->error.map.wl_fw_hang)
- seq_puts(m, " (WL FW Hang!!)");
- seq_puts(m, "\n");
- seq_printf(m,
- " %-15s : send_ok:%d, send_fail:%d, recv:%d",
- "[mailbox]", prptctrl->mb_send_ok_cnt,
- prptctrl->mb_send_fail_cnt, prptctrl->mb_recv_cnt);
-
- seq_printf(m,
- "(A2DP_empty:%d, A2DP_flowstop:%d, A2DP_full:%d)\n",
- prptctrl->mb_a2dp_empty_cnt,
- prptctrl->mb_a2dp_flct_cnt,
- prptctrl->mb_a2dp_full_cnt);
-
- seq_printf(m,
- " %-15s : wl_rfk[req:%d/go:%d/reject:%d/timeout:%d]",
- "[RFK]", cx->cnt_wl[BTC_WCNT_RFK_REQ],
- cx->cnt_wl[BTC_WCNT_RFK_GO],
- cx->cnt_wl[BTC_WCNT_RFK_REJECT],
- cx->cnt_wl[BTC_WCNT_RFK_TIMEOUT]);
-
- seq_printf(m,
- ", bt_rfk[req:%d/go:%d/reject:%d/timeout:%d/fail:%d]\n",
- prptctrl->bt_rfk_cnt[BTC_BCNT_RFK_REQ],
- prptctrl->bt_rfk_cnt[BTC_BCNT_RFK_GO],
- prptctrl->bt_rfk_cnt[BTC_BCNT_RFK_REJECT],
- prptctrl->bt_rfk_cnt[BTC_BCNT_RFK_TIMEOUT],
- prptctrl->bt_rfk_cnt[BTC_BCNT_RFK_FAIL]);
+ p += scnprintf(p, end - p, " (WL FW Hang!!)");
+ p += scnprintf(p, end - p, "\n");
+ p += scnprintf(p, end - p,
+ " %-15s : send_ok:%d, send_fail:%d, recv:%d",
+ "[mailbox]", prptctrl->mb_send_ok_cnt,
+ prptctrl->mb_send_fail_cnt,
+ prptctrl->mb_recv_cnt);
+
+ p += scnprintf(p, end - p,
+ "(A2DP_empty:%d, A2DP_flowstop:%d, A2DP_full:%d)\n",
+ prptctrl->mb_a2dp_empty_cnt,
+ prptctrl->mb_a2dp_flct_cnt,
+ prptctrl->mb_a2dp_full_cnt);
+
+ p += scnprintf(p, end - p,
+ " %-15s : wl_rfk[req:%d/go:%d/reject:%d/timeout:%d]",
+ "[RFK]", cx->cnt_wl[BTC_WCNT_RFK_REQ],
+ cx->cnt_wl[BTC_WCNT_RFK_GO],
+ cx->cnt_wl[BTC_WCNT_RFK_REJECT],
+ cx->cnt_wl[BTC_WCNT_RFK_TIMEOUT]);
+
+ p += scnprintf(p, end - p,
+ ", bt_rfk[req:%d/go:%d/reject:%d/timeout:%d/fail:%d]\n",
+ prptctrl->bt_rfk_cnt[BTC_BCNT_RFK_REQ],
+ prptctrl->bt_rfk_cnt[BTC_BCNT_RFK_GO],
+ prptctrl->bt_rfk_cnt[BTC_BCNT_RFK_REJECT],
+ prptctrl->bt_rfk_cnt[BTC_BCNT_RFK_TIMEOUT],
+ prptctrl->bt_rfk_cnt[BTC_BCNT_RFK_FAIL]);
if (prptctrl->bt_rfk_cnt[BTC_BCNT_RFK_TIMEOUT] > 0)
bt->rfk_info.map.timeout = 1;
@@ -10370,42 +10510,44 @@ static void _show_summary_v1(struct rtw89_dev *rtwdev, struct seq_file *m)
dm->error.map.wl_rfk_timeout = bt->rfk_info.map.timeout;
} else {
- seq_printf(m,
- " %-15s : h2c_cnt=%d(fail:%d), c2h_cnt=%d, rpt_cnt=%d, rpt_map=0x%x",
- "[summary]", pfwinfo->cnt_h2c,
- pfwinfo->cnt_h2c_fail, pfwinfo->cnt_c2h,
- pfwinfo->event[BTF_EVNT_RPT],
- btc->fwinfo.rpt_en_map);
- seq_puts(m, " (WL FW report invalid!!)\n");
+ p += scnprintf(p, end - p,
+ " %-15s : h2c_cnt=%d(fail:%d), c2h_cnt=%d, rpt_cnt=%d, rpt_map=0x%x",
+ "[summary]", pfwinfo->cnt_h2c,
+ pfwinfo->cnt_h2c_fail, pfwinfo->cnt_c2h,
+ pfwinfo->event[BTF_EVNT_RPT],
+ btc->fwinfo.rpt_en_map);
+ p += scnprintf(p, end - p, " (WL FW report invalid!!)\n");
}
for (i = 0; i < BTC_NCNT_NUM; i++)
cnt_sum += dm->cnt_notify[i];
- seq_printf(m,
- " %-15s : total=%d, show_coex_info=%d, power_on=%d, init_coex=%d, ",
- "[notify_cnt]", cnt_sum, cnt[BTC_NCNT_SHOW_COEX_INFO],
- cnt[BTC_NCNT_POWER_ON], cnt[BTC_NCNT_INIT_COEX]);
+ p += scnprintf(p, end - p,
+ " %-15s : total=%d, show_coex_info=%d, power_on=%d, init_coex=%d, ",
+ "[notify_cnt]", cnt_sum, cnt[BTC_NCNT_SHOW_COEX_INFO],
+ cnt[BTC_NCNT_POWER_ON], cnt[BTC_NCNT_INIT_COEX]);
+
+ p += scnprintf(p, end - p,
+ "power_off=%d, radio_state=%d, role_info=%d, wl_rfk=%d, wl_sta=%d\n",
+ cnt[BTC_NCNT_POWER_OFF], cnt[BTC_NCNT_RADIO_STATE],
+ cnt[BTC_NCNT_ROLE_INFO], cnt[BTC_NCNT_WL_RFK],
+ cnt[BTC_NCNT_WL_STA]);
- seq_printf(m,
- "power_off=%d, radio_state=%d, role_info=%d, wl_rfk=%d, wl_sta=%d\n",
- cnt[BTC_NCNT_POWER_OFF], cnt[BTC_NCNT_RADIO_STATE],
- cnt[BTC_NCNT_ROLE_INFO], cnt[BTC_NCNT_WL_RFK],
- cnt[BTC_NCNT_WL_STA]);
+ p += scnprintf(p, end - p,
+ " %-15s : scan_start=%d, scan_finish=%d, switch_band=%d, special_pkt=%d, ",
+ "[notify_cnt]", cnt[BTC_NCNT_SCAN_START],
+ cnt[BTC_NCNT_SCAN_FINISH], cnt[BTC_NCNT_SWITCH_BAND],
+ cnt[BTC_NCNT_SPECIAL_PACKET]);
- seq_printf(m,
- " %-15s : scan_start=%d, scan_finish=%d, switch_band=%d, special_pkt=%d, ",
- "[notify_cnt]", cnt[BTC_NCNT_SCAN_START],
- cnt[BTC_NCNT_SCAN_FINISH], cnt[BTC_NCNT_SWITCH_BAND],
- cnt[BTC_NCNT_SPECIAL_PACKET]);
+ p += scnprintf(p, end - p,
+ "timer=%d, control=%d, customerize=%d\n",
+ cnt[BTC_NCNT_TIMER], cnt[BTC_NCNT_CONTROL],
+ cnt[BTC_NCNT_CUSTOMERIZE]);
- seq_printf(m,
- "timer=%d, control=%d, customerize=%d\n",
- cnt[BTC_NCNT_TIMER], cnt[BTC_NCNT_CONTROL],
- cnt[BTC_NCNT_CUSTOMERIZE]);
+ return p - buf;
}
-static void _show_summary_v4(struct rtw89_dev *rtwdev, struct seq_file *m)
+static int _show_summary_v4(struct rtw89_dev *rtwdev, char *buf, size_t bufsz)
{
struct rtw89_btc *btc = &rtwdev->btc;
struct rtw89_btc_btf_fwinfo *pfwinfo = &btc->fwinfo;
@@ -10416,64 +10558,65 @@ static void _show_summary_v4(struct rtw89_dev *rtwdev, struct seq_file *m)
struct rtw89_btc_wl_info *wl = &cx->wl;
struct rtw89_btc_bt_info *bt = &cx->bt;
u32 cnt_sum = 0, *cnt = btc->dm.cnt_notify;
+ char *p = buf, *end = buf + bufsz;
u8 i;
if (!(dm->coex_info_map & BTC_COEX_INFO_SUMMARY))
- return;
+ return 0;
- seq_puts(m, "========== [Statistics] ==========\n");
+ p += scnprintf(p, end - p, "========== [Statistics] ==========\n");
pcinfo = &pfwinfo->rpt_ctrl.cinfo;
if (pcinfo->valid && !wl->status.map.lps && !wl->status.map.rf_off) {
prptctrl = &pfwinfo->rpt_ctrl.finfo.v4;
- seq_printf(m,
- " %-15s : h2c_cnt=%d(fail:%d, fw_recv:%d), c2h_cnt=%d(fw_send:%d), ",
- "[summary]", pfwinfo->cnt_h2c,
- pfwinfo->cnt_h2c_fail,
- le32_to_cpu(prptctrl->rpt_info.cnt_h2c),
- pfwinfo->cnt_c2h,
- le32_to_cpu(prptctrl->rpt_info.cnt_c2h));
-
- seq_printf(m,
- "rpt_cnt=%d(fw_send:%d), rpt_map=0x%x, dm_error_map:0x%x",
- pfwinfo->event[BTF_EVNT_RPT],
- le32_to_cpu(prptctrl->rpt_info.cnt),
- le32_to_cpu(prptctrl->rpt_info.en),
- dm->error.val);
+ p += scnprintf(p, end - p,
+ " %-15s : h2c_cnt=%d(fail:%d, fw_recv:%d), c2h_cnt=%d(fw_send:%d), ",
+ "[summary]", pfwinfo->cnt_h2c,
+ pfwinfo->cnt_h2c_fail,
+ le32_to_cpu(prptctrl->rpt_info.cnt_h2c),
+ pfwinfo->cnt_c2h,
+ le32_to_cpu(prptctrl->rpt_info.cnt_c2h));
+
+ p += scnprintf(p, end - p,
+ "rpt_cnt=%d(fw_send:%d), rpt_map=0x%x, dm_error_map:0x%x",
+ pfwinfo->event[BTF_EVNT_RPT],
+ le32_to_cpu(prptctrl->rpt_info.cnt),
+ le32_to_cpu(prptctrl->rpt_info.en),
+ dm->error.val);
if (dm->error.map.wl_fw_hang)
- seq_puts(m, " (WL FW Hang!!)");
- seq_puts(m, "\n");
- seq_printf(m,
- " %-15s : send_ok:%d, send_fail:%d, recv:%d, ",
- "[mailbox]",
- le32_to_cpu(prptctrl->bt_mbx_info.cnt_send_ok),
- le32_to_cpu(prptctrl->bt_mbx_info.cnt_send_fail),
- le32_to_cpu(prptctrl->bt_mbx_info.cnt_recv));
-
- seq_printf(m,
- "A2DP_empty:%d(stop:%d, tx:%d, ack:%d, nack:%d)\n",
- le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_empty),
- le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_flowctrl),
- le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_tx),
- le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_ack),
- le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_nack));
-
- seq_printf(m,
- " %-15s : wl_rfk[req:%d/go:%d/reject:%d/timeout:%d]",
- "[RFK]", cx->cnt_wl[BTC_WCNT_RFK_REQ],
- cx->cnt_wl[BTC_WCNT_RFK_GO],
- cx->cnt_wl[BTC_WCNT_RFK_REJECT],
- cx->cnt_wl[BTC_WCNT_RFK_TIMEOUT]);
-
- seq_printf(m,
- ", bt_rfk[req:%d/go:%d/reject:%d/timeout:%d/fail:%d]\n",
- le32_to_cpu(prptctrl->bt_cnt[BTC_BCNT_RFK_REQ]),
- le32_to_cpu(prptctrl->bt_cnt[BTC_BCNT_RFK_GO]),
- le32_to_cpu(prptctrl->bt_cnt[BTC_BCNT_RFK_REJECT]),
- le32_to_cpu(prptctrl->bt_cnt[BTC_BCNT_RFK_TIMEOUT]),
- le32_to_cpu(prptctrl->bt_cnt[BTC_BCNT_RFK_FAIL]));
+ p += scnprintf(p, end - p, " (WL FW Hang!!)");
+ p += scnprintf(p, end - p, "\n");
+ p += scnprintf(p, end - p,
+ " %-15s : send_ok:%d, send_fail:%d, recv:%d, ",
+ "[mailbox]",
+ le32_to_cpu(prptctrl->bt_mbx_info.cnt_send_ok),
+ le32_to_cpu(prptctrl->bt_mbx_info.cnt_send_fail),
+ le32_to_cpu(prptctrl->bt_mbx_info.cnt_recv));
+
+ p += scnprintf(p, end - p,
+ "A2DP_empty:%d(stop:%d, tx:%d, ack:%d, nack:%d)\n",
+ le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_empty),
+ le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_flowctrl),
+ le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_tx),
+ le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_ack),
+ le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_nack));
+
+ p += scnprintf(p, end - p,
+ " %-15s : wl_rfk[req:%d/go:%d/reject:%d/timeout:%d]",
+ "[RFK]", cx->cnt_wl[BTC_WCNT_RFK_REQ],
+ cx->cnt_wl[BTC_WCNT_RFK_GO],
+ cx->cnt_wl[BTC_WCNT_RFK_REJECT],
+ cx->cnt_wl[BTC_WCNT_RFK_TIMEOUT]);
+
+ p += scnprintf(p, end - p,
+ ", bt_rfk[req:%d/go:%d/reject:%d/timeout:%d/fail:%d]\n",
+ le32_to_cpu(prptctrl->bt_cnt[BTC_BCNT_RFK_REQ]),
+ le32_to_cpu(prptctrl->bt_cnt[BTC_BCNT_RFK_GO]),
+ le32_to_cpu(prptctrl->bt_cnt[BTC_BCNT_RFK_REJECT]),
+ le32_to_cpu(prptctrl->bt_cnt[BTC_BCNT_RFK_TIMEOUT]),
+ le32_to_cpu(prptctrl->bt_cnt[BTC_BCNT_RFK_FAIL]));
if (le32_to_cpu(prptctrl->bt_cnt[BTC_BCNT_RFK_TIMEOUT]) > 0)
bt->rfk_info.map.timeout = 1;
@@ -10482,42 +10625,44 @@ static void _show_summary_v4(struct rtw89_dev *rtwdev, struct seq_file *m)
dm->error.map.wl_rfk_timeout = bt->rfk_info.map.timeout;
} else {
- seq_printf(m,
- " %-15s : h2c_cnt=%d(fail:%d), c2h_cnt=%d, rpt_cnt=%d, rpt_map=0x%x",
- "[summary]", pfwinfo->cnt_h2c,
- pfwinfo->cnt_h2c_fail, pfwinfo->cnt_c2h,
- pfwinfo->event[BTF_EVNT_RPT],
- btc->fwinfo.rpt_en_map);
- seq_puts(m, " (WL FW report invalid!!)\n");
+ p += scnprintf(p, end - p,
+ " %-15s : h2c_cnt=%d(fail:%d), c2h_cnt=%d, rpt_cnt=%d, rpt_map=0x%x",
+ "[summary]", pfwinfo->cnt_h2c,
+ pfwinfo->cnt_h2c_fail, pfwinfo->cnt_c2h,
+ pfwinfo->event[BTF_EVNT_RPT],
+ btc->fwinfo.rpt_en_map);
+ p += scnprintf(p, end - p, " (WL FW report invalid!!)\n");
}
for (i = 0; i < BTC_NCNT_NUM; i++)
cnt_sum += dm->cnt_notify[i];
- seq_printf(m,
- " %-15s : total=%d, show_coex_info=%d, power_on=%d, init_coex=%d, ",
- "[notify_cnt]", cnt_sum, cnt[BTC_NCNT_SHOW_COEX_INFO],
- cnt[BTC_NCNT_POWER_ON], cnt[BTC_NCNT_INIT_COEX]);
+ p += scnprintf(p, end - p,
+ " %-15s : total=%d, show_coex_info=%d, power_on=%d, init_coex=%d, ",
+ "[notify_cnt]", cnt_sum, cnt[BTC_NCNT_SHOW_COEX_INFO],
+ cnt[BTC_NCNT_POWER_ON], cnt[BTC_NCNT_INIT_COEX]);
+
+ p += scnprintf(p, end - p,
+ "power_off=%d, radio_state=%d, role_info=%d, wl_rfk=%d, wl_sta=%d\n",
+ cnt[BTC_NCNT_POWER_OFF], cnt[BTC_NCNT_RADIO_STATE],
+ cnt[BTC_NCNT_ROLE_INFO], cnt[BTC_NCNT_WL_RFK],
+ cnt[BTC_NCNT_WL_STA]);
- seq_printf(m,
- "power_off=%d, radio_state=%d, role_info=%d, wl_rfk=%d, wl_sta=%d\n",
- cnt[BTC_NCNT_POWER_OFF], cnt[BTC_NCNT_RADIO_STATE],
- cnt[BTC_NCNT_ROLE_INFO], cnt[BTC_NCNT_WL_RFK],
- cnt[BTC_NCNT_WL_STA]);
+ p += scnprintf(p, end - p,
+ " %-15s : scan_start=%d, scan_finish=%d, switch_band=%d, special_pkt=%d, ",
+ "[notify_cnt]", cnt[BTC_NCNT_SCAN_START],
+ cnt[BTC_NCNT_SCAN_FINISH], cnt[BTC_NCNT_SWITCH_BAND],
+ cnt[BTC_NCNT_SPECIAL_PACKET]);
- seq_printf(m,
- " %-15s : scan_start=%d, scan_finish=%d, switch_band=%d, special_pkt=%d, ",
- "[notify_cnt]", cnt[BTC_NCNT_SCAN_START],
- cnt[BTC_NCNT_SCAN_FINISH], cnt[BTC_NCNT_SWITCH_BAND],
- cnt[BTC_NCNT_SPECIAL_PACKET]);
+ p += scnprintf(p, end - p,
+ "timer=%d, control=%d, customerize=%d\n",
+ cnt[BTC_NCNT_TIMER], cnt[BTC_NCNT_CONTROL],
+ cnt[BTC_NCNT_CUSTOMERIZE]);
- seq_printf(m,
- "timer=%d, control=%d, customerize=%d\n",
- cnt[BTC_NCNT_TIMER], cnt[BTC_NCNT_CONTROL],
- cnt[BTC_NCNT_CUSTOMERIZE]);
+ return p - buf;
}
-static void _show_summary_v5(struct rtw89_dev *rtwdev, struct seq_file *m)
+static int _show_summary_v5(struct rtw89_dev *rtwdev, char *buf, size_t bufsz)
{
struct rtw89_btc *btc = &rtwdev->btc;
struct rtw89_btc_btf_fwinfo *pfwinfo = &btc->fwinfo;
@@ -10527,112 +10672,118 @@ static void _show_summary_v5(struct rtw89_dev *rtwdev, struct seq_file *m)
struct rtw89_btc_dm *dm = &btc->dm;
struct rtw89_btc_wl_info *wl = &cx->wl;
u32 cnt_sum = 0, *cnt = btc->dm.cnt_notify;
+ char *p = buf, *end = buf + bufsz;
u8 i;
if (!(dm->coex_info_map & BTC_COEX_INFO_SUMMARY))
- return;
+ return 0;
- seq_puts(m, "========== [Statistics] ==========\n");
+ p += scnprintf(p, end - p, "========== [Statistics] ==========\n");
pcinfo = &pfwinfo->rpt_ctrl.cinfo;
if (pcinfo->valid && !wl->status.map.lps && !wl->status.map.rf_off) {
prptctrl = &pfwinfo->rpt_ctrl.finfo.v5;
- seq_printf(m,
- " %-15s : h2c_cnt=%d(fail:%d, fw_recv:%d), c2h_cnt=%d(fw_send:%d, len:%d), ",
- "[summary]", pfwinfo->cnt_h2c, pfwinfo->cnt_h2c_fail,
- le16_to_cpu(prptctrl->rpt_info.cnt_h2c),
- pfwinfo->cnt_c2h,
- le16_to_cpu(prptctrl->rpt_info.cnt_c2h),
- le16_to_cpu(prptctrl->rpt_info.len_c2h));
-
- seq_printf(m,
- "rpt_cnt=%d(fw_send:%d), rpt_map=0x%x",
- pfwinfo->event[BTF_EVNT_RPT],
- le16_to_cpu(prptctrl->rpt_info.cnt),
- le32_to_cpu(prptctrl->rpt_info.en));
+ p += scnprintf(p, end - p,
+ " %-15s : h2c_cnt=%d(fail:%d, fw_recv:%d), c2h_cnt=%d(fw_send:%d, len:%d), ",
+ "[summary]", pfwinfo->cnt_h2c,
+ pfwinfo->cnt_h2c_fail,
+ le16_to_cpu(prptctrl->rpt_info.cnt_h2c),
+ pfwinfo->cnt_c2h,
+ le16_to_cpu(prptctrl->rpt_info.cnt_c2h),
+ le16_to_cpu(prptctrl->rpt_info.len_c2h));
+
+ p += scnprintf(p, end - p,
+ "rpt_cnt=%d(fw_send:%d), rpt_map=0x%x",
+ pfwinfo->event[BTF_EVNT_RPT],
+ le16_to_cpu(prptctrl->rpt_info.cnt),
+ le32_to_cpu(prptctrl->rpt_info.en));
if (dm->error.map.wl_fw_hang)
- seq_puts(m, " (WL FW Hang!!)");
- seq_puts(m, "\n");
- seq_printf(m,
- " %-15s : send_ok:%d, send_fail:%d, recv:%d, ",
- "[mailbox]",
- le32_to_cpu(prptctrl->bt_mbx_info.cnt_send_ok),
- le32_to_cpu(prptctrl->bt_mbx_info.cnt_send_fail),
- le32_to_cpu(prptctrl->bt_mbx_info.cnt_recv));
-
- seq_printf(m,
- "A2DP_empty:%d(stop:%d, tx:%d, ack:%d, nack:%d)\n",
- le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_empty),
- le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_flowctrl),
- le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_tx),
- le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_ack),
- le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_nack));
-
- seq_printf(m,
- " %-15s : wl_rfk[req:%d/go:%d/reject:%d/tout:%d]",
- "[RFK/LPS]", cx->cnt_wl[BTC_WCNT_RFK_REQ],
- cx->cnt_wl[BTC_WCNT_RFK_GO],
- cx->cnt_wl[BTC_WCNT_RFK_REJECT],
- cx->cnt_wl[BTC_WCNT_RFK_TIMEOUT]);
-
- seq_printf(m,
- ", bt_rfk[req:%d]",
- le16_to_cpu(prptctrl->bt_cnt[BTC_BCNT_RFK_REQ]));
-
- seq_printf(m,
- ", AOAC[RF_on:%d/RF_off:%d]",
- le16_to_cpu(prptctrl->rpt_info.cnt_aoac_rf_on),
- le16_to_cpu(prptctrl->rpt_info.cnt_aoac_rf_off));
+ p += scnprintf(p, end - p, " (WL FW Hang!!)");
+ p += scnprintf(p, end - p, "\n");
+ p += scnprintf(p, end - p,
+ " %-15s : send_ok:%d, send_fail:%d, recv:%d, ",
+ "[mailbox]",
+ le32_to_cpu(prptctrl->bt_mbx_info.cnt_send_ok),
+ le32_to_cpu(prptctrl->bt_mbx_info.cnt_send_fail),
+ le32_to_cpu(prptctrl->bt_mbx_info.cnt_recv));
+
+ p += scnprintf(p, end - p,
+ "A2DP_empty:%d(stop:%d, tx:%d, ack:%d, nack:%d)\n",
+ le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_empty),
+ le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_flowctrl),
+ le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_tx),
+ le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_ack),
+ le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_nack));
+
+ p += scnprintf(p, end - p,
+ " %-15s : wl_rfk[req:%d/go:%d/reject:%d/tout:%d]",
+ "[RFK/LPS]", cx->cnt_wl[BTC_WCNT_RFK_REQ],
+ cx->cnt_wl[BTC_WCNT_RFK_GO],
+ cx->cnt_wl[BTC_WCNT_RFK_REJECT],
+ cx->cnt_wl[BTC_WCNT_RFK_TIMEOUT]);
+
+ p += scnprintf(p, end - p,
+ ", bt_rfk[req:%d]",
+ le16_to_cpu(prptctrl->bt_cnt[BTC_BCNT_RFK_REQ]));
+
+ p += scnprintf(p, end - p,
+ ", AOAC[RF_on:%d/RF_off:%d]",
+ le16_to_cpu(prptctrl->rpt_info.cnt_aoac_rf_on),
+ le16_to_cpu(prptctrl->rpt_info.cnt_aoac_rf_off));
} else {
- seq_printf(m,
- " %-15s : h2c_cnt=%d(fail:%d), c2h_cnt=%d",
- "[summary]", pfwinfo->cnt_h2c,
- pfwinfo->cnt_h2c_fail, pfwinfo->cnt_c2h);
+ p += scnprintf(p, end - p,
+ " %-15s : h2c_cnt=%d(fail:%d), c2h_cnt=%d",
+ "[summary]", pfwinfo->cnt_h2c,
+ pfwinfo->cnt_h2c_fail, pfwinfo->cnt_c2h);
}
if (!pcinfo->valid || pfwinfo->len_mismch || pfwinfo->fver_mismch ||
pfwinfo->err[BTFRE_EXCEPTION]) {
- seq_puts(m, "\n");
- seq_printf(m,
- " %-15s : WL FW rpt error!![rpt_ctrl_valid:%d/len:"
- "0x%x/ver:0x%x/ex:%d/lps=%d/rf_off=%d]",
- "[ERROR]", pcinfo->valid, pfwinfo->len_mismch,
- pfwinfo->fver_mismch, pfwinfo->err[BTFRE_EXCEPTION],
- wl->status.map.lps, wl->status.map.rf_off);
+ p += scnprintf(p, end - p, "\n");
+ p += scnprintf(p, end - p,
+ " %-15s : WL FW rpt error!![rpt_ctrl_valid:%d/len:"
+ "0x%x/ver:0x%x/ex:%d/lps=%d/rf_off=%d]",
+ "[ERROR]", pcinfo->valid, pfwinfo->len_mismch,
+ pfwinfo->fver_mismch,
+ pfwinfo->err[BTFRE_EXCEPTION],
+ wl->status.map.lps, wl->status.map.rf_off);
}
for (i = 0; i < BTC_NCNT_NUM; i++)
cnt_sum += dm->cnt_notify[i];
- seq_puts(m, "\n");
- seq_printf(m,
- " %-15s : total=%d, show_coex_info=%d, power_on=%d, init_coex=%d, ",
- "[notify_cnt]",
- cnt_sum, cnt[BTC_NCNT_SHOW_COEX_INFO],
- cnt[BTC_NCNT_POWER_ON], cnt[BTC_NCNT_INIT_COEX]);
+ p += scnprintf(p, end - p, "\n");
+ p += scnprintf(p, end - p,
+ " %-15s : total=%d, show_coex_info=%d, power_on=%d, init_coex=%d, ",
+ "[notify_cnt]",
+ cnt_sum, cnt[BTC_NCNT_SHOW_COEX_INFO],
+ cnt[BTC_NCNT_POWER_ON], cnt[BTC_NCNT_INIT_COEX]);
+
+ p += scnprintf(p, end - p,
+ "power_off=%d, radio_state=%d, role_info=%d, wl_rfk=%d, wl_sta=%d",
+ cnt[BTC_NCNT_POWER_OFF], cnt[BTC_NCNT_RADIO_STATE],
+ cnt[BTC_NCNT_ROLE_INFO], cnt[BTC_NCNT_WL_RFK],
+ cnt[BTC_NCNT_WL_STA]);
- seq_printf(m,
- "power_off=%d, radio_state=%d, role_info=%d, wl_rfk=%d, wl_sta=%d",
- cnt[BTC_NCNT_POWER_OFF], cnt[BTC_NCNT_RADIO_STATE],
- cnt[BTC_NCNT_ROLE_INFO], cnt[BTC_NCNT_WL_RFK],
- cnt[BTC_NCNT_WL_STA]);
+ p += scnprintf(p, end - p, "\n");
+ p += scnprintf(p, end - p,
+ " %-15s : scan_start=%d, scan_finish=%d, switch_band=%d, special_pkt=%d, ",
+ "[notify_cnt]",
+ cnt[BTC_NCNT_SCAN_START], cnt[BTC_NCNT_SCAN_FINISH],
+ cnt[BTC_NCNT_SWITCH_BAND],
+ cnt[BTC_NCNT_SPECIAL_PACKET]);
- seq_puts(m, "\n");
- seq_printf(m,
- " %-15s : scan_start=%d, scan_finish=%d, switch_band=%d, special_pkt=%d, ",
- "[notify_cnt]",
- cnt[BTC_NCNT_SCAN_START], cnt[BTC_NCNT_SCAN_FINISH],
- cnt[BTC_NCNT_SWITCH_BAND], cnt[BTC_NCNT_SPECIAL_PACKET]);
+ p += scnprintf(p, end - p,
+ "timer=%d, control=%d, customerize=%d",
+ cnt[BTC_NCNT_TIMER], cnt[BTC_NCNT_CONTROL],
+ cnt[BTC_NCNT_CUSTOMERIZE]);
- seq_printf(m,
- "timer=%d, control=%d, customerize=%d",
- cnt[BTC_NCNT_TIMER], cnt[BTC_NCNT_CONTROL],
- cnt[BTC_NCNT_CUSTOMERIZE]);
+ return p - buf;
}
-static void _show_summary_v105(struct rtw89_dev *rtwdev, struct seq_file *m)
+static int _show_summary_v105(struct rtw89_dev *rtwdev, char *buf, size_t bufsz)
{
struct rtw89_btc *btc = &rtwdev->btc;
struct rtw89_btc_btf_fwinfo *pfwinfo = &btc->fwinfo;
@@ -10642,112 +10793,118 @@ static void _show_summary_v105(struct rtw89_dev *rtwdev, struct seq_file *m)
struct rtw89_btc_dm *dm = &btc->dm;
struct rtw89_btc_wl_info *wl = &cx->wl;
u32 cnt_sum = 0, *cnt = btc->dm.cnt_notify;
+ char *p = buf, *end = buf + bufsz;
u8 i;
if (!(dm->coex_info_map & BTC_COEX_INFO_SUMMARY))
- return;
+ return 0;
- seq_puts(m, "========== [Statistics] ==========\n");
+ p += scnprintf(p, end - p, "========== [Statistics] ==========\n");
pcinfo = &pfwinfo->rpt_ctrl.cinfo;
if (pcinfo->valid && !wl->status.map.lps && !wl->status.map.rf_off) {
prptctrl = &pfwinfo->rpt_ctrl.finfo.v105;
- seq_printf(m,
- " %-15s : h2c_cnt=%d(fail:%d, fw_recv:%d), c2h_cnt=%d(fw_send:%d, len:%d), ",
- "[summary]", pfwinfo->cnt_h2c, pfwinfo->cnt_h2c_fail,
- le16_to_cpu(prptctrl->rpt_info.cnt_h2c),
- pfwinfo->cnt_c2h,
- le16_to_cpu(prptctrl->rpt_info.cnt_c2h),
- le16_to_cpu(prptctrl->rpt_info.len_c2h));
-
- seq_printf(m,
- "rpt_cnt=%d(fw_send:%d), rpt_map=0x%x",
- pfwinfo->event[BTF_EVNT_RPT],
- le16_to_cpu(prptctrl->rpt_info.cnt),
- le32_to_cpu(prptctrl->rpt_info.en));
+ p += scnprintf(p, end - p,
+ " %-15s : h2c_cnt=%d(fail:%d, fw_recv:%d), c2h_cnt=%d(fw_send:%d, len:%d), ",
+ "[summary]", pfwinfo->cnt_h2c,
+ pfwinfo->cnt_h2c_fail,
+ le16_to_cpu(prptctrl->rpt_info.cnt_h2c),
+ pfwinfo->cnt_c2h,
+ le16_to_cpu(prptctrl->rpt_info.cnt_c2h),
+ le16_to_cpu(prptctrl->rpt_info.len_c2h));
+
+ p += scnprintf(p, end - p,
+ "rpt_cnt=%d(fw_send:%d), rpt_map=0x%x",
+ pfwinfo->event[BTF_EVNT_RPT],
+ le16_to_cpu(prptctrl->rpt_info.cnt),
+ le32_to_cpu(prptctrl->rpt_info.en));
if (dm->error.map.wl_fw_hang)
- seq_puts(m, " (WL FW Hang!!)");
- seq_puts(m, "\n");
- seq_printf(m,
- " %-15s : send_ok:%d, send_fail:%d, recv:%d, ",
- "[mailbox]",
- le32_to_cpu(prptctrl->bt_mbx_info.cnt_send_ok),
- le32_to_cpu(prptctrl->bt_mbx_info.cnt_send_fail),
- le32_to_cpu(prptctrl->bt_mbx_info.cnt_recv));
-
- seq_printf(m,
- "A2DP_empty:%d(stop:%d, tx:%d, ack:%d, nack:%d)\n",
- le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_empty),
- le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_flowctrl),
- le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_tx),
- le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_ack),
- le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_nack));
-
- seq_printf(m,
- " %-15s : wl_rfk[req:%d/go:%d/reject:%d/tout:%d]",
- "[RFK/LPS]", cx->cnt_wl[BTC_WCNT_RFK_REQ],
- cx->cnt_wl[BTC_WCNT_RFK_GO],
- cx->cnt_wl[BTC_WCNT_RFK_REJECT],
- cx->cnt_wl[BTC_WCNT_RFK_TIMEOUT]);
-
- seq_printf(m,
- ", bt_rfk[req:%d]",
- le16_to_cpu(prptctrl->bt_cnt[BTC_BCNT_RFK_REQ]));
-
- seq_printf(m,
- ", AOAC[RF_on:%d/RF_off:%d]",
- le16_to_cpu(prptctrl->rpt_info.cnt_aoac_rf_on),
- le16_to_cpu(prptctrl->rpt_info.cnt_aoac_rf_off));
+ p += scnprintf(p, end - p, " (WL FW Hang!!)");
+ p += scnprintf(p, end - p, "\n");
+ p += scnprintf(p, end - p,
+ " %-15s : send_ok:%d, send_fail:%d, recv:%d, ",
+ "[mailbox]",
+ le32_to_cpu(prptctrl->bt_mbx_info.cnt_send_ok),
+ le32_to_cpu(prptctrl->bt_mbx_info.cnt_send_fail),
+ le32_to_cpu(prptctrl->bt_mbx_info.cnt_recv));
+
+ p += scnprintf(p, end - p,
+ "A2DP_empty:%d(stop:%d, tx:%d, ack:%d, nack:%d)\n",
+ le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_empty),
+ le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_flowctrl),
+ le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_tx),
+ le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_ack),
+ le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_nack));
+
+ p += scnprintf(p, end - p,
+ " %-15s : wl_rfk[req:%d/go:%d/reject:%d/tout:%d]",
+ "[RFK/LPS]", cx->cnt_wl[BTC_WCNT_RFK_REQ],
+ cx->cnt_wl[BTC_WCNT_RFK_GO],
+ cx->cnt_wl[BTC_WCNT_RFK_REJECT],
+ cx->cnt_wl[BTC_WCNT_RFK_TIMEOUT]);
+
+ p += scnprintf(p, end - p,
+ ", bt_rfk[req:%d]",
+ le16_to_cpu(prptctrl->bt_cnt[BTC_BCNT_RFK_REQ]));
+
+ p += scnprintf(p, end - p,
+ ", AOAC[RF_on:%d/RF_off:%d]",
+ le16_to_cpu(prptctrl->rpt_info.cnt_aoac_rf_on),
+ le16_to_cpu(prptctrl->rpt_info.cnt_aoac_rf_off));
} else {
- seq_printf(m,
- " %-15s : h2c_cnt=%d(fail:%d), c2h_cnt=%d",
- "[summary]", pfwinfo->cnt_h2c,
- pfwinfo->cnt_h2c_fail, pfwinfo->cnt_c2h);
+ p += scnprintf(p, end - p,
+ " %-15s : h2c_cnt=%d(fail:%d), c2h_cnt=%d",
+ "[summary]", pfwinfo->cnt_h2c,
+ pfwinfo->cnt_h2c_fail, pfwinfo->cnt_c2h);
}
if (!pcinfo->valid || pfwinfo->len_mismch || pfwinfo->fver_mismch ||
pfwinfo->err[BTFRE_EXCEPTION]) {
- seq_puts(m, "\n");
- seq_printf(m,
- " %-15s : WL FW rpt error!![rpt_ctrl_valid:%d/len:"
- "0x%x/ver:0x%x/ex:%d/lps=%d/rf_off=%d]",
- "[ERROR]", pcinfo->valid, pfwinfo->len_mismch,
- pfwinfo->fver_mismch, pfwinfo->err[BTFRE_EXCEPTION],
- wl->status.map.lps, wl->status.map.rf_off);
+ p += scnprintf(p, end - p, "\n");
+ p += scnprintf(p, end - p,
+ " %-15s : WL FW rpt error!![rpt_ctrl_valid:%d/len:"
+ "0x%x/ver:0x%x/ex:%d/lps=%d/rf_off=%d]",
+ "[ERROR]", pcinfo->valid, pfwinfo->len_mismch,
+ pfwinfo->fver_mismch,
+ pfwinfo->err[BTFRE_EXCEPTION],
+ wl->status.map.lps, wl->status.map.rf_off);
}
for (i = 0; i < BTC_NCNT_NUM; i++)
cnt_sum += dm->cnt_notify[i];
- seq_puts(m, "\n");
- seq_printf(m,
- " %-15s : total=%d, show_coex_info=%d, power_on=%d, init_coex=%d, ",
- "[notify_cnt]",
- cnt_sum, cnt[BTC_NCNT_SHOW_COEX_INFO],
- cnt[BTC_NCNT_POWER_ON], cnt[BTC_NCNT_INIT_COEX]);
+ p += scnprintf(p, end - p, "\n");
+ p += scnprintf(p, end - p,
+ " %-15s : total=%d, show_coex_info=%d, power_on=%d, init_coex=%d, ",
+ "[notify_cnt]",
+ cnt_sum, cnt[BTC_NCNT_SHOW_COEX_INFO],
+ cnt[BTC_NCNT_POWER_ON], cnt[BTC_NCNT_INIT_COEX]);
- seq_printf(m,
- "power_off=%d, radio_state=%d, role_info=%d, wl_rfk=%d, wl_sta=%d",
- cnt[BTC_NCNT_POWER_OFF], cnt[BTC_NCNT_RADIO_STATE],
- cnt[BTC_NCNT_ROLE_INFO], cnt[BTC_NCNT_WL_RFK],
- cnt[BTC_NCNT_WL_STA]);
+ p += scnprintf(p, end - p,
+ "power_off=%d, radio_state=%d, role_info=%d, wl_rfk=%d, wl_sta=%d",
+ cnt[BTC_NCNT_POWER_OFF], cnt[BTC_NCNT_RADIO_STATE],
+ cnt[BTC_NCNT_ROLE_INFO], cnt[BTC_NCNT_WL_RFK],
+ cnt[BTC_NCNT_WL_STA]);
- seq_puts(m, "\n");
- seq_printf(m,
- " %-15s : scan_start=%d, scan_finish=%d, switch_band=%d, special_pkt=%d, ",
- "[notify_cnt]",
- cnt[BTC_NCNT_SCAN_START], cnt[BTC_NCNT_SCAN_FINISH],
- cnt[BTC_NCNT_SWITCH_BAND], cnt[BTC_NCNT_SPECIAL_PACKET]);
+ p += scnprintf(p, end - p, "\n");
+ p += scnprintf(p, end - p,
+ " %-15s : scan_start=%d, scan_finish=%d, switch_band=%d, special_pkt=%d, ",
+ "[notify_cnt]",
+ cnt[BTC_NCNT_SCAN_START], cnt[BTC_NCNT_SCAN_FINISH],
+ cnt[BTC_NCNT_SWITCH_BAND],
+ cnt[BTC_NCNT_SPECIAL_PACKET]);
- seq_printf(m,
- "timer=%d, control=%d, customerize=%d",
- cnt[BTC_NCNT_TIMER], cnt[BTC_NCNT_CONTROL],
- cnt[BTC_NCNT_CUSTOMERIZE]);
+ p += scnprintf(p, end - p,
+ "timer=%d, control=%d, customerize=%d",
+ cnt[BTC_NCNT_TIMER], cnt[BTC_NCNT_CONTROL],
+ cnt[BTC_NCNT_CUSTOMERIZE]);
+
+ return p - buf;
}
-static void _show_summary_v7(struct rtw89_dev *rtwdev, struct seq_file *m)
+static int _show_summary_v7(struct rtw89_dev *rtwdev, char *buf, size_t bufsz)
{
struct rtw89_btc_btf_fwinfo *pfwinfo = &rtwdev->btc.fwinfo;
struct rtw89_btc_fbtc_rpt_ctrl_v7 *prptctrl = NULL;
@@ -10756,100 +10913,111 @@ static void _show_summary_v7(struct rtw89_dev *rtwdev, struct seq_file *m)
struct rtw89_btc_dm *dm = &rtwdev->btc.dm;
struct rtw89_btc_wl_info *wl = &cx->wl;
u32 *cnt = rtwdev->btc.dm.cnt_notify;
+ char *p = buf, *end = buf + bufsz;
u32 cnt_sum = 0;
u8 i;
if (!(dm->coex_info_map & BTC_COEX_INFO_SUMMARY))
- return;
+ return 0;
- seq_printf(m, "%s", "\n\r========== [Statistics] ==========");
+ p += scnprintf(p, end - p, "%s",
+ "\n\r========== [Statistics] ==========");
pcinfo = &pfwinfo->rpt_ctrl.cinfo;
if (pcinfo->valid && wl->status.map.lps != BTC_LPS_RF_OFF &&
!wl->status.map.rf_off) {
prptctrl = &pfwinfo->rpt_ctrl.finfo.v7;
- seq_printf(m,
- "\n\r %-15s : h2c_cnt=%d(fail:%d, fw_recv:%d),"
- "c2h_cnt=%d(fw_send:%d, len:%d, max:%d), ",
- "[summary]", pfwinfo->cnt_h2c, pfwinfo->cnt_h2c_fail,
- le16_to_cpu(prptctrl->rpt_info.cnt_h2c), pfwinfo->cnt_c2h,
- le16_to_cpu(prptctrl->rpt_info.cnt_c2h),
- le16_to_cpu(prptctrl->rpt_info.len_c2h),
- rtwdev->btc.ver->info_buf);
-
- seq_printf(m, "rpt_cnt=%d(fw_send:%d), rpt_map=0x%x",
- pfwinfo->event[BTF_EVNT_RPT],
- le16_to_cpu(prptctrl->rpt_info.cnt),
- le32_to_cpu(prptctrl->rpt_info.en));
+ p += scnprintf(p, end - p,
+ "\n\r %-15s : h2c_cnt=%d(fail:%d, fw_recv:%d),"
+ "c2h_cnt=%d(fw_send:%d, len:%d, max:%d), ",
+ "[summary]", pfwinfo->cnt_h2c,
+ pfwinfo->cnt_h2c_fail,
+ le16_to_cpu(prptctrl->rpt_info.cnt_h2c),
+ pfwinfo->cnt_c2h,
+ le16_to_cpu(prptctrl->rpt_info.cnt_c2h),
+ le16_to_cpu(prptctrl->rpt_info.len_c2h),
+ rtwdev->btc.ver->info_buf);
+
+ p += scnprintf(p, end - p,
+ "rpt_cnt=%d(fw_send:%d), rpt_map=0x%x",
+ pfwinfo->event[BTF_EVNT_RPT],
+ le16_to_cpu(prptctrl->rpt_info.cnt),
+ le32_to_cpu(prptctrl->rpt_info.en));
if (dm->error.map.wl_fw_hang)
- seq_puts(m, " (WL FW Hang!!)");
-
- seq_printf(m, "\n\r %-15s : send_ok:%d, send_fail:%d, recv:%d, ",
- "[mailbox]", le32_to_cpu(prptctrl->bt_mbx_info.cnt_send_ok),
- le32_to_cpu(prptctrl->bt_mbx_info.cnt_send_fail),
- le32_to_cpu(prptctrl->bt_mbx_info.cnt_recv));
-
- seq_printf(m, "A2DP_empty:%d(stop:%d/tx:%d/ack:%d/nack:%d)",
- le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_empty),
- le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_flowctrl),
- le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_tx),
- le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_ack),
- le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_nack));
-
- seq_printf(m,
- "\n\r %-15s : wl_rfk[req:%d/go:%d/reject:%d/tout:%d/time:%dms]",
- "[RFK/LPS]", cx->cnt_wl[BTC_WCNT_RFK_REQ],
- cx->cnt_wl[BTC_WCNT_RFK_GO],
- cx->cnt_wl[BTC_WCNT_RFK_REJECT],
- cx->cnt_wl[BTC_WCNT_RFK_TIMEOUT],
- wl->rfk_info.proc_time);
-
- seq_printf(m, ", bt_rfk[req:%d]",
- le16_to_cpu(prptctrl->bt_cnt[BTC_BCNT_RFK_REQ]));
-
- seq_printf(m, ", AOAC[RF_on:%d/RF_off:%d]",
- le16_to_cpu(prptctrl->rpt_info.cnt_aoac_rf_on),
- le16_to_cpu(prptctrl->rpt_info.cnt_aoac_rf_off));
+ p += scnprintf(p, end - p, " (WL FW Hang!!)");
+
+ p += scnprintf(p, end - p,
+ "\n\r %-15s : send_ok:%d, send_fail:%d, recv:%d, ",
+ "[mailbox]",
+ le32_to_cpu(prptctrl->bt_mbx_info.cnt_send_ok),
+ le32_to_cpu(prptctrl->bt_mbx_info.cnt_send_fail),
+ le32_to_cpu(prptctrl->bt_mbx_info.cnt_recv));
+
+ p += scnprintf(p, end - p,
+ "A2DP_empty:%d(stop:%d/tx:%d/ack:%d/nack:%d)",
+ le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_empty),
+ le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_flowctrl),
+ le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_tx),
+ le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_ack),
+ le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_nack));
+
+ p += scnprintf(p, end - p,
+ "\n\r %-15s : wl_rfk[req:%d/go:%d/reject:%d/tout:%d/time:%dms]",
+ "[RFK/LPS]", cx->cnt_wl[BTC_WCNT_RFK_REQ],
+ cx->cnt_wl[BTC_WCNT_RFK_GO],
+ cx->cnt_wl[BTC_WCNT_RFK_REJECT],
+ cx->cnt_wl[BTC_WCNT_RFK_TIMEOUT],
+ wl->rfk_info.proc_time);
+
+ p += scnprintf(p, end - p, ", bt_rfk[req:%d]",
+ le16_to_cpu(prptctrl->bt_cnt[BTC_BCNT_RFK_REQ]));
+
+ p += scnprintf(p, end - p, ", AOAC[RF_on:%d/RF_off:%d]",
+ le16_to_cpu(prptctrl->rpt_info.cnt_aoac_rf_on),
+ le16_to_cpu(prptctrl->rpt_info.cnt_aoac_rf_off));
} else {
- seq_printf(m,
- "\n\r %-15s : h2c_cnt=%d(fail:%d), c2h_cnt=%d (lps=%d/rf_off=%d)",
- "[summary]",
- pfwinfo->cnt_h2c, pfwinfo->cnt_h2c_fail,
- pfwinfo->cnt_c2h,
- wl->status.map.lps, wl->status.map.rf_off);
+ p += scnprintf(p, end - p,
+ "\n\r %-15s : h2c_cnt=%d(fail:%d), c2h_cnt=%d (lps=%d/rf_off=%d)",
+ "[summary]",
+ pfwinfo->cnt_h2c, pfwinfo->cnt_h2c_fail,
+ pfwinfo->cnt_c2h,
+ wl->status.map.lps, wl->status.map.rf_off);
}
for (i = 0; i < BTC_NCNT_NUM; i++)
cnt_sum += dm->cnt_notify[i];
- seq_printf(m,
- "\n\r %-15s : total=%d, show_coex_info=%d, power_on=%d, init_coex=%d, ",
- "[notify_cnt]",
- cnt_sum, cnt[BTC_NCNT_SHOW_COEX_INFO],
- cnt[BTC_NCNT_POWER_ON], cnt[BTC_NCNT_INIT_COEX]);
+ p += scnprintf(p, end - p,
+ "\n\r %-15s : total=%d, show_coex_info=%d, power_on=%d, init_coex=%d, ",
+ "[notify_cnt]",
+ cnt_sum, cnt[BTC_NCNT_SHOW_COEX_INFO],
+ cnt[BTC_NCNT_POWER_ON], cnt[BTC_NCNT_INIT_COEX]);
+
+ p += scnprintf(p, end - p,
+ "power_off=%d, radio_state=%d, role_info=%d, wl_rfk=%d, wl_sta=%d",
+ cnt[BTC_NCNT_POWER_OFF], cnt[BTC_NCNT_RADIO_STATE],
+ cnt[BTC_NCNT_ROLE_INFO], cnt[BTC_NCNT_WL_RFK],
+ cnt[BTC_NCNT_WL_STA]);
- seq_printf(m,
- "power_off=%d, radio_state=%d, role_info=%d, wl_rfk=%d, wl_sta=%d",
- cnt[BTC_NCNT_POWER_OFF], cnt[BTC_NCNT_RADIO_STATE],
- cnt[BTC_NCNT_ROLE_INFO], cnt[BTC_NCNT_WL_RFK],
- cnt[BTC_NCNT_WL_STA]);
+ p += scnprintf(p, end - p,
+ "\n\r %-15s : scan_start=%d, scan_finish=%d, switch_band=%d, switch_chbw=%d, special_pkt=%d, ",
+ "[notify_cnt]",
+ cnt[BTC_NCNT_SCAN_START], cnt[BTC_NCNT_SCAN_FINISH],
+ cnt[BTC_NCNT_SWITCH_BAND], cnt[BTC_NCNT_SWITCH_CHBW],
+ cnt[BTC_NCNT_SPECIAL_PACKET]);
- seq_printf(m,
- "\n\r %-15s : scan_start=%d, scan_finish=%d, switch_band=%d, switch_chbw=%d, special_pkt=%d, ",
- "[notify_cnt]",
- cnt[BTC_NCNT_SCAN_START], cnt[BTC_NCNT_SCAN_FINISH],
- cnt[BTC_NCNT_SWITCH_BAND], cnt[BTC_NCNT_SWITCH_CHBW],
- cnt[BTC_NCNT_SPECIAL_PACKET]);
+ p += scnprintf(p, end - p,
+ "timer=%d, customerize=%d, hub_msg=%d, chg_fw=%d, send_cc=%d",
+ cnt[BTC_NCNT_TIMER], cnt[BTC_NCNT_CUSTOMERIZE],
+ rtwdev->btc.hubmsg_cnt, cnt[BTC_NCNT_RESUME_DL_FW],
+ cnt[BTC_NCNT_COUNTRYCODE]);
- seq_printf(m, "timer=%d, customerize=%d, hub_msg=%d, chg_fw=%d, send_cc=%d",
- cnt[BTC_NCNT_TIMER], cnt[BTC_NCNT_CUSTOMERIZE],
- rtwdev->btc.hubmsg_cnt, cnt[BTC_NCNT_RESUME_DL_FW],
- cnt[BTC_NCNT_COUNTRYCODE]);
+ return p - buf;
}
-static void _show_summary_v8(struct rtw89_dev *rtwdev, struct seq_file *m)
+static int _show_summary_v8(struct rtw89_dev *rtwdev, char *buf, size_t bufsz)
{
struct rtw89_btc_btf_fwinfo *pfwinfo = &rtwdev->btc.fwinfo;
struct rtw89_btc_rpt_cmn_info *pcinfo = NULL;
@@ -10858,153 +11026,173 @@ static void _show_summary_v8(struct rtw89_dev *rtwdev, struct seq_file *m)
struct rtw89_btc_dm *dm = &rtwdev->btc.dm;
struct rtw89_btc_wl_info *wl = &cx->wl;
u32 *cnt = rtwdev->btc.dm.cnt_notify;
+ char *p = buf, *end = buf + bufsz;
u32 cnt_sum = 0;
u8 i;
if (!(dm->coex_info_map & BTC_COEX_INFO_SUMMARY))
- return;
+ return 0;
- seq_printf(m, "%s", "\n\r========== [Statistics] ==========");
+ p += scnprintf(p, end - p, "%s",
+ "\n\r========== [Statistics] ==========");
pcinfo = &pfwinfo->rpt_ctrl.cinfo;
if (pcinfo->valid && wl->status.map.lps != BTC_LPS_RF_OFF &&
!wl->status.map.rf_off) {
prptctrl = &pfwinfo->rpt_ctrl.finfo.v8;
- seq_printf(m,
- "\n\r %-15s : h2c_cnt=%d(fail:%d, fw_recv:%d), c2h_cnt=%d(fw_send:%d, len:%d, max:fw-%d/drv-%d), ",
- "[summary]", pfwinfo->cnt_h2c, pfwinfo->cnt_h2c_fail,
- le16_to_cpu(prptctrl->rpt_info.cnt_h2c), pfwinfo->cnt_c2h,
- le16_to_cpu(prptctrl->rpt_info.cnt_c2h),
- le16_to_cpu(prptctrl->rpt_info.len_c2h),
- (prptctrl->rpt_len_max_h << 8) + prptctrl->rpt_len_max_l,
- rtwdev->btc.ver->info_buf);
-
- seq_printf(m, "rpt_cnt=%d(fw_send:%d), rpt_map=0x%x",
- pfwinfo->event[BTF_EVNT_RPT],
- le16_to_cpu(prptctrl->rpt_info.cnt),
- le32_to_cpu(prptctrl->rpt_info.en));
+ p += scnprintf(p, end - p,
+ "\n\r %-15s : h2c_cnt=%d(fail:%d, fw_recv:%d), c2h_cnt=%d(fw_send:%d, len:%d, max:fw-%d/drv-%d), ",
+ "[summary]", pfwinfo->cnt_h2c,
+ pfwinfo->cnt_h2c_fail,
+ le16_to_cpu(prptctrl->rpt_info.cnt_h2c),
+ pfwinfo->cnt_c2h,
+ le16_to_cpu(prptctrl->rpt_info.cnt_c2h),
+ le16_to_cpu(prptctrl->rpt_info.len_c2h),
+ (prptctrl->rpt_len_max_h << 8) + prptctrl->rpt_len_max_l,
+ rtwdev->btc.ver->info_buf);
+
+ p += scnprintf(p, end - p,
+ "rpt_cnt=%d(fw_send:%d), rpt_map=0x%x",
+ pfwinfo->event[BTF_EVNT_RPT],
+ le16_to_cpu(prptctrl->rpt_info.cnt),
+ le32_to_cpu(prptctrl->rpt_info.en));
if (dm->error.map.wl_fw_hang)
- seq_puts(m, " (WL FW Hang!!)");
-
- seq_printf(m, "\n\r %-15s : send_ok:%d, send_fail:%d, recv:%d, ",
- "[mailbox]", le32_to_cpu(prptctrl->bt_mbx_info.cnt_send_ok),
- le32_to_cpu(prptctrl->bt_mbx_info.cnt_send_fail),
- le32_to_cpu(prptctrl->bt_mbx_info.cnt_recv));
-
- seq_printf(m, "A2DP_empty:%d(stop:%d/tx:%d/ack:%d/nack:%d)",
- le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_empty),
- le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_flowctrl),
- le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_tx),
- le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_ack),
- le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_nack));
-
- seq_printf(m,
- "\n\r %-15s : wl_rfk[req:%d/go:%d/reject:%d/tout:%d/time:%dms]",
- "[RFK/LPS]", cx->cnt_wl[BTC_WCNT_RFK_REQ],
- cx->cnt_wl[BTC_WCNT_RFK_GO],
- cx->cnt_wl[BTC_WCNT_RFK_REJECT],
- cx->cnt_wl[BTC_WCNT_RFK_TIMEOUT],
- wl->rfk_info.proc_time);
-
- seq_printf(m, ", bt_rfk[req:%d]",
- le16_to_cpu(prptctrl->bt_cnt[BTC_BCNT_RFK_REQ]));
-
- seq_printf(m, ", AOAC[RF_on:%d/RF_off:%d]",
- le16_to_cpu(prptctrl->rpt_info.cnt_aoac_rf_on),
- le16_to_cpu(prptctrl->rpt_info.cnt_aoac_rf_off));
+ p += scnprintf(p, end - p, " (WL FW Hang!!)");
+
+ p += scnprintf(p, end - p,
+ "\n\r %-15s : send_ok:%d, send_fail:%d, recv:%d, ",
+ "[mailbox]",
+ le32_to_cpu(prptctrl->bt_mbx_info.cnt_send_ok),
+ le32_to_cpu(prptctrl->bt_mbx_info.cnt_send_fail),
+ le32_to_cpu(prptctrl->bt_mbx_info.cnt_recv));
+
+ p += scnprintf(p, end - p,
+ "A2DP_empty:%d(stop:%d/tx:%d/ack:%d/nack:%d)",
+ le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_empty),
+ le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_flowctrl),
+ le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_tx),
+ le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_ack),
+ le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_nack));
+
+ p += scnprintf(p, end - p,
+ "\n\r %-15s : wl_rfk[req:%d/go:%d/reject:%d/tout:%d/time:%dms]",
+ "[RFK/LPS]", cx->cnt_wl[BTC_WCNT_RFK_REQ],
+ cx->cnt_wl[BTC_WCNT_RFK_GO],
+ cx->cnt_wl[BTC_WCNT_RFK_REJECT],
+ cx->cnt_wl[BTC_WCNT_RFK_TIMEOUT],
+ wl->rfk_info.proc_time);
+
+ p += scnprintf(p, end - p, ", bt_rfk[req:%d]",
+ le16_to_cpu(prptctrl->bt_cnt[BTC_BCNT_RFK_REQ]));
+
+ p += scnprintf(p, end - p, ", AOAC[RF_on:%d/RF_off:%d]",
+ le16_to_cpu(prptctrl->rpt_info.cnt_aoac_rf_on),
+ le16_to_cpu(prptctrl->rpt_info.cnt_aoac_rf_off));
} else {
- seq_printf(m,
- "\n\r %-15s : h2c_cnt=%d(fail:%d), c2h_cnt=%d (lps=%d/rf_off=%d)",
- "[summary]",
- pfwinfo->cnt_h2c, pfwinfo->cnt_h2c_fail,
- pfwinfo->cnt_c2h,
- wl->status.map.lps, wl->status.map.rf_off);
+ p += scnprintf(p, end - p,
+ "\n\r %-15s : h2c_cnt=%d(fail:%d), c2h_cnt=%d (lps=%d/rf_off=%d)",
+ "[summary]",
+ pfwinfo->cnt_h2c, pfwinfo->cnt_h2c_fail,
+ pfwinfo->cnt_c2h,
+ wl->status.map.lps, wl->status.map.rf_off);
}
for (i = 0; i < BTC_NCNT_NUM; i++)
cnt_sum += dm->cnt_notify[i];
- seq_printf(m,
- "\n\r %-15s : total=%d, show_coex_info=%d, power_on=%d, init_coex=%d, ",
- "[notify_cnt]",
- cnt_sum, cnt[BTC_NCNT_SHOW_COEX_INFO],
- cnt[BTC_NCNT_POWER_ON], cnt[BTC_NCNT_INIT_COEX]);
+ p += scnprintf(p, end - p,
+ "\n\r %-15s : total=%d, show_coex_info=%d, power_on=%d, init_coex=%d, ",
+ "[notify_cnt]",
+ cnt_sum, cnt[BTC_NCNT_SHOW_COEX_INFO],
+ cnt[BTC_NCNT_POWER_ON], cnt[BTC_NCNT_INIT_COEX]);
- seq_printf(m,
- "power_off=%d, radio_state=%d, role_info=%d, wl_rfk=%d, wl_sta=%d",
- cnt[BTC_NCNT_POWER_OFF], cnt[BTC_NCNT_RADIO_STATE],
- cnt[BTC_NCNT_ROLE_INFO], cnt[BTC_NCNT_WL_RFK],
- cnt[BTC_NCNT_WL_STA]);
+ p += scnprintf(p, end - p,
+ "power_off=%d, radio_state=%d, role_info=%d, wl_rfk=%d, wl_sta=%d",
+ cnt[BTC_NCNT_POWER_OFF], cnt[BTC_NCNT_RADIO_STATE],
+ cnt[BTC_NCNT_ROLE_INFO], cnt[BTC_NCNT_WL_RFK],
+ cnt[BTC_NCNT_WL_STA]);
- seq_printf(m,
- "\n\r %-15s : scan_start=%d, scan_finish=%d, switch_band=%d, switch_chbw=%d, special_pkt=%d, ",
- "[notify_cnt]",
- cnt[BTC_NCNT_SCAN_START], cnt[BTC_NCNT_SCAN_FINISH],
- cnt[BTC_NCNT_SWITCH_BAND], cnt[BTC_NCNT_SWITCH_CHBW],
- cnt[BTC_NCNT_SPECIAL_PACKET]);
+ p += scnprintf(p, end - p,
+ "\n\r %-15s : scan_start=%d, scan_finish=%d, switch_band=%d, switch_chbw=%d, special_pkt=%d, ",
+ "[notify_cnt]",
+ cnt[BTC_NCNT_SCAN_START], cnt[BTC_NCNT_SCAN_FINISH],
+ cnt[BTC_NCNT_SWITCH_BAND], cnt[BTC_NCNT_SWITCH_CHBW],
+ cnt[BTC_NCNT_SPECIAL_PACKET]);
- seq_printf(m, "timer=%d, customerize=%d, hub_msg=%d, chg_fw=%d, send_cc=%d",
- cnt[BTC_NCNT_TIMER], cnt[BTC_NCNT_CUSTOMERIZE],
- rtwdev->btc.hubmsg_cnt, cnt[BTC_NCNT_RESUME_DL_FW],
- cnt[BTC_NCNT_COUNTRYCODE]);
+ p += scnprintf(p, end - p,
+ "timer=%d, customerize=%d, hub_msg=%d, chg_fw=%d, send_cc=%d",
+ cnt[BTC_NCNT_TIMER], cnt[BTC_NCNT_CUSTOMERIZE],
+ rtwdev->btc.hubmsg_cnt, cnt[BTC_NCNT_RESUME_DL_FW],
+ cnt[BTC_NCNT_COUNTRYCODE]);
+
+ return p - buf;
}
-void rtw89_btc_dump_info(struct rtw89_dev *rtwdev, struct seq_file *m)
+ssize_t rtw89_btc_dump_info(struct rtw89_dev *rtwdev, char *buf, size_t bufsz)
{
struct rtw89_fw_suit *fw_suit = &rtwdev->fw.normal;
struct rtw89_btc *btc = &rtwdev->btc;
const struct rtw89_btc_ver *ver = btc->ver;
struct rtw89_btc_cx *cx = &btc->cx;
struct rtw89_btc_bt_info *bt = &cx->bt;
-
- seq_puts(m, "=========================================\n");
- seq_printf(m, "WL FW / BT FW %d.%d.%d.%d / NA\n",
- fw_suit->major_ver, fw_suit->minor_ver,
- fw_suit->sub_ver, fw_suit->sub_idex);
- seq_printf(m, "manual %d\n", btc->manual_ctrl);
-
- seq_puts(m, "=========================================\n");
-
- seq_printf(m, "\n\r %-15s : raw_data[%02x %02x %02x %02x %02x %02x] (type:%s/cnt:%d/same:%d)",
- "[bt_info]",
- bt->raw_info[2], bt->raw_info[3],
- bt->raw_info[4], bt->raw_info[5],
- bt->raw_info[6], bt->raw_info[7],
- bt->raw_info[0] == BTC_BTINFO_AUTO ? "auto" : "reply",
- cx->cnt_bt[BTC_BCNT_INFOUPDATE],
- cx->cnt_bt[BTC_BCNT_INFOSAME]);
-
- seq_puts(m, "\n=========================================\n");
-
- _show_cx_info(rtwdev, m);
- _show_wl_info(rtwdev, m);
- _show_bt_info(rtwdev, m);
- _show_dm_info(rtwdev, m);
- _show_fw_dm_msg(rtwdev, m);
+ char *p = buf, *end = buf + bufsz;
+
+ p += scnprintf(p, end - p,
+ "=========================================\n");
+ p += scnprintf(p, end - p,
+ "WL FW / BT FW %d.%d.%d.%d / NA\n",
+ fw_suit->major_ver, fw_suit->minor_ver,
+ fw_suit->sub_ver, fw_suit->sub_idex);
+ p += scnprintf(p, end - p, "manual %d\n",
+ btc->manual_ctrl);
+
+ p += scnprintf(p, end - p,
+ "=========================================\n");
+
+ p += scnprintf(p, end - p,
+ "\n\r %-15s : raw_data[%02x %02x %02x %02x %02x %02x] (type:%s/cnt:%d/same:%d)",
+ "[bt_info]",
+ bt->raw_info[2], bt->raw_info[3],
+ bt->raw_info[4], bt->raw_info[5],
+ bt->raw_info[6], bt->raw_info[7],
+ bt->raw_info[0] == BTC_BTINFO_AUTO ? "auto" : "reply",
+ cx->cnt_bt[BTC_BCNT_INFOUPDATE],
+ cx->cnt_bt[BTC_BCNT_INFOSAME]);
+
+ p += scnprintf(p, end - p,
+ "\n=========================================\n");
+
+ p += _show_cx_info(rtwdev, p, end - p);
+ p += _show_wl_info(rtwdev, p, end - p);
+ p += _show_bt_info(rtwdev, p, end - p);
+ p += _show_dm_info(rtwdev, p, end - p);
+ p += _show_fw_dm_msg(rtwdev, p, end - p);
if (ver->fcxmreg == 1)
- _show_mreg_v1(rtwdev, m);
+ p += _show_mreg_v1(rtwdev, p, end - p);
else if (ver->fcxmreg == 2)
- _show_mreg_v2(rtwdev, m);
+ p += _show_mreg_v2(rtwdev, p, end - p);
else if (ver->fcxmreg == 7)
- _show_mreg_v7(rtwdev, m);
+ p += _show_mreg_v7(rtwdev, p, end - p);
- _show_gpio_dbg(rtwdev, m);
+ p += _show_gpio_dbg(rtwdev, p, end - p);
if (ver->fcxbtcrpt == 1)
- _show_summary_v1(rtwdev, m);
+ p += _show_summary_v1(rtwdev, p, end - p);
else if (ver->fcxbtcrpt == 4)
- _show_summary_v4(rtwdev, m);
+ p += _show_summary_v4(rtwdev, p, end - p);
else if (ver->fcxbtcrpt == 5)
- _show_summary_v5(rtwdev, m);
+ p += _show_summary_v5(rtwdev, p, end - p);
else if (ver->fcxbtcrpt == 105)
- _show_summary_v105(rtwdev, m);
+ p += _show_summary_v105(rtwdev, p, end - p);
else if (ver->fcxbtcrpt == 7)
- _show_summary_v7(rtwdev, m);
+ p += _show_summary_v7(rtwdev, p, end - p);
else if (ver->fcxbtcrpt == 8)
- _show_summary_v8(rtwdev, m);
+ p += _show_summary_v8(rtwdev, p, end - p);
+
+ return p - buf;
}
void rtw89_coex_recognize_ver(struct rtw89_dev *rtwdev)
@@ -11037,3 +11225,24 @@ out:
rtw89_debug(rtwdev, RTW89_DBG_BTC, "[BTC] use version def[%d] = 0x%08x\n",
(int)(btc->ver - rtw89_btc_ver_defs), btc->ver->fw_ver_code);
}
+
+void rtw89_btc_ntfy_preserve_bt_time(struct rtw89_dev *rtwdev, u32 ms)
+{
+ struct rtw89_btc_bt_link_info *bt_linfo = &rtwdev->btc.cx.bt.link_info;
+ struct rtw89_btc_bt_a2dp_desc a2dp = bt_linfo->a2dp_desc;
+
+ if (test_bit(RTW89_FLAG_SER_HANDLING, rtwdev->flags))
+ return;
+
+ if (!a2dp.exist)
+ return;
+
+ fsleep(ms * 1000);
+}
+EXPORT_SYMBOL(rtw89_btc_ntfy_preserve_bt_time);
+
+void rtw89_btc_ntfy_conn_rfk(struct rtw89_dev *rtwdev, bool state)
+{
+ rtwdev->btc.cx.wl.rfk_info.con_rfk = state;
+}
+EXPORT_SYMBOL(rtw89_btc_ntfy_conn_rfk);
diff --git a/drivers/net/wireless/realtek/rtw89/coex.h b/drivers/net/wireless/realtek/rtw89/coex.h
index dbdb56e063ef..e3a1fcd79620 100644
--- a/drivers/net/wireless/realtek/rtw89/coex.h
+++ b/drivers/net/wireless/realtek/rtw89/coex.h
@@ -267,10 +267,10 @@ void rtw89_btc_ntfy_scan_finish(struct rtw89_dev *rtwdev, u8 phy_idx);
void rtw89_btc_ntfy_switch_band(struct rtw89_dev *rtwdev, u8 phy_idx, u8 band);
void rtw89_btc_ntfy_specific_packet(struct rtw89_dev *rtwdev,
enum btc_pkt_type pkt_type);
-void rtw89_btc_ntfy_eapol_packet_work(struct work_struct *work);
-void rtw89_btc_ntfy_arp_packet_work(struct work_struct *work);
-void rtw89_btc_ntfy_dhcp_packet_work(struct work_struct *work);
-void rtw89_btc_ntfy_icmp_packet_work(struct work_struct *work);
+void rtw89_btc_ntfy_eapol_packet_work(struct wiphy *wiphy, struct wiphy_work *work);
+void rtw89_btc_ntfy_arp_packet_work(struct wiphy *wiphy, struct wiphy_work *work);
+void rtw89_btc_ntfy_dhcp_packet_work(struct wiphy *wiphy, struct wiphy_work *work);
+void rtw89_btc_ntfy_icmp_packet_work(struct wiphy *wiphy, struct wiphy_work *work);
void rtw89_btc_ntfy_role_info(struct rtw89_dev *rtwdev,
struct rtw89_vif_link *rtwvif_link,
struct rtw89_sta_link *rtwsta_link,
@@ -282,14 +282,16 @@ void rtw89_btc_ntfy_wl_rfk(struct rtw89_dev *rtwdev, u8 phy_map,
void rtw89_btc_ntfy_wl_sta(struct rtw89_dev *rtwdev);
void rtw89_btc_c2h_handle(struct rtw89_dev *rtwdev, struct sk_buff *skb,
u32 len, u8 class, u8 func);
-void rtw89_btc_dump_info(struct rtw89_dev *rtwdev, struct seq_file *m);
-void rtw89_coex_act1_work(struct work_struct *work);
-void rtw89_coex_bt_devinfo_work(struct work_struct *work);
-void rtw89_coex_rfk_chk_work(struct work_struct *work);
+ssize_t rtw89_btc_dump_info(struct rtw89_dev *rtwdev, char *buf, size_t bufsz);
+void rtw89_coex_act1_work(struct wiphy *wiphy, struct wiphy_work *work);
+void rtw89_coex_bt_devinfo_work(struct wiphy *wiphy, struct wiphy_work *work);
+void rtw89_coex_rfk_chk_work(struct wiphy *wiphy, struct wiphy_work *work);
void rtw89_coex_power_on(struct rtw89_dev *rtwdev);
void rtw89_btc_set_policy(struct rtw89_dev *rtwdev, u16 policy_type);
void rtw89_btc_set_policy_v1(struct rtw89_dev *rtwdev, u16 policy_type);
void rtw89_coex_recognize_ver(struct rtw89_dev *rtwdev);
+void rtw89_btc_ntfy_preserve_bt_time(struct rtw89_dev *rtwdev, u32 ms);
+void rtw89_btc_ntfy_conn_rfk(struct rtw89_dev *rtwdev, bool state);
static inline u8 rtw89_btc_phymap(struct rtw89_dev *rtwdev,
enum rtw89_phy_idx phy_idx,
diff --git a/drivers/net/wireless/realtek/rtw89/core.c b/drivers/net/wireless/realtek/rtw89/core.c
index 85f739f1173d..49447668cbf3 100644
--- a/drivers/net/wireless/realtek/rtw89/core.c
+++ b/drivers/net/wireless/realtek/rtw89/core.c
@@ -203,6 +203,23 @@ static const struct ieee80211_iface_combination rtw89_iface_combs[] = {
},
};
+static const u8 rtw89_ext_capa_sta[] = {
+ [2] = WLAN_EXT_CAPA3_MULTI_BSSID_SUPPORT,
+ [7] = WLAN_EXT_CAPA8_OPMODE_NOTIF,
+};
+
+static const struct wiphy_iftype_ext_capab rtw89_iftypes_ext_capa[] = {
+ {
+ .iftype = NL80211_IFTYPE_STATION,
+ .extended_capabilities = rtw89_ext_capa_sta,
+ .extended_capabilities_mask = rtw89_ext_capa_sta,
+ .extended_capabilities_len = sizeof(rtw89_ext_capa_sta),
+ /* relevant only if EHT is supported */
+ .eml_capabilities = 0,
+ .mld_capa_and_ops = 0,
+ },
+};
+
#define RTW89_6GHZ_SPAN_HEAD 6145
#define RTW89_6GHZ_SPAN_IDX(center_freq) \
((((int)(center_freq) - RTW89_6GHZ_SPAN_HEAD) / 5) / 2)
@@ -211,6 +228,8 @@ static const struct ieee80211_iface_combination rtw89_iface_combs[] = {
[RTW89_6GHZ_SPAN_IDX(center_freq)] = { \
.sar_subband_low = RTW89_SAR_6GHZ_ ## subband_l, \
.sar_subband_high = RTW89_SAR_6GHZ_ ## subband_h, \
+ .acpi_sar_subband_low = RTW89_ACPI_SAR_6GHZ_ ## subband_l, \
+ .acpi_sar_subband_high = RTW89_ACPI_SAR_6GHZ_ ## subband_h, \
.ant_gain_subband_low = RTW89_ANT_GAIN_6GHZ_ ## subband_l, \
.ant_gain_subband_high = RTW89_ANT_GAIN_6GHZ_ ## subband_h, \
}
@@ -639,9 +658,17 @@ out:
static u8 rtw89_core_tx_get_mac_id(struct rtw89_dev *rtwdev,
struct rtw89_core_tx_request *tx_req)
{
+ struct rtw89_tx_desc_info *desc_info = &tx_req->desc_info;
struct rtw89_vif_link *rtwvif_link = tx_req->rtwvif_link;
struct rtw89_sta_link *rtwsta_link = tx_req->rtwsta_link;
+ if (desc_info->mlo && !desc_info->sw_mld) {
+ if (rtwsta_link)
+ return rtw89_sta_get_main_macid(rtwsta_link->rtwsta);
+ else
+ return rtw89_vif_get_main_macid(rtwvif_link->rtwvif);
+ }
+
if (!rtwsta_link)
return rtwvif_link->mac_id;
@@ -671,7 +698,7 @@ rtw89_core_tx_update_mgmt_info(struct rtw89_dev *rtwdev,
struct sk_buff *skb = tx_req->skb;
u8 qsel, ch_dma;
- qsel = desc_info->hiq ? RTW89_TX_QSEL_B0_HI : RTW89_TX_QSEL_B0_MGMT;
+ qsel = rtw89_core_get_qsel_mgmt(rtwdev, tx_req);
ch_dma = rtw89_core_get_ch_dma(rtwdev, qsel);
desc_info->qsel = qsel;
@@ -913,16 +940,17 @@ static enum btc_pkt_type
rtw89_core_tx_btc_spec_pkt_notify(struct rtw89_dev *rtwdev,
struct rtw89_core_tx_request *tx_req)
{
+ struct wiphy *wiphy = rtwdev->hw->wiphy;
struct sk_buff *skb = tx_req->skb;
struct udphdr *udphdr;
if (IEEE80211_SKB_CB(skb)->control.flags & IEEE80211_TX_CTRL_PORT_CTRL_PROTO) {
- ieee80211_queue_work(rtwdev->hw, &rtwdev->btc.eapol_notify_work);
+ wiphy_work_queue(wiphy, &rtwdev->btc.eapol_notify_work);
return PACKET_EAPOL;
}
if (skb->protocol == htons(ETH_P_ARP)) {
- ieee80211_queue_work(rtwdev->hw, &rtwdev->btc.arp_notify_work);
+ wiphy_work_queue(wiphy, &rtwdev->btc.arp_notify_work);
return PACKET_ARP;
}
@@ -932,14 +960,14 @@ rtw89_core_tx_btc_spec_pkt_notify(struct rtw89_dev *rtwdev,
if (((udphdr->source == htons(67) && udphdr->dest == htons(68)) ||
(udphdr->source == htons(68) && udphdr->dest == htons(67))) &&
skb->len > 282) {
- ieee80211_queue_work(rtwdev->hw, &rtwdev->btc.dhcp_notify_work);
+ wiphy_work_queue(wiphy, &rtwdev->btc.dhcp_notify_work);
return PACKET_DHCP;
}
}
if (skb->protocol == htons(ETH_P_IP) &&
ip_hdr(skb)->protocol == IPPROTO_ICMP) {
- ieee80211_queue_work(rtwdev->hw, &rtwdev->btc.icmp_notify_work);
+ wiphy_work_queue(wiphy, &rtwdev->btc.icmp_notify_work);
return PACKET_ICMP;
}
@@ -1103,39 +1131,23 @@ int rtw89_h2c_tx(struct rtw89_dev *rtwdev,
return 0;
}
-int rtw89_core_tx_write(struct rtw89_dev *rtwdev, struct ieee80211_vif *vif,
- struct ieee80211_sta *sta, struct sk_buff *skb, int *qsel)
+static int rtw89_core_tx_write_link(struct rtw89_dev *rtwdev,
+ struct rtw89_vif_link *rtwvif_link,
+ struct rtw89_sta_link *rtwsta_link,
+ struct sk_buff *skb, int *qsel, bool sw_mld)
{
- struct rtw89_sta *rtwsta = sta_to_rtwsta_safe(sta);
- struct rtw89_vif *rtwvif = vif_to_rtwvif(vif);
- struct rtw89_core_tx_request tx_req = {0};
- struct rtw89_sta_link *rtwsta_link = NULL;
- struct rtw89_vif_link *rtwvif_link;
+ struct ieee80211_sta *sta = rtwsta_link_to_sta_safe(rtwsta_link);
+ struct ieee80211_vif *vif = rtwvif_link_to_vif(rtwvif_link);
+ struct rtw89_vif *rtwvif = rtwvif_link->rtwvif;
+ struct rtw89_core_tx_request tx_req = {};
int ret;
- /* By default, driver writes tx via the link on HW-0. And then,
- * according to links' status, HW can change tx to another link.
- */
-
- if (rtwsta) {
- rtwsta_link = rtw89_sta_get_link_inst(rtwsta, 0);
- if (unlikely(!rtwsta_link)) {
- rtw89_err(rtwdev, "tx: find no sta link on HW-0\n");
- return -ENOLINK;
- }
- }
-
- rtwvif_link = rtw89_vif_get_link_inst(rtwvif, 0);
- if (unlikely(!rtwvif_link)) {
- rtw89_err(rtwdev, "tx: find no vif link on HW-0\n");
- return -ENOLINK;
- }
-
tx_req.skb = skb;
tx_req.vif = vif;
tx_req.sta = sta;
tx_req.rtwvif_link = rtwvif_link;
tx_req.rtwsta_link = rtwsta_link;
+ tx_req.desc_info.sw_mld = sw_mld;
rtw89_traffic_stats_accu(rtwdev, &rtwdev->stats, skb, true);
rtw89_traffic_stats_accu(rtwdev, &rtwvif->stats, skb, true);
@@ -1154,6 +1166,33 @@ int rtw89_core_tx_write(struct rtw89_dev *rtwdev, struct ieee80211_vif *vif,
return 0;
}
+int rtw89_core_tx_write(struct rtw89_dev *rtwdev, struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta, struct sk_buff *skb, int *qsel)
+{
+ struct rtw89_sta *rtwsta = sta_to_rtwsta_safe(sta);
+ struct rtw89_vif *rtwvif = vif_to_rtwvif(vif);
+ struct rtw89_sta_link *rtwsta_link = NULL;
+ struct rtw89_vif_link *rtwvif_link;
+
+ if (rtwsta) {
+ rtwsta_link = rtw89_get_designated_link(rtwsta);
+ if (unlikely(!rtwsta_link)) {
+ rtw89_err(rtwdev, "tx: find no sta designated link\n");
+ return -ENOLINK;
+ }
+
+ rtwvif_link = rtwsta_link->rtwvif_link;
+ } else {
+ rtwvif_link = rtw89_get_designated_link(rtwvif);
+ if (unlikely(!rtwvif_link)) {
+ rtw89_err(rtwdev, "tx: find no vif designated link\n");
+ return -ENOLINK;
+ }
+ }
+
+ return rtw89_core_tx_write_link(rtwdev, rtwvif_link, rtwsta_link, skb, qsel, false);
+}
+
static __le32 rtw89_build_txwd_body0(struct rtw89_tx_desc_info *desc_info)
{
u32 dword = FIELD_PREP(RTW89_TXWD_BODY0_WP_OFFSET, desc_info->wp_offset) |
@@ -1381,7 +1420,9 @@ static __le32 rtw89_build_txwd_body2_v2(struct rtw89_tx_desc_info *desc_info)
static __le32 rtw89_build_txwd_body3_v2(struct rtw89_tx_desc_info *desc_info)
{
- u32 dword = FIELD_PREP(BE_TXD_BODY3_WIFI_SEQ, desc_info->seq);
+ u32 dword = FIELD_PREP(BE_TXD_BODY3_WIFI_SEQ, desc_info->seq) |
+ FIELD_PREP(BE_TXD_BODY3_MLO_FLAG, desc_info->mlo) |
+ FIELD_PREP(BE_TXD_BODY3_IS_MLD_SW_EN, desc_info->sw_mld);
return cpu_to_le32(dword);
}
@@ -1634,10 +1675,7 @@ static void rtw89_core_rx_process_phy_ppdu_iter(void *data,
u8 evm_pos = 0;
int i;
- /* FIXME: For single link, taking link on HW-0 here is okay. But, when
- * enabling multiple active links, we should determine the right link.
- */
- rtwsta_link = rtw89_sta_get_link_inst(rtwsta, 0);
+ rtwsta_link = rtw89_sta_get_link_inst(rtwsta, phy_ppdu->phy_idx);
if (unlikely(!rtwsta_link))
return;
@@ -2057,10 +2095,21 @@ static void rtw89_stats_trigger_frame(struct rtw89_dev *rtwdev,
break;
if (aid == vif->cfg.aid) {
- enum nl80211_he_ru_alloc rua = rtw89_he_rua_to_ru_alloc(tf_rua >> 1);
+ enum nl80211_he_ru_alloc rua;
rtwvif->stats.rx_tf_acc++;
rtwdev->stats.rx_tf_acc++;
+
+ /* The following only required for HE trigger frame, but we
+ * cannot use UL HE-SIG-A2 reserved subfield to identify it
+ * since some 11ax APs will fill it with all 0s, which will
+ * be misunderstood as EHT trigger frame.
+ */
+ if (bss_conf->eht_support)
+ break;
+
+ rua = rtw89_he_rua_to_ru_alloc(tf_rua >> 1);
+
if (tf_bw == IEEE80211_TRIGGER_ULBW_160_80P80MHZ &&
rua <= NL80211_RATE_INFO_HE_RU_ALLOC_106)
rtwvif_link->pwr_diff_en = true;
@@ -2071,17 +2120,17 @@ static void rtw89_stats_trigger_frame(struct rtw89_dev *rtwdev,
}
}
-static void rtw89_cancel_6ghz_probe_work(struct work_struct *work)
+static void rtw89_cancel_6ghz_probe_work(struct wiphy *wiphy, struct wiphy_work *work)
{
struct rtw89_dev *rtwdev = container_of(work, struct rtw89_dev,
cancel_6ghz_probe_work);
struct list_head *pkt_list = rtwdev->scan_info.pkt_list;
struct rtw89_pktofld_info *info;
- mutex_lock(&rtwdev->mutex);
+ lockdep_assert_wiphy(wiphy);
if (!rtwdev->scanning)
- goto out;
+ return;
list_for_each_entry(info, &pkt_list[NL80211_BAND_6GHZ], list) {
if (!info->cancel || !test_bit(info->id, rtwdev->pkt_offload))
@@ -2094,9 +2143,6 @@ static void rtw89_cancel_6ghz_probe_work(struct work_struct *work)
* since if during scanning, pkt_list is accessed in bottom half.
*/
}
-
-out:
- mutex_unlock(&rtwdev->mutex);
}
static void rtw89_core_cancel_6ghz_probe_tx(struct rtw89_dev *rtwdev,
@@ -2131,7 +2177,7 @@ static void rtw89_core_cancel_6ghz_probe_tx(struct rtw89_dev *rtwdev,
}
if (queue_work)
- ieee80211_queue_work(rtwdev->hw, &rtwdev->cancel_6ghz_probe_work);
+ wiphy_work_queue(rtwdev->hw->wiphy, &rtwdev->cancel_6ghz_probe_work);
}
static void rtw89_vif_sync_bcn_tsf(struct rtw89_vif_link *rtwvif_link,
@@ -2154,8 +2200,10 @@ static void rtw89_vif_rx_stats_iter(void *data, u8 *mac,
struct rtw89_pkt_stat *pkt_stat = &rtwdev->phystat.cur_pkt_stat;
struct rtw89_rx_desc_info *desc_info = iter_data->desc_info;
struct sk_buff *skb = iter_data->skb;
+ struct ieee80211_rx_status *rx_status = IEEE80211_SKB_RXCB(skb);
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
struct rtw89_rx_phy_ppdu *phy_ppdu = iter_data->phy_ppdu;
+ bool is_mld = ieee80211_vif_is_mld(vif);
struct ieee80211_bss_conf *bss_conf;
struct rtw89_vif_link *rtwvif_link;
const u8 *bssid = iter_data->bssid;
@@ -2167,10 +2215,7 @@ static void rtw89_vif_rx_stats_iter(void *data, u8 *mac,
rcu_read_lock();
- /* FIXME: For single link, taking link on HW-0 here is okay. But, when
- * enabling multiple active links, we should determine the right link.
- */
- rtwvif_link = rtw89_vif_get_link_inst(rtwvif, 0);
+ rtwvif_link = rtw89_vif_get_link_inst(rtwvif, desc_info->bb_sel);
if (unlikely(!rtwvif_link))
goto out;
@@ -2186,6 +2231,11 @@ static void rtw89_vif_rx_stats_iter(void *data, u8 *mac,
if (!ether_addr_equal(bss_conf->bssid, bssid))
goto out;
+ if (is_mld) {
+ rx_status->link_valid = true;
+ rx_status->link_id = rtwvif_link->link_id;
+ }
+
if (ieee80211_is_beacon(hdr->frame_control)) {
if (vif->type == NL80211_IFTYPE_STATION &&
!test_bit(RTW89_FLAG_WOWLAN, rtwdev->flags)) {
@@ -2194,8 +2244,11 @@ static void rtw89_vif_rx_stats_iter(void *data, u8 *mac,
}
pkt_stat->beacon_nr++;
- if (phy_ppdu)
+ if (phy_ppdu) {
ewma_rssi_add(&rtwdev->phystat.bcn_rssi, phy_ppdu->rssi_avg);
+ if (!test_bit(RTW89_FLAG_LOW_POWER_MODE, rtwdev->flags))
+ rtwvif_link->bcn_bw_idx = phy_ppdu->bw_idx;
+ }
pkt_stat->beacon_rate = desc_info->data_rate;
}
@@ -2381,6 +2434,49 @@ static void rtw89_core_validate_rx_signal(struct ieee80211_rx_status *rx_status)
rx_status->flag |= RX_FLAG_NO_SIGNAL_VAL;
}
+static void rtw89_core_update_rx_freq_from_ie(struct rtw89_dev *rtwdev,
+ struct sk_buff *skb,
+ struct ieee80211_rx_status *rx_status)
+{
+ struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)skb->data;
+ size_t hdr_len, ielen;
+ u8 *variable;
+ int chan;
+
+ if (!rtwdev->chip->rx_freq_frome_ie)
+ return;
+
+ if (!rtwdev->scanning)
+ return;
+
+ if (ieee80211_is_beacon(mgmt->frame_control)) {
+ variable = mgmt->u.beacon.variable;
+ hdr_len = offsetof(struct ieee80211_mgmt,
+ u.beacon.variable);
+ } else if (ieee80211_is_probe_resp(mgmt->frame_control)) {
+ variable = mgmt->u.probe_resp.variable;
+ hdr_len = offsetof(struct ieee80211_mgmt,
+ u.probe_resp.variable);
+ } else {
+ return;
+ }
+
+ if (skb->len > hdr_len)
+ ielen = skb->len - hdr_len;
+ else
+ return;
+
+ /* The parsing code for both 2GHz and 5GHz bands is the same in this
+ * function.
+ */
+ chan = cfg80211_get_ies_channel_number(variable, ielen, NL80211_BAND_2GHZ);
+ if (chan == -1)
+ return;
+
+ rx_status->band = chan > 14 ? RTW89_BAND_5G : RTW89_BAND_2G;
+ rx_status->freq = ieee80211_channel_to_frequency(chan, rx_status->band);
+}
+
static void rtw89_core_rx_to_mac80211(struct rtw89_dev *rtwdev,
struct rtw89_rx_phy_ppdu *phy_ppdu,
struct rtw89_rx_desc_info *desc_info,
@@ -2398,6 +2494,7 @@ static void rtw89_core_rx_to_mac80211(struct rtw89_dev *rtwdev,
rtw89_core_update_rx_status_by_ppdu(rtwdev, rx_status, phy_ppdu);
rtw89_core_update_radiotap(rtwdev, skb_ppdu, rx_status);
rtw89_core_validate_rx_signal(rx_status);
+ rtw89_core_update_rx_freq_from_ie(rtwdev, skb_ppdu, rx_status);
/* In low power mode, it does RX in thread context. */
local_bh_disable();
@@ -2437,7 +2534,8 @@ static void rtw89_core_rx_process_ppdu_sts(struct rtw89_dev *rtwdev,
.len = skb->len,
.to_self = desc_info->addr1_match,
.rate = desc_info->data_rate,
- .mac_id = desc_info->mac_id};
+ .mac_id = desc_info->mac_id,
+ .phy_idx = desc_info->bb_sel};
int ret;
if (desc_info->mac_info_valid) {
@@ -2548,6 +2646,7 @@ void rtw89_core_query_rxdesc_v2(struct rtw89_dev *rtwdev,
desc_info->shift = le32_get_bits(rxd_s->dword0, BE_RXD_SHIFT_MASK);
desc_info->long_rxdesc = le32_get_bits(rxd_s->dword0, BE_RXD_LONG_RXD);
desc_info->pkt_type = le32_get_bits(rxd_s->dword0, BE_RXD_RPKT_TYPE_MASK);
+ desc_info->bb_sel = le32_get_bits(rxd_s->dword0, BE_RXD_BB_SEL);
if (desc_info->pkt_type == RTW89_CORE_RX_TYPE_PPDU_STAT)
desc_info->mac_info_valid = true;
@@ -2620,10 +2719,7 @@ void rtw89_core_stats_sta_rx_status_iter(void *data, struct ieee80211_sta *sta)
struct rtw89_sta_link *rtwsta_link;
u8 mac_id = iter_data->mac_id;
- /* FIXME: For single link, taking link on HW-0 here is okay. But, when
- * enabling multiple active links, we should determine the right link.
- */
- rtwsta_link = rtw89_sta_get_link_inst(rtwsta, 0);
+ rtwsta_link = rtw89_sta_get_link_inst(rtwsta, desc_info->bb_sel);
if (unlikely(!rtwsta_link))
return;
@@ -3072,9 +3168,9 @@ static bool rtw89_core_txq_agg_wait(struct rtw89_dev *rtwdev,
if (!rtwsta)
return false;
- rtwsta_link = rtw89_sta_get_link_inst(rtwsta, 0);
+ rtwsta_link = rtw89_get_designated_link(rtwsta);
if (unlikely(!rtwsta_link)) {
- rtw89_err(rtwdev, "agg wait: find no link on HW-0\n");
+ rtw89_err(rtwdev, "agg wait: find no designated link\n");
return false;
}
@@ -3143,13 +3239,14 @@ static void rtw89_core_txq_schedule(struct rtw89_dev *rtwdev, u8 ac, bool *reinv
ieee80211_txq_schedule_end(hw, ac);
}
-static void rtw89_ips_work(struct work_struct *work)
+static void rtw89_ips_work(struct wiphy *wiphy, struct wiphy_work *work)
{
struct rtw89_dev *rtwdev = container_of(work, struct rtw89_dev,
ips_work);
- mutex_lock(&rtwdev->mutex);
+
+ lockdep_assert_wiphy(wiphy);
+
rtw89_enter_ips_by_hwflags(rtwdev);
- mutex_unlock(&rtwdev->mutex);
}
static void rtw89_core_txq_work(struct work_struct *w)
@@ -3238,8 +3335,10 @@ static int rtw89_core_send_nullfunc(struct rtw89_dev *rtwdev,
{
struct ieee80211_vif *vif = rtwvif_link_to_vif(rtwvif_link);
int link_id = ieee80211_vif_is_mld(vif) ? rtwvif_link->link_id : -1;
+ struct rtw89_sta_link *rtwsta_link;
struct ieee80211_sta *sta;
struct ieee80211_hdr *hdr;
+ struct rtw89_sta *rtwsta;
struct sk_buff *skb;
int ret, qsel;
@@ -3252,6 +3351,7 @@ static int rtw89_core_send_nullfunc(struct rtw89_dev *rtwdev,
ret = -EINVAL;
goto out;
}
+ rtwsta = sta_to_rtwsta(sta);
skb = ieee80211_nullfunc_get(rtwdev->hw, vif, link_id, qos);
if (!skb) {
@@ -3263,7 +3363,13 @@ static int rtw89_core_send_nullfunc(struct rtw89_dev *rtwdev,
if (ps)
hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_PM);
- ret = rtw89_core_tx_write(rtwdev, vif, sta, skb, &qsel);
+ rtwsta_link = rtwsta->links[rtwvif_link->link_id];
+ if (unlikely(!rtwsta_link)) {
+ ret = -ENOLINK;
+ goto out;
+ }
+
+ ret = rtw89_core_tx_write_link(rtwdev, rtwvif_link, rtwsta_link, skb, &qsel, true);
if (ret) {
rtw89_warn(rtwdev, "nullfunc transmit failed: %d\n", ret);
dev_kfree_skb_any(skb);
@@ -3283,6 +3389,9 @@ out:
void rtw89_roc_start(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif)
{
const struct rtw89_mac_gen_def *mac = rtwdev->chip->mac_def;
+ struct rtw89_chanctx_pause_parm pause_parm = {
+ .rsn = RTW89_CHANCTX_PAUSE_REASON_ROC,
+ };
struct ieee80211_hw *hw = rtwdev->hw;
struct rtw89_roc *roc = &rtwvif->roc;
struct rtw89_vif_link *rtwvif_link;
@@ -3291,19 +3400,21 @@ void rtw89_roc_start(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif)
u32 reg;
int ret;
- lockdep_assert_held(&rtwdev->mutex);
+ lockdep_assert_wiphy(hw->wiphy);
rtw89_leave_ips_by_hwflags(rtwdev);
rtw89_leave_lps(rtwdev);
- rtwvif_link = rtw89_vif_get_link_inst(rtwvif, RTW89_ROC_BY_LINK_INDEX);
+ rtwvif_link = rtw89_get_designated_link(rtwvif);
if (unlikely(!rtwvif_link)) {
- rtw89_err(rtwdev, "roc start: find no link on HW-%u\n",
- RTW89_ROC_BY_LINK_INDEX);
+ rtw89_err(rtwdev, "roc start: find no designated link\n");
return;
}
- rtw89_chanctx_pause(rtwdev, RTW89_CHANCTX_PAUSE_REASON_ROC);
+ roc->link_id = rtwvif_link->link_id;
+
+ pause_parm.trigger = rtwvif_link;
+ rtw89_chanctx_pause(rtwdev, &pause_parm);
ret = rtw89_core_send_nullfunc(rtwdev, rtwvif_link, true, true);
if (ret)
@@ -3323,16 +3434,16 @@ void rtw89_roc_start(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif)
}
cfg80211_chandef_create(&roc_chan, &roc->chan, NL80211_CHAN_NO_HT);
- rtw89_config_roc_chandef(rtwdev, rtwvif_link->chanctx_idx, &roc_chan);
+ rtw89_config_roc_chandef(rtwdev, rtwvif_link, &roc_chan);
rtw89_set_channel(rtwdev);
reg = rtw89_mac_reg_by_idx(rtwdev, mac->rx_fltr, rtwvif_link->mac_idx);
rtw89_write32_clr(rtwdev, reg, B_AX_A_UC_CAM_MATCH | B_AX_A_BC_CAM_MATCH);
ieee80211_ready_on_channel(hw);
- cancel_delayed_work(&rtwvif->roc.roc_work);
- ieee80211_queue_delayed_work(hw, &rtwvif->roc.roc_work,
- msecs_to_jiffies(rtwvif->roc.duration));
+ wiphy_delayed_work_cancel(hw->wiphy, &rtwvif->roc.roc_work);
+ wiphy_delayed_work_queue(hw->wiphy, &rtwvif->roc.roc_work,
+ msecs_to_jiffies(rtwvif->roc.duration));
}
void rtw89_roc_end(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif)
@@ -3345,17 +3456,17 @@ void rtw89_roc_end(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif)
u32 reg;
int ret;
- lockdep_assert_held(&rtwdev->mutex);
+ lockdep_assert_wiphy(hw->wiphy);
ieee80211_remain_on_channel_expired(hw);
rtw89_leave_ips_by_hwflags(rtwdev);
rtw89_leave_lps(rtwdev);
- rtwvif_link = rtw89_vif_get_link_inst(rtwvif, RTW89_ROC_BY_LINK_INDEX);
+ rtwvif_link = rtwvif->links[roc->link_id];
if (unlikely(!rtwvif_link)) {
- rtw89_err(rtwdev, "roc end: find no link on HW-%u\n",
- RTW89_ROC_BY_LINK_INDEX);
+ rtw89_err(rtwdev, "roc end: find no link (link id %u)\n",
+ roc->link_id);
return;
}
@@ -3363,7 +3474,7 @@ void rtw89_roc_end(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif)
rtw89_write32_mask(rtwdev, reg, B_AX_RX_FLTR_CFG_MASK, rtwdev->hal.rx_fltr);
roc->state = RTW89_ROC_IDLE;
- rtw89_config_roc_chandef(rtwdev, rtwvif_link->chanctx_idx, NULL);
+ rtw89_config_roc_chandef(rtwdev, rtwvif_link, NULL);
rtw89_chanctx_proceed(rtwdev, NULL);
ret = rtw89_core_send_nullfunc(rtwdev, rtwvif_link, true, false);
if (ret)
@@ -3377,18 +3488,18 @@ void rtw89_roc_end(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif)
queue_work(rtwdev->txq_wq, &rtwdev->txq_work);
if (hw->conf.flags & IEEE80211_CONF_IDLE)
- ieee80211_queue_delayed_work(hw, &roc->roc_work,
- msecs_to_jiffies(RTW89_ROC_IDLE_TIMEOUT));
+ wiphy_delayed_work_queue(hw->wiphy, &roc->roc_work,
+ msecs_to_jiffies(RTW89_ROC_IDLE_TIMEOUT));
}
-void rtw89_roc_work(struct work_struct *work)
+void rtw89_roc_work(struct wiphy *wiphy, struct wiphy_work *work)
{
struct rtw89_vif *rtwvif = container_of(work, struct rtw89_vif,
roc.roc_work.work);
struct rtw89_dev *rtwdev = rtwvif->rtwdev;
struct rtw89_roc *roc = &rtwvif->roc;
- mutex_lock(&rtwdev->mutex);
+ lockdep_assert_wiphy(wiphy);
switch (roc->state) {
case RTW89_ROC_IDLE:
@@ -3401,8 +3512,6 @@ void rtw89_roc_work(struct work_struct *work)
default:
break;
}
-
- mutex_unlock(&rtwdev->mutex);
}
static enum rtw89_tfc_lv rtw89_get_traffic_level(struct rtw89_dev *rtwdev,
@@ -3533,26 +3642,118 @@ void rtw89_traffic_stats_init(struct rtw89_dev *rtwdev,
ewma_tp_init(&stats->rx_ewma_tp);
}
-static void rtw89_track_work(struct work_struct *work)
+#define RTW89_MLSR_GOTO_2GHZ_THRESHOLD -53
+#define RTW89_MLSR_EXIT_2GHZ_THRESHOLD -38
+static void rtw89_core_mlsr_link_decision(struct rtw89_dev *rtwdev,
+ struct rtw89_vif *rtwvif)
+{
+ unsigned int sel_link_id = IEEE80211_MLD_MAX_NUM_LINKS;
+ struct ieee80211_vif *vif = rtwvif_to_vif(rtwvif);
+ struct rtw89_vif_link *rtwvif_link;
+ const struct rtw89_chan *chan;
+ unsigned long usable_links;
+ unsigned int link_id;
+ u8 decided_bands;
+ u8 rssi;
+
+ rssi = ewma_rssi_read(&rtwdev->phystat.bcn_rssi);
+ if (unlikely(!rssi))
+ return;
+
+ if (RTW89_RSSI_RAW_TO_DBM(rssi) >= RTW89_MLSR_EXIT_2GHZ_THRESHOLD)
+ decided_bands = BIT(RTW89_BAND_5G) | BIT(RTW89_BAND_6G);
+ else if (RTW89_RSSI_RAW_TO_DBM(rssi) <= RTW89_MLSR_GOTO_2GHZ_THRESHOLD)
+ decided_bands = BIT(RTW89_BAND_2G);
+ else
+ return;
+
+ usable_links = ieee80211_vif_usable_links(vif);
+
+ rtwvif_link = rtw89_get_designated_link(rtwvif);
+ if (unlikely(!rtwvif_link))
+ goto select;
+
+ chan = rtw89_chan_get(rtwdev, rtwvif_link->chanctx_idx);
+ if (decided_bands & BIT(chan->band_type))
+ return;
+
+ usable_links &= ~BIT(rtwvif_link->link_id);
+
+select:
+ rcu_read_lock();
+
+ for_each_set_bit(link_id, &usable_links, IEEE80211_MLD_MAX_NUM_LINKS) {
+ struct ieee80211_bss_conf *link_conf;
+ struct ieee80211_channel *channel;
+ enum rtw89_band band;
+
+ link_conf = rcu_dereference(vif->link_conf[link_id]);
+ if (unlikely(!link_conf))
+ continue;
+
+ channel = link_conf->chanreq.oper.chan;
+ if (unlikely(!channel))
+ continue;
+
+ band = rtw89_nl80211_to_hw_band(channel->band);
+ if (decided_bands & BIT(band)) {
+ sel_link_id = link_id;
+ break;
+ }
+ }
+
+ rcu_read_unlock();
+
+ if (sel_link_id == IEEE80211_MLD_MAX_NUM_LINKS)
+ return;
+
+ rtw89_core_mlsr_switch(rtwdev, rtwvif, sel_link_id);
+}
+
+static void rtw89_core_mlo_track(struct rtw89_dev *rtwdev)
+{
+ struct rtw89_hal *hal = &rtwdev->hal;
+ struct ieee80211_vif *vif;
+ struct rtw89_vif *rtwvif;
+
+ if (hal->disabled_dm_bitmap & BIT(RTW89_DM_MLO))
+ return;
+
+ rtw89_for_each_rtwvif(rtwdev, rtwvif) {
+ vif = rtwvif_to_vif(rtwvif);
+ if (!vif->cfg.assoc || !ieee80211_vif_is_mld(vif))
+ continue;
+
+ switch (rtwvif->mlo_mode) {
+ case RTW89_MLO_MODE_MLSR:
+ rtw89_core_mlsr_link_decision(rtwdev, rtwvif);
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+static void rtw89_track_work(struct wiphy *wiphy, struct wiphy_work *work)
{
struct rtw89_dev *rtwdev = container_of(work, struct rtw89_dev,
track_work.work);
bool tfc_changed;
+ lockdep_assert_wiphy(wiphy);
+
if (test_bit(RTW89_FLAG_FORBIDDEN_TRACK_WROK, rtwdev->flags))
return;
- mutex_lock(&rtwdev->mutex);
-
if (!test_bit(RTW89_FLAG_RUNNING, rtwdev->flags))
- goto out;
+ return;
- ieee80211_queue_delayed_work(rtwdev->hw, &rtwdev->track_work,
- RTW89_TRACK_WORK_PERIOD);
+ wiphy_delayed_work_queue(wiphy, &rtwdev->track_work,
+ RTW89_TRACK_WORK_PERIOD);
tfc_changed = rtw89_traffic_stats_track(rtwdev);
if (rtwdev->scanning)
- goto out;
+ return;
rtw89_leave_lps(rtwdev);
@@ -3571,15 +3772,13 @@ static void rtw89_track_work(struct work_struct *work)
rtw89_phy_antdiv_track(rtwdev);
rtw89_phy_ul_tb_ctrl_track(rtwdev);
rtw89_phy_edcca_track(rtwdev);
- rtw89_tas_track(rtwdev);
+ rtw89_sar_track(rtwdev);
rtw89_chanctx_track(rtwdev);
rtw89_core_rfkill_poll(rtwdev, false);
+ rtw89_core_mlo_track(rtwdev);
if (rtwdev->lps_enabled && !rtwdev->btc.lps)
rtw89_enter_lps_track(rtwdev);
-
-out:
- mutex_unlock(&rtwdev->mutex);
}
u8 rtw89_core_acquire_bit_map(unsigned long *addr, unsigned long size)
@@ -3613,7 +3812,7 @@ int rtw89_core_acquire_sta_ba_entry(struct rtw89_dev *rtwdev,
u8 idx;
int i;
- lockdep_assert_held(&rtwdev->mutex);
+ lockdep_assert_wiphy(rtwdev->hw->wiphy);
idx = rtw89_core_acquire_bit_map(cam_info->ba_cam_map, chip->bacam_num);
if (idx == chip->bacam_num) {
@@ -3657,7 +3856,7 @@ int rtw89_core_release_sta_ba_entry(struct rtw89_dev *rtwdev,
struct rtw89_ba_cam_entry *entry = NULL, *tmp;
u8 idx;
- lockdep_assert_held(&rtwdev->mutex);
+ lockdep_assert_wiphy(rtwdev->hw->wiphy);
list_for_each_entry_safe(entry, tmp, &rtwsta_link->ba_cam_list, list) {
if (entry->tid != tid)
@@ -3805,6 +4004,9 @@ int rtw89_core_sta_link_disassoc(struct rtw89_dev *rtwdev,
if (vif->type == NL80211_IFTYPE_STATION)
rtw89_fw_h2c_set_bcn_fltr_cfg(rtwdev, rtwvif_link, false);
+ if (rtwvif_link->wifi_role == RTW89_WIFI_ROLE_P2P_CLIENT)
+ rtw89_p2p_noa_once_deinit(rtwvif_link);
+
return 0;
}
@@ -4320,17 +4522,18 @@ static void rtw89_init_eht_cap(struct rtw89_dev *rtwdev,
#define RTW89_SBAND_IFTYPES_NR 2
-static void rtw89_init_he_eht_cap(struct rtw89_dev *rtwdev,
- enum nl80211_band band,
- struct ieee80211_supported_band *sband)
+static int rtw89_init_he_eht_cap(struct rtw89_dev *rtwdev,
+ enum nl80211_band band,
+ struct ieee80211_supported_band *sband)
{
struct ieee80211_sband_iftype_data *iftype_data;
enum nl80211_iftype iftype;
int idx = 0;
- iftype_data = kcalloc(RTW89_SBAND_IFTYPES_NR, sizeof(*iftype_data), GFP_KERNEL);
+ iftype_data = devm_kcalloc(rtwdev->dev, RTW89_SBAND_IFTYPES_NR,
+ sizeof(*iftype_data), GFP_KERNEL);
if (!iftype_data)
- return;
+ return -ENOMEM;
for (iftype = 0; iftype < NUM_NL80211_IFTYPES; iftype++) {
switch (iftype) {
@@ -4355,103 +4558,101 @@ static void rtw89_init_he_eht_cap(struct rtw89_dev *rtwdev,
}
_ieee80211_set_sband_iftype_data(sband, iftype_data, idx);
+ return 0;
+}
+
+static struct ieee80211_supported_band *
+rtw89_core_sband_dup(struct rtw89_dev *rtwdev,
+ const struct ieee80211_supported_band *sband)
+{
+ struct ieee80211_supported_band *dup;
+
+ dup = devm_kmemdup(rtwdev->dev, sband, sizeof(*sband), GFP_KERNEL);
+ if (!dup)
+ return NULL;
+
+ dup->channels = devm_kmemdup(rtwdev->dev, sband->channels,
+ sizeof(*sband->channels) * sband->n_channels,
+ GFP_KERNEL);
+ if (!dup->channels)
+ return NULL;
+
+ dup->bitrates = devm_kmemdup(rtwdev->dev, sband->bitrates,
+ sizeof(*sband->bitrates) * sband->n_bitrates,
+ GFP_KERNEL);
+ if (!dup->bitrates)
+ return NULL;
+
+ return dup;
}
static int rtw89_core_set_supported_band(struct rtw89_dev *rtwdev)
{
struct ieee80211_hw *hw = rtwdev->hw;
- struct ieee80211_supported_band *sband_2ghz = NULL, *sband_5ghz = NULL;
- struct ieee80211_supported_band *sband_6ghz = NULL;
- u32 size = sizeof(struct ieee80211_supported_band);
+ struct ieee80211_supported_band *sband;
u8 support_bands = rtwdev->chip->support_bands;
+ int ret;
if (support_bands & BIT(NL80211_BAND_2GHZ)) {
- sband_2ghz = kmemdup(&rtw89_sband_2ghz, size, GFP_KERNEL);
- if (!sband_2ghz)
- goto err;
- rtw89_init_ht_cap(rtwdev, &sband_2ghz->ht_cap);
- rtw89_init_he_eht_cap(rtwdev, NL80211_BAND_2GHZ, sband_2ghz);
- hw->wiphy->bands[NL80211_BAND_2GHZ] = sband_2ghz;
+ sband = rtw89_core_sband_dup(rtwdev, &rtw89_sband_2ghz);
+ if (!sband)
+ return -ENOMEM;
+ rtw89_init_ht_cap(rtwdev, &sband->ht_cap);
+ ret = rtw89_init_he_eht_cap(rtwdev, NL80211_BAND_2GHZ, sband);
+ if (ret)
+ return ret;
+ hw->wiphy->bands[NL80211_BAND_2GHZ] = sband;
}
if (support_bands & BIT(NL80211_BAND_5GHZ)) {
- sband_5ghz = kmemdup(&rtw89_sband_5ghz, size, GFP_KERNEL);
- if (!sband_5ghz)
- goto err;
- rtw89_init_ht_cap(rtwdev, &sband_5ghz->ht_cap);
- rtw89_init_vht_cap(rtwdev, &sband_5ghz->vht_cap);
- rtw89_init_he_eht_cap(rtwdev, NL80211_BAND_5GHZ, sband_5ghz);
- hw->wiphy->bands[NL80211_BAND_5GHZ] = sband_5ghz;
+ sband = rtw89_core_sband_dup(rtwdev, &rtw89_sband_5ghz);
+ if (!sband)
+ return -ENOMEM;
+ rtw89_init_ht_cap(rtwdev, &sband->ht_cap);
+ rtw89_init_vht_cap(rtwdev, &sband->vht_cap);
+ ret = rtw89_init_he_eht_cap(rtwdev, NL80211_BAND_5GHZ, sband);
+ if (ret)
+ return ret;
+ hw->wiphy->bands[NL80211_BAND_5GHZ] = sband;
}
if (support_bands & BIT(NL80211_BAND_6GHZ)) {
- sband_6ghz = kmemdup(&rtw89_sband_6ghz, size, GFP_KERNEL);
- if (!sband_6ghz)
- goto err;
- rtw89_init_he_eht_cap(rtwdev, NL80211_BAND_6GHZ, sband_6ghz);
- hw->wiphy->bands[NL80211_BAND_6GHZ] = sband_6ghz;
+ sband = rtw89_core_sband_dup(rtwdev, &rtw89_sband_6ghz);
+ if (!sband)
+ return -ENOMEM;
+ ret = rtw89_init_he_eht_cap(rtwdev, NL80211_BAND_6GHZ, sband);
+ if (ret)
+ return ret;
+ hw->wiphy->bands[NL80211_BAND_6GHZ] = sband;
}
return 0;
-
-err:
- hw->wiphy->bands[NL80211_BAND_2GHZ] = NULL;
- hw->wiphy->bands[NL80211_BAND_5GHZ] = NULL;
- hw->wiphy->bands[NL80211_BAND_6GHZ] = NULL;
- if (sband_2ghz)
- kfree((__force void *)sband_2ghz->iftype_data);
- if (sband_5ghz)
- kfree((__force void *)sband_5ghz->iftype_data);
- if (sband_6ghz)
- kfree((__force void *)sband_6ghz->iftype_data);
- kfree(sband_2ghz);
- kfree(sband_5ghz);
- kfree(sband_6ghz);
- return -ENOMEM;
-}
-
-static void rtw89_core_clr_supported_band(struct rtw89_dev *rtwdev)
-{
- struct ieee80211_hw *hw = rtwdev->hw;
-
- if (hw->wiphy->bands[NL80211_BAND_2GHZ])
- kfree((__force void *)hw->wiphy->bands[NL80211_BAND_2GHZ]->iftype_data);
- if (hw->wiphy->bands[NL80211_BAND_5GHZ])
- kfree((__force void *)hw->wiphy->bands[NL80211_BAND_5GHZ]->iftype_data);
- if (hw->wiphy->bands[NL80211_BAND_6GHZ])
- kfree((__force void *)hw->wiphy->bands[NL80211_BAND_6GHZ]->iftype_data);
- kfree(hw->wiphy->bands[NL80211_BAND_2GHZ]);
- kfree(hw->wiphy->bands[NL80211_BAND_5GHZ]);
- kfree(hw->wiphy->bands[NL80211_BAND_6GHZ]);
- hw->wiphy->bands[NL80211_BAND_2GHZ] = NULL;
- hw->wiphy->bands[NL80211_BAND_5GHZ] = NULL;
- hw->wiphy->bands[NL80211_BAND_6GHZ] = NULL;
}
static void rtw89_core_ppdu_sts_init(struct rtw89_dev *rtwdev)
{
int i;
- for (i = 0; i < RTW89_PHY_MAX; i++)
+ for (i = 0; i < RTW89_PHY_NUM; i++)
skb_queue_head_init(&rtwdev->ppdu_sts.rx_queue[i]);
- for (i = 0; i < RTW89_PHY_MAX; i++)
+ for (i = 0; i < RTW89_PHY_NUM; i++)
rtwdev->ppdu_sts.curr_rx_ppdu_cnt[i] = U8_MAX;
}
-void rtw89_core_update_beacon_work(struct work_struct *work)
+void rtw89_core_update_beacon_work(struct wiphy *wiphy, struct wiphy_work *work)
{
struct rtw89_dev *rtwdev;
struct rtw89_vif_link *rtwvif_link = container_of(work, struct rtw89_vif_link,
update_beacon_work);
+ lockdep_assert_wiphy(wiphy);
+
if (rtwvif_link->net_type != RTW89_NET_TYPE_AP_MODE)
return;
rtwdev = rtwvif_link->rtwvif->rtwdev;
- mutex_lock(&rtwdev->mutex);
rtw89_chip_h2c_update_beacon(rtwdev, rtwvif_link);
- mutex_unlock(&rtwdev->mutex);
}
int rtw89_wait_for_cond(struct rtw89_wait_info *wait, unsigned int cond)
@@ -4560,16 +4761,14 @@ int rtw89_core_start(struct rtw89_dev *rtwdev)
rtw89_mac_cfg_phy_rpt_bands(rtwdev, true);
rtw89_mac_update_rts_threshold(rtwdev);
- rtw89_tas_reset(rtwdev);
-
ret = rtw89_hci_start(rtwdev);
if (ret) {
rtw89_err(rtwdev, "failed to start hci\n");
return ret;
}
- ieee80211_queue_delayed_work(rtwdev->hw, &rtwdev->track_work,
- RTW89_TRACK_WORK_PERIOD);
+ wiphy_delayed_work_queue(rtwdev->hw->wiphy, &rtwdev->track_work,
+ RTW89_TRACK_WORK_PERIOD);
set_bit(RTW89_FLAG_RUNNING, rtwdev->flags);
@@ -4583,8 +4782,11 @@ int rtw89_core_start(struct rtw89_dev *rtwdev)
void rtw89_core_stop(struct rtw89_dev *rtwdev)
{
+ struct wiphy *wiphy = rtwdev->hw->wiphy;
struct rtw89_btc *btc = &rtwdev->btc;
+ lockdep_assert_wiphy(wiphy);
+
/* Prvent to stop twice; enter_ips and ops_stop */
if (!test_bit(RTW89_FLAG_RUNNING, rtwdev->flags))
return;
@@ -4593,25 +4795,21 @@ void rtw89_core_stop(struct rtw89_dev *rtwdev)
clear_bit(RTW89_FLAG_RUNNING, rtwdev->flags);
- mutex_unlock(&rtwdev->mutex);
-
- cancel_work_sync(&rtwdev->c2h_work);
- cancel_work_sync(&rtwdev->cancel_6ghz_probe_work);
- cancel_work_sync(&btc->eapol_notify_work);
- cancel_work_sync(&btc->arp_notify_work);
- cancel_work_sync(&btc->dhcp_notify_work);
- cancel_work_sync(&btc->icmp_notify_work);
+ wiphy_work_cancel(wiphy, &rtwdev->c2h_work);
+ wiphy_work_cancel(wiphy, &rtwdev->cancel_6ghz_probe_work);
+ wiphy_work_cancel(wiphy, &btc->eapol_notify_work);
+ wiphy_work_cancel(wiphy, &btc->arp_notify_work);
+ wiphy_work_cancel(wiphy, &btc->dhcp_notify_work);
+ wiphy_work_cancel(wiphy, &btc->icmp_notify_work);
cancel_delayed_work_sync(&rtwdev->txq_reinvoke_work);
- cancel_delayed_work_sync(&rtwdev->track_work);
- cancel_delayed_work_sync(&rtwdev->chanctx_work);
- cancel_delayed_work_sync(&rtwdev->coex_act1_work);
- cancel_delayed_work_sync(&rtwdev->coex_bt_devinfo_work);
- cancel_delayed_work_sync(&rtwdev->coex_rfk_chk_work);
- cancel_delayed_work_sync(&rtwdev->cfo_track_work);
+ wiphy_delayed_work_cancel(wiphy, &rtwdev->track_work);
+ wiphy_delayed_work_cancel(wiphy, &rtwdev->chanctx_work);
+ wiphy_delayed_work_cancel(wiphy, &rtwdev->coex_act1_work);
+ wiphy_delayed_work_cancel(wiphy, &rtwdev->coex_bt_devinfo_work);
+ wiphy_delayed_work_cancel(wiphy, &rtwdev->coex_rfk_chk_work);
+ wiphy_delayed_work_cancel(wiphy, &rtwdev->cfo_track_work);
cancel_delayed_work_sync(&rtwdev->forbid_ba_work);
- cancel_delayed_work_sync(&rtwdev->antdiv_work);
-
- mutex_lock(&rtwdev->mutex);
+ wiphy_delayed_work_cancel(wiphy, &rtwdev->antdiv_work);
rtw89_btc_ntfy_poweroff(rtwdev);
rtw89_hci_flush_queues(rtwdev, BIT(rtwdev->hw->queues) - 1, true);
@@ -4736,6 +4934,7 @@ struct rtw89_vif_link *rtw89_vif_set_link(struct rtw89_vif *rtwvif,
set_bit(index, rtwvif->links_inst_map);
rtwvif->links[link_id] = rtwvif_link;
+ list_add_tail(&rtwvif_link->dlink_schd, &rtwvif->dlink_pool);
return rtwvif_link;
err:
@@ -4756,6 +4955,7 @@ void rtw89_vif_unset_link(struct rtw89_vif *rtwvif, unsigned int link_id)
index = rtw89_vif_link_inst_get_index(link);
clear_bit(index, rtwvif->links_inst_map);
*container = NULL;
+ list_del(&link->dlink_schd);
}
struct rtw89_sta_link *rtw89_sta_set_link(struct rtw89_sta *rtwsta,
@@ -4786,6 +4986,7 @@ struct rtw89_sta_link *rtw89_sta_set_link(struct rtw89_sta *rtwsta,
set_bit(index, rtwsta->links_inst_map);
rtwsta->links[link_id] = rtwsta_link;
+ list_add_tail(&rtwsta_link->dlink_schd, &rtwsta->dlink_pool);
return rtwsta_link;
err:
@@ -4806,6 +5007,7 @@ void rtw89_sta_unset_link(struct rtw89_sta *rtwsta, unsigned int link_id)
index = rtw89_sta_link_inst_get_index(link);
clear_bit(index, rtwsta->links_inst_map);
*container = NULL;
+ list_del(&link->dlink_schd);
}
int rtw89_core_init(struct rtw89_dev *rtwdev)
@@ -4822,35 +5024,36 @@ int rtw89_core_init(struct rtw89_dev *rtwdev)
continue;
INIT_LIST_HEAD(&rtwdev->scan_info.pkt_list[band]);
}
+ INIT_LIST_HEAD(&rtwdev->scan_info.chan_list);
INIT_WORK(&rtwdev->ba_work, rtw89_core_ba_work);
INIT_WORK(&rtwdev->txq_work, rtw89_core_txq_work);
INIT_DELAYED_WORK(&rtwdev->txq_reinvoke_work, rtw89_core_txq_reinvoke_work);
- INIT_DELAYED_WORK(&rtwdev->track_work, rtw89_track_work);
- INIT_DELAYED_WORK(&rtwdev->chanctx_work, rtw89_chanctx_work);
- INIT_DELAYED_WORK(&rtwdev->coex_act1_work, rtw89_coex_act1_work);
- INIT_DELAYED_WORK(&rtwdev->coex_bt_devinfo_work, rtw89_coex_bt_devinfo_work);
- INIT_DELAYED_WORK(&rtwdev->coex_rfk_chk_work, rtw89_coex_rfk_chk_work);
- INIT_DELAYED_WORK(&rtwdev->cfo_track_work, rtw89_phy_cfo_track_work);
+ wiphy_delayed_work_init(&rtwdev->track_work, rtw89_track_work);
+ wiphy_delayed_work_init(&rtwdev->chanctx_work, rtw89_chanctx_work);
+ wiphy_delayed_work_init(&rtwdev->coex_act1_work, rtw89_coex_act1_work);
+ wiphy_delayed_work_init(&rtwdev->coex_bt_devinfo_work, rtw89_coex_bt_devinfo_work);
+ wiphy_delayed_work_init(&rtwdev->coex_rfk_chk_work, rtw89_coex_rfk_chk_work);
+ wiphy_delayed_work_init(&rtwdev->cfo_track_work, rtw89_phy_cfo_track_work);
INIT_DELAYED_WORK(&rtwdev->forbid_ba_work, rtw89_forbid_ba_work);
- INIT_DELAYED_WORK(&rtwdev->antdiv_work, rtw89_phy_antdiv_work);
+ wiphy_delayed_work_init(&rtwdev->antdiv_work, rtw89_phy_antdiv_work);
rtwdev->txq_wq = alloc_workqueue("rtw89_tx_wq", WQ_UNBOUND | WQ_HIGHPRI, 0);
if (!rtwdev->txq_wq)
return -ENOMEM;
spin_lock_init(&rtwdev->ba_lock);
spin_lock_init(&rtwdev->rpwm_lock);
- mutex_init(&rtwdev->mutex);
mutex_init(&rtwdev->rf_mutex);
rtwdev->total_sta_assoc = 0;
rtw89_init_wait(&rtwdev->mcc.wait);
+ rtw89_init_wait(&rtwdev->mlo.wait);
rtw89_init_wait(&rtwdev->mac.fw_ofld_wait);
rtw89_init_wait(&rtwdev->wow.wait);
rtw89_init_wait(&rtwdev->mac.ps_wait);
- INIT_WORK(&rtwdev->c2h_work, rtw89_fw_c2h_work);
- INIT_WORK(&rtwdev->ips_work, rtw89_ips_work);
+ wiphy_work_init(&rtwdev->c2h_work, rtw89_fw_c2h_work);
+ wiphy_work_init(&rtwdev->ips_work, rtw89_ips_work);
+ wiphy_work_init(&rtwdev->cancel_6ghz_probe_work, rtw89_cancel_6ghz_probe_work);
INIT_WORK(&rtwdev->load_firmware_work, rtw89_load_firmware_work);
- INIT_WORK(&rtwdev->cancel_6ghz_probe_work, rtw89_cancel_6ghz_probe_work);
skb_queue_head_init(&rtwdev->c2h_queue);
rtw89_core_ppdu_sts_init(rtwdev);
@@ -4864,13 +5067,16 @@ int rtw89_core_init(struct rtw89_dev *rtwdev)
if (rtwdev->chip->chip_gen == RTW89_CHIP_BE) {
rtwdev->dbcc_en = true;
rtwdev->mac.qta_mode = RTW89_QTA_DBCC;
- rtwdev->mlo_dbcc_mode = MLO_2_PLUS_0_1RF;
+ rtwdev->mlo_dbcc_mode = MLO_1_PLUS_1_1RF;
}
- INIT_WORK(&btc->eapol_notify_work, rtw89_btc_ntfy_eapol_packet_work);
- INIT_WORK(&btc->arp_notify_work, rtw89_btc_ntfy_arp_packet_work);
- INIT_WORK(&btc->dhcp_notify_work, rtw89_btc_ntfy_dhcp_packet_work);
- INIT_WORK(&btc->icmp_notify_work, rtw89_btc_ntfy_icmp_packet_work);
+ rtwdev->bbs[RTW89_PHY_0].phy_idx = RTW89_PHY_0;
+ rtwdev->bbs[RTW89_PHY_1].phy_idx = RTW89_PHY_1;
+
+ wiphy_work_init(&btc->eapol_notify_work, rtw89_btc_ntfy_eapol_packet_work);
+ wiphy_work_init(&btc->arp_notify_work, rtw89_btc_ntfy_arp_packet_work);
+ wiphy_work_init(&btc->dhcp_notify_work, rtw89_btc_ntfy_dhcp_packet_work);
+ wiphy_work_init(&btc->icmp_notify_work, rtw89_btc_ntfy_icmp_packet_work);
init_completion(&rtwdev->fw.req.completion);
init_completion(&rtwdev->rfk_wait.completion);
@@ -4879,7 +5085,7 @@ int rtw89_core_init(struct rtw89_dev *rtwdev)
rtw89_ser_init(rtwdev);
rtw89_entity_init(rtwdev);
- rtw89_tas_init(rtwdev);
+ rtw89_sar_init(rtwdev);
rtw89_phy_ant_gain_init(rtwdev);
return 0;
@@ -4890,11 +5096,10 @@ void rtw89_core_deinit(struct rtw89_dev *rtwdev)
{
rtw89_ser_deinit(rtwdev);
rtw89_unload_firmware(rtwdev);
- rtw89_fw_free_all_early_h2c(rtwdev);
+ __rtw89_fw_free_all_early_h2c(rtwdev);
destroy_workqueue(rtwdev->txq_wq);
mutex_destroy(&rtwdev->rf_mutex);
- mutex_destroy(&rtwdev->mutex);
}
EXPORT_SYMBOL(rtw89_core_deinit);
@@ -4903,17 +5108,16 @@ void rtw89_core_scan_start(struct rtw89_dev *rtwdev, struct rtw89_vif_link *rtwv
{
const struct rtw89_chan *chan = rtw89_chan_get(rtwdev,
rtwvif_link->chanctx_idx);
+ struct rtw89_bb_ctx *bb = rtw89_get_bb_ctx(rtwdev, rtwvif_link->phy_idx);
rtwdev->scanning = true;
- rtw89_leave_lps(rtwdev);
- if (hw_scan)
- rtw89_leave_ips_by_hwflags(rtwdev);
ether_addr_copy(rtwvif_link->mac_addr, mac_addr);
rtw89_btc_ntfy_scan_start(rtwdev, rtwvif_link->phy_idx, chan->band_type);
rtw89_chip_rfk_scan(rtwdev, rtwvif_link, true);
rtw89_hci_recalc_int_mit(rtwdev);
- rtw89_phy_config_edcca(rtwdev, true);
+ rtw89_phy_config_edcca(rtwdev, bb, true);
+ rtw89_tas_scan(rtwdev, true);
rtw89_fw_h2c_cam(rtwdev, rtwvif_link, NULL, mac_addr);
}
@@ -4922,6 +5126,7 @@ void rtw89_core_scan_complete(struct rtw89_dev *rtwdev,
struct rtw89_vif_link *rtwvif_link, bool hw_scan)
{
struct ieee80211_bss_conf *bss_conf;
+ struct rtw89_bb_ctx *bb;
if (!rtwvif_link)
return;
@@ -4937,12 +5142,15 @@ void rtw89_core_scan_complete(struct rtw89_dev *rtwdev,
rtw89_chip_rfk_scan(rtwdev, rtwvif_link, false);
rtw89_btc_ntfy_scan_finish(rtwdev, rtwvif_link->phy_idx);
- rtw89_phy_config_edcca(rtwdev, false);
+ bb = rtw89_get_bb_ctx(rtwdev, rtwvif_link->phy_idx);
+ rtw89_phy_config_edcca(rtwdev, bb, false);
+ rtw89_tas_scan(rtwdev, false);
rtwdev->scanning = false;
- rtwdev->dig.bypass_dig = true;
+ rtw89_for_each_active_bb(rtwdev, bb)
+ bb->dig.bypass_dig = true;
if (hw_scan && (rtwdev->hw->conf.flags & IEEE80211_CONF_IDLE))
- ieee80211_queue_work(rtwdev->hw, &rtwdev->ips_work);
+ wiphy_work_queue(rtwdev->hw->wiphy, &rtwdev->ips_work);
}
static void rtw89_read_chip_ver(struct rtw89_dev *rtwdev)
@@ -5017,6 +5225,76 @@ out:
rtw89_load_txpwr_table(rtwdev, rtwdev->rfe_parms->byr_tbl);
}
+int rtw89_core_mlsr_switch(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif,
+ unsigned int link_id)
+{
+ struct ieee80211_vif *vif = rtwvif_to_vif(rtwvif);
+ u16 usable_links = ieee80211_vif_usable_links(vif);
+ u16 active_links = vif->active_links;
+ struct rtw89_vif_link *target, *cur;
+ int ret;
+
+ lockdep_assert_wiphy(rtwdev->hw->wiphy);
+
+ if (unlikely(!ieee80211_vif_is_mld(vif)))
+ return -EOPNOTSUPP;
+
+ if (unlikely(!(usable_links & BIT(link_id)))) {
+ rtw89_warn(rtwdev, "%s: link id %u is not usable\n", __func__,
+ link_id);
+ return -ENOLINK;
+ }
+
+ if (active_links == BIT(link_id))
+ return 0;
+
+ rtw89_debug(rtwdev, RTW89_DBG_STATE, "%s: switch to link id %u MLSR\n",
+ __func__, link_id);
+
+ rtw89_leave_lps(rtwdev);
+
+ ieee80211_stop_queues(rtwdev->hw);
+ flush_work(&rtwdev->txq_work);
+
+ cur = rtw89_get_designated_link(rtwvif);
+
+ ret = ieee80211_set_active_links(vif, active_links | BIT(link_id));
+ if (ret) {
+ rtw89_err(rtwdev, "%s: failed to activate link id %u\n",
+ __func__, link_id);
+ goto wake_queue;
+ }
+
+ target = rtwvif->links[link_id];
+ if (unlikely(!target)) {
+ rtw89_err(rtwdev, "%s: failed to confirm link id %u\n",
+ __func__, link_id);
+
+ ieee80211_set_active_links(vif, active_links);
+ ret = -EFAULT;
+ goto wake_queue;
+ }
+
+ if (likely(cur))
+ rtw89_fw_h2c_mlo_link_cfg(rtwdev, cur, false);
+
+ rtw89_fw_h2c_mlo_link_cfg(rtwdev, target, true);
+
+ ret = ieee80211_set_active_links(vif, BIT(link_id));
+ if (ret)
+ rtw89_err(rtwdev, "%s: failed to inactivate links 0x%x\n",
+ __func__, active_links);
+
+ rtw89_chip_rfk_channel(rtwdev, target);
+
+ rtwvif->mlo_mode = RTW89_MLO_MODE_MLSR;
+
+wake_queue:
+ ieee80211_wake_queues(rtwdev->hw);
+
+ return ret;
+}
+
static int rtw89_chip_efuse_info_setup(struct rtw89_dev *rtwdev)
{
const struct rtw89_mac_gen_def *mac = rtwdev->chip->mac_def;
@@ -5042,8 +5320,6 @@ static int rtw89_chip_efuse_info_setup(struct rtw89_dev *rtwdev)
rtw89_hci_mac_pre_deinit(rtwdev);
- rtw89_mac_pwr_off(rtwdev);
-
return 0;
}
@@ -5124,36 +5400,45 @@ int rtw89_chip_info_setup(struct rtw89_dev *rtwdev)
rtw89_read_chip_ver(rtwdev);
+ ret = rtw89_mac_pwr_on(rtwdev);
+ if (ret) {
+ rtw89_err(rtwdev, "failed to power on\n");
+ return ret;
+ }
+
ret = rtw89_wait_firmware_completion(rtwdev);
if (ret) {
rtw89_err(rtwdev, "failed to wait firmware completion\n");
- return ret;
+ goto out;
}
ret = rtw89_fw_recognize(rtwdev);
if (ret) {
rtw89_err(rtwdev, "failed to recognize firmware\n");
- return ret;
+ goto out;
}
ret = rtw89_chip_efuse_info_setup(rtwdev);
if (ret)
- return ret;
+ goto out;
ret = rtw89_fw_recognize_elements(rtwdev);
if (ret) {
rtw89_err(rtwdev, "failed to recognize firmware elements\n");
- return ret;
+ goto out;
}
ret = rtw89_chip_board_info_setup(rtwdev);
if (ret)
- return ret;
+ goto out;
rtw89_core_setup_rfe_parms(rtwdev);
rtwdev->ps_mode = rtw89_update_ps_mode(rtwdev);
- return 0;
+out:
+ rtw89_mac_pwr_off(rtwdev);
+
+ return ret;
}
EXPORT_SYMBOL(rtw89_chip_info_setup);
@@ -5253,8 +5538,11 @@ static int rtw89_core_register_hw(struct rtw89_dev *rtwdev)
if (chip->chip_gen == RTW89_CHIP_BE)
hw->wiphy->flags |= WIPHY_FLAG_DISABLE_WEXT;
- if (rtwdev->support_mlo)
+ if (rtwdev->support_mlo) {
hw->wiphy->flags |= WIPHY_FLAG_SUPPORTS_MLO;
+ hw->wiphy->iftype_ext_capab = rtw89_iftypes_ext_capa;
+ hw->wiphy->num_iftype_ext_capab = ARRAY_SIZE(rtw89_iftypes_ext_capa);
+ }
hw->wiphy->features |= NL80211_FEATURE_SCAN_RANDOM_MAC_ADDR;
@@ -5285,7 +5573,7 @@ static int rtw89_core_register_hw(struct rtw89_dev *rtwdev)
ret = rtw89_regd_setup(rtwdev);
if (ret) {
rtw89_err(rtwdev, "failed to set up regd\n");
- goto err_free_supported_band;
+ return ret;
}
hw->wiphy->sar_capa = &rtw89_sar_capa;
@@ -5293,10 +5581,10 @@ static int rtw89_core_register_hw(struct rtw89_dev *rtwdev)
ret = ieee80211_register_hw(hw);
if (ret) {
rtw89_err(rtwdev, "failed to register hw\n");
- goto err_free_supported_band;
+ return ret;
}
- ret = rtw89_regd_init(rtwdev, rtw89_regd_notifier);
+ ret = rtw89_regd_init_hint(rtwdev);
if (ret) {
rtw89_err(rtwdev, "failed to init regd\n");
goto err_unregister_hw;
@@ -5308,8 +5596,6 @@ static int rtw89_core_register_hw(struct rtw89_dev *rtwdev)
err_unregister_hw:
ieee80211_unregister_hw(hw);
-err_free_supported_band:
- rtw89_core_clr_supported_band(rtwdev);
return ret;
}
@@ -5320,7 +5606,6 @@ static void rtw89_core_unregister_hw(struct rtw89_dev *rtwdev)
rtw89_rfkill_polling_deinit(rtwdev);
ieee80211_unregister_hw(hw);
- rtw89_core_clr_supported_band(rtwdev);
}
int rtw89_core_register(struct rtw89_dev *rtwdev)
@@ -5388,13 +5673,13 @@ struct rtw89_dev *rtw89_alloc_ieee80211_hw(struct device *device,
if (!hw)
goto err;
- /* TODO: When driver MLO arch. is done, determine whether to support MLO
- * according to the following conditions.
- * 1. run with chanctx_ops
- * 2. chip->support_link_num != 0
- * 3. FW feature supports AP_LINK_PS
+ /* Currently, our AP_LINK_PS handling only works for non-MLD softap
+ * or MLD-single-link softap. If RTW89_MLD_NON_STA_LINK_NUM enlarges,
+ * please tweak entire AP_LINKS_PS handling before supporting MLO.
*/
- support_mlo = false;
+ support_mlo = !no_chanctx && chip->support_link_num &&
+ RTW89_CHK_FW_FEATURE(NOTIFY_AP_INFO, &early_fw) &&
+ RTW89_MLD_NON_STA_LINK_NUM == 1;
hw->wiphy->iface_combinations = rtw89_iface_combs;
diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h
index ff4894c7fa8a..1c8f3b9b7c4c 100644
--- a/drivers/net/wireless/realtek/rtw89/core.h
+++ b/drivers/net/wireless/realtek/rtw89/core.h
@@ -17,11 +17,13 @@ struct rtw89_dev;
struct rtw89_pci_info;
struct rtw89_mac_gen_def;
struct rtw89_phy_gen_def;
+struct rtw89_fw_blacklist;
struct rtw89_efuse_block_cfg;
struct rtw89_h2c_rf_tssi;
struct rtw89_fw_txpwr_track_cfg;
struct rtw89_phy_rfk_log_fmt;
struct rtw89_debugfs;
+struct rtw89_regd_data;
extern const struct ieee80211_ops rtw89_ops;
@@ -718,6 +720,7 @@ enum rtw89_ofdma_type {
RTW89_OFDMA_NUM,
};
+/* neither insert new in the middle, nor change any given definition */
enum rtw89_regulation_type {
RTW89_WW = 0,
RTW89_ETSI = 1,
@@ -795,6 +798,7 @@ struct rtw89_rx_phy_ppdu {
u8 rssi[RF_PATH_MAX];
u8 mac_id;
u8 chan_idx;
+ u8 phy_idx;
u8 ie;
u16 rate;
u8 rpl_avg;
@@ -826,7 +830,7 @@ enum rtw89_mac_idx {
enum rtw89_phy_idx {
RTW89_PHY_0 = 0,
RTW89_PHY_1 = 1,
- RTW89_PHY_MAX
+ RTW89_PHY_NUM,
};
#define __RTW89_MLD_MAX_LINK_NUM 2
@@ -1171,6 +1175,7 @@ struct rtw89_tx_desc_info {
bool ldpc;
bool upd_wlan_hdr;
bool mlo;
+ bool sw_mld;
};
struct rtw89_core_tx_request {
@@ -1540,16 +1545,16 @@ struct rtw89_btc_u8_sta_chg {
};
struct rtw89_btc_wl_scan_info {
- u8 band[RTW89_PHY_MAX];
+ u8 band[RTW89_PHY_NUM];
u8 phy_map;
u8 rsvd;
};
struct rtw89_btc_wl_dbcc_info {
- u8 op_band[RTW89_PHY_MAX]; /* op band in each phy */
- u8 scan_band[RTW89_PHY_MAX]; /* scan band in each phy */
- u8 real_band[RTW89_PHY_MAX];
- u8 role[RTW89_PHY_MAX]; /* role in each phy */
+ u8 op_band[RTW89_PHY_NUM]; /* op band in each phy */
+ u8 scan_band[RTW89_PHY_NUM]; /* scan band in each phy */
+ u8 real_band[RTW89_PHY_NUM];
+ u8 role[RTW89_PHY_NUM]; /* role in each phy */
};
struct rtw89_btc_wl_active_role {
@@ -1761,7 +1766,8 @@ struct rtw89_btc_wl_rfk_info {
u32 phy_map: 2;
u32 band: 2;
u32 type: 8;
- u32 rsvd: 14;
+ u32 con_rfk: 1;
+ u32 rsvd: 13;
u32 start_time;
u32 proc_time;
@@ -1898,7 +1904,7 @@ struct rtw89_btc_wl_info {
u8 cn_report;
u8 coex_mode;
u8 pta_req_mac;
- u8 bt_polut_type[RTW89_PHY_MAX]; /* BT polluted WL-Tx type for phy0/1 */
+ u8 bt_polut_type[RTW89_PHY_NUM]; /* BT polluted WL-Tx type for phy0/1 */
bool is_5g_hi_channel;
bool pta_reg_mac_chg;
@@ -2230,7 +2236,7 @@ struct rtw89_btc_fbtc_rpt_ctrl_v4 {
struct rtw89_btc_fbtc_rpt_ctrl_wl_fw_info wl_fw_info;
struct rtw89_btc_fbtc_rpt_ctrl_bt_mailbox bt_mbx_info;
__le32 bt_cnt[BTC_BCNT_STA_MAX];
- struct rtw89_mac_ax_gnt gnt_val[RTW89_PHY_MAX];
+ struct rtw89_mac_ax_gnt gnt_val[RTW89_PHY_NUM];
} __packed;
struct rtw89_btc_fbtc_rpt_ctrl_v5 {
@@ -2238,7 +2244,7 @@ struct rtw89_btc_fbtc_rpt_ctrl_v5 {
u8 rsvd;
__le16 rsvd1;
- u8 gnt_val[RTW89_PHY_MAX][4];
+ u8 gnt_val[RTW89_PHY_NUM][4];
__le16 bt_cnt[BTC_BCNT_STA_MAX];
struct rtw89_btc_fbtc_rpt_ctrl_info_v5 rpt_info;
@@ -2250,7 +2256,7 @@ struct rtw89_btc_fbtc_rpt_ctrl_v105 {
u8 rsvd;
__le16 rsvd1;
- u8 gnt_val[RTW89_PHY_MAX][4];
+ u8 gnt_val[RTW89_PHY_NUM][4];
__le16 bt_cnt[BTC_BCNT_STA_MAX_V105];
struct rtw89_btc_fbtc_rpt_ctrl_info_v5 rpt_info;
@@ -2263,7 +2269,7 @@ struct rtw89_btc_fbtc_rpt_ctrl_v7 {
u8 rsvd1;
u8 rsvd2;
- u8 gnt_val[RTW89_PHY_MAX][4];
+ u8 gnt_val[RTW89_PHY_NUM][4];
__le16 bt_cnt[BTC_BCNT_STA_MAX_V105];
struct rtw89_btc_fbtc_rpt_ctrl_info_v8 rpt_info;
@@ -2276,7 +2282,7 @@ struct rtw89_btc_fbtc_rpt_ctrl_v8 {
u8 rpt_len_max_l; /* BTC_RPT_MAX bit0~7 */
u8 rpt_len_max_h; /* BTC_RPT_MAX bit8~15 */
- u8 gnt_val[RTW89_PHY_MAX][4];
+ u8 gnt_val[RTW89_PHY_NUM][4];
__le16 bt_cnt[BTC_BCNT_STA_MAX_V105];
struct rtw89_btc_fbtc_rpt_ctrl_info_v8 rpt_info;
@@ -3040,6 +3046,7 @@ struct rtw89_btc_rpt_cmn_info {
union rtw89_btc_fbtc_btafh_info {
struct rtw89_btc_fbtc_btafh v1;
struct rtw89_btc_fbtc_btafh_v2 v2;
+ struct rtw89_btc_fbtc_btafh_v7 v7;
};
struct rtw89_btc_report_ctrl_state {
@@ -3174,10 +3181,10 @@ struct rtw89_btc {
struct rtw89_btc_btf_fwinfo fwinfo;
struct rtw89_btc_dbg dbg;
- struct work_struct eapol_notify_work;
- struct work_struct arp_notify_work;
- struct work_struct dhcp_notify_work;
- struct work_struct icmp_notify_work;
+ struct wiphy_work eapol_notify_work;
+ struct wiphy_work arp_notify_work;
+ struct wiphy_work dhcp_notify_work;
+ struct wiphy_work icmp_notify_work;
u32 bt_req_len;
@@ -3374,6 +3381,7 @@ struct rtw89_sec_cam_entry {
struct rtw89_sta_link {
struct rtw89_sta *rtwsta;
+ struct list_head dlink_schd;
unsigned int link_id;
u8 mac_id;
@@ -3440,14 +3448,13 @@ enum rtw89_roc_state {
RTW89_ROC_MGMT,
};
-#define RTW89_ROC_BY_LINK_INDEX 0
-
struct rtw89_roc {
struct ieee80211_channel chan;
- struct delayed_work roc_work;
+ struct wiphy_delayed_work roc_work;
enum ieee80211_roc_type type;
enum rtw89_roc_state state;
int duration;
+ unsigned int link_id;
};
#define RTW89_P2P_MAX_NOA_NUM 2
@@ -3478,8 +3485,17 @@ struct rtw89_p2p_noa_setter {
u8 noa_index;
};
+struct rtw89_ps_noa_once_handler {
+ bool in_duration;
+ u64 tsf_begin;
+ u64 tsf_end;
+ struct wiphy_delayed_work set_work;
+ struct wiphy_delayed_work clr_work;
+};
+
struct rtw89_vif_link {
struct rtw89_vif *rtwvif;
+ struct list_head dlink_schd;
unsigned int link_id;
bool chanctx_assigned; /* only valid when running with chanctx_ops */
@@ -3498,9 +3514,11 @@ struct rtw89_vif_link {
u8 self_role;
u8 wmm;
u8 bcn_hit_cond;
+ u8 bcn_bw_idx;
u8 hit_rule;
u8 last_noa_nr;
u64 sync_bcn_tsf;
+ bool rand_tsf_done;
bool trigger;
bool lsig_txop;
u8 tgt_ind;
@@ -3514,13 +3532,14 @@ struct rtw89_vif_link {
bool pre_pwr_diff_en;
bool pwr_diff_en;
u8 def_tri_idx;
- struct work_struct update_beacon_work;
+ struct wiphy_work update_beacon_work;
struct rtw89_addr_cam_entry addr_cam;
struct rtw89_bssid_cam_entry bssid_cam;
struct ieee80211_tx_queue_params tx_params[IEEE80211_NUM_ACS];
struct rtw89_phy_rate_pattern rate_pattern;
struct list_head general_pkt_list;
struct rtw89_p2p_noa_setter p2p_noa;
+ struct rtw89_ps_noa_once_handler noa_once;
};
enum rtw89_lv1_rcvy_step {
@@ -3672,6 +3691,8 @@ struct rtw89_chip_ops {
int (*h2c_ampdu_cmac_tbl)(struct rtw89_dev *rtwdev,
struct rtw89_vif_link *rtwvif_link,
struct rtw89_sta_link *rtwsta_link);
+ int (*h2c_txtime_cmac_tbl)(struct rtw89_dev *rtwdev,
+ struct rtw89_sta_link *rtwsta_link);
int (*h2c_default_dmac_tbl)(struct rtw89_dev *rtwdev,
struct rtw89_vif_link *rtwvif_link,
struct rtw89_sta_link *rtwsta_link);
@@ -3978,7 +3999,11 @@ struct rtw89_rfe_parms {
struct rtw89_txpwr_rule_2ghz rule_2ghz;
struct rtw89_txpwr_rule_5ghz rule_5ghz;
struct rtw89_txpwr_rule_6ghz rule_6ghz;
+ struct rtw89_txpwr_rule_2ghz rule_da_2ghz;
+ struct rtw89_txpwr_rule_5ghz rule_da_5ghz;
+ struct rtw89_txpwr_rule_6ghz rule_da_6ghz;
struct rtw89_tx_shape tx_shape;
+ bool has_da;
};
struct rtw89_rfe_parms_conf {
@@ -4073,9 +4098,15 @@ struct rtw89_rfe_data {
struct rtw89_txpwr_lmt_2ghz_data lmt_2ghz;
struct rtw89_txpwr_lmt_5ghz_data lmt_5ghz;
struct rtw89_txpwr_lmt_6ghz_data lmt_6ghz;
+ struct rtw89_txpwr_lmt_2ghz_data da_lmt_2ghz;
+ struct rtw89_txpwr_lmt_5ghz_data da_lmt_5ghz;
+ struct rtw89_txpwr_lmt_6ghz_data da_lmt_6ghz;
struct rtw89_txpwr_lmt_ru_2ghz_data lmt_ru_2ghz;
struct rtw89_txpwr_lmt_ru_5ghz_data lmt_ru_5ghz;
struct rtw89_txpwr_lmt_ru_6ghz_data lmt_ru_6ghz;
+ struct rtw89_txpwr_lmt_ru_2ghz_data da_lmt_ru_2ghz;
+ struct rtw89_txpwr_lmt_ru_5ghz_data da_lmt_ru_5ghz;
+ struct rtw89_txpwr_lmt_ru_6ghz_data da_lmt_ru_6ghz;
struct rtw89_tx_shape_lmt_data tx_shape_lmt;
struct rtw89_tx_shape_lmt_ru_data tx_shape_lmt_ru;
struct rtw89_rfe_parms rfe_parms;
@@ -4189,10 +4220,12 @@ struct rtw89_edcca_regs {
u32 edcca_p_mask;
u32 ppdu_level;
u32 ppdu_mask;
- u32 rpt_a;
- u32 rpt_b;
- u32 rpt_sel;
- u32 rpt_sel_mask;
+ struct rtw89_edcca_p_regs {
+ u32 rpt_a;
+ u32 rpt_b;
+ u32 rpt_sel;
+ u32 rpt_sel_mask;
+ } p[RTW89_PHY_NUM];
u32 rpt_sel_be;
u32 rpt_sel_be_mask;
u32 tx_collision_t2r_st;
@@ -4231,6 +4264,7 @@ enum rtw89_chanctx_state {
enum rtw89_chanctx_callbacks {
RTW89_CHANCTX_CALLBACK_PLACEHOLDER,
RTW89_CHANCTX_CALLBACK_RFK,
+ RTW89_CHANCTX_CALLBACK_TAS,
NUM_OF_RTW89_CHANCTX_CALLBACKS,
};
@@ -4251,6 +4285,7 @@ struct rtw89_chip_info {
bool try_ce_fw;
u8 bbmcu_nr;
u32 needed_fw_elms;
+ const struct rtw89_fw_blacklist *fw_blacklist;
u32 fifo_size;
bool small_fifo_size;
u32 dle_scc_rsvd_size;
@@ -4271,10 +4306,15 @@ struct rtw89_chip_info {
bool support_unii4;
bool support_rnr;
bool support_ant_gain;
+ bool support_tas;
+ bool support_sar_by_ant;
bool ul_tb_waveform_ctrl;
bool ul_tb_pwr_diff;
+ bool rx_freq_frome_ie;
bool hw_sec_hdr;
bool hw_mgmt_tx_encrypt;
+ bool hw_tkip_crypto;
+ bool hw_mlo_bmc_crypto;
u8 rf_path_num;
u8 tx_nss;
u8 rx_nss;
@@ -4478,6 +4518,8 @@ enum rtw89_fw_feature {
RTW89_FW_FEATURE_CH_INFO_BE_V0,
RTW89_FW_FEATURE_LPS_CH_INFO,
RTW89_FW_FEATURE_NO_PHYCAP_P1,
+ RTW89_FW_FEATURE_NO_POWER_DIFFERENCE,
+ RTW89_FW_FEATURE_BEACON_LOSS_COUNT_V1,
};
struct rtw89_fw_suit {
@@ -4536,6 +4578,7 @@ struct rtw89_fw_elm_info {
struct rtw89_phy_table *rf_nctl;
struct rtw89_fw_txpwr_track_cfg *txpwr_trk;
struct rtw89_phy_rfk_log_fmt *rfk_log_fmt;
+ const struct rtw89_regd_data *regd;
};
enum rtw89_fw_mss_dev_type {
@@ -4589,6 +4632,7 @@ struct rtw89_cam_info {
enum rtw89_sar_sources {
RTW89_SAR_SOURCE_NONE,
RTW89_SAR_SOURCE_COMMON,
+ RTW89_SAR_SOURCE_ACPI,
RTW89_SAR_SOURCE_NR,
};
@@ -4613,8 +4657,62 @@ struct rtw89_sar_cfg_common {
s32 cfg[RTW89_SAR_SUBBAND_NR];
};
+enum rtw89_acpi_sar_subband {
+ RTW89_ACPI_SAR_2GHZ_SUBBAND,
+ RTW89_ACPI_SAR_5GHZ_SUBBAND_1, /* U-NII-1 */
+ RTW89_ACPI_SAR_5GHZ_SUBBAND_2, /* U-NII-2 */
+ RTW89_ACPI_SAR_5GHZ_SUBBAND_2E, /* U-NII-2-Extended */
+ RTW89_ACPI_SAR_5GHZ_SUBBAND_3_4, /* U-NII-3 and U-NII-4 */
+ RTW89_ACPI_SAR_6GHZ_SUBBAND_5_L, /* U-NII-5 lower part */
+ RTW89_ACPI_SAR_6GHZ_SUBBAND_5_H, /* U-NII-5 higher part */
+ RTW89_ACPI_SAR_6GHZ_SUBBAND_6, /* U-NII-6 */
+ RTW89_ACPI_SAR_6GHZ_SUBBAND_7_L, /* U-NII-7 lower part */
+ RTW89_ACPI_SAR_6GHZ_SUBBAND_7_H, /* U-NII-7 higher part */
+ RTW89_ACPI_SAR_6GHZ_SUBBAND_8, /* U-NII-8 */
+
+ NUM_OF_RTW89_ACPI_SAR_SUBBAND,
+ RTW89_ACPI_SAR_SUBBAND_NR_LEGACY = RTW89_ACPI_SAR_5GHZ_SUBBAND_3_4 + 1,
+ RTW89_ACPI_SAR_SUBBAND_NR_HAS_6GHZ = RTW89_ACPI_SAR_6GHZ_SUBBAND_8 + 1,
+};
+
+#define TXPWR_FACTOR_OF_RTW89_ACPI_SAR 3 /* unit: 0.125 dBm */
+#define MAX_VAL_OF_RTW89_ACPI_SAR S16_MAX
+#define MIN_VAL_OF_RTW89_ACPI_SAR S16_MIN
+#define MAX_NUM_OF_RTW89_ACPI_SAR_TBL 6
+#define NUM_OF_RTW89_ACPI_SAR_RF_PATH (RF_PATH_B + 1)
+
+struct rtw89_sar_entry_from_acpi {
+ s16 v[NUM_OF_RTW89_ACPI_SAR_SUBBAND][NUM_OF_RTW89_ACPI_SAR_RF_PATH];
+};
+
+struct rtw89_sar_table_from_acpi {
+ /* If this table is active, must fill all fields according to either
+ * configuration in BIOS or some default values for SAR to work well.
+ */
+ struct rtw89_sar_entry_from_acpi entries[RTW89_REGD_NUM];
+};
+
+struct rtw89_sar_indicator_from_acpi {
+ bool enable_sync;
+ unsigned int fields;
+ u8 (*rfpath_to_antidx)(enum rtw89_rf_path rfpath);
+
+ /* Select among @tables of container, rtw89_sar_cfg_acpi, by path.
+ * Not design with pointers since addresses will be invalid after
+ * sync content with local container instance.
+ */
+ u8 tblsel[NUM_OF_RTW89_ACPI_SAR_RF_PATH];
+};
+
+struct rtw89_sar_cfg_acpi {
+ u8 downgrade_2tx;
+ unsigned int valid_num;
+ struct rtw89_sar_table_from_acpi tables[MAX_NUM_OF_RTW89_ACPI_SAR_TBL];
+ struct rtw89_sar_indicator_from_acpi indicator;
+};
+
struct rtw89_sar_info {
- /* used to decide how to acces SAR cfg union */
+ /* used to decide how to access SAR cfg union */
enum rtw89_sar_sources src;
/* reserved for different knids of SAR cfg struct.
@@ -4622,6 +4720,7 @@ struct rtw89_sar_info {
*/
union {
struct rtw89_sar_cfg_common cfg_common;
+ struct rtw89_sar_cfg_acpi cfg_acpi;
};
};
@@ -4651,33 +4750,49 @@ enum rtw89_ant_gain_domain_type {
struct rtw89_ant_gain_info {
s8 offset[RTW89_ANT_GAIN_CHAIN_NUM][RTW89_ANT_GAIN_SUBBAND_NR];
u32 regd_enabled;
+ bool block_country;
};
struct rtw89_6ghz_span {
enum rtw89_sar_subband sar_subband_low;
enum rtw89_sar_subband sar_subband_high;
+ enum rtw89_acpi_sar_subband acpi_sar_subband_low;
+ enum rtw89_acpi_sar_subband acpi_sar_subband_high;
enum rtw89_ant_gain_subband ant_gain_subband_low;
enum rtw89_ant_gain_subband ant_gain_subband_high;
};
#define RTW89_SAR_SPAN_VALID(span) ((span)->sar_subband_high)
+#define RTW89_ACPI_SAR_SPAN_VALID(span) ((span)->acpi_sar_subband_high)
#define RTW89_ANT_GAIN_SPAN_VALID(span) ((span)->ant_gain_subband_high)
enum rtw89_tas_state {
RTW89_TAS_STATE_DPR_OFF,
RTW89_TAS_STATE_DPR_ON,
- RTW89_TAS_STATE_DPR_FORBID,
+ RTW89_TAS_STATE_STATIC_SAR,
};
-#define RTW89_TAS_MAX_WINDOW 50
+#define RTW89_TAS_TX_RATIO_WINDOW 6
+#define RTW89_TAS_TXPWR_WINDOW 180
struct rtw89_tas_info {
- s16 txpwr_history[RTW89_TAS_MAX_WINDOW];
- s32 total_txpwr;
- u8 cur_idx;
- s8 dpr_gap;
- s8 delta;
+ u16 tx_ratio_history[RTW89_TAS_TX_RATIO_WINDOW];
+ u64 txpwr_history[RTW89_TAS_TXPWR_WINDOW];
+ u8 enabled_countries;
+ u8 txpwr_head_idx;
+ u8 txpwr_tail_idx;
+ u8 tx_ratio_idx;
+ u16 total_tx_ratio;
+ u64 total_txpwr;
+ u64 instant_txpwr;
+ u32 window_size;
+ s8 dpr_on_threshold;
+ s8 dpr_off_threshold;
+ enum rtw89_tas_state backup_state;
enum rtw89_tas_state state;
+ bool keep_history;
+ bool block_regd;
bool enable;
+ bool pause;
};
struct rtw89_chanctx_cfg {
@@ -4735,6 +4850,8 @@ struct rtw89_edcca_bak {
enum rtw89_dm_type {
RTW89_DM_DYNAMIC_EDCCA,
RTW89_DM_THERMAL_PROTECT,
+ RTW89_DM_TAS,
+ RTW89_DM_MLO,
};
#define RTW89_THERMAL_PROT_LV_MAX 5
@@ -4756,18 +4873,18 @@ struct rtw89_hal {
bool no_mcs_12_13;
atomic_t roc_chanctx_idx;
+ u8 roc_link_index;
DECLARE_BITMAP(changes, NUM_OF_RTW89_CHANCTX_CHANGES);
DECLARE_BITMAP(entity_map, NUM_OF_RTW89_CHANCTX);
struct rtw89_chanctx chanctx[NUM_OF_RTW89_CHANCTX];
struct cfg80211_chan_def roc_chandef;
- bool entity_active[RTW89_PHY_MAX];
+ bool entity_active[RTW89_PHY_NUM];
bool entity_pause;
enum rtw89_entity_mode entity_mode;
struct rtw89_entity_mgnt entity_mgnt;
- struct rtw89_edcca_bak edcca_bak;
u32 disabled_dm_bitmap; /* bitmap of enum rtw89_dm_type */
u8 thermal_prot_th;
@@ -4973,7 +5090,7 @@ struct rtw89_dpk_bkup_para {
struct rtw89_dpk_info {
bool is_dpk_enable;
bool is_dpk_reload_en;
- u8 dpk_gs[RTW89_PHY_MAX];
+ u8 dpk_gs[RTW89_PHY_NUM];
u16 dc_i[RTW89_DPK_RF_PATH][RTW89_DPK_BKUP_NUM];
u16 dc_q[RTW89_DPK_RF_PATH][RTW89_DPK_BKUP_NUM];
u8 corr_val[RTW89_DPK_RF_PATH][RTW89_DPK_BKUP_NUM];
@@ -5135,7 +5252,7 @@ struct rtw89_tssi_info {
u32 alignment_backup_by_ch[RF_PATH_MAX][TSSI_MAX_CH_NUM][TSSI_ALIMK_VALUE_NUM];
u32 alignment_value[RF_PATH_MAX][TSSI_ALIMK_MAX][TSSI_ALIMK_VALUE_NUM];
bool alignment_done[RF_PATH_MAX][TSSI_ALIMK_MAX];
- u32 tssi_alimk_time;
+ u64 tssi_alimk_time;
};
struct rtw89_power_trim_info {
@@ -5146,9 +5263,27 @@ struct rtw89_power_trim_info {
u8 pad_bias_trim[RF_PATH_MAX];
};
+enum rtw89_regd_func {
+ RTW89_REGD_FUNC_TAS = 0, /* TAS (Time Average SAR) */
+ RTW89_REGD_FUNC_DAG = 1, /* DAG (Dynamic Antenna Gain) */
+
+ NUM_OF_RTW89_REGD_FUNC,
+};
+
struct rtw89_regd {
char alpha2[3];
u8 txpwr_regd[RTW89_BAND_NUM];
+ DECLARE_BITMAP(func_bitmap, NUM_OF_RTW89_REGD_FUNC);
+};
+
+struct rtw89_regd_data {
+ unsigned int nr;
+ struct rtw89_regd map[] __counted_by(nr);
+};
+
+struct rtw89_regd_ctrl {
+ unsigned int nr;
+ const struct rtw89_regd *map;
};
#define RTW89_REGD_MAX_COUNTRY_NUM U8_MAX
@@ -5156,9 +5291,12 @@ struct rtw89_regd {
#define RTW89_5GHZ_UNII4_START_INDEX 25
struct rtw89_regulatory_info {
+ struct rtw89_regd_ctrl ctrl;
const struct rtw89_regd *regd;
enum rtw89_reg_6ghz_power reg_6ghz_power;
struct rtw89_reg_6ghz_tpe reg_6ghz_tpe;
+ bool txpwr_uk_follow_etsi;
+
DECLARE_BITMAP(block_unii4, RTW89_REGD_MAX_COUNTRY_NUM);
DECLARE_BITMAP(block_6ghz, RTW89_REGD_MAX_COUNTRY_NUM);
DECLARE_BITMAP(block_6ghz_sp, RTW89_REGD_MAX_COUNTRY_NUM);
@@ -5299,8 +5437,8 @@ struct rtw89_lps_parm {
};
struct rtw89_ppdu_sts_info {
- struct sk_buff_head rx_queue[RTW89_PHY_MAX];
- u8 curr_rx_ppdu_cnt[RTW89_PHY_MAX];
+ struct sk_buff_head rx_queue[RTW89_PHY_NUM];
+ u8 curr_rx_ppdu_cnt[RTW89_PHY_NUM];
};
struct rtw89_early_h2c {
@@ -5312,9 +5450,10 @@ struct rtw89_early_h2c {
struct rtw89_hw_scan_info {
struct rtw89_vif_link *scanning_vif;
struct list_head pkt_list[NUM_NL80211_BANDS];
+ struct list_head chan_list;
struct rtw89_chan op_chan;
+ bool connected;
bool abort;
- u32 last_chan_idx;
};
enum rtw89_phy_bb_gain_band {
@@ -5419,8 +5558,8 @@ struct rtw89_phy_efuse_gain {
bool offset_valid;
bool comp_valid;
s8 offset[RF_PATH_MAX][RTW89_GAIN_OFFSET_NR]; /* S(8, 0) */
- s8 offset_base[RTW89_PHY_MAX]; /* S(8, 4) */
- s8 rssi_base[RTW89_PHY_MAX]; /* S(8, 4) */
+ s8 offset_base[RTW89_PHY_NUM]; /* S(8, 4) */
+ s8 rssi_base[RTW89_PHY_NUM]; /* S(8, 4) */
s8 comp[RF_PATH_MAX][RTW89_SUBBAND_NR]; /* S(8, 0) */
};
@@ -5526,6 +5665,8 @@ struct rtw89_mcc_role {
struct rtw89_mcc_policy policy;
struct rtw89_mcc_limit limit;
+ const struct rtw89_mcc_courtesy_cfg *crtz;
+
/* only valid when running with FW MRC mechanism */
u8 slot_idx;
@@ -5543,13 +5684,16 @@ struct rtw89_mcc_bt_role {
u16 duration; /* TU */
};
-struct rtw89_mcc_courtesy {
- bool enable;
+struct rtw89_mcc_courtesy_cfg {
u8 slot_num;
- u8 macid_src;
u8 macid_tgt;
};
+struct rtw89_mcc_courtesy {
+ struct rtw89_mcc_courtesy_cfg ref;
+ struct rtw89_mcc_courtesy_cfg aux;
+};
+
enum rtw89_mcc_plan {
RTW89_MCC_PLAN_TAIL_BT,
RTW89_MCC_PLAN_MID_BT,
@@ -5583,6 +5727,7 @@ struct rtw89_mcc_config {
struct rtw89_mcc_pattern pattern;
struct rtw89_mcc_sync sync;
u64 start_tsf;
+ u64 start_tsf_in_aux_domain;
u16 mcc_interval; /* TU */
u16 beacon_offset; /* TU */
};
@@ -5603,6 +5748,16 @@ struct rtw89_mcc_info {
struct rtw89_mcc_config config;
};
+enum rtw89_mlo_mode {
+ RTW89_MLO_MODE_MLSR = 0,
+
+ NUM_OF_RTW89_MLO_MODE,
+};
+
+struct rtw89_mlo_info {
+ struct rtw89_wait_info wait;
+};
+
struct rtw89_dev {
struct ieee80211_hw *hw;
struct device *dev;
@@ -5618,6 +5773,7 @@ struct rtw89_dev {
const struct rtw89_rfe_parms *rfe_parms;
struct rtw89_hal hal;
struct rtw89_mcc_info mcc;
+ struct rtw89_mlo_info mlo;
struct rtw89_mac_info mac;
struct rtw89_fw_info fw;
struct rtw89_hci_info hci;
@@ -5629,8 +5785,6 @@ struct rtw89_dev {
struct rtw89_sta_link __rcu *assoc_link_on_macid[RTW89_MAX_MAC_ID_NUM];
refcount_t refcount_ap_info;
- /* ensures exclusive access from mac80211 callbacks */
- struct mutex mutex;
struct list_head rtwvifs_list;
/* used to protect rf read write */
struct mutex rf_mutex;
@@ -5650,10 +5804,10 @@ struct rtw89_dev {
struct rtw89_cam_info cam_info;
struct sk_buff_head c2h_queue;
- struct work_struct c2h_work;
- struct work_struct ips_work;
+ struct wiphy_work c2h_work;
+ struct wiphy_work ips_work;
+ struct wiphy_work cancel_6ghz_probe_work;
struct work_struct load_firmware_work;
- struct work_struct cancel_6ghz_probe_work;
struct list_head early_h2c_list;
@@ -5682,9 +5836,6 @@ struct rtw89_dev {
struct rtw89_power_trim_info pwr_trim;
struct rtw89_cfo_tracking_info cfo_tracking;
- struct rtw89_env_monitor_info env_monitor;
- struct rtw89_dig_info dig;
- struct rtw89_phy_ch_info ch_info;
union {
struct rtw89_phy_bb_gain_info ax;
struct rtw89_phy_bb_gain_info_be be;
@@ -5693,15 +5844,22 @@ struct rtw89_dev {
struct rtw89_phy_ul_tb_info ul_tb_info;
struct rtw89_antdiv_info antdiv;
- struct delayed_work track_work;
- struct delayed_work chanctx_work;
- struct delayed_work coex_act1_work;
- struct delayed_work coex_bt_devinfo_work;
- struct delayed_work coex_rfk_chk_work;
- struct delayed_work cfo_track_work;
+ struct rtw89_bb_ctx {
+ enum rtw89_phy_idx phy_idx;
+ struct rtw89_env_monitor_info env_monitor;
+ struct rtw89_dig_info dig;
+ struct rtw89_phy_ch_info ch_info;
+ struct rtw89_edcca_bak edcca_bak;
+ } bbs[RTW89_PHY_NUM];
+
+ struct wiphy_delayed_work track_work;
+ struct wiphy_delayed_work chanctx_work;
+ struct wiphy_delayed_work coex_act1_work;
+ struct wiphy_delayed_work coex_bt_devinfo_work;
+ struct wiphy_delayed_work coex_rfk_chk_work;
+ struct wiphy_delayed_work cfo_track_work;
struct delayed_work forbid_ba_work;
- struct delayed_work roc_work;
- struct delayed_work antdiv_work;
+ struct wiphy_delayed_work antdiv_work;
struct rtw89_ppdu_sts_info ppdu_sts;
u8 total_sta_assoc;
bool scanning;
@@ -5752,6 +5910,9 @@ struct rtw89_vif {
struct rtw89_roc roc;
bool offchan;
+ enum rtw89_mlo_mode mlo_mode;
+
+ struct list_head dlink_pool;
u8 links_inst_valid_num;
DECLARE_BITMAP(links_inst_map, __RTW89_MLD_MAX_LINK_NUM);
struct rtw89_vif_link *links[IEEE80211_MLD_MAX_NUM_LINKS];
@@ -5791,6 +5952,7 @@ struct rtw89_sta {
DECLARE_BITMAP(pairwise_sec_cam_map, RTW89_MAX_SEC_CAM_NUM);
+ struct list_head dlink_pool;
u8 links_inst_valid_num;
DECLARE_BITMAP(links_inst_map, __RTW89_MLD_MAX_LINK_NUM);
struct rtw89_sta_link *links[IEEE80211_MLD_MAX_NUM_LINKS];
@@ -5886,6 +6048,12 @@ rtw89_assoc_link_rcu_dereference(struct rtw89_dev *rtwdev, u8 macid)
return rcu_dereference(rtwdev->assoc_link_on_macid[macid]);
}
+#define rtw89_get_designated_link(links_holder) \
+({ \
+ typeof(links_holder) p = links_holder; \
+ list_first_entry_or_null(&p->dlink_pool, typeof(*p->links_inst), dlink_schd); \
+})
+
static inline int rtw89_hci_tx_write(struct rtw89_dev *rtwdev,
struct rtw89_core_tx_request *tx_req)
{
@@ -6775,9 +6943,14 @@ static inline void rtw89_load_txpwr_table(struct rtw89_dev *rtwdev,
static inline u8 rtw89_regd_get(struct rtw89_dev *rtwdev, u8 band)
{
- const struct rtw89_regd *regd = rtwdev->regulatory.regd;
+ const struct rtw89_regulatory_info *regulatory = &rtwdev->regulatory;
+ const struct rtw89_regd *regd = regulatory->regd;
+ u8 txpwr_regd = regd->txpwr_regd[band];
+
+ if (regulatory->txpwr_uk_follow_etsi && txpwr_regd == RTW89_UK)
+ return RTW89_ETSI;
- return regd->txpwr_regd[band];
+ return txpwr_regd;
}
static inline void rtw89_ctrl_btg_bt_rx(struct rtw89_dev *rtwdev, bool en,
@@ -6978,6 +7151,48 @@ static inline bool rtw89_is_mlo_1_1(struct rtw89_dev *rtwdev)
}
}
+static inline u8 rtw89_get_active_phy_bitmap(struct rtw89_dev *rtwdev)
+{
+ if (!rtwdev->dbcc_en)
+ return BIT(RTW89_PHY_0);
+
+ switch (rtwdev->mlo_dbcc_mode) {
+ case MLO_0_PLUS_2_1RF:
+ case MLO_0_PLUS_2_2RF:
+ return BIT(RTW89_PHY_1);
+ case MLO_1_PLUS_1_1RF:
+ case MLO_1_PLUS_1_2RF:
+ case MLO_2_PLUS_2_2RF:
+ case DBCC_LEGACY:
+ return BIT(RTW89_PHY_0) | BIT(RTW89_PHY_1);
+ case MLO_2_PLUS_0_1RF:
+ case MLO_2_PLUS_0_2RF:
+ default:
+ return BIT(RTW89_PHY_0);
+ }
+}
+
+#define rtw89_for_each_active_bb(rtwdev, bb) \
+ for (u8 __active_bb_bitmap = rtw89_get_active_phy_bitmap(rtwdev), \
+ __phy_idx = 0; __phy_idx < RTW89_PHY_NUM; __phy_idx++) \
+ if (__active_bb_bitmap & BIT(__phy_idx) && \
+ (bb = &rtwdev->bbs[__phy_idx]))
+
+#define rtw89_for_each_capab_bb(rtwdev, bb) \
+ for (u8 __phy_idx_max = rtwdev->dbcc_en ? RTW89_PHY_1 : RTW89_PHY_0, \
+ __phy_idx = 0; __phy_idx <= __phy_idx_max; __phy_idx++) \
+ if ((bb = &rtwdev->bbs[__phy_idx]))
+
+static inline
+struct rtw89_bb_ctx *rtw89_get_bb_ctx(struct rtw89_dev *rtwdev,
+ enum rtw89_phy_idx phy_idx)
+{
+ if (phy_idx >= RTW89_PHY_NUM)
+ return &rtwdev->bbs[RTW89_PHY_0];
+
+ return &rtwdev->bbs[phy_idx];
+}
+
static inline bool rtw89_is_rtl885xb(struct rtw89_dev *rtwdev)
{
enum rtw89_core_chip_id chip_id = rtwdev->chip->chip_id;
@@ -7092,9 +7307,8 @@ void rtw89_chip_cfg_txpwr_ul_tb_offset(struct rtw89_dev *rtwdev,
struct rtw89_vif_link *rtwvif_link);
bool rtw89_ra_report_to_bitrate(struct rtw89_dev *rtwdev, u8 rpt_rate, u16 *bitrate);
int rtw89_regd_setup(struct rtw89_dev *rtwdev);
-int rtw89_regd_init(struct rtw89_dev *rtwdev,
- void (*reg_notifier)(struct wiphy *wiphy, struct regulatory_request *request));
-void rtw89_regd_notifier(struct wiphy *wiphy, struct regulatory_request *request);
+int rtw89_regd_init_hint(struct rtw89_dev *rtwdev);
+const char *rtw89_regd_get_string(enum rtw89_regulation_type regd);
void rtw89_traffic_stats_init(struct rtw89_dev *rtwdev,
struct rtw89_traffic_stats *stats);
int rtw89_wait_for_cond(struct rtw89_wait_info *wait, unsigned int cond);
@@ -7102,8 +7316,8 @@ void rtw89_complete_cond(struct rtw89_wait_info *wait, unsigned int cond,
const struct rtw89_completion_data *data);
int rtw89_core_start(struct rtw89_dev *rtwdev);
void rtw89_core_stop(struct rtw89_dev *rtwdev);
-void rtw89_core_update_beacon_work(struct work_struct *work);
-void rtw89_roc_work(struct work_struct *work);
+void rtw89_core_update_beacon_work(struct wiphy *wiphy, struct wiphy_work *work);
+void rtw89_roc_work(struct wiphy *wiphy, struct wiphy_work *work);
void rtw89_roc_start(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif);
void rtw89_roc_end(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif);
void rtw89_core_scan_start(struct rtw89_dev *rtwdev, struct rtw89_vif_link *rtwvif_link,
@@ -7116,5 +7330,7 @@ void rtw89_core_update_p2p_ps(struct rtw89_dev *rtwdev,
struct rtw89_vif_link *rtwvif_link,
struct ieee80211_bss_conf *bss_conf);
void rtw89_core_ntfy_btc_event(struct rtw89_dev *rtwdev, enum rtw89_btc_hmsg event);
+int rtw89_core_mlsr_switch(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif,
+ unsigned int link_id);
#endif
diff --git a/drivers/net/wireless/realtek/rtw89/debug.c b/drivers/net/wireless/realtek/rtw89/debug.c
index 09fa977a6e6d..d6016fa107fb 100644
--- a/drivers/net/wireless/realtek/rtw89/debug.c
+++ b/drivers/net/wireless/realtek/rtw89/debug.c
@@ -13,6 +13,7 @@
#include "ps.h"
#include "reg.h"
#include "sar.h"
+#include "util.h"
#ifdef CONFIG_RTW89_DEBUGMSG
unsigned int rtw89_debug_mask;
@@ -22,11 +23,21 @@ MODULE_PARM_DESC(debug_mask, "Debugging mask");
#endif
#ifdef CONFIG_RTW89_DEBUGFS
+struct rtw89_debugfs_priv_opt {
+ bool rlock:1;
+ bool wlock:1;
+ size_t rsize;
+};
+
struct rtw89_debugfs_priv {
struct rtw89_dev *rtwdev;
- int (*cb_read)(struct seq_file *m, void *v);
- ssize_t (*cb_write)(struct file *filp, const char __user *buffer,
- size_t count, loff_t *loff);
+ ssize_t (*cb_read)(struct rtw89_dev *rtwdev,
+ struct rtw89_debugfs_priv *debugfs_priv,
+ char *buf, size_t bufsz);
+ ssize_t (*cb_write)(struct rtw89_dev *rtwdev,
+ struct rtw89_debugfs_priv *debugfs_priv,
+ const char *buf, size_t count);
+ struct rtw89_debugfs_priv_opt opt;
union {
u32 cb_data;
struct {
@@ -51,6 +62,8 @@ struct rtw89_debugfs_priv {
u8 sel;
} mac_mem;
};
+ ssize_t rused;
+ char *rbuf;
};
struct rtw89_debugfs {
@@ -72,8 +85,31 @@ struct rtw89_debugfs {
struct rtw89_debugfs_priv phy_info;
struct rtw89_debugfs_priv stations;
struct rtw89_debugfs_priv disable_dm;
+ struct rtw89_debugfs_priv mlo_mode;
+};
+
+struct rtw89_debugfs_iter_data {
+ char *buf;
+ size_t bufsz;
+ int written_sz;
};
+static void rtw89_debugfs_iter_data_setup(struct rtw89_debugfs_iter_data *iter_data,
+ char *buf, size_t bufsz)
+{
+ iter_data->buf = buf;
+ iter_data->bufsz = bufsz;
+ iter_data->written_sz = 0;
+}
+
+static void rtw89_debugfs_iter_data_next(struct rtw89_debugfs_iter_data *iter_data,
+ char *buf, size_t bufsz, int written_sz)
+{
+ iter_data->buf = buf;
+ iter_data->bufsz = bufsz;
+ iter_data->written_sz += written_sz;
+}
+
static const u16 rtw89_rate_info_bw_to_mhz_map[] = {
[RATE_INFO_BW_20] = 20,
[RATE_INFO_BW_40] = 40,
@@ -90,84 +126,122 @@ static u16 rtw89_rate_info_bw_to_mhz(enum rate_info_bw bw)
return 0;
}
-static int rtw89_debugfs_single_show(struct seq_file *m, void *v)
+static ssize_t rtw89_debugfs_file_read_helper(struct wiphy *wiphy, struct file *file,
+ char *buf, size_t bufsz, void *data)
{
- struct rtw89_debugfs_priv *debugfs_priv = m->private;
+ struct rtw89_debugfs_priv *debugfs_priv = data;
+ struct rtw89_dev *rtwdev = debugfs_priv->rtwdev;
+ ssize_t n;
- return debugfs_priv->cb_read(m, v);
+ n = debugfs_priv->cb_read(rtwdev, debugfs_priv, buf, bufsz);
+ rtw89_might_trailing_ellipsis(buf, bufsz, n);
+
+ return n;
}
-static ssize_t rtw89_debugfs_single_write(struct file *filp,
- const char __user *buffer,
- size_t count, loff_t *loff)
+static ssize_t rtw89_debugfs_file_read(struct file *file, char __user *userbuf,
+ size_t count, loff_t *ppos)
{
- struct rtw89_debugfs_priv *debugfs_priv = filp->private_data;
+ struct rtw89_debugfs_priv *debugfs_priv = file->private_data;
+ struct rtw89_debugfs_priv_opt *opt = &debugfs_priv->opt;
+ struct rtw89_dev *rtwdev = debugfs_priv->rtwdev;
+ size_t bufsz = opt->rsize ? opt->rsize : PAGE_SIZE;
+ char *buf;
+ ssize_t n;
- return debugfs_priv->cb_write(filp, buffer, count, loff);
-}
+ if (!debugfs_priv->rbuf)
+ debugfs_priv->rbuf = devm_kzalloc(rtwdev->dev, bufsz, GFP_KERNEL);
-static ssize_t rtw89_debugfs_seq_file_write(struct file *filp,
- const char __user *buffer,
- size_t count, loff_t *loff)
-{
- struct seq_file *seqpriv = (struct seq_file *)filp->private_data;
- struct rtw89_debugfs_priv *debugfs_priv = seqpriv->private;
+ buf = debugfs_priv->rbuf;
+ if (!buf)
+ return -ENOMEM;
+
+ if (*ppos) {
+ n = debugfs_priv->rused;
+ goto out;
+ }
- return debugfs_priv->cb_write(filp, buffer, count, loff);
+ if (opt->rlock) {
+ n = wiphy_locked_debugfs_read(rtwdev->hw->wiphy, file, buf, bufsz,
+ userbuf, count, ppos,
+ rtw89_debugfs_file_read_helper,
+ debugfs_priv);
+ debugfs_priv->rused = n;
+
+ return n;
+ }
+
+ n = rtw89_debugfs_file_read_helper(rtwdev->hw->wiphy, file, buf, bufsz,
+ debugfs_priv);
+ debugfs_priv->rused = n;
+
+out:
+ return simple_read_from_buffer(userbuf, count, ppos, buf, n);
}
-static int rtw89_debugfs_single_open(struct inode *inode, struct file *filp)
+static ssize_t rtw89_debugfs_file_write_helper(struct wiphy *wiphy, struct file *file,
+ char *buf, size_t count, void *data)
{
- return single_open(filp, rtw89_debugfs_single_show, inode->i_private);
+ struct rtw89_debugfs_priv *debugfs_priv = data;
+ struct rtw89_dev *rtwdev = debugfs_priv->rtwdev;
+
+ return debugfs_priv->cb_write(rtwdev, debugfs_priv, buf, count);
}
-static int rtw89_debugfs_close(struct inode *inode, struct file *filp)
+static ssize_t rtw89_debugfs_file_write(struct file *file,
+ const char __user *userbuf,
+ size_t count, loff_t *loff)
{
- return 0;
+ struct rtw89_debugfs_priv *debugfs_priv = file->private_data;
+ struct rtw89_debugfs_priv_opt *opt = &debugfs_priv->opt;
+ struct rtw89_dev *rtwdev = debugfs_priv->rtwdev;
+ char *buf __free(kfree) = kmalloc(count + 1, GFP_KERNEL);
+ ssize_t n;
+
+ if (!buf)
+ return -ENOMEM;
+
+ if (opt->wlock) {
+ n = wiphy_locked_debugfs_write(rtwdev->hw->wiphy,
+ file, buf, count + 1,
+ userbuf, count,
+ rtw89_debugfs_file_write_helper,
+ debugfs_priv);
+ return n;
+ }
+
+ if (copy_from_user(buf, userbuf, count))
+ return -EFAULT;
+
+ buf[count] = '\0';
+
+ return debugfs_priv->cb_write(rtwdev, debugfs_priv, buf, count);
}
-static const struct file_operations file_ops_single_r = {
- .owner = THIS_MODULE,
- .open = rtw89_debugfs_single_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = single_release,
+static const struct debugfs_short_fops file_ops_single_r = {
+ .read = rtw89_debugfs_file_read,
+ .llseek = generic_file_llseek,
};
-static const struct file_operations file_ops_common_rw = {
- .owner = THIS_MODULE,
- .open = rtw89_debugfs_single_open,
- .release = single_release,
- .read = seq_read,
- .llseek = seq_lseek,
- .write = rtw89_debugfs_seq_file_write,
+static const struct debugfs_short_fops file_ops_common_rw = {
+ .read = rtw89_debugfs_file_read,
+ .write = rtw89_debugfs_file_write,
+ .llseek = generic_file_llseek,
};
-static const struct file_operations file_ops_single_w = {
- .owner = THIS_MODULE,
- .write = rtw89_debugfs_single_write,
- .open = simple_open,
- .release = rtw89_debugfs_close,
+static const struct debugfs_short_fops file_ops_single_w = {
+ .write = rtw89_debugfs_file_write,
+ .llseek = generic_file_llseek,
};
static ssize_t
-rtw89_debug_priv_read_reg_select(struct file *filp,
- const char __user *user_buf,
- size_t count, loff_t *loff)
+rtw89_debug_priv_read_reg_select(struct rtw89_dev *rtwdev,
+ struct rtw89_debugfs_priv *debugfs_priv,
+ const char *buf, size_t count)
{
- struct seq_file *m = (struct seq_file *)filp->private_data;
- struct rtw89_debugfs_priv *debugfs_priv = m->private;
- struct rtw89_dev *rtwdev = debugfs_priv->rtwdev;
- char buf[32];
- size_t buf_size;
u32 addr, len;
int num;
- buf_size = min(count, sizeof(buf) - 1);
- if (copy_from_user(buf, user_buf, buf_size))
- return -EFAULT;
-
- buf[buf_size] = '\0';
num = sscanf(buf, "%x %x", &addr, &len);
if (num != 2) {
rtw89_info(rtwdev, "invalid format: <addr> <len>\n");
@@ -182,11 +256,13 @@ rtw89_debug_priv_read_reg_select(struct file *filp,
return count;
}
-static int rtw89_debug_priv_read_reg_get(struct seq_file *m, void *v)
+static
+ssize_t rtw89_debug_priv_read_reg_get(struct rtw89_dev *rtwdev,
+ struct rtw89_debugfs_priv *debugfs_priv,
+ char *buf, size_t bufsz)
{
- struct rtw89_debugfs_priv *debugfs_priv = m->private;
- struct rtw89_dev *rtwdev = debugfs_priv->rtwdev;
- u32 addr, end, data, k;
+ char *p = buf, *end = buf + bufsz;
+ u32 addr, addr_end, data, k;
u32 len;
len = debugfs_priv->read_reg.len;
@@ -210,41 +286,34 @@ static int rtw89_debug_priv_read_reg_get(struct seq_file *m, void *v)
return -EINVAL;
}
- seq_printf(m, "get %d bytes at 0x%08x=0x%08x\n", len, addr, data);
+ p += scnprintf(p, end - p, "get %d bytes at 0x%08x=0x%08x\n", len,
+ addr, data);
- return 0;
+ return p - buf;
ndata:
- end = addr + len;
+ addr_end = addr + len;
- for (; addr < end; addr += 16) {
- seq_printf(m, "%08xh : ", 0x18600000 + addr);
+ for (; addr < addr_end; addr += 16) {
+ p += scnprintf(p, end - p, "%08xh : ", 0x18600000 + addr);
for (k = 0; k < 16; k += 4) {
data = rtw89_read32(rtwdev, addr + k);
- seq_printf(m, "%08x ", data);
+ p += scnprintf(p, end - p, "%08x ", data);
}
- seq_puts(m, "\n");
+ p += scnprintf(p, end - p, "\n");
}
- return 0;
+ return p - buf;
}
-static ssize_t rtw89_debug_priv_write_reg_set(struct file *filp,
- const char __user *user_buf,
- size_t count, loff_t *loff)
+static
+ssize_t rtw89_debug_priv_write_reg_set(struct rtw89_dev *rtwdev,
+ struct rtw89_debugfs_priv *debugfs_priv,
+ const char *buf, size_t count)
{
- struct rtw89_debugfs_priv *debugfs_priv = filp->private_data;
- struct rtw89_dev *rtwdev = debugfs_priv->rtwdev;
- char buf[32];
- size_t buf_size;
u32 addr, val, len;
int num;
- buf_size = min(count, sizeof(buf) - 1);
- if (copy_from_user(buf, user_buf, buf_size))
- return -EFAULT;
-
- buf[buf_size] = '\0';
num = sscanf(buf, "%x %x %x", &addr, &val, &len);
if (num != 3) {
rtw89_info(rtwdev, "invalid format: <addr> <val> <len>\n");
@@ -273,24 +342,14 @@ static ssize_t rtw89_debug_priv_write_reg_set(struct file *filp,
}
static ssize_t
-rtw89_debug_priv_read_rf_select(struct file *filp,
- const char __user *user_buf,
- size_t count, loff_t *loff)
+rtw89_debug_priv_read_rf_select(struct rtw89_dev *rtwdev,
+ struct rtw89_debugfs_priv *debugfs_priv,
+ const char *buf, size_t count)
{
- struct seq_file *m = (struct seq_file *)filp->private_data;
- struct rtw89_debugfs_priv *debugfs_priv = m->private;
- struct rtw89_dev *rtwdev = debugfs_priv->rtwdev;
- char buf[32];
- size_t buf_size;
u32 addr, mask;
u8 path;
int num;
- buf_size = min(count, sizeof(buf) - 1);
- if (copy_from_user(buf, user_buf, buf_size))
- return -EFAULT;
-
- buf[buf_size] = '\0';
num = sscanf(buf, "%hhd %x %x", &path, &addr, &mask);
if (num != 3) {
rtw89_info(rtwdev, "invalid format: <path> <addr> <mask>\n");
@@ -310,10 +369,12 @@ rtw89_debug_priv_read_rf_select(struct file *filp,
return count;
}
-static int rtw89_debug_priv_read_rf_get(struct seq_file *m, void *v)
+static
+ssize_t rtw89_debug_priv_read_rf_get(struct rtw89_dev *rtwdev,
+ struct rtw89_debugfs_priv *debugfs_priv,
+ char *buf, size_t bufsz)
{
- struct rtw89_debugfs_priv *debugfs_priv = m->private;
- struct rtw89_dev *rtwdev = debugfs_priv->rtwdev;
+ char *p = buf, *end = buf + bufsz;
u32 addr, data, mask;
u8 path;
@@ -323,28 +384,21 @@ static int rtw89_debug_priv_read_rf_get(struct seq_file *m, void *v)
data = rtw89_read_rf(rtwdev, path, addr, mask);
- seq_printf(m, "path %d, rf register 0x%08x=0x%08x\n", path, addr, data);
+ p += scnprintf(p, end - p, "path %d, rf register 0x%08x=0x%08x\n",
+ path, addr, data);
- return 0;
+ return p - buf;
}
-static ssize_t rtw89_debug_priv_write_rf_set(struct file *filp,
- const char __user *user_buf,
- size_t count, loff_t *loff)
+static
+ssize_t rtw89_debug_priv_write_rf_set(struct rtw89_dev *rtwdev,
+ struct rtw89_debugfs_priv *debugfs_priv,
+ const char *buf, size_t count)
{
- struct rtw89_debugfs_priv *debugfs_priv = filp->private_data;
- struct rtw89_dev *rtwdev = debugfs_priv->rtwdev;
- char buf[32];
- size_t buf_size;
u32 addr, val, mask;
u8 path;
int num;
- buf_size = min(count, sizeof(buf) - 1);
- if (copy_from_user(buf, user_buf, buf_size))
- return -EFAULT;
-
- buf[buf_size] = '\0';
num = sscanf(buf, "%hhd %x %x %x", &path, &addr, &mask, &val);
if (num != 4) {
rtw89_info(rtwdev, "invalid format: <path> <addr> <mask> <val>\n");
@@ -363,29 +417,31 @@ static ssize_t rtw89_debug_priv_write_rf_set(struct file *filp,
return count;
}
-static int rtw89_debug_priv_rf_reg_dump_get(struct seq_file *m, void *v)
+static
+ssize_t rtw89_debug_priv_rf_reg_dump_get(struct rtw89_dev *rtwdev,
+ struct rtw89_debugfs_priv *debugfs_priv,
+ char *buf, size_t bufsz)
{
- struct rtw89_debugfs_priv *debugfs_priv = m->private;
- struct rtw89_dev *rtwdev = debugfs_priv->rtwdev;
const struct rtw89_chip_info *chip = rtwdev->chip;
+ char *p = buf, *end = buf + bufsz;
u32 addr, offset, data;
u8 path;
for (path = 0; path < chip->rf_path_num; path++) {
- seq_printf(m, "RF path %d:\n\n", path);
+ p += scnprintf(p, end - p, "RF path %d:\n\n", path);
for (addr = 0; addr < 0x100; addr += 4) {
- seq_printf(m, "0x%08x: ", addr);
+ p += scnprintf(p, end - p, "0x%08x: ", addr);
for (offset = 0; offset < 4; offset++) {
data = rtw89_read_rf(rtwdev, path,
addr + offset, RFREG_MASK);
- seq_printf(m, "0x%05x ", data);
+ p += scnprintf(p, end - p, "0x%05x ", data);
}
- seq_puts(m, "\n");
+ p += scnprintf(p, end - p, "\n");
}
- seq_puts(m, "\n");
+ p += scnprintf(p, end - p, "\n");
}
- return 0;
+ return p - buf;
}
struct txpwr_ent {
@@ -704,56 +760,71 @@ static const struct txpwr_map __txpwr_map_lmt_ru_be = {
};
static unsigned int
-__print_txpwr_ent(struct seq_file *m, const struct txpwr_ent *ent,
- const s8 *buf, const unsigned int cur)
+__print_txpwr_ent(char *buf, size_t bufsz, const struct txpwr_ent *ent,
+ const s8 *bufp, const unsigned int cur, unsigned int *ate)
{
+ char *p = buf, *end = buf + bufsz;
unsigned int cnt, i;
+ unsigned int eaten;
char *fmt;
if (ent->nested) {
- for (cnt = 0, i = 0; i < ent->len; i++)
- cnt += __print_txpwr_ent(m, ent->ptr + i, buf,
- cur + cnt);
- return cnt;
+ for (cnt = 0, i = 0; i < ent->len; i++, cnt += eaten)
+ p += __print_txpwr_ent(p, end - p, ent->ptr + i, bufp,
+ cur + cnt, &eaten);
+ *ate = cnt;
+ goto out;
}
switch (ent->len) {
case 0:
- seq_printf(m, "\t<< %s >>\n", ent->txt);
- return 0;
+ p += scnprintf(p, end - p, "\t<< %s >>\n", ent->txt);
+ *ate = 0;
+ goto out;
case 2:
fmt = "%s\t| %3d, %3d,\t\tdBm\n";
- seq_printf(m, fmt, ent->txt, buf[cur], buf[cur + 1]);
- return 2;
+ p += scnprintf(p, end - p, fmt, ent->txt, bufp[cur],
+ bufp[cur + 1]);
+ *ate = 2;
+ goto out;
case 4:
fmt = "%s\t| %3d, %3d, %3d, %3d,\tdBm\n";
- seq_printf(m, fmt, ent->txt, buf[cur], buf[cur + 1],
- buf[cur + 2], buf[cur + 3]);
- return 4;
+ p += scnprintf(p, end - p, fmt, ent->txt, bufp[cur],
+ bufp[cur + 1],
+ bufp[cur + 2], bufp[cur + 3]);
+ *ate = 4;
+ goto out;
case 8:
fmt = "%s\t| %3d, %3d, %3d, %3d, %3d, %3d, %3d, %3d,\tdBm\n";
- seq_printf(m, fmt, ent->txt, buf[cur], buf[cur + 1],
- buf[cur + 2], buf[cur + 3], buf[cur + 4],
- buf[cur + 5], buf[cur + 6], buf[cur + 7]);
- return 8;
+ p += scnprintf(p, end - p, fmt, ent->txt, bufp[cur],
+ bufp[cur + 1],
+ bufp[cur + 2], bufp[cur + 3], bufp[cur + 4],
+ bufp[cur + 5], bufp[cur + 6], bufp[cur + 7]);
+ *ate = 8;
+ goto out;
default:
return 0;
}
+
+out:
+ return p - buf;
}
-static int __print_txpwr_map(struct seq_file *m, struct rtw89_dev *rtwdev,
- const struct txpwr_map *map)
+static ssize_t __print_txpwr_map(struct rtw89_dev *rtwdev, char *buf, size_t bufsz,
+ const struct txpwr_map *map)
{
u8 fct = rtwdev->chip->txpwr_factor_mac;
u8 path_num = rtwdev->chip->rf_path_num;
+ char *p = buf, *end = buf + bufsz;
unsigned int cur, i;
+ unsigned int eaten;
u32 max_valid_addr;
u32 val, addr;
- s8 *buf, tmp;
+ s8 *bufp, tmp;
int ret;
- buf = vzalloc(map->addr_to - map->addr_from + 4);
- if (!buf)
+ bufp = vzalloc(map->addr_to - map->addr_from + 4);
+ if (!bufp)
return -ENOMEM;
if (path_num == 1)
@@ -773,52 +844,31 @@ static int __print_txpwr_map(struct seq_file *m, struct rtw89_dev *rtwdev,
for (i = 0; i < 4; i++, val >>= 8) {
/* signed 7 bits, and reserved BIT(7) */
tmp = sign_extend32(val, 6);
- buf[cur + i] = tmp >> fct;
+ bufp[cur + i] = tmp >> fct;
}
}
- for (cur = 0, i = 0; i < map->size; i++)
- cur += __print_txpwr_ent(m, &map->ent[i], buf, cur);
+ for (cur = 0, i = 0; i < map->size; i++, cur += eaten)
+ p += __print_txpwr_ent(p, end - p, &map->ent[i], bufp, cur, &eaten);
- vfree(buf);
- return 0;
+ vfree(bufp);
+ return p - buf;
}
-#define case_REGD(_regd) \
- case RTW89_ ## _regd: \
- seq_puts(m, #_regd "\n"); \
- break
-
-static void __print_regd(struct seq_file *m, struct rtw89_dev *rtwdev,
- const struct rtw89_chan *chan)
+static int __print_regd(struct rtw89_dev *rtwdev, char *buf, size_t bufsz,
+ const struct rtw89_chan *chan)
{
+ const struct rtw89_regulatory_info *regulatory = &rtwdev->regulatory;
+ char *p = buf, *end = buf + bufsz;
u8 band = chan->band_type;
u8 regd = rtw89_regd_get(rtwdev, band);
- switch (regd) {
- default:
- seq_printf(m, "UNKNOWN: %d\n", regd);
- break;
- case_REGD(WW);
- case_REGD(ETSI);
- case_REGD(FCC);
- case_REGD(MKK);
- case_REGD(NA);
- case_REGD(IC);
- case_REGD(KCC);
- case_REGD(NCC);
- case_REGD(CHILE);
- case_REGD(ACMA);
- case_REGD(MEXICO);
- case_REGD(UKRAINE);
- case_REGD(CN);
- case_REGD(QATAR);
- case_REGD(UK);
- case_REGD(THAILAND);
- }
-}
+ p += scnprintf(p, end - p, "%s\n", rtw89_regd_get_string(regd));
+ p += scnprintf(p, end - p, "\t(txpwr UK follow ETSI: %s)\n",
+ str_yes_no(regulatory->txpwr_uk_follow_etsi));
-#undef case_REGD
+ return p - buf;
+}
struct dbgfs_txpwr_table {
const struct txpwr_map *byr;
@@ -844,96 +894,95 @@ static const struct dbgfs_txpwr_table *dbgfs_txpwr_tables[RTW89_CHIP_GEN_NUM] =
};
static
-void rtw89_debug_priv_txpwr_table_get_regd(struct seq_file *m,
- struct rtw89_dev *rtwdev,
- const struct rtw89_chan *chan)
+int rtw89_debug_priv_txpwr_table_get_regd(struct rtw89_dev *rtwdev,
+ char *buf, size_t bufsz,
+ const struct rtw89_chan *chan)
{
const struct rtw89_regulatory_info *regulatory = &rtwdev->regulatory;
const struct rtw89_reg_6ghz_tpe *tpe6 = &regulatory->reg_6ghz_tpe;
+ char *p = buf, *end = buf + bufsz;
- seq_printf(m, "[Chanctx] band %u, ch %u, bw %u\n",
- chan->band_type, chan->channel, chan->band_width);
+ p += scnprintf(p, end - p, "[Chanctx] band %u, ch %u, bw %u\n",
+ chan->band_type, chan->channel, chan->band_width);
- seq_puts(m, "[Regulatory] ");
- __print_regd(m, rtwdev, chan);
+ p += scnprintf(p, end - p, "[Regulatory] ");
+ p += __print_regd(rtwdev, p, end - p, chan);
if (chan->band_type == RTW89_BAND_6G) {
- seq_printf(m, "[reg6_pwr_type] %u\n", regulatory->reg_6ghz_power);
+ p += scnprintf(p, end - p, "[reg6_pwr_type] %u\n",
+ regulatory->reg_6ghz_power);
if (tpe6->valid)
- seq_printf(m, "[TPE] %d dBm\n", tpe6->constraint);
+ p += scnprintf(p, end - p, "[TPE] %d dBm\n",
+ tpe6->constraint);
}
+
+ return p - buf;
}
-static int rtw89_debug_priv_txpwr_table_get(struct seq_file *m, void *v)
+static
+ssize_t rtw89_debug_priv_txpwr_table_get(struct rtw89_dev *rtwdev,
+ struct rtw89_debugfs_priv *debugfs_priv,
+ char *buf, size_t bufsz)
{
- struct rtw89_debugfs_priv *debugfs_priv = m->private;
- struct rtw89_dev *rtwdev = debugfs_priv->rtwdev;
enum rtw89_chip_gen chip_gen = rtwdev->chip->chip_gen;
+ struct rtw89_sar_parm sar_parm = {};
const struct dbgfs_txpwr_table *tbl;
const struct rtw89_chan *chan;
- int ret = 0;
+ char *p = buf, *end = buf + bufsz;
+ ssize_t n;
+
+ lockdep_assert_wiphy(rtwdev->hw->wiphy);
- mutex_lock(&rtwdev->mutex);
rtw89_leave_ps_mode(rtwdev);
chan = rtw89_chan_get(rtwdev, RTW89_CHANCTX_0);
+ sar_parm.center_freq = chan->freq;
- rtw89_debug_priv_txpwr_table_get_regd(m, rtwdev, chan);
+ p += rtw89_debug_priv_txpwr_table_get_regd(rtwdev, p, end - p, chan);
- seq_puts(m, "[SAR]\n");
- rtw89_print_sar(m, rtwdev, chan->freq);
+ p += scnprintf(p, end - p, "[SAR]\n");
+ p += rtw89_print_sar(rtwdev, p, end - p, &sar_parm);
- seq_puts(m, "[TAS]\n");
- rtw89_print_tas(m, rtwdev);
+ p += scnprintf(p, end - p, "[TAS]\n");
+ p += rtw89_print_tas(rtwdev, p, end - p);
- seq_puts(m, "[DAG]\n");
- rtw89_print_ant_gain(m, rtwdev, chan);
+ p += scnprintf(p, end - p, "[DAG]\n");
+ p += rtw89_print_ant_gain(rtwdev, p, end - p, chan);
tbl = dbgfs_txpwr_tables[chip_gen];
- if (!tbl) {
- ret = -EOPNOTSUPP;
- goto err;
- }
-
- seq_puts(m, "\n[TX power byrate]\n");
- ret = __print_txpwr_map(m, rtwdev, tbl->byr);
- if (ret)
- goto err;
-
- seq_puts(m, "\n[TX power limit]\n");
- ret = __print_txpwr_map(m, rtwdev, tbl->lmt);
- if (ret)
- goto err;
-
- seq_puts(m, "\n[TX power limit_ru]\n");
- ret = __print_txpwr_map(m, rtwdev, tbl->lmt_ru);
- if (ret)
- goto err;
+ if (!tbl)
+ return -EOPNOTSUPP;
-err:
- mutex_unlock(&rtwdev->mutex);
- return ret;
+ p += scnprintf(p, end - p, "\n[TX power byrate]\n");
+ n = __print_txpwr_map(rtwdev, p, end - p, tbl->byr);
+ if (n < 0)
+ return n;
+ p += n;
+
+ p += scnprintf(p, end - p, "\n[TX power limit]\n");
+ n = __print_txpwr_map(rtwdev, p, end - p, tbl->lmt);
+ if (n < 0)
+ return n;
+ p += n;
+
+ p += scnprintf(p, end - p, "\n[TX power limit_ru]\n");
+ n = __print_txpwr_map(rtwdev, p, end - p, tbl->lmt_ru);
+ if (n < 0)
+ return n;
+ p += n;
+
+ return p - buf;
}
static ssize_t
-rtw89_debug_priv_mac_reg_dump_select(struct file *filp,
- const char __user *user_buf,
- size_t count, loff_t *loff)
+rtw89_debug_priv_mac_reg_dump_select(struct rtw89_dev *rtwdev,
+ struct rtw89_debugfs_priv *debugfs_priv,
+ const char *buf, size_t count)
{
- struct seq_file *m = (struct seq_file *)filp->private_data;
- struct rtw89_debugfs_priv *debugfs_priv = m->private;
- struct rtw89_dev *rtwdev = debugfs_priv->rtwdev;
const struct rtw89_chip_info *chip = rtwdev->chip;
- char buf[32];
- size_t buf_size;
int sel;
int ret;
- buf_size = min(count, sizeof(buf) - 1);
- if (copy_from_user(buf, user_buf, buf_size))
- return -EFAULT;
-
- buf[buf_size] = '\0';
ret = kstrtoint(buf, 0, &sel);
if (ret)
return ret;
@@ -957,99 +1006,91 @@ rtw89_debug_priv_mac_reg_dump_select(struct file *filp,
#define RTW89_MAC_PAGE_SIZE 0x100
-static int rtw89_debug_priv_mac_reg_dump_get(struct seq_file *m, void *v)
+static
+ssize_t rtw89_debug_priv_mac_reg_dump_get(struct rtw89_dev *rtwdev,
+ struct rtw89_debugfs_priv *debugfs_priv,
+ char *buf, size_t bufsz)
{
- struct rtw89_debugfs_priv *debugfs_priv = m->private;
- struct rtw89_dev *rtwdev = debugfs_priv->rtwdev;
enum rtw89_debug_mac_reg_sel reg_sel = debugfs_priv->cb_data;
- u32 start, end;
+ char *p = buf, *end = buf + bufsz;
+ u32 start, end_addr;
u32 i, j, k, page;
u32 val;
switch (reg_sel) {
case RTW89_DBG_SEL_MAC_00:
- seq_puts(m, "Debug selected MAC page 0x00\n");
+ p += scnprintf(p, end - p, "Debug selected MAC page 0x00\n");
start = 0x000;
- end = 0x014;
+ end_addr = 0x014;
break;
case RTW89_DBG_SEL_MAC_30:
- seq_puts(m, "Debug selected MAC page 0x30\n");
+ p += scnprintf(p, end - p, "Debug selected MAC page 0x30\n");
start = 0x030;
- end = 0x033;
+ end_addr = 0x033;
break;
case RTW89_DBG_SEL_MAC_40:
- seq_puts(m, "Debug selected MAC page 0x40\n");
+ p += scnprintf(p, end - p, "Debug selected MAC page 0x40\n");
start = 0x040;
- end = 0x07f;
+ end_addr = 0x07f;
break;
case RTW89_DBG_SEL_MAC_80:
- seq_puts(m, "Debug selected MAC page 0x80\n");
+ p += scnprintf(p, end - p, "Debug selected MAC page 0x80\n");
start = 0x080;
- end = 0x09f;
+ end_addr = 0x09f;
break;
case RTW89_DBG_SEL_MAC_C0:
- seq_puts(m, "Debug selected MAC page 0xc0\n");
+ p += scnprintf(p, end - p, "Debug selected MAC page 0xc0\n");
start = 0x0c0;
- end = 0x0df;
+ end_addr = 0x0df;
break;
case RTW89_DBG_SEL_MAC_E0:
- seq_puts(m, "Debug selected MAC page 0xe0\n");
+ p += scnprintf(p, end - p, "Debug selected MAC page 0xe0\n");
start = 0x0e0;
- end = 0x0ff;
+ end_addr = 0x0ff;
break;
case RTW89_DBG_SEL_BB:
- seq_puts(m, "Debug selected BB register\n");
+ p += scnprintf(p, end - p, "Debug selected BB register\n");
start = 0x100;
- end = 0x17f;
+ end_addr = 0x17f;
break;
case RTW89_DBG_SEL_IQK:
- seq_puts(m, "Debug selected IQK register\n");
+ p += scnprintf(p, end - p, "Debug selected IQK register\n");
start = 0x180;
- end = 0x1bf;
+ end_addr = 0x1bf;
break;
case RTW89_DBG_SEL_RFC:
- seq_puts(m, "Debug selected RFC register\n");
+ p += scnprintf(p, end - p, "Debug selected RFC register\n");
start = 0x1c0;
- end = 0x1ff;
+ end_addr = 0x1ff;
break;
default:
- seq_puts(m, "Selected invalid register page\n");
+ p += scnprintf(p, end - p, "Selected invalid register page\n");
return -EINVAL;
}
- for (i = start; i <= end; i++) {
+ for (i = start; i <= end_addr; i++) {
page = i << 8;
for (j = page; j < page + RTW89_MAC_PAGE_SIZE; j += 16) {
- seq_printf(m, "%08xh : ", 0x18600000 + j);
+ p += scnprintf(p, end - p, "%08xh : ", 0x18600000 + j);
for (k = 0; k < 4; k++) {
val = rtw89_read32(rtwdev, j + (k << 2));
- seq_printf(m, "%08x ", val);
+ p += scnprintf(p, end - p, "%08x ", val);
}
- seq_puts(m, "\n");
+ p += scnprintf(p, end - p, "\n");
}
}
- return 0;
+ return p - buf;
}
static ssize_t
-rtw89_debug_priv_mac_mem_dump_select(struct file *filp,
- const char __user *user_buf,
- size_t count, loff_t *loff)
+rtw89_debug_priv_mac_mem_dump_select(struct rtw89_dev *rtwdev,
+ struct rtw89_debugfs_priv *debugfs_priv,
+ const char *buf, size_t count)
{
- struct seq_file *m = (struct seq_file *)filp->private_data;
- struct rtw89_debugfs_priv *debugfs_priv = m->private;
- struct rtw89_dev *rtwdev = debugfs_priv->rtwdev;
- char buf[32];
- size_t buf_size;
u32 sel, start_addr, len;
int num;
- buf_size = min(count, sizeof(buf) - 1);
- if (copy_from_user(buf, user_buf, buf_size))
- return -EFAULT;
-
- buf[buf_size] = '\0';
num = sscanf(buf, "%x %x %x", &sel, &start_addr, &len);
if (num != 3) {
rtw89_info(rtwdev, "invalid format: <sel> <start> <len>\n");
@@ -1066,15 +1107,16 @@ rtw89_debug_priv_mac_mem_dump_select(struct file *filp,
return count;
}
-static void rtw89_debug_dump_mac_mem(struct seq_file *m,
- struct rtw89_dev *rtwdev,
- u8 sel, u32 start_addr, u32 len)
+static int rtw89_debug_dump_mac_mem(struct rtw89_dev *rtwdev,
+ char *buf, size_t bufsz,
+ u8 sel, u32 start_addr, u32 len)
{
const struct rtw89_mac_gen_def *mac = rtwdev->chip->mac_def;
u32 filter_model_addr = mac->filter_model_addr;
u32 indir_access_addr = mac->indir_access_addr;
u32 base_addr, start_page, residue;
- u32 i, j, p, pages;
+ char *p = buf, *end = buf + bufsz;
+ u32 i, j, pp, pages;
u32 dump_len, remain;
u32 val;
@@ -1085,32 +1127,37 @@ static void rtw89_debug_dump_mac_mem(struct seq_file *m,
base_addr = mac->mem_base_addrs[sel];
base_addr += start_page * MAC_MEM_DUMP_PAGE_SIZE;
- for (p = 0; p < pages; p++) {
+ for (pp = 0; pp < pages; pp++) {
dump_len = min_t(u32, remain, MAC_MEM_DUMP_PAGE_SIZE);
rtw89_write32(rtwdev, filter_model_addr, base_addr);
for (i = indir_access_addr + residue;
i < indir_access_addr + dump_len;) {
- seq_printf(m, "%08xh:", i);
+ p += scnprintf(p, end - p, "%08xh:", i);
for (j = 0;
j < 4 && i < indir_access_addr + dump_len;
j++, i += 4) {
val = rtw89_read32(rtwdev, i);
- seq_printf(m, " %08x", val);
+ p += scnprintf(p, end - p, " %08x", val);
remain -= 4;
}
- seq_puts(m, "\n");
+ p += scnprintf(p, end - p, "\n");
}
base_addr += MAC_MEM_DUMP_PAGE_SIZE;
}
+
+ return p - buf;
}
-static int
-rtw89_debug_priv_mac_mem_dump_get(struct seq_file *m, void *v)
+static ssize_t
+rtw89_debug_priv_mac_mem_dump_get(struct rtw89_dev *rtwdev,
+ struct rtw89_debugfs_priv *debugfs_priv,
+ char *buf, size_t bufsz)
{
- struct rtw89_debugfs_priv *debugfs_priv = m->private;
- struct rtw89_dev *rtwdev = debugfs_priv->rtwdev;
+ char *p = buf, *end = buf + bufsz;
bool grant_read = false;
+ lockdep_assert_wiphy(rtwdev->hw->wiphy);
+
if (debugfs_priv->mac_mem.sel >= RTW89_MAC_MEM_NUM)
return -ENOENT;
@@ -1127,40 +1174,28 @@ rtw89_debug_priv_mac_mem_dump_get(struct seq_file *m, void *v)
}
}
- mutex_lock(&rtwdev->mutex);
rtw89_leave_ps_mode(rtwdev);
if (grant_read)
rtw89_write32_set(rtwdev, R_AX_TCR1, B_AX_TCR_FORCE_READ_TXDFIFO);
- rtw89_debug_dump_mac_mem(m, rtwdev,
- debugfs_priv->mac_mem.sel,
- debugfs_priv->mac_mem.start,
- debugfs_priv->mac_mem.len);
+ p += rtw89_debug_dump_mac_mem(rtwdev, p, end - p,
+ debugfs_priv->mac_mem.sel,
+ debugfs_priv->mac_mem.start,
+ debugfs_priv->mac_mem.len);
if (grant_read)
rtw89_write32_clr(rtwdev, R_AX_TCR1, B_AX_TCR_FORCE_READ_TXDFIFO);
- mutex_unlock(&rtwdev->mutex);
- return 0;
+ return p - buf;
}
static ssize_t
-rtw89_debug_priv_mac_dbg_port_dump_select(struct file *filp,
- const char __user *user_buf,
- size_t count, loff_t *loff)
+rtw89_debug_priv_mac_dbg_port_dump_select(struct rtw89_dev *rtwdev,
+ struct rtw89_debugfs_priv *debugfs_priv,
+ const char *buf, size_t count)
{
- struct seq_file *m = (struct seq_file *)filp->private_data;
- struct rtw89_debugfs_priv *debugfs_priv = m->private;
- struct rtw89_dev *rtwdev = debugfs_priv->rtwdev;
- char buf[32];
- size_t buf_size;
int sel, set;
int num;
bool enable;
- buf_size = min(count, sizeof(buf) - 1);
- if (copy_from_user(buf, user_buf, buf_size))
- return -EFAULT;
-
- buf[buf_size] = '\0';
num = sscanf(buf, "%d %d", &sel, &set);
if (num != 2) {
rtw89_info(rtwdev, "invalid format: <sel> <set>\n");
@@ -1196,13 +1231,13 @@ rtw89_debug_priv_mac_dbg_port_dump_select(struct file *filp,
}
static int rtw89_debug_mac_dump_ss_dbg(struct rtw89_dev *rtwdev,
- struct seq_file *m)
+ char *buf, size_t bufsz)
{
return 0;
}
static int rtw89_debug_mac_dump_dle_dbg(struct rtw89_dev *rtwdev,
- struct seq_file *m)
+ char *buf, size_t bufsz)
{
#define DLE_DFI_DUMP(__type, __target, __sel) \
({ \
@@ -1231,7 +1266,7 @@ static int rtw89_debug_mac_dump_dle_dbg(struct rtw89_dev *rtwdev,
__data; \
})
-#define DLE_DFI_FREE_PAGE_DUMP(__m, __type) \
+#define DLE_DFI_FREE_PAGE_DUMP(__p, __end, __type) \
({ \
u32 __freepg, __pubpg; \
u32 __freepg_head, __freepg_tail, __pubpg_num; \
@@ -1241,24 +1276,25 @@ static int rtw89_debug_mac_dump_dle_dbg(struct rtw89_dev *rtwdev,
__freepg_head = FIELD_GET(B_AX_DLE_FREE_HEADPG, __freepg); \
__freepg_tail = FIELD_GET(B_AX_DLE_FREE_TAILPG, __freepg); \
__pubpg_num = FIELD_GET(B_AX_DLE_PUB_PGNUM, __pubpg); \
- seq_printf(__m, "[%s] freepg head: %d\n", \
- #__type, __freepg_head); \
- seq_printf(__m, "[%s] freepg tail: %d\n", \
- #__type, __freepg_tail); \
- seq_printf(__m, "[%s] pubpg num : %d\n", \
- #__type, __pubpg_num); \
+ __p += scnprintf(__p, __end - __p, "[%s] freepg head: %d\n", \
+ #__type, __freepg_head); \
+ __p += scnprintf(__p, __end - __p, "[%s] freepg tail: %d\n", \
+ #__type, __freepg_tail); \
+ __p += scnprintf(__p, __end - __p, "[%s] pubpg num : %d\n", \
+ #__type, __pubpg_num); \
})
-#define case_QUOTA(__m, __type, __id) \
+#define case_QUOTA(__p, __end, __type, __id) \
case __type##_QTAID_##__id: \
- val32 = DLE_DFI_DUMP(__type, QUOTA, __type##_QTAID_##__id); \
+ val32 = DLE_DFI_DUMP(__type, QUOTA, __type##_QTAID_##__id); \
rsv_pgnum = FIELD_GET(B_AX_DLE_RSV_PGNUM, val32); \
use_pgnum = FIELD_GET(B_AX_DLE_USE_PGNUM, val32); \
- seq_printf(__m, "[%s][%s] rsv_pgnum: %d\n", \
- #__type, #__id, rsv_pgnum); \
- seq_printf(__m, "[%s][%s] use_pgnum: %d\n", \
- #__type, #__id, use_pgnum); \
+ __p += scnprintf(__p, __end - __p, "[%s][%s] rsv_pgnum: %d\n", \
+ #__type, #__id, rsv_pgnum); \
+ __p += scnprintf(__p, __end - __p, "[%s][%s] use_pgnum: %d\n", \
+ #__type, #__id, use_pgnum); \
break
+ char *p = buf, *end = buf + bufsz;
u32 quota_id;
u32 val32;
u16 rsv_pgnum, use_pgnum;
@@ -1266,38 +1302,39 @@ static int rtw89_debug_mac_dump_dle_dbg(struct rtw89_dev *rtwdev,
ret = rtw89_mac_check_mac_en(rtwdev, 0, RTW89_DMAC_SEL);
if (ret) {
- seq_puts(m, "[DLE] : DMAC not enabled\n");
- return ret;
+ p += scnprintf(p, end - p, "[DLE] : DMAC not enabled\n");
+ goto out;
}
- DLE_DFI_FREE_PAGE_DUMP(m, WDE);
- DLE_DFI_FREE_PAGE_DUMP(m, PLE);
+ DLE_DFI_FREE_PAGE_DUMP(p, end, WDE);
+ DLE_DFI_FREE_PAGE_DUMP(p, end, PLE);
for (quota_id = 0; quota_id <= WDE_QTAID_CPUIO; quota_id++) {
switch (quota_id) {
- case_QUOTA(m, WDE, HOST_IF);
- case_QUOTA(m, WDE, WLAN_CPU);
- case_QUOTA(m, WDE, DATA_CPU);
- case_QUOTA(m, WDE, PKTIN);
- case_QUOTA(m, WDE, CPUIO);
+ case_QUOTA(p, end, WDE, HOST_IF);
+ case_QUOTA(p, end, WDE, WLAN_CPU);
+ case_QUOTA(p, end, WDE, DATA_CPU);
+ case_QUOTA(p, end, WDE, PKTIN);
+ case_QUOTA(p, end, WDE, CPUIO);
}
}
for (quota_id = 0; quota_id <= PLE_QTAID_CPUIO; quota_id++) {
switch (quota_id) {
- case_QUOTA(m, PLE, B0_TXPL);
- case_QUOTA(m, PLE, B1_TXPL);
- case_QUOTA(m, PLE, C2H);
- case_QUOTA(m, PLE, H2C);
- case_QUOTA(m, PLE, WLAN_CPU);
- case_QUOTA(m, PLE, MPDU);
- case_QUOTA(m, PLE, CMAC0_RX);
- case_QUOTA(m, PLE, CMAC1_RX);
- case_QUOTA(m, PLE, CMAC1_BBRPT);
- case_QUOTA(m, PLE, WDRLS);
- case_QUOTA(m, PLE, CPUIO);
+ case_QUOTA(p, end, PLE, B0_TXPL);
+ case_QUOTA(p, end, PLE, B1_TXPL);
+ case_QUOTA(p, end, PLE, C2H);
+ case_QUOTA(p, end, PLE, H2C);
+ case_QUOTA(p, end, PLE, WLAN_CPU);
+ case_QUOTA(p, end, PLE, MPDU);
+ case_QUOTA(p, end, PLE, CMAC0_RX);
+ case_QUOTA(p, end, PLE, CMAC1_RX);
+ case_QUOTA(p, end, PLE, CMAC1_BBRPT);
+ case_QUOTA(p, end, PLE, WDRLS);
+ case_QUOTA(p, end, PLE, CPUIO);
}
}
- return 0;
+out:
+ return p - buf;
#undef case_QUOTA
#undef DLE_DFI_DUMP
@@ -1305,73 +1342,88 @@ static int rtw89_debug_mac_dump_dle_dbg(struct rtw89_dev *rtwdev,
}
static int rtw89_debug_mac_dump_dmac_dbg(struct rtw89_dev *rtwdev,
- struct seq_file *m)
+ char *buf, size_t bufsz)
{
const struct rtw89_chip_info *chip = rtwdev->chip;
+ char *p = buf, *end = buf + bufsz;
u32 dmac_err;
int i, ret;
ret = rtw89_mac_check_mac_en(rtwdev, 0, RTW89_DMAC_SEL);
if (ret) {
- seq_puts(m, "[DMAC] : DMAC not enabled\n");
- return ret;
+ p += scnprintf(p, end - p, "[DMAC] : DMAC not enabled\n");
+ goto out;
}
dmac_err = rtw89_read32(rtwdev, R_AX_DMAC_ERR_ISR);
- seq_printf(m, "R_AX_DMAC_ERR_ISR=0x%08x\n", dmac_err);
- seq_printf(m, "R_AX_DMAC_ERR_IMR=0x%08x\n",
- rtw89_read32(rtwdev, R_AX_DMAC_ERR_IMR));
+ p += scnprintf(p, end - p, "R_AX_DMAC_ERR_ISR=0x%08x\n", dmac_err);
+ p += scnprintf(p, end - p, "R_AX_DMAC_ERR_IMR=0x%08x\n",
+ rtw89_read32(rtwdev, R_AX_DMAC_ERR_IMR));
if (dmac_err) {
- seq_printf(m, "R_AX_WDE_ERR_FLAG_CFG=0x%08x\n",
- rtw89_read32(rtwdev, R_AX_WDE_ERR_FLAG_CFG_NUM1));
- seq_printf(m, "R_AX_PLE_ERR_FLAG_CFG=0x%08x\n",
- rtw89_read32(rtwdev, R_AX_PLE_ERR_FLAG_CFG_NUM1));
+ p += scnprintf(p, end - p, "R_AX_WDE_ERR_FLAG_CFG=0x%08x\n",
+ rtw89_read32(rtwdev, R_AX_WDE_ERR_FLAG_CFG_NUM1));
+ p += scnprintf(p, end - p, "R_AX_PLE_ERR_FLAG_CFG=0x%08x\n",
+ rtw89_read32(rtwdev, R_AX_PLE_ERR_FLAG_CFG_NUM1));
if (chip->chip_id == RTL8852C) {
- seq_printf(m, "R_AX_PLE_ERRFLAG_MSG=0x%08x\n",
- rtw89_read32(rtwdev, R_AX_PLE_ERRFLAG_MSG));
- seq_printf(m, "R_AX_WDE_ERRFLAG_MSG=0x%08x\n",
- rtw89_read32(rtwdev, R_AX_WDE_ERRFLAG_MSG));
- seq_printf(m, "R_AX_PLE_DBGERR_LOCKEN=0x%08x\n",
- rtw89_read32(rtwdev, R_AX_PLE_DBGERR_LOCKEN));
- seq_printf(m, "R_AX_PLE_DBGERR_STS=0x%08x\n",
- rtw89_read32(rtwdev, R_AX_PLE_DBGERR_STS));
+ p += scnprintf(p, end - p,
+ "R_AX_PLE_ERRFLAG_MSG=0x%08x\n",
+ rtw89_read32(rtwdev, R_AX_PLE_ERRFLAG_MSG));
+ p += scnprintf(p, end - p,
+ "R_AX_WDE_ERRFLAG_MSG=0x%08x\n",
+ rtw89_read32(rtwdev, R_AX_WDE_ERRFLAG_MSG));
+ p += scnprintf(p, end - p,
+ "R_AX_PLE_DBGERR_LOCKEN=0x%08x\n",
+ rtw89_read32(rtwdev, R_AX_PLE_DBGERR_LOCKEN));
+ p += scnprintf(p, end - p,
+ "R_AX_PLE_DBGERR_STS=0x%08x\n",
+ rtw89_read32(rtwdev, R_AX_PLE_DBGERR_STS));
}
}
if (dmac_err & B_AX_WDRLS_ERR_FLAG) {
- seq_printf(m, "R_AX_WDRLS_ERR_IMR=0x%08x\n",
- rtw89_read32(rtwdev, R_AX_WDRLS_ERR_IMR));
- seq_printf(m, "R_AX_WDRLS_ERR_ISR=0x%08x\n",
- rtw89_read32(rtwdev, R_AX_WDRLS_ERR_ISR));
+ p += scnprintf(p, end - p, "R_AX_WDRLS_ERR_IMR=0x%08x\n",
+ rtw89_read32(rtwdev, R_AX_WDRLS_ERR_IMR));
+ p += scnprintf(p, end - p, "R_AX_WDRLS_ERR_ISR=0x%08x\n",
+ rtw89_read32(rtwdev, R_AX_WDRLS_ERR_ISR));
if (chip->chip_id == RTL8852C)
- seq_printf(m, "R_AX_RPQ_RXBD_IDX=0x%08x\n",
- rtw89_read32(rtwdev, R_AX_RPQ_RXBD_IDX_V1));
+ p += scnprintf(p, end - p,
+ "R_AX_RPQ_RXBD_IDX=0x%08x\n",
+ rtw89_read32(rtwdev, R_AX_RPQ_RXBD_IDX_V1));
else
- seq_printf(m, "R_AX_RPQ_RXBD_IDX=0x%08x\n",
- rtw89_read32(rtwdev, R_AX_RPQ_RXBD_IDX));
+ p += scnprintf(p, end - p,
+ "R_AX_RPQ_RXBD_IDX=0x%08x\n",
+ rtw89_read32(rtwdev, R_AX_RPQ_RXBD_IDX));
}
if (dmac_err & B_AX_WSEC_ERR_FLAG) {
if (chip->chip_id == RTL8852C) {
- seq_printf(m, "R_AX_SEC_ERR_IMR=0x%08x\n",
- rtw89_read32(rtwdev, R_AX_SEC_ERROR_FLAG_IMR));
- seq_printf(m, "R_AX_SEC_ERR_ISR=0x%08x\n",
- rtw89_read32(rtwdev, R_AX_SEC_ERROR_FLAG));
- seq_printf(m, "R_AX_SEC_ENG_CTRL=0x%08x\n",
- rtw89_read32(rtwdev, R_AX_SEC_ENG_CTRL));
- seq_printf(m, "R_AX_SEC_MPDU_PROC=0x%08x\n",
- rtw89_read32(rtwdev, R_AX_SEC_MPDU_PROC));
- seq_printf(m, "R_AX_SEC_CAM_ACCESS=0x%08x\n",
- rtw89_read32(rtwdev, R_AX_SEC_CAM_ACCESS));
- seq_printf(m, "R_AX_SEC_CAM_RDATA=0x%08x\n",
- rtw89_read32(rtwdev, R_AX_SEC_CAM_RDATA));
- seq_printf(m, "R_AX_SEC_DEBUG1=0x%08x\n",
- rtw89_read32(rtwdev, R_AX_SEC_DEBUG1));
- seq_printf(m, "R_AX_SEC_TX_DEBUG=0x%08x\n",
- rtw89_read32(rtwdev, R_AX_SEC_TX_DEBUG));
- seq_printf(m, "R_AX_SEC_RX_DEBUG=0x%08x\n",
- rtw89_read32(rtwdev, R_AX_SEC_RX_DEBUG));
+ p += scnprintf(p, end - p,
+ "R_AX_SEC_ERR_IMR=0x%08x\n",
+ rtw89_read32(rtwdev, R_AX_SEC_ERROR_FLAG_IMR));
+ p += scnprintf(p, end - p,
+ "R_AX_SEC_ERR_ISR=0x%08x\n",
+ rtw89_read32(rtwdev, R_AX_SEC_ERROR_FLAG));
+ p += scnprintf(p, end - p,
+ "R_AX_SEC_ENG_CTRL=0x%08x\n",
+ rtw89_read32(rtwdev, R_AX_SEC_ENG_CTRL));
+ p += scnprintf(p, end - p,
+ "R_AX_SEC_MPDU_PROC=0x%08x\n",
+ rtw89_read32(rtwdev, R_AX_SEC_MPDU_PROC));
+ p += scnprintf(p, end - p,
+ "R_AX_SEC_CAM_ACCESS=0x%08x\n",
+ rtw89_read32(rtwdev, R_AX_SEC_CAM_ACCESS));
+ p += scnprintf(p, end - p,
+ "R_AX_SEC_CAM_RDATA=0x%08x\n",
+ rtw89_read32(rtwdev, R_AX_SEC_CAM_RDATA));
+ p += scnprintf(p, end - p, "R_AX_SEC_DEBUG1=0x%08x\n",
+ rtw89_read32(rtwdev, R_AX_SEC_DEBUG1));
+ p += scnprintf(p, end - p,
+ "R_AX_SEC_TX_DEBUG=0x%08x\n",
+ rtw89_read32(rtwdev, R_AX_SEC_TX_DEBUG));
+ p += scnprintf(p, end - p,
+ "R_AX_SEC_RX_DEBUG=0x%08x\n",
+ rtw89_read32(rtwdev, R_AX_SEC_RX_DEBUG));
rtw89_write32_mask(rtwdev, R_AX_DBG_CTRL,
B_AX_DBG_SEL0, 0x8B);
@@ -1382,187 +1434,229 @@ static int rtw89_debug_mac_dump_dmac_dbg(struct rtw89_dev *rtwdev,
for (i = 0; i < 0x10; i++) {
rtw89_write32_mask(rtwdev, R_AX_SEC_ENG_CTRL,
B_AX_SEC_DBG_PORT_FIELD_MASK, i);
- seq_printf(m, "sel=%x,R_AX_SEC_DEBUG2=0x%08x\n",
- i, rtw89_read32(rtwdev, R_AX_SEC_DEBUG2));
+ p += scnprintf(p, end - p,
+ "sel=%x,R_AX_SEC_DEBUG2=0x%08x\n",
+ i,
+ rtw89_read32(rtwdev, R_AX_SEC_DEBUG2));
}
} else {
- seq_printf(m, "R_AX_SEC_ERR_IMR_ISR=0x%08x\n",
- rtw89_read32(rtwdev, R_AX_SEC_DEBUG));
- seq_printf(m, "R_AX_SEC_ENG_CTRL=0x%08x\n",
- rtw89_read32(rtwdev, R_AX_SEC_ENG_CTRL));
- seq_printf(m, "R_AX_SEC_MPDU_PROC=0x%08x\n",
- rtw89_read32(rtwdev, R_AX_SEC_MPDU_PROC));
- seq_printf(m, "R_AX_SEC_CAM_ACCESS=0x%08x\n",
- rtw89_read32(rtwdev, R_AX_SEC_CAM_ACCESS));
- seq_printf(m, "R_AX_SEC_CAM_RDATA=0x%08x\n",
- rtw89_read32(rtwdev, R_AX_SEC_CAM_RDATA));
- seq_printf(m, "R_AX_SEC_CAM_WDATA=0x%08x\n",
- rtw89_read32(rtwdev, R_AX_SEC_CAM_WDATA));
- seq_printf(m, "R_AX_SEC_TX_DEBUG=0x%08x\n",
- rtw89_read32(rtwdev, R_AX_SEC_TX_DEBUG));
- seq_printf(m, "R_AX_SEC_RX_DEBUG=0x%08x\n",
- rtw89_read32(rtwdev, R_AX_SEC_RX_DEBUG));
- seq_printf(m, "R_AX_SEC_TRX_PKT_CNT=0x%08x\n",
- rtw89_read32(rtwdev, R_AX_SEC_TRX_PKT_CNT));
- seq_printf(m, "R_AX_SEC_TRX_BLK_CNT=0x%08x\n",
- rtw89_read32(rtwdev, R_AX_SEC_TRX_BLK_CNT));
+ p += scnprintf(p, end - p,
+ "R_AX_SEC_ERR_IMR_ISR=0x%08x\n",
+ rtw89_read32(rtwdev, R_AX_SEC_DEBUG));
+ p += scnprintf(p, end - p,
+ "R_AX_SEC_ENG_CTRL=0x%08x\n",
+ rtw89_read32(rtwdev, R_AX_SEC_ENG_CTRL));
+ p += scnprintf(p, end - p,
+ "R_AX_SEC_MPDU_PROC=0x%08x\n",
+ rtw89_read32(rtwdev, R_AX_SEC_MPDU_PROC));
+ p += scnprintf(p, end - p,
+ "R_AX_SEC_CAM_ACCESS=0x%08x\n",
+ rtw89_read32(rtwdev, R_AX_SEC_CAM_ACCESS));
+ p += scnprintf(p, end - p,
+ "R_AX_SEC_CAM_RDATA=0x%08x\n",
+ rtw89_read32(rtwdev, R_AX_SEC_CAM_RDATA));
+ p += scnprintf(p, end - p,
+ "R_AX_SEC_CAM_WDATA=0x%08x\n",
+ rtw89_read32(rtwdev, R_AX_SEC_CAM_WDATA));
+ p += scnprintf(p, end - p,
+ "R_AX_SEC_TX_DEBUG=0x%08x\n",
+ rtw89_read32(rtwdev, R_AX_SEC_TX_DEBUG));
+ p += scnprintf(p, end - p,
+ "R_AX_SEC_RX_DEBUG=0x%08x\n",
+ rtw89_read32(rtwdev, R_AX_SEC_RX_DEBUG));
+ p += scnprintf(p, end - p,
+ "R_AX_SEC_TRX_PKT_CNT=0x%08x\n",
+ rtw89_read32(rtwdev, R_AX_SEC_TRX_PKT_CNT));
+ p += scnprintf(p, end - p,
+ "R_AX_SEC_TRX_BLK_CNT=0x%08x\n",
+ rtw89_read32(rtwdev, R_AX_SEC_TRX_BLK_CNT));
}
}
if (dmac_err & B_AX_MPDU_ERR_FLAG) {
- seq_printf(m, "R_AX_MPDU_TX_ERR_IMR=0x%08x\n",
- rtw89_read32(rtwdev, R_AX_MPDU_TX_ERR_IMR));
- seq_printf(m, "R_AX_MPDU_TX_ERR_ISR=0x%08x\n",
- rtw89_read32(rtwdev, R_AX_MPDU_TX_ERR_ISR));
- seq_printf(m, "R_AX_MPDU_RX_ERR_IMR=0x%08x\n",
- rtw89_read32(rtwdev, R_AX_MPDU_RX_ERR_IMR));
- seq_printf(m, "R_AX_MPDU_RX_ERR_ISR=0x%08x\n",
- rtw89_read32(rtwdev, R_AX_MPDU_RX_ERR_ISR));
+ p += scnprintf(p, end - p, "R_AX_MPDU_TX_ERR_IMR=0x%08x\n",
+ rtw89_read32(rtwdev, R_AX_MPDU_TX_ERR_IMR));
+ p += scnprintf(p, end - p, "R_AX_MPDU_TX_ERR_ISR=0x%08x\n",
+ rtw89_read32(rtwdev, R_AX_MPDU_TX_ERR_ISR));
+ p += scnprintf(p, end - p, "R_AX_MPDU_RX_ERR_IMR=0x%08x\n",
+ rtw89_read32(rtwdev, R_AX_MPDU_RX_ERR_IMR));
+ p += scnprintf(p, end - p, "R_AX_MPDU_RX_ERR_ISR=0x%08x\n",
+ rtw89_read32(rtwdev, R_AX_MPDU_RX_ERR_ISR));
}
if (dmac_err & B_AX_STA_SCHEDULER_ERR_FLAG) {
- seq_printf(m, "R_AX_STA_SCHEDULER_ERR_IMR=0x%08x\n",
- rtw89_read32(rtwdev, R_AX_STA_SCHEDULER_ERR_IMR));
- seq_printf(m, "R_AX_STA_SCHEDULER_ERR_ISR=0x%08x\n",
- rtw89_read32(rtwdev, R_AX_STA_SCHEDULER_ERR_ISR));
+ p += scnprintf(p, end - p,
+ "R_AX_STA_SCHEDULER_ERR_IMR=0x%08x\n",
+ rtw89_read32(rtwdev, R_AX_STA_SCHEDULER_ERR_IMR));
+ p += scnprintf(p, end - p,
+ "R_AX_STA_SCHEDULER_ERR_ISR=0x%08x\n",
+ rtw89_read32(rtwdev, R_AX_STA_SCHEDULER_ERR_ISR));
}
if (dmac_err & B_AX_WDE_DLE_ERR_FLAG) {
- seq_printf(m, "R_AX_WDE_ERR_IMR=0x%08x\n",
- rtw89_read32(rtwdev, R_AX_WDE_ERR_IMR));
- seq_printf(m, "R_AX_WDE_ERR_ISR=0x%08x\n",
- rtw89_read32(rtwdev, R_AX_WDE_ERR_ISR));
- seq_printf(m, "R_AX_PLE_ERR_IMR=0x%08x\n",
- rtw89_read32(rtwdev, R_AX_PLE_ERR_IMR));
- seq_printf(m, "R_AX_PLE_ERR_FLAG_ISR=0x%08x\n",
- rtw89_read32(rtwdev, R_AX_PLE_ERR_FLAG_ISR));
+ p += scnprintf(p, end - p, "R_AX_WDE_ERR_IMR=0x%08x\n",
+ rtw89_read32(rtwdev, R_AX_WDE_ERR_IMR));
+ p += scnprintf(p, end - p, "R_AX_WDE_ERR_ISR=0x%08x\n",
+ rtw89_read32(rtwdev, R_AX_WDE_ERR_ISR));
+ p += scnprintf(p, end - p, "R_AX_PLE_ERR_IMR=0x%08x\n",
+ rtw89_read32(rtwdev, R_AX_PLE_ERR_IMR));
+ p += scnprintf(p, end - p, "R_AX_PLE_ERR_FLAG_ISR=0x%08x\n",
+ rtw89_read32(rtwdev, R_AX_PLE_ERR_FLAG_ISR));
}
if (dmac_err & B_AX_TXPKTCTRL_ERR_FLAG) {
if (chip->chip_id == RTL8852C) {
- seq_printf(m, "R_AX_TXPKTCTL_B0_ERRFLAG_IMR=0x%08x\n",
- rtw89_read32(rtwdev, R_AX_TXPKTCTL_B0_ERRFLAG_IMR));
- seq_printf(m, "R_AX_TXPKTCTL_B0_ERRFLAG_ISR=0x%08x\n",
- rtw89_read32(rtwdev, R_AX_TXPKTCTL_B0_ERRFLAG_ISR));
- seq_printf(m, "R_AX_TXPKTCTL_B1_ERRFLAG_IMR=0x%08x\n",
- rtw89_read32(rtwdev, R_AX_TXPKTCTL_B1_ERRFLAG_IMR));
- seq_printf(m, "R_AX_TXPKTCTL_B1_ERRFLAG_ISR=0x%08x\n",
- rtw89_read32(rtwdev, R_AX_TXPKTCTL_B1_ERRFLAG_ISR));
+ p += scnprintf(p, end - p,
+ "R_AX_TXPKTCTL_B0_ERRFLAG_IMR=0x%08x\n",
+ rtw89_read32(rtwdev, R_AX_TXPKTCTL_B0_ERRFLAG_IMR));
+ p += scnprintf(p, end - p,
+ "R_AX_TXPKTCTL_B0_ERRFLAG_ISR=0x%08x\n",
+ rtw89_read32(rtwdev, R_AX_TXPKTCTL_B0_ERRFLAG_ISR));
+ p += scnprintf(p, end - p,
+ "R_AX_TXPKTCTL_B1_ERRFLAG_IMR=0x%08x\n",
+ rtw89_read32(rtwdev, R_AX_TXPKTCTL_B1_ERRFLAG_IMR));
+ p += scnprintf(p, end - p,
+ "R_AX_TXPKTCTL_B1_ERRFLAG_ISR=0x%08x\n",
+ rtw89_read32(rtwdev, R_AX_TXPKTCTL_B1_ERRFLAG_ISR));
} else {
- seq_printf(m, "R_AX_TXPKTCTL_ERR_IMR_ISR=0x%08x\n",
- rtw89_read32(rtwdev, R_AX_TXPKTCTL_ERR_IMR_ISR));
- seq_printf(m, "R_AX_TXPKTCTL_ERR_IMR_ISR_B1=0x%08x\n",
- rtw89_read32(rtwdev, R_AX_TXPKTCTL_ERR_IMR_ISR_B1));
+ p += scnprintf(p, end - p,
+ "R_AX_TXPKTCTL_ERR_IMR_ISR=0x%08x\n",
+ rtw89_read32(rtwdev, R_AX_TXPKTCTL_ERR_IMR_ISR));
+ p += scnprintf(p, end - p,
+ "R_AX_TXPKTCTL_ERR_IMR_ISR_B1=0x%08x\n",
+ rtw89_read32(rtwdev, R_AX_TXPKTCTL_ERR_IMR_ISR_B1));
}
}
if (dmac_err & B_AX_PLE_DLE_ERR_FLAG) {
- seq_printf(m, "R_AX_WDE_ERR_IMR=0x%08x\n",
- rtw89_read32(rtwdev, R_AX_WDE_ERR_IMR));
- seq_printf(m, "R_AX_WDE_ERR_ISR=0x%08x\n",
- rtw89_read32(rtwdev, R_AX_WDE_ERR_ISR));
- seq_printf(m, "R_AX_PLE_ERR_IMR=0x%08x\n",
- rtw89_read32(rtwdev, R_AX_PLE_ERR_IMR));
- seq_printf(m, "R_AX_PLE_ERR_FLAG_ISR=0x%08x\n",
- rtw89_read32(rtwdev, R_AX_PLE_ERR_FLAG_ISR));
- seq_printf(m, "R_AX_WD_CPUQ_OP_0=0x%08x\n",
- rtw89_read32(rtwdev, R_AX_WD_CPUQ_OP_0));
- seq_printf(m, "R_AX_WD_CPUQ_OP_1=0x%08x\n",
- rtw89_read32(rtwdev, R_AX_WD_CPUQ_OP_1));
- seq_printf(m, "R_AX_WD_CPUQ_OP_2=0x%08x\n",
- rtw89_read32(rtwdev, R_AX_WD_CPUQ_OP_2));
- seq_printf(m, "R_AX_WD_CPUQ_OP_STATUS=0x%08x\n",
- rtw89_read32(rtwdev, R_AX_WD_CPUQ_OP_STATUS));
- seq_printf(m, "R_AX_PL_CPUQ_OP_0=0x%08x\n",
- rtw89_read32(rtwdev, R_AX_PL_CPUQ_OP_0));
- seq_printf(m, "R_AX_PL_CPUQ_OP_1=0x%08x\n",
- rtw89_read32(rtwdev, R_AX_PL_CPUQ_OP_1));
- seq_printf(m, "R_AX_PL_CPUQ_OP_2=0x%08x\n",
- rtw89_read32(rtwdev, R_AX_PL_CPUQ_OP_2));
- seq_printf(m, "R_AX_PL_CPUQ_OP_STATUS=0x%08x\n",
- rtw89_read32(rtwdev, R_AX_PL_CPUQ_OP_STATUS));
+ p += scnprintf(p, end - p, "R_AX_WDE_ERR_IMR=0x%08x\n",
+ rtw89_read32(rtwdev, R_AX_WDE_ERR_IMR));
+ p += scnprintf(p, end - p, "R_AX_WDE_ERR_ISR=0x%08x\n",
+ rtw89_read32(rtwdev, R_AX_WDE_ERR_ISR));
+ p += scnprintf(p, end - p, "R_AX_PLE_ERR_IMR=0x%08x\n",
+ rtw89_read32(rtwdev, R_AX_PLE_ERR_IMR));
+ p += scnprintf(p, end - p, "R_AX_PLE_ERR_FLAG_ISR=0x%08x\n",
+ rtw89_read32(rtwdev, R_AX_PLE_ERR_FLAG_ISR));
+ p += scnprintf(p, end - p, "R_AX_WD_CPUQ_OP_0=0x%08x\n",
+ rtw89_read32(rtwdev, R_AX_WD_CPUQ_OP_0));
+ p += scnprintf(p, end - p, "R_AX_WD_CPUQ_OP_1=0x%08x\n",
+ rtw89_read32(rtwdev, R_AX_WD_CPUQ_OP_1));
+ p += scnprintf(p, end - p, "R_AX_WD_CPUQ_OP_2=0x%08x\n",
+ rtw89_read32(rtwdev, R_AX_WD_CPUQ_OP_2));
+ p += scnprintf(p, end - p, "R_AX_WD_CPUQ_OP_STATUS=0x%08x\n",
+ rtw89_read32(rtwdev, R_AX_WD_CPUQ_OP_STATUS));
+ p += scnprintf(p, end - p, "R_AX_PL_CPUQ_OP_0=0x%08x\n",
+ rtw89_read32(rtwdev, R_AX_PL_CPUQ_OP_0));
+ p += scnprintf(p, end - p, "R_AX_PL_CPUQ_OP_1=0x%08x\n",
+ rtw89_read32(rtwdev, R_AX_PL_CPUQ_OP_1));
+ p += scnprintf(p, end - p, "R_AX_PL_CPUQ_OP_2=0x%08x\n",
+ rtw89_read32(rtwdev, R_AX_PL_CPUQ_OP_2));
+ p += scnprintf(p, end - p, "R_AX_PL_CPUQ_OP_STATUS=0x%08x\n",
+ rtw89_read32(rtwdev, R_AX_PL_CPUQ_OP_STATUS));
if (chip->chip_id == RTL8852C) {
- seq_printf(m, "R_AX_RX_CTRL0=0x%08x\n",
- rtw89_read32(rtwdev, R_AX_RX_CTRL0));
- seq_printf(m, "R_AX_RX_CTRL1=0x%08x\n",
- rtw89_read32(rtwdev, R_AX_RX_CTRL1));
- seq_printf(m, "R_AX_RX_CTRL2=0x%08x\n",
- rtw89_read32(rtwdev, R_AX_RX_CTRL2));
+ p += scnprintf(p, end - p, "R_AX_RX_CTRL0=0x%08x\n",
+ rtw89_read32(rtwdev, R_AX_RX_CTRL0));
+ p += scnprintf(p, end - p, "R_AX_RX_CTRL1=0x%08x\n",
+ rtw89_read32(rtwdev, R_AX_RX_CTRL1));
+ p += scnprintf(p, end - p, "R_AX_RX_CTRL2=0x%08x\n",
+ rtw89_read32(rtwdev, R_AX_RX_CTRL2));
} else {
- seq_printf(m, "R_AX_RXDMA_PKT_INFO_0=0x%08x\n",
- rtw89_read32(rtwdev, R_AX_RXDMA_PKT_INFO_0));
- seq_printf(m, "R_AX_RXDMA_PKT_INFO_1=0x%08x\n",
- rtw89_read32(rtwdev, R_AX_RXDMA_PKT_INFO_1));
- seq_printf(m, "R_AX_RXDMA_PKT_INFO_2=0x%08x\n",
- rtw89_read32(rtwdev, R_AX_RXDMA_PKT_INFO_2));
+ p += scnprintf(p, end - p,
+ "R_AX_RXDMA_PKT_INFO_0=0x%08x\n",
+ rtw89_read32(rtwdev, R_AX_RXDMA_PKT_INFO_0));
+ p += scnprintf(p, end - p,
+ "R_AX_RXDMA_PKT_INFO_1=0x%08x\n",
+ rtw89_read32(rtwdev, R_AX_RXDMA_PKT_INFO_1));
+ p += scnprintf(p, end - p,
+ "R_AX_RXDMA_PKT_INFO_2=0x%08x\n",
+ rtw89_read32(rtwdev, R_AX_RXDMA_PKT_INFO_2));
}
}
if (dmac_err & B_AX_PKTIN_ERR_FLAG) {
- seq_printf(m, "R_AX_PKTIN_ERR_IMR=0x%08x\n",
- rtw89_read32(rtwdev, R_AX_PKTIN_ERR_IMR));
- seq_printf(m, "R_AX_PKTIN_ERR_ISR=0x%08x\n",
- rtw89_read32(rtwdev, R_AX_PKTIN_ERR_ISR));
+ p += scnprintf(p, end - p, "R_AX_PKTIN_ERR_IMR=0x%08x\n",
+ rtw89_read32(rtwdev, R_AX_PKTIN_ERR_IMR));
+ p += scnprintf(p, end - p, "R_AX_PKTIN_ERR_ISR=0x%08x\n",
+ rtw89_read32(rtwdev, R_AX_PKTIN_ERR_ISR));
}
if (dmac_err & B_AX_DISPATCH_ERR_FLAG) {
- seq_printf(m, "R_AX_HOST_DISPATCHER_ERR_IMR=0x%08x\n",
- rtw89_read32(rtwdev, R_AX_HOST_DISPATCHER_ERR_IMR));
- seq_printf(m, "R_AX_HOST_DISPATCHER_ERR_ISR=0x%08x\n",
- rtw89_read32(rtwdev, R_AX_HOST_DISPATCHER_ERR_ISR));
- seq_printf(m, "R_AX_CPU_DISPATCHER_ERR_IMR=0x%08x\n",
- rtw89_read32(rtwdev, R_AX_CPU_DISPATCHER_ERR_IMR));
- seq_printf(m, "R_AX_CPU_DISPATCHER_ERR_ISR=0x%08x\n",
- rtw89_read32(rtwdev, R_AX_CPU_DISPATCHER_ERR_ISR));
- seq_printf(m, "R_AX_OTHER_DISPATCHER_ERR_IMR=0x%08x\n",
- rtw89_read32(rtwdev, R_AX_OTHER_DISPATCHER_ERR_IMR));
- seq_printf(m, "R_AX_OTHER_DISPATCHER_ERR_ISR=0x%08x\n",
- rtw89_read32(rtwdev, R_AX_OTHER_DISPATCHER_ERR_ISR));
+ p += scnprintf(p, end - p,
+ "R_AX_HOST_DISPATCHER_ERR_IMR=0x%08x\n",
+ rtw89_read32(rtwdev, R_AX_HOST_DISPATCHER_ERR_IMR));
+ p += scnprintf(p, end - p,
+ "R_AX_HOST_DISPATCHER_ERR_ISR=0x%08x\n",
+ rtw89_read32(rtwdev, R_AX_HOST_DISPATCHER_ERR_ISR));
+ p += scnprintf(p, end - p,
+ "R_AX_CPU_DISPATCHER_ERR_IMR=0x%08x\n",
+ rtw89_read32(rtwdev, R_AX_CPU_DISPATCHER_ERR_IMR));
+ p += scnprintf(p, end - p,
+ "R_AX_CPU_DISPATCHER_ERR_ISR=0x%08x\n",
+ rtw89_read32(rtwdev, R_AX_CPU_DISPATCHER_ERR_ISR));
+ p += scnprintf(p, end - p,
+ "R_AX_OTHER_DISPATCHER_ERR_IMR=0x%08x\n",
+ rtw89_read32(rtwdev, R_AX_OTHER_DISPATCHER_ERR_IMR));
+ p += scnprintf(p, end - p,
+ "R_AX_OTHER_DISPATCHER_ERR_ISR=0x%08x\n",
+ rtw89_read32(rtwdev, R_AX_OTHER_DISPATCHER_ERR_ISR));
}
if (dmac_err & B_AX_BBRPT_ERR_FLAG) {
if (chip->chip_id == RTL8852C) {
- seq_printf(m, "R_AX_BBRPT_COM_ERR_IMR=0x%08x\n",
- rtw89_read32(rtwdev, R_AX_BBRPT_COM_ERR_IMR));
- seq_printf(m, "R_AX_BBRPT_COM_ERR_ISR=0x%08x\n",
- rtw89_read32(rtwdev, R_AX_BBRPT_COM_ERR_ISR));
- seq_printf(m, "R_AX_BBRPT_CHINFO_ERR_ISR=0x%08x\n",
- rtw89_read32(rtwdev, R_AX_BBRPT_CHINFO_ERR_ISR));
- seq_printf(m, "R_AX_BBRPT_CHINFO_ERR_IMR=0x%08x\n",
- rtw89_read32(rtwdev, R_AX_BBRPT_CHINFO_ERR_IMR));
- seq_printf(m, "R_AX_BBRPT_DFS_ERR_IMR=0x%08x\n",
- rtw89_read32(rtwdev, R_AX_BBRPT_DFS_ERR_IMR));
- seq_printf(m, "R_AX_BBRPT_DFS_ERR_ISR=0x%08x\n",
- rtw89_read32(rtwdev, R_AX_BBRPT_DFS_ERR_ISR));
+ p += scnprintf(p, end - p,
+ "R_AX_BBRPT_COM_ERR_IMR=0x%08x\n",
+ rtw89_read32(rtwdev, R_AX_BBRPT_COM_ERR_IMR));
+ p += scnprintf(p, end - p,
+ "R_AX_BBRPT_COM_ERR_ISR=0x%08x\n",
+ rtw89_read32(rtwdev, R_AX_BBRPT_COM_ERR_ISR));
+ p += scnprintf(p, end - p,
+ "R_AX_BBRPT_CHINFO_ERR_ISR=0x%08x\n",
+ rtw89_read32(rtwdev, R_AX_BBRPT_CHINFO_ERR_ISR));
+ p += scnprintf(p, end - p,
+ "R_AX_BBRPT_CHINFO_ERR_IMR=0x%08x\n",
+ rtw89_read32(rtwdev, R_AX_BBRPT_CHINFO_ERR_IMR));
+ p += scnprintf(p, end - p,
+ "R_AX_BBRPT_DFS_ERR_IMR=0x%08x\n",
+ rtw89_read32(rtwdev, R_AX_BBRPT_DFS_ERR_IMR));
+ p += scnprintf(p, end - p,
+ "R_AX_BBRPT_DFS_ERR_ISR=0x%08x\n",
+ rtw89_read32(rtwdev, R_AX_BBRPT_DFS_ERR_ISR));
} else {
- seq_printf(m, "R_AX_BBRPT_COM_ERR_IMR_ISR=0x%08x\n",
- rtw89_read32(rtwdev, R_AX_BBRPT_COM_ERR_IMR_ISR));
- seq_printf(m, "R_AX_BBRPT_CHINFO_ERR_ISR=0x%08x\n",
- rtw89_read32(rtwdev, R_AX_BBRPT_CHINFO_ERR_ISR));
- seq_printf(m, "R_AX_BBRPT_CHINFO_ERR_IMR=0x%08x\n",
- rtw89_read32(rtwdev, R_AX_BBRPT_CHINFO_ERR_IMR));
- seq_printf(m, "R_AX_BBRPT_DFS_ERR_IMR=0x%08x\n",
- rtw89_read32(rtwdev, R_AX_BBRPT_DFS_ERR_IMR));
- seq_printf(m, "R_AX_BBRPT_DFS_ERR_ISR=0x%08x\n",
- rtw89_read32(rtwdev, R_AX_BBRPT_DFS_ERR_ISR));
+ p += scnprintf(p, end - p,
+ "R_AX_BBRPT_COM_ERR_IMR_ISR=0x%08x\n",
+ rtw89_read32(rtwdev, R_AX_BBRPT_COM_ERR_IMR_ISR));
+ p += scnprintf(p, end - p,
+ "R_AX_BBRPT_CHINFO_ERR_ISR=0x%08x\n",
+ rtw89_read32(rtwdev, R_AX_BBRPT_CHINFO_ERR_ISR));
+ p += scnprintf(p, end - p,
+ "R_AX_BBRPT_CHINFO_ERR_IMR=0x%08x\n",
+ rtw89_read32(rtwdev, R_AX_BBRPT_CHINFO_ERR_IMR));
+ p += scnprintf(p, end - p,
+ "R_AX_BBRPT_DFS_ERR_IMR=0x%08x\n",
+ rtw89_read32(rtwdev, R_AX_BBRPT_DFS_ERR_IMR));
+ p += scnprintf(p, end - p,
+ "R_AX_BBRPT_DFS_ERR_ISR=0x%08x\n",
+ rtw89_read32(rtwdev, R_AX_BBRPT_DFS_ERR_ISR));
}
}
if (dmac_err & B_AX_HAXIDMA_ERR_FLAG && chip->chip_id == RTL8852C) {
- seq_printf(m, "R_AX_HAXIDMA_ERR_IMR=0x%08x\n",
- rtw89_read32(rtwdev, R_AX_HAXI_IDCT_MSK));
- seq_printf(m, "R_AX_HAXIDMA_ERR_ISR=0x%08x\n",
- rtw89_read32(rtwdev, R_AX_HAXI_IDCT));
+ p += scnprintf(p, end - p, "R_AX_HAXIDMA_ERR_IMR=0x%08x\n",
+ rtw89_read32(rtwdev, R_AX_HAXI_IDCT_MSK));
+ p += scnprintf(p, end - p, "R_AX_HAXIDMA_ERR_ISR=0x%08x\n",
+ rtw89_read32(rtwdev, R_AX_HAXI_IDCT));
}
- return 0;
+out:
+ return p - buf;
}
static int rtw89_debug_mac_dump_cmac_err(struct rtw89_dev *rtwdev,
- struct seq_file *m,
+ char *buf, size_t bufsz,
enum rtw89_mac_idx band)
{
const struct rtw89_chip_info *chip = rtwdev->chip;
+ char *p = buf, *end = buf + bufsz;
u32 offset = 0;
u32 cmac_err;
int ret;
@@ -1570,96 +1664,127 @@ static int rtw89_debug_mac_dump_cmac_err(struct rtw89_dev *rtwdev,
ret = rtw89_mac_check_mac_en(rtwdev, band, RTW89_CMAC_SEL);
if (ret) {
if (band)
- seq_puts(m, "[CMAC] : CMAC1 not enabled\n");
+ p += scnprintf(p, end - p,
+ "[CMAC] : CMAC1 not enabled\n");
else
- seq_puts(m, "[CMAC] : CMAC0 not enabled\n");
- return ret;
+ p += scnprintf(p, end - p,
+ "[CMAC] : CMAC0 not enabled\n");
+ goto out;
}
if (band)
offset = RTW89_MAC_AX_BAND_REG_OFFSET;
cmac_err = rtw89_read32(rtwdev, R_AX_CMAC_ERR_ISR + offset);
- seq_printf(m, "R_AX_CMAC_ERR_ISR [%d]=0x%08x\n", band,
- rtw89_read32(rtwdev, R_AX_CMAC_ERR_ISR + offset));
- seq_printf(m, "R_AX_CMAC_FUNC_EN [%d]=0x%08x\n", band,
- rtw89_read32(rtwdev, R_AX_CMAC_FUNC_EN + offset));
- seq_printf(m, "R_AX_CK_EN [%d]=0x%08x\n", band,
- rtw89_read32(rtwdev, R_AX_CK_EN + offset));
+ p += scnprintf(p, end - p, "R_AX_CMAC_ERR_ISR [%d]=0x%08x\n", band,
+ rtw89_read32(rtwdev, R_AX_CMAC_ERR_ISR + offset));
+ p += scnprintf(p, end - p, "R_AX_CMAC_FUNC_EN [%d]=0x%08x\n", band,
+ rtw89_read32(rtwdev, R_AX_CMAC_FUNC_EN + offset));
+ p += scnprintf(p, end - p, "R_AX_CK_EN [%d]=0x%08x\n", band,
+ rtw89_read32(rtwdev, R_AX_CK_EN + offset));
if (cmac_err & B_AX_SCHEDULE_TOP_ERR_IND) {
- seq_printf(m, "R_AX_SCHEDULE_ERR_IMR [%d]=0x%08x\n", band,
- rtw89_read32(rtwdev, R_AX_SCHEDULE_ERR_IMR + offset));
- seq_printf(m, "R_AX_SCHEDULE_ERR_ISR [%d]=0x%08x\n", band,
- rtw89_read32(rtwdev, R_AX_SCHEDULE_ERR_ISR + offset));
+ p += scnprintf(p, end - p,
+ "R_AX_SCHEDULE_ERR_IMR [%d]=0x%08x\n", band,
+ rtw89_read32(rtwdev, R_AX_SCHEDULE_ERR_IMR + offset));
+ p += scnprintf(p, end - p,
+ "R_AX_SCHEDULE_ERR_ISR [%d]=0x%08x\n", band,
+ rtw89_read32(rtwdev, R_AX_SCHEDULE_ERR_ISR + offset));
}
if (cmac_err & B_AX_PTCL_TOP_ERR_IND) {
- seq_printf(m, "R_AX_PTCL_IMR0 [%d]=0x%08x\n", band,
- rtw89_read32(rtwdev, R_AX_PTCL_IMR0 + offset));
- seq_printf(m, "R_AX_PTCL_ISR0 [%d]=0x%08x\n", band,
- rtw89_read32(rtwdev, R_AX_PTCL_ISR0 + offset));
+ p += scnprintf(p, end - p, "R_AX_PTCL_IMR0 [%d]=0x%08x\n",
+ band,
+ rtw89_read32(rtwdev, R_AX_PTCL_IMR0 + offset));
+ p += scnprintf(p, end - p, "R_AX_PTCL_ISR0 [%d]=0x%08x\n",
+ band,
+ rtw89_read32(rtwdev, R_AX_PTCL_ISR0 + offset));
}
if (cmac_err & B_AX_DMA_TOP_ERR_IND) {
if (chip->chip_id == RTL8852C) {
- seq_printf(m, "R_AX_RX_ERR_FLAG [%d]=0x%08x\n", band,
- rtw89_read32(rtwdev, R_AX_RX_ERR_FLAG + offset));
- seq_printf(m, "R_AX_RX_ERR_FLAG_IMR [%d]=0x%08x\n", band,
- rtw89_read32(rtwdev, R_AX_RX_ERR_FLAG_IMR + offset));
+ p += scnprintf(p, end - p,
+ "R_AX_RX_ERR_FLAG [%d]=0x%08x\n", band,
+ rtw89_read32(rtwdev, R_AX_RX_ERR_FLAG + offset));
+ p += scnprintf(p, end - p,
+ "R_AX_RX_ERR_FLAG_IMR [%d]=0x%08x\n",
+ band,
+ rtw89_read32(rtwdev, R_AX_RX_ERR_FLAG_IMR + offset));
} else {
- seq_printf(m, "R_AX_DLE_CTRL [%d]=0x%08x\n", band,
- rtw89_read32(rtwdev, R_AX_DLE_CTRL + offset));
+ p += scnprintf(p, end - p,
+ "R_AX_DLE_CTRL [%d]=0x%08x\n", band,
+ rtw89_read32(rtwdev, R_AX_DLE_CTRL + offset));
}
}
if (cmac_err & B_AX_DMA_TOP_ERR_IND || cmac_err & B_AX_WMAC_RX_ERR_IND) {
if (chip->chip_id == RTL8852C) {
- seq_printf(m, "R_AX_PHYINFO_ERR_ISR [%d]=0x%08x\n", band,
- rtw89_read32(rtwdev, R_AX_PHYINFO_ERR_ISR + offset));
- seq_printf(m, "R_AX_PHYINFO_ERR_IMR [%d]=0x%08x\n", band,
- rtw89_read32(rtwdev, R_AX_PHYINFO_ERR_IMR + offset));
+ p += scnprintf(p, end - p,
+ "R_AX_PHYINFO_ERR_ISR [%d]=0x%08x\n",
+ band,
+ rtw89_read32(rtwdev, R_AX_PHYINFO_ERR_ISR + offset));
+ p += scnprintf(p, end - p,
+ "R_AX_PHYINFO_ERR_IMR [%d]=0x%08x\n",
+ band,
+ rtw89_read32(rtwdev, R_AX_PHYINFO_ERR_IMR + offset));
} else {
- seq_printf(m, "R_AX_PHYINFO_ERR_IMR [%d]=0x%08x\n", band,
- rtw89_read32(rtwdev, R_AX_PHYINFO_ERR_IMR + offset));
+ p += scnprintf(p, end - p,
+ "R_AX_PHYINFO_ERR_IMR [%d]=0x%08x\n",
+ band,
+ rtw89_read32(rtwdev, R_AX_PHYINFO_ERR_IMR + offset));
}
}
if (cmac_err & B_AX_TXPWR_CTRL_ERR_IND) {
- seq_printf(m, "R_AX_TXPWR_IMR [%d]=0x%08x\n", band,
- rtw89_read32(rtwdev, R_AX_TXPWR_IMR + offset));
- seq_printf(m, "R_AX_TXPWR_ISR [%d]=0x%08x\n", band,
- rtw89_read32(rtwdev, R_AX_TXPWR_ISR + offset));
+ p += scnprintf(p, end - p, "R_AX_TXPWR_IMR [%d]=0x%08x\n",
+ band,
+ rtw89_read32(rtwdev, R_AX_TXPWR_IMR + offset));
+ p += scnprintf(p, end - p, "R_AX_TXPWR_ISR [%d]=0x%08x\n",
+ band,
+ rtw89_read32(rtwdev, R_AX_TXPWR_ISR + offset));
}
if (cmac_err & B_AX_WMAC_TX_ERR_IND) {
if (chip->chip_id == RTL8852C) {
- seq_printf(m, "R_AX_TRXPTCL_ERROR_INDICA [%d]=0x%08x\n", band,
- rtw89_read32(rtwdev, R_AX_TRXPTCL_ERROR_INDICA + offset));
- seq_printf(m, "R_AX_TRXPTCL_ERROR_INDICA_MASK [%d]=0x%08x\n", band,
- rtw89_read32(rtwdev, R_AX_TRXPTCL_ERROR_INDICA_MASK + offset));
+ p += scnprintf(p, end - p,
+ "R_AX_TRXPTCL_ERROR_INDICA [%d]=0x%08x\n",
+ band,
+ rtw89_read32(rtwdev,
+ R_AX_TRXPTCL_ERROR_INDICA + offset));
+ p += scnprintf(p, end - p,
+ "R_AX_TRXPTCL_ERROR_INDICA_MASK [%d]=0x%08x\n",
+ band,
+ rtw89_read32(rtwdev,
+ R_AX_TRXPTCL_ERROR_INDICA_MASK + offset));
} else {
- seq_printf(m, "R_AX_TMAC_ERR_IMR_ISR [%d]=0x%08x\n", band,
- rtw89_read32(rtwdev, R_AX_TMAC_ERR_IMR_ISR + offset));
+ p += scnprintf(p, end - p,
+ "R_AX_TMAC_ERR_IMR_ISR [%d]=0x%08x\n",
+ band,
+ rtw89_read32(rtwdev,
+ R_AX_TMAC_ERR_IMR_ISR + offset));
}
- seq_printf(m, "R_AX_DBGSEL_TRXPTCL [%d]=0x%08x\n", band,
- rtw89_read32(rtwdev, R_AX_DBGSEL_TRXPTCL + offset));
+ p += scnprintf(p, end - p,
+ "R_AX_DBGSEL_TRXPTCL [%d]=0x%08x\n", band,
+ rtw89_read32(rtwdev, R_AX_DBGSEL_TRXPTCL + offset));
}
- seq_printf(m, "R_AX_CMAC_ERR_IMR [%d]=0x%08x\n", band,
- rtw89_read32(rtwdev, R_AX_CMAC_ERR_IMR + offset));
+ p += scnprintf(p, end - p, "R_AX_CMAC_ERR_IMR [%d]=0x%08x\n", band,
+ rtw89_read32(rtwdev, R_AX_CMAC_ERR_IMR + offset));
- return 0;
+out:
+ return p - buf;
}
static int rtw89_debug_mac_dump_cmac_dbg(struct rtw89_dev *rtwdev,
- struct seq_file *m)
+ char *buf, size_t bufsz)
{
- rtw89_debug_mac_dump_cmac_err(rtwdev, m, RTW89_MAC_0);
+ char *p = buf, *end = buf + bufsz;
+
+ p += rtw89_debug_mac_dump_cmac_err(rtwdev, p, end - p, RTW89_MAC_0);
if (rtwdev->dbcc_en)
- rtw89_debug_mac_dump_cmac_err(rtwdev, m, RTW89_MAC_1);
+ p += rtw89_debug_mac_dump_cmac_err(rtwdev, p, end - p, RTW89_MAC_1);
- return 0;
+ return p - buf;
}
static const struct rtw89_mac_dbg_port_info dbg_port_ptcl_c0 = {
@@ -2465,11 +2590,12 @@ static const struct rtw89_mac_dbg_port_info dbg_port_pcie_misc2 = {
.rd_msk = B_AX_DEBUG_ST_MASK
};
-static const struct rtw89_mac_dbg_port_info *
-rtw89_debug_mac_dbg_port_sel(struct seq_file *m,
- struct rtw89_dev *rtwdev, u32 sel)
+static int
+rtw89_debug_mac_dbg_port_sel(struct rtw89_dev *rtwdev, char *buf, size_t bufsz,
+ u32 sel, const struct rtw89_mac_dbg_port_info **ppinfo)
{
- const struct rtw89_mac_dbg_port_info *info;
+ const struct rtw89_mac_dbg_port_info *info = NULL;
+ char *p = buf, *end = buf + bufsz;
u32 index;
u32 val32;
u16 val16;
@@ -2481,28 +2607,28 @@ rtw89_debug_mac_dbg_port_sel(struct seq_file *m,
val16 = rtw89_read16(rtwdev, R_AX_PTCL_DBG);
val16 |= B_AX_PTCL_DBG_EN;
rtw89_write16(rtwdev, R_AX_PTCL_DBG, val16);
- seq_puts(m, "Enable PTCL C0 dbgport.\n");
+ p += scnprintf(p, end - p, "Enable PTCL C0 dbgport.\n");
break;
case RTW89_DBG_PORT_SEL_PTCL_C1:
info = &dbg_port_ptcl_c1;
val16 = rtw89_read16(rtwdev, R_AX_PTCL_DBG_C1);
val16 |= B_AX_PTCL_DBG_EN;
rtw89_write16(rtwdev, R_AX_PTCL_DBG_C1, val16);
- seq_puts(m, "Enable PTCL C1 dbgport.\n");
+ p += scnprintf(p, end - p, "Enable PTCL C1 dbgport.\n");
break;
case RTW89_DBG_PORT_SEL_SCH_C0:
info = &dbg_port_sch_c0;
val32 = rtw89_read32(rtwdev, R_AX_SCH_DBG_SEL);
val32 |= B_AX_SCH_DBG_EN;
rtw89_write32(rtwdev, R_AX_SCH_DBG_SEL, val32);
- seq_puts(m, "Enable SCH C0 dbgport.\n");
+ p += scnprintf(p, end - p, "Enable SCH C0 dbgport.\n");
break;
case RTW89_DBG_PORT_SEL_SCH_C1:
info = &dbg_port_sch_c1;
val32 = rtw89_read32(rtwdev, R_AX_SCH_DBG_SEL_C1);
val32 |= B_AX_SCH_DBG_EN;
rtw89_write32(rtwdev, R_AX_SCH_DBG_SEL_C1, val32);
- seq_puts(m, "Enable SCH C1 dbgport.\n");
+ p += scnprintf(p, end - p, "Enable SCH C1 dbgport.\n");
break;
case RTW89_DBG_PORT_SEL_TMAC_C0:
info = &dbg_port_tmac_c0;
@@ -2519,7 +2645,7 @@ rtw89_debug_mac_dbg_port_sel(struct seq_file *m,
val32 = rtw89_read32(rtwdev, R_AX_SYS_STATUS1);
val32 = u32_replace_bits(val32, MAC_DBG_SEL, B_AX_SEL_0XC0_MASK);
rtw89_write32(rtwdev, R_AX_SYS_STATUS1, val32);
- seq_puts(m, "Enable TMAC C0 dbgport.\n");
+ p += scnprintf(p, end - p, "Enable TMAC C0 dbgport.\n");
break;
case RTW89_DBG_PORT_SEL_TMAC_C1:
info = &dbg_port_tmac_c1;
@@ -2536,7 +2662,7 @@ rtw89_debug_mac_dbg_port_sel(struct seq_file *m,
val32 = rtw89_read32(rtwdev, R_AX_SYS_STATUS1);
val32 = u32_replace_bits(val32, MAC_DBG_SEL, B_AX_SEL_0XC0_MASK);
rtw89_write32(rtwdev, R_AX_SYS_STATUS1, val32);
- seq_puts(m, "Enable TMAC C1 dbgport.\n");
+ p += scnprintf(p, end - p, "Enable TMAC C1 dbgport.\n");
break;
case RTW89_DBG_PORT_SEL_RMAC_C0:
info = &dbg_port_rmac_c0;
@@ -2558,7 +2684,7 @@ rtw89_debug_mac_dbg_port_sel(struct seq_file *m,
val8 = u8_replace_bits(val8, RMAC_CMAC_DBG_SEL,
B_AX_DBGSEL_TRXPTCL_MASK);
rtw89_write8(rtwdev, R_AX_DBGSEL_TRXPTCL, val8);
- seq_puts(m, "Enable RMAC C0 dbgport.\n");
+ p += scnprintf(p, end - p, "Enable RMAC C0 dbgport.\n");
break;
case RTW89_DBG_PORT_SEL_RMAC_C1:
info = &dbg_port_rmac_c1;
@@ -2580,23 +2706,23 @@ rtw89_debug_mac_dbg_port_sel(struct seq_file *m,
val8 = u8_replace_bits(val8, RMAC_CMAC_DBG_SEL,
B_AX_DBGSEL_TRXPTCL_MASK);
rtw89_write8(rtwdev, R_AX_DBGSEL_TRXPTCL_C1, val8);
- seq_puts(m, "Enable RMAC C1 dbgport.\n");
+ p += scnprintf(p, end - p, "Enable RMAC C1 dbgport.\n");
break;
case RTW89_DBG_PORT_SEL_RMACST_C0:
info = &dbg_port_rmacst_c0;
- seq_puts(m, "Enable RMAC state C0 dbgport.\n");
+ p += scnprintf(p, end - p, "Enable RMAC state C0 dbgport.\n");
break;
case RTW89_DBG_PORT_SEL_RMACST_C1:
info = &dbg_port_rmacst_c1;
- seq_puts(m, "Enable RMAC state C1 dbgport.\n");
+ p += scnprintf(p, end - p, "Enable RMAC state C1 dbgport.\n");
break;
case RTW89_DBG_PORT_SEL_RMAC_PLCP_C0:
info = &dbg_port_rmac_plcp_c0;
- seq_puts(m, "Enable RMAC PLCP C0 dbgport.\n");
+ p += scnprintf(p, end - p, "Enable RMAC PLCP C0 dbgport.\n");
break;
case RTW89_DBG_PORT_SEL_RMAC_PLCP_C1:
info = &dbg_port_rmac_plcp_c1;
- seq_puts(m, "Enable RMAC PLCP C1 dbgport.\n");
+ p += scnprintf(p, end - p, "Enable RMAC PLCP C1 dbgport.\n");
break;
case RTW89_DBG_PORT_SEL_TRXPTCL_C0:
info = &dbg_port_trxptcl_c0;
@@ -2608,7 +2734,7 @@ rtw89_debug_mac_dbg_port_sel(struct seq_file *m,
val32 = rtw89_read32(rtwdev, R_AX_SYS_STATUS1);
val32 = u32_replace_bits(val32, MAC_DBG_SEL, B_AX_SEL_0XC0_MASK);
rtw89_write32(rtwdev, R_AX_SYS_STATUS1, val32);
- seq_puts(m, "Enable TRXPTCL C0 dbgport.\n");
+ p += scnprintf(p, end - p, "Enable TRXPTCL C0 dbgport.\n");
break;
case RTW89_DBG_PORT_SEL_TRXPTCL_C1:
info = &dbg_port_trxptcl_c1;
@@ -2620,131 +2746,137 @@ rtw89_debug_mac_dbg_port_sel(struct seq_file *m,
val32 = rtw89_read32(rtwdev, R_AX_SYS_STATUS1);
val32 = u32_replace_bits(val32, MAC_DBG_SEL, B_AX_SEL_0XC0_MASK);
rtw89_write32(rtwdev, R_AX_SYS_STATUS1, val32);
- seq_puts(m, "Enable TRXPTCL C1 dbgport.\n");
+ p += scnprintf(p, end - p, "Enable TRXPTCL C1 dbgport.\n");
break;
case RTW89_DBG_PORT_SEL_TX_INFOL_C0:
info = &dbg_port_tx_infol_c0;
val32 = rtw89_read32(rtwdev, R_AX_TCR1);
val32 |= B_AX_TCR_FORCE_READ_TXDFIFO;
rtw89_write32(rtwdev, R_AX_TCR1, val32);
- seq_puts(m, "Enable tx infol dump.\n");
+ p += scnprintf(p, end - p, "Enable tx infol dump.\n");
break;
case RTW89_DBG_PORT_SEL_TX_INFOH_C0:
info = &dbg_port_tx_infoh_c0;
val32 = rtw89_read32(rtwdev, R_AX_TCR1);
val32 |= B_AX_TCR_FORCE_READ_TXDFIFO;
rtw89_write32(rtwdev, R_AX_TCR1, val32);
- seq_puts(m, "Enable tx infoh dump.\n");
+ p += scnprintf(p, end - p, "Enable tx infoh dump.\n");
break;
case RTW89_DBG_PORT_SEL_TX_INFOL_C1:
info = &dbg_port_tx_infol_c1;
val32 = rtw89_read32(rtwdev, R_AX_TCR1_C1);
val32 |= B_AX_TCR_FORCE_READ_TXDFIFO;
rtw89_write32(rtwdev, R_AX_TCR1_C1, val32);
- seq_puts(m, "Enable tx infol dump.\n");
+ p += scnprintf(p, end - p, "Enable tx infol dump.\n");
break;
case RTW89_DBG_PORT_SEL_TX_INFOH_C1:
info = &dbg_port_tx_infoh_c1;
val32 = rtw89_read32(rtwdev, R_AX_TCR1_C1);
val32 |= B_AX_TCR_FORCE_READ_TXDFIFO;
rtw89_write32(rtwdev, R_AX_TCR1_C1, val32);
- seq_puts(m, "Enable tx infoh dump.\n");
+ p += scnprintf(p, end - p, "Enable tx infoh dump.\n");
break;
case RTW89_DBG_PORT_SEL_TXTF_INFOL_C0:
info = &dbg_port_txtf_infol_c0;
val32 = rtw89_read32(rtwdev, R_AX_TCR1);
val32 |= B_AX_TCR_FORCE_READ_TXDFIFO;
rtw89_write32(rtwdev, R_AX_TCR1, val32);
- seq_puts(m, "Enable tx tf infol dump.\n");
+ p += scnprintf(p, end - p, "Enable tx tf infol dump.\n");
break;
case RTW89_DBG_PORT_SEL_TXTF_INFOH_C0:
info = &dbg_port_txtf_infoh_c0;
val32 = rtw89_read32(rtwdev, R_AX_TCR1);
val32 |= B_AX_TCR_FORCE_READ_TXDFIFO;
rtw89_write32(rtwdev, R_AX_TCR1, val32);
- seq_puts(m, "Enable tx tf infoh dump.\n");
+ p += scnprintf(p, end - p, "Enable tx tf infoh dump.\n");
break;
case RTW89_DBG_PORT_SEL_TXTF_INFOL_C1:
info = &dbg_port_txtf_infol_c1;
val32 = rtw89_read32(rtwdev, R_AX_TCR1_C1);
val32 |= B_AX_TCR_FORCE_READ_TXDFIFO;
rtw89_write32(rtwdev, R_AX_TCR1_C1, val32);
- seq_puts(m, "Enable tx tf infol dump.\n");
+ p += scnprintf(p, end - p, "Enable tx tf infol dump.\n");
break;
case RTW89_DBG_PORT_SEL_TXTF_INFOH_C1:
info = &dbg_port_txtf_infoh_c1;
val32 = rtw89_read32(rtwdev, R_AX_TCR1_C1);
val32 |= B_AX_TCR_FORCE_READ_TXDFIFO;
rtw89_write32(rtwdev, R_AX_TCR1_C1, val32);
- seq_puts(m, "Enable tx tf infoh dump.\n");
+ p += scnprintf(p, end - p, "Enable tx tf infoh dump.\n");
break;
case RTW89_DBG_PORT_SEL_WDE_BUFMGN_FREEPG:
info = &dbg_port_wde_bufmgn_freepg;
- seq_puts(m, "Enable wde bufmgn freepg dump.\n");
+ p += scnprintf(p, end - p, "Enable wde bufmgn freepg dump.\n");
break;
case RTW89_DBG_PORT_SEL_WDE_BUFMGN_QUOTA:
info = &dbg_port_wde_bufmgn_quota;
- seq_puts(m, "Enable wde bufmgn quota dump.\n");
+ p += scnprintf(p, end - p, "Enable wde bufmgn quota dump.\n");
break;
case RTW89_DBG_PORT_SEL_WDE_BUFMGN_PAGELLT:
info = &dbg_port_wde_bufmgn_pagellt;
- seq_puts(m, "Enable wde bufmgn pagellt dump.\n");
+ p += scnprintf(p, end - p,
+ "Enable wde bufmgn pagellt dump.\n");
break;
case RTW89_DBG_PORT_SEL_WDE_BUFMGN_PKTINFO:
info = &dbg_port_wde_bufmgn_pktinfo;
- seq_puts(m, "Enable wde bufmgn pktinfo dump.\n");
+ p += scnprintf(p, end - p,
+ "Enable wde bufmgn pktinfo dump.\n");
break;
case RTW89_DBG_PORT_SEL_WDE_QUEMGN_PREPKT:
info = &dbg_port_wde_quemgn_prepkt;
- seq_puts(m, "Enable wde quemgn prepkt dump.\n");
+ p += scnprintf(p, end - p, "Enable wde quemgn prepkt dump.\n");
break;
case RTW89_DBG_PORT_SEL_WDE_QUEMGN_NXTPKT:
info = &dbg_port_wde_quemgn_nxtpkt;
- seq_puts(m, "Enable wde quemgn nxtpkt dump.\n");
+ p += scnprintf(p, end - p, "Enable wde quemgn nxtpkt dump.\n");
break;
case RTW89_DBG_PORT_SEL_WDE_QUEMGN_QLNKTBL:
info = &dbg_port_wde_quemgn_qlnktbl;
- seq_puts(m, "Enable wde quemgn qlnktbl dump.\n");
+ p += scnprintf(p, end - p,
+ "Enable wde quemgn qlnktbl dump.\n");
break;
case RTW89_DBG_PORT_SEL_WDE_QUEMGN_QEMPTY:
info = &dbg_port_wde_quemgn_qempty;
- seq_puts(m, "Enable wde quemgn qempty dump.\n");
+ p += scnprintf(p, end - p, "Enable wde quemgn qempty dump.\n");
break;
case RTW89_DBG_PORT_SEL_PLE_BUFMGN_FREEPG:
info = &dbg_port_ple_bufmgn_freepg;
- seq_puts(m, "Enable ple bufmgn freepg dump.\n");
+ p += scnprintf(p, end - p, "Enable ple bufmgn freepg dump.\n");
break;
case RTW89_DBG_PORT_SEL_PLE_BUFMGN_QUOTA:
info = &dbg_port_ple_bufmgn_quota;
- seq_puts(m, "Enable ple bufmgn quota dump.\n");
+ p += scnprintf(p, end - p, "Enable ple bufmgn quota dump.\n");
break;
case RTW89_DBG_PORT_SEL_PLE_BUFMGN_PAGELLT:
info = &dbg_port_ple_bufmgn_pagellt;
- seq_puts(m, "Enable ple bufmgn pagellt dump.\n");
+ p += scnprintf(p, end - p,
+ "Enable ple bufmgn pagellt dump.\n");
break;
case RTW89_DBG_PORT_SEL_PLE_BUFMGN_PKTINFO:
info = &dbg_port_ple_bufmgn_pktinfo;
- seq_puts(m, "Enable ple bufmgn pktinfo dump.\n");
+ p += scnprintf(p, end - p,
+ "Enable ple bufmgn pktinfo dump.\n");
break;
case RTW89_DBG_PORT_SEL_PLE_QUEMGN_PREPKT:
info = &dbg_port_ple_quemgn_prepkt;
- seq_puts(m, "Enable ple quemgn prepkt dump.\n");
+ p += scnprintf(p, end - p, "Enable ple quemgn prepkt dump.\n");
break;
case RTW89_DBG_PORT_SEL_PLE_QUEMGN_NXTPKT:
info = &dbg_port_ple_quemgn_nxtpkt;
- seq_puts(m, "Enable ple quemgn nxtpkt dump.\n");
+ p += scnprintf(p, end - p, "Enable ple quemgn nxtpkt dump.\n");
break;
case RTW89_DBG_PORT_SEL_PLE_QUEMGN_QLNKTBL:
info = &dbg_port_ple_quemgn_qlnktbl;
- seq_puts(m, "Enable ple quemgn qlnktbl dump.\n");
+ p += scnprintf(p, end - p,
+ "Enable ple quemgn qlnktbl dump.\n");
break;
case RTW89_DBG_PORT_SEL_PLE_QUEMGN_QEMPTY:
info = &dbg_port_ple_quemgn_qempty;
- seq_puts(m, "Enable ple quemgn qempty dump.\n");
+ p += scnprintf(p, end - p, "Enable ple quemgn qempty dump.\n");
break;
case RTW89_DBG_PORT_SEL_PKTINFO:
info = &dbg_port_pktinfo;
- seq_puts(m, "Enable pktinfo dump.\n");
+ p += scnprintf(p, end - p, "Enable pktinfo dump.\n");
break;
case RTW89_DBG_PORT_SEL_DSPT_HDT_TX0:
rtw89_write32_mask(rtwdev, R_AX_DBG_CTRL,
@@ -2763,7 +2895,8 @@ rtw89_debug_mac_dbg_port_sel(struct seq_file *m,
B_AX_DISPATCHER_INTN_SEL_MASK, 0);
rtw89_write16_mask(rtwdev, info->sel_addr,
B_AX_DISPATCHER_CH_SEL_MASK, index);
- seq_printf(m, "Enable Dispatcher hdt tx%x dump.\n", index);
+ p += scnprintf(p, end - p,
+ "Enable Dispatcher hdt tx%x dump.\n", index);
break;
case RTW89_DBG_PORT_SEL_DSPT_HDT_TX6:
info = &dbg_port_dspt_hdt_tx6;
@@ -2771,7 +2904,8 @@ rtw89_debug_mac_dbg_port_sel(struct seq_file *m,
B_AX_DISPATCHER_INTN_SEL_MASK, 0);
rtw89_write16_mask(rtwdev, info->sel_addr,
B_AX_DISPATCHER_CH_SEL_MASK, 6);
- seq_puts(m, "Enable Dispatcher hdt tx6 dump.\n");
+ p += scnprintf(p, end - p,
+ "Enable Dispatcher hdt tx6 dump.\n");
break;
case RTW89_DBG_PORT_SEL_DSPT_HDT_TX7:
info = &dbg_port_dspt_hdt_tx7;
@@ -2779,7 +2913,8 @@ rtw89_debug_mac_dbg_port_sel(struct seq_file *m,
B_AX_DISPATCHER_INTN_SEL_MASK, 0);
rtw89_write16_mask(rtwdev, info->sel_addr,
B_AX_DISPATCHER_CH_SEL_MASK, 7);
- seq_puts(m, "Enable Dispatcher hdt tx7 dump.\n");
+ p += scnprintf(p, end - p,
+ "Enable Dispatcher hdt tx7 dump.\n");
break;
case RTW89_DBG_PORT_SEL_DSPT_HDT_TX8:
info = &dbg_port_dspt_hdt_tx8;
@@ -2787,7 +2922,8 @@ rtw89_debug_mac_dbg_port_sel(struct seq_file *m,
B_AX_DISPATCHER_INTN_SEL_MASK, 0);
rtw89_write16_mask(rtwdev, info->sel_addr,
B_AX_DISPATCHER_CH_SEL_MASK, 8);
- seq_puts(m, "Enable Dispatcher hdt tx8 dump.\n");
+ p += scnprintf(p, end - p,
+ "Enable Dispatcher hdt tx8 dump.\n");
break;
case RTW89_DBG_PORT_SEL_DSPT_HDT_TX9:
case RTW89_DBG_PORT_SEL_DSPT_HDT_TXA:
@@ -2799,7 +2935,8 @@ rtw89_debug_mac_dbg_port_sel(struct seq_file *m,
B_AX_DISPATCHER_INTN_SEL_MASK, 0);
rtw89_write16_mask(rtwdev, info->sel_addr,
B_AX_DISPATCHER_CH_SEL_MASK, index);
- seq_printf(m, "Enable Dispatcher hdt tx%x dump.\n", index);
+ p += scnprintf(p, end - p,
+ "Enable Dispatcher hdt tx%x dump.\n", index);
break;
case RTW89_DBG_PORT_SEL_DSPT_HDT_TXD:
info = &dbg_port_dspt_hdt_txD;
@@ -2807,7 +2944,8 @@ rtw89_debug_mac_dbg_port_sel(struct seq_file *m,
B_AX_DISPATCHER_INTN_SEL_MASK, 0);
rtw89_write16_mask(rtwdev, info->sel_addr,
B_AX_DISPATCHER_CH_SEL_MASK, 0xD);
- seq_puts(m, "Enable Dispatcher hdt txD dump.\n");
+ p += scnprintf(p, end - p,
+ "Enable Dispatcher hdt txD dump.\n");
break;
case RTW89_DBG_PORT_SEL_DSPT_CDT_TX0:
info = &dbg_port_dspt_cdt_tx0;
@@ -2815,7 +2953,8 @@ rtw89_debug_mac_dbg_port_sel(struct seq_file *m,
B_AX_DISPATCHER_INTN_SEL_MASK, 1);
rtw89_write16_mask(rtwdev, info->sel_addr,
B_AX_DISPATCHER_CH_SEL_MASK, 0);
- seq_puts(m, "Enable Dispatcher cdt tx0 dump.\n");
+ p += scnprintf(p, end - p,
+ "Enable Dispatcher cdt tx0 dump.\n");
break;
case RTW89_DBG_PORT_SEL_DSPT_CDT_TX1:
info = &dbg_port_dspt_cdt_tx1;
@@ -2823,7 +2962,8 @@ rtw89_debug_mac_dbg_port_sel(struct seq_file *m,
B_AX_DISPATCHER_INTN_SEL_MASK, 1);
rtw89_write16_mask(rtwdev, info->sel_addr,
B_AX_DISPATCHER_CH_SEL_MASK, 1);
- seq_puts(m, "Enable Dispatcher cdt tx1 dump.\n");
+ p += scnprintf(p, end - p,
+ "Enable Dispatcher cdt tx1 dump.\n");
break;
case RTW89_DBG_PORT_SEL_DSPT_CDT_TX3:
info = &dbg_port_dspt_cdt_tx3;
@@ -2831,7 +2971,8 @@ rtw89_debug_mac_dbg_port_sel(struct seq_file *m,
B_AX_DISPATCHER_INTN_SEL_MASK, 1);
rtw89_write16_mask(rtwdev, info->sel_addr,
B_AX_DISPATCHER_CH_SEL_MASK, 3);
- seq_puts(m, "Enable Dispatcher cdt tx3 dump.\n");
+ p += scnprintf(p, end - p,
+ "Enable Dispatcher cdt tx3 dump.\n");
break;
case RTW89_DBG_PORT_SEL_DSPT_CDT_TX4:
info = &dbg_port_dspt_cdt_tx4;
@@ -2839,7 +2980,8 @@ rtw89_debug_mac_dbg_port_sel(struct seq_file *m,
B_AX_DISPATCHER_INTN_SEL_MASK, 1);
rtw89_write16_mask(rtwdev, info->sel_addr,
B_AX_DISPATCHER_CH_SEL_MASK, 4);
- seq_puts(m, "Enable Dispatcher cdt tx4 dump.\n");
+ p += scnprintf(p, end - p,
+ "Enable Dispatcher cdt tx4 dump.\n");
break;
case RTW89_DBG_PORT_SEL_DSPT_CDT_TX5:
case RTW89_DBG_PORT_SEL_DSPT_CDT_TX6:
@@ -2851,7 +2993,8 @@ rtw89_debug_mac_dbg_port_sel(struct seq_file *m,
B_AX_DISPATCHER_INTN_SEL_MASK, 1);
rtw89_write16_mask(rtwdev, info->sel_addr,
B_AX_DISPATCHER_CH_SEL_MASK, index);
- seq_printf(m, "Enable Dispatcher cdt tx%x dump.\n", index);
+ p += scnprintf(p, end - p,
+ "Enable Dispatcher cdt tx%x dump.\n", index);
break;
case RTW89_DBG_PORT_SEL_DSPT_CDT_TX9:
info = &dbg_port_dspt_cdt_tx9;
@@ -2859,7 +3002,8 @@ rtw89_debug_mac_dbg_port_sel(struct seq_file *m,
B_AX_DISPATCHER_INTN_SEL_MASK, 1);
rtw89_write16_mask(rtwdev, info->sel_addr,
B_AX_DISPATCHER_CH_SEL_MASK, 9);
- seq_puts(m, "Enable Dispatcher cdt tx9 dump.\n");
+ p += scnprintf(p, end - p,
+ "Enable Dispatcher cdt tx9 dump.\n");
break;
case RTW89_DBG_PORT_SEL_DSPT_CDT_TXA:
case RTW89_DBG_PORT_SEL_DSPT_CDT_TXB:
@@ -2870,7 +3014,8 @@ rtw89_debug_mac_dbg_port_sel(struct seq_file *m,
B_AX_DISPATCHER_INTN_SEL_MASK, 1);
rtw89_write16_mask(rtwdev, info->sel_addr,
B_AX_DISPATCHER_CH_SEL_MASK, index);
- seq_printf(m, "Enable Dispatcher cdt tx%x dump.\n", index);
+ p += scnprintf(p, end - p,
+ "Enable Dispatcher cdt tx%x dump.\n", index);
break;
case RTW89_DBG_PORT_SEL_DSPT_HDT_RX0:
info = &dbg_port_dspt_hdt_rx0;
@@ -2878,7 +3023,8 @@ rtw89_debug_mac_dbg_port_sel(struct seq_file *m,
B_AX_DISPATCHER_INTN_SEL_MASK, 2);
rtw89_write16_mask(rtwdev, info->sel_addr,
B_AX_DISPATCHER_CH_SEL_MASK, 0);
- seq_puts(m, "Enable Dispatcher hdt rx0 dump.\n");
+ p += scnprintf(p, end - p,
+ "Enable Dispatcher hdt rx0 dump.\n");
break;
case RTW89_DBG_PORT_SEL_DSPT_HDT_RX1:
case RTW89_DBG_PORT_SEL_DSPT_HDT_RX2:
@@ -2888,7 +3034,8 @@ rtw89_debug_mac_dbg_port_sel(struct seq_file *m,
B_AX_DISPATCHER_INTN_SEL_MASK, 2);
rtw89_write16_mask(rtwdev, info->sel_addr,
B_AX_DISPATCHER_CH_SEL_MASK, index);
- seq_printf(m, "Enable Dispatcher hdt rx%x dump.\n", index);
+ p += scnprintf(p, end - p,
+ "Enable Dispatcher hdt rx%x dump.\n", index);
break;
case RTW89_DBG_PORT_SEL_DSPT_HDT_RX3:
info = &dbg_port_dspt_hdt_rx3;
@@ -2896,7 +3043,8 @@ rtw89_debug_mac_dbg_port_sel(struct seq_file *m,
B_AX_DISPATCHER_INTN_SEL_MASK, 2);
rtw89_write16_mask(rtwdev, info->sel_addr,
B_AX_DISPATCHER_CH_SEL_MASK, 3);
- seq_puts(m, "Enable Dispatcher hdt rx3 dump.\n");
+ p += scnprintf(p, end - p,
+ "Enable Dispatcher hdt rx3 dump.\n");
break;
case RTW89_DBG_PORT_SEL_DSPT_HDT_RX4:
info = &dbg_port_dspt_hdt_rx4;
@@ -2904,7 +3052,8 @@ rtw89_debug_mac_dbg_port_sel(struct seq_file *m,
B_AX_DISPATCHER_INTN_SEL_MASK, 2);
rtw89_write16_mask(rtwdev, info->sel_addr,
B_AX_DISPATCHER_CH_SEL_MASK, 4);
- seq_puts(m, "Enable Dispatcher hdt rx4 dump.\n");
+ p += scnprintf(p, end - p,
+ "Enable Dispatcher hdt rx4 dump.\n");
break;
case RTW89_DBG_PORT_SEL_DSPT_HDT_RX5:
info = &dbg_port_dspt_hdt_rx5;
@@ -2912,7 +3061,8 @@ rtw89_debug_mac_dbg_port_sel(struct seq_file *m,
B_AX_DISPATCHER_INTN_SEL_MASK, 2);
rtw89_write16_mask(rtwdev, info->sel_addr,
B_AX_DISPATCHER_CH_SEL_MASK, 5);
- seq_puts(m, "Enable Dispatcher hdt rx5 dump.\n");
+ p += scnprintf(p, end - p,
+ "Enable Dispatcher hdt rx5 dump.\n");
break;
case RTW89_DBG_PORT_SEL_DSPT_CDT_RX_P0_0:
info = &dbg_port_dspt_cdt_rx_p0_0;
@@ -2920,7 +3070,8 @@ rtw89_debug_mac_dbg_port_sel(struct seq_file *m,
B_AX_DISPATCHER_INTN_SEL_MASK, 3);
rtw89_write16_mask(rtwdev, info->sel_addr,
B_AX_DISPATCHER_CH_SEL_MASK, 0);
- seq_puts(m, "Enable Dispatcher cdt rx part0 0 dump.\n");
+ p += scnprintf(p, end - p,
+ "Enable Dispatcher cdt rx part0 0 dump.\n");
break;
case RTW89_DBG_PORT_SEL_DSPT_CDT_RX_P0:
case RTW89_DBG_PORT_SEL_DSPT_CDT_RX_P0_1:
@@ -2929,7 +3080,8 @@ rtw89_debug_mac_dbg_port_sel(struct seq_file *m,
B_AX_DISPATCHER_INTN_SEL_MASK, 3);
rtw89_write16_mask(rtwdev, info->sel_addr,
B_AX_DISPATCHER_CH_SEL_MASK, 1);
- seq_puts(m, "Enable Dispatcher cdt rx part0 1 dump.\n");
+ p += scnprintf(p, end - p,
+ "Enable Dispatcher cdt rx part0 1 dump.\n");
break;
case RTW89_DBG_PORT_SEL_DSPT_CDT_RX_P0_2:
info = &dbg_port_dspt_cdt_rx_p0_2;
@@ -2937,43 +3089,50 @@ rtw89_debug_mac_dbg_port_sel(struct seq_file *m,
B_AX_DISPATCHER_INTN_SEL_MASK, 3);
rtw89_write16_mask(rtwdev, info->sel_addr,
B_AX_DISPATCHER_CH_SEL_MASK, 2);
- seq_puts(m, "Enable Dispatcher cdt rx part0 2 dump.\n");
+ p += scnprintf(p, end - p,
+ "Enable Dispatcher cdt rx part0 2 dump.\n");
break;
case RTW89_DBG_PORT_SEL_DSPT_CDT_RX_P1:
info = &dbg_port_dspt_cdt_rx_p1;
rtw89_write8_mask(rtwdev, info->sel_addr,
B_AX_DISPATCHER_INTN_SEL_MASK, 3);
- seq_puts(m, "Enable Dispatcher cdt rx part1 dump.\n");
+ p += scnprintf(p, end - p,
+ "Enable Dispatcher cdt rx part1 dump.\n");
break;
case RTW89_DBG_PORT_SEL_DSPT_STF_CTRL:
info = &dbg_port_dspt_stf_ctrl;
rtw89_write8_mask(rtwdev, info->sel_addr,
B_AX_DISPATCHER_INTN_SEL_MASK, 4);
- seq_puts(m, "Enable Dispatcher stf control dump.\n");
+ p += scnprintf(p, end - p,
+ "Enable Dispatcher stf control dump.\n");
break;
case RTW89_DBG_PORT_SEL_DSPT_ADDR_CTRL:
info = &dbg_port_dspt_addr_ctrl;
rtw89_write8_mask(rtwdev, info->sel_addr,
B_AX_DISPATCHER_INTN_SEL_MASK, 5);
- seq_puts(m, "Enable Dispatcher addr control dump.\n");
+ p += scnprintf(p, end - p,
+ "Enable Dispatcher addr control dump.\n");
break;
case RTW89_DBG_PORT_SEL_DSPT_WDE_INTF:
info = &dbg_port_dspt_wde_intf;
rtw89_write8_mask(rtwdev, info->sel_addr,
B_AX_DISPATCHER_INTN_SEL_MASK, 6);
- seq_puts(m, "Enable Dispatcher wde interface dump.\n");
+ p += scnprintf(p, end - p,
+ "Enable Dispatcher wde interface dump.\n");
break;
case RTW89_DBG_PORT_SEL_DSPT_PLE_INTF:
info = &dbg_port_dspt_ple_intf;
rtw89_write8_mask(rtwdev, info->sel_addr,
B_AX_DISPATCHER_INTN_SEL_MASK, 7);
- seq_puts(m, "Enable Dispatcher ple interface dump.\n");
+ p += scnprintf(p, end - p,
+ "Enable Dispatcher ple interface dump.\n");
break;
case RTW89_DBG_PORT_SEL_DSPT_FLOW_CTRL:
info = &dbg_port_dspt_flow_ctrl;
rtw89_write8_mask(rtwdev, info->sel_addr,
B_AX_DISPATCHER_INTN_SEL_MASK, 8);
- seq_puts(m, "Enable Dispatcher flow control dump.\n");
+ p += scnprintf(p, end - p,
+ "Enable Dispatcher flow control dump.\n");
break;
case RTW89_DBG_PORT_SEL_PCIE_TXDMA:
info = &dbg_port_pcie_txdma;
@@ -2981,7 +3140,7 @@ rtw89_debug_mac_dbg_port_sel(struct seq_file *m,
val32 = u32_replace_bits(val32, PCIE_TXDMA_DBG_SEL, B_AX_DBG_SEL0);
val32 = u32_replace_bits(val32, PCIE_TXDMA_DBG_SEL, B_AX_DBG_SEL1);
rtw89_write32(rtwdev, R_AX_DBG_CTRL, val32);
- seq_puts(m, "Enable pcie txdma dump.\n");
+ p += scnprintf(p, end - p, "Enable pcie txdma dump.\n");
break;
case RTW89_DBG_PORT_SEL_PCIE_RXDMA:
info = &dbg_port_pcie_rxdma;
@@ -2989,7 +3148,7 @@ rtw89_debug_mac_dbg_port_sel(struct seq_file *m,
val32 = u32_replace_bits(val32, PCIE_RXDMA_DBG_SEL, B_AX_DBG_SEL0);
val32 = u32_replace_bits(val32, PCIE_RXDMA_DBG_SEL, B_AX_DBG_SEL1);
rtw89_write32(rtwdev, R_AX_DBG_CTRL, val32);
- seq_puts(m, "Enable pcie rxdma dump.\n");
+ p += scnprintf(p, end - p, "Enable pcie rxdma dump.\n");
break;
case RTW89_DBG_PORT_SEL_PCIE_CVT:
info = &dbg_port_pcie_cvt;
@@ -2997,7 +3156,7 @@ rtw89_debug_mac_dbg_port_sel(struct seq_file *m,
val32 = u32_replace_bits(val32, PCIE_CVT_DBG_SEL, B_AX_DBG_SEL0);
val32 = u32_replace_bits(val32, PCIE_CVT_DBG_SEL, B_AX_DBG_SEL1);
rtw89_write32(rtwdev, R_AX_DBG_CTRL, val32);
- seq_puts(m, "Enable pcie cvt dump.\n");
+ p += scnprintf(p, end - p, "Enable pcie cvt dump.\n");
break;
case RTW89_DBG_PORT_SEL_PCIE_CXPL:
info = &dbg_port_pcie_cxpl;
@@ -3005,7 +3164,7 @@ rtw89_debug_mac_dbg_port_sel(struct seq_file *m,
val32 = u32_replace_bits(val32, PCIE_CXPL_DBG_SEL, B_AX_DBG_SEL0);
val32 = u32_replace_bits(val32, PCIE_CXPL_DBG_SEL, B_AX_DBG_SEL1);
rtw89_write32(rtwdev, R_AX_DBG_CTRL, val32);
- seq_puts(m, "Enable pcie cxpl dump.\n");
+ p += scnprintf(p, end - p, "Enable pcie cxpl dump.\n");
break;
case RTW89_DBG_PORT_SEL_PCIE_IO:
info = &dbg_port_pcie_io;
@@ -3013,7 +3172,7 @@ rtw89_debug_mac_dbg_port_sel(struct seq_file *m,
val32 = u32_replace_bits(val32, PCIE_IO_DBG_SEL, B_AX_DBG_SEL0);
val32 = u32_replace_bits(val32, PCIE_IO_DBG_SEL, B_AX_DBG_SEL1);
rtw89_write32(rtwdev, R_AX_DBG_CTRL, val32);
- seq_puts(m, "Enable pcie io dump.\n");
+ p += scnprintf(p, end - p, "Enable pcie io dump.\n");
break;
case RTW89_DBG_PORT_SEL_PCIE_MISC:
info = &dbg_port_pcie_misc;
@@ -3021,7 +3180,7 @@ rtw89_debug_mac_dbg_port_sel(struct seq_file *m,
val32 = u32_replace_bits(val32, PCIE_MISC_DBG_SEL, B_AX_DBG_SEL0);
val32 = u32_replace_bits(val32, PCIE_MISC_DBG_SEL, B_AX_DBG_SEL1);
rtw89_write32(rtwdev, R_AX_DBG_CTRL, val32);
- seq_puts(m, "Enable pcie misc dump.\n");
+ p += scnprintf(p, end - p, "Enable pcie misc dump.\n");
break;
case RTW89_DBG_PORT_SEL_PCIE_MISC2:
info = &dbg_port_pcie_misc2;
@@ -3029,14 +3188,16 @@ rtw89_debug_mac_dbg_port_sel(struct seq_file *m,
val16 = u16_replace_bits(val16, PCIE_MISC2_DBG_SEL,
B_AX_PCIE_DBG_SEL_MASK);
rtw89_write16(rtwdev, R_AX_PCIE_DBG_CTRL, val16);
- seq_puts(m, "Enable pcie misc2 dump.\n");
+ p += scnprintf(p, end - p, "Enable pcie misc2 dump.\n");
break;
default:
- seq_puts(m, "Dbg port select err\n");
- return NULL;
+ p += scnprintf(p, end - p, "Dbg port select err\n");
+ break;
}
- return info;
+ *ppinfo = info;
+
+ return p - buf;
}
static bool is_dbg_port_valid(struct rtw89_dev *rtwdev, u32 sel)
@@ -3070,23 +3231,25 @@ static bool is_dbg_port_valid(struct rtw89_dev *rtwdev, u32 sel)
}
static int rtw89_debug_mac_dbg_port_dump(struct rtw89_dev *rtwdev,
- struct seq_file *m, u32 sel)
+ char *buf, size_t bufsz, u32 sel)
{
- const struct rtw89_mac_dbg_port_info *info;
- u8 val8;
- u16 val16;
+ const struct rtw89_mac_dbg_port_info *info = NULL;
+ char *p = buf, *end = buf + bufsz;
u32 val32;
+ u16 val16;
+ u8 val8;
u32 i;
- info = rtw89_debug_mac_dbg_port_sel(m, rtwdev, sel);
+ p += rtw89_debug_mac_dbg_port_sel(rtwdev, p, end - p, sel, &info);
+
if (!info) {
rtw89_err(rtwdev, "failed to select debug port %d\n", sel);
- return -EINVAL;
+ goto out;
}
#define case_DBG_SEL(__sel) \
case RTW89_DBG_PORT_SEL_##__sel: \
- seq_puts(m, "Dump debug port " #__sel ":\n"); \
+ p += scnprintf(p, end - p, "Dump debug port " #__sel ":\n"); \
break
switch (sel) {
@@ -3182,8 +3345,8 @@ static int rtw89_debug_mac_dbg_port_dump(struct rtw89_dev *rtwdev,
#undef case_DBG_SEL
- seq_printf(m, "Sel addr = 0x%X\n", info->sel_addr);
- seq_printf(m, "Read addr = 0x%X\n", info->rd_addr);
+ p += scnprintf(p, end - p, "Sel addr = 0x%X\n", info->sel_addr);
+ p += scnprintf(p, end - p, "Read addr = 0x%X\n", info->rd_addr);
for (i = info->srt; i <= info->end; i++) {
switch (info->sel_byte) {
@@ -3191,17 +3354,17 @@ static int rtw89_debug_mac_dbg_port_dump(struct rtw89_dev *rtwdev,
default:
rtw89_write8_mask(rtwdev, info->sel_addr,
info->sel_msk, i);
- seq_printf(m, "0x%02X: ", i);
+ p += scnprintf(p, end - p, "0x%02X: ", i);
break;
case 2:
rtw89_write16_mask(rtwdev, info->sel_addr,
info->sel_msk, i);
- seq_printf(m, "0x%04X: ", i);
+ p += scnprintf(p, end - p, "0x%04X: ", i);
break;
case 4:
rtw89_write32_mask(rtwdev, info->sel_addr,
info->sel_msk, i);
- seq_printf(m, "0x%04X: ", i);
+ p += scnprintf(p, end - p, "0x%04X: ", i);
break;
}
@@ -3212,77 +3375,75 @@ static int rtw89_debug_mac_dbg_port_dump(struct rtw89_dev *rtwdev,
default:
val8 = rtw89_read8_mask(rtwdev,
info->rd_addr, info->rd_msk);
- seq_printf(m, "0x%02X\n", val8);
+ p += scnprintf(p, end - p, "0x%02X\n", val8);
break;
case 2:
val16 = rtw89_read16_mask(rtwdev,
info->rd_addr, info->rd_msk);
- seq_printf(m, "0x%04X\n", val16);
+ p += scnprintf(p, end - p, "0x%04X\n", val16);
break;
case 4:
val32 = rtw89_read32_mask(rtwdev,
info->rd_addr, info->rd_msk);
- seq_printf(m, "0x%08X\n", val32);
+ p += scnprintf(p, end - p, "0x%08X\n", val32);
break;
}
}
- return 0;
+out:
+ return p - buf;
}
static int rtw89_debug_mac_dump_dbg_port(struct rtw89_dev *rtwdev,
- struct seq_file *m)
+ char *buf, size_t bufsz)
{
+ char *p = buf, *end = buf + bufsz;
+ ssize_t n;
u32 sel;
- int ret = 0;
for (sel = RTW89_DBG_PORT_SEL_PTCL_C0;
sel < RTW89_DBG_PORT_SEL_LAST; sel++) {
if (!is_dbg_port_valid(rtwdev, sel))
continue;
- ret = rtw89_debug_mac_dbg_port_dump(rtwdev, m, sel);
- if (ret) {
+ n = rtw89_debug_mac_dbg_port_dump(rtwdev, p, end - p, sel);
+ if (n < 0) {
rtw89_err(rtwdev,
"failed to dump debug port %d\n", sel);
break;
}
+ p += n;
}
- return ret;
+ return p - buf;
}
-static int
-rtw89_debug_priv_mac_dbg_port_dump_get(struct seq_file *m, void *v)
+static ssize_t
+rtw89_debug_priv_mac_dbg_port_dump_get(struct rtw89_dev *rtwdev,
+ struct rtw89_debugfs_priv *debugfs_priv,
+ char *buf, size_t bufsz)
{
- struct rtw89_debugfs_priv *debugfs_priv = m->private;
- struct rtw89_dev *rtwdev = debugfs_priv->rtwdev;
+ char *p = buf, *end = buf + bufsz;
if (debugfs_priv->dbgpkg_en.ss_dbg)
- rtw89_debug_mac_dump_ss_dbg(rtwdev, m);
+ p += rtw89_debug_mac_dump_ss_dbg(rtwdev, p, end - p);
if (debugfs_priv->dbgpkg_en.dle_dbg)
- rtw89_debug_mac_dump_dle_dbg(rtwdev, m);
+ p += rtw89_debug_mac_dump_dle_dbg(rtwdev, p, end - p);
if (debugfs_priv->dbgpkg_en.dmac_dbg)
- rtw89_debug_mac_dump_dmac_dbg(rtwdev, m);
+ p += rtw89_debug_mac_dump_dmac_dbg(rtwdev, p, end - p);
if (debugfs_priv->dbgpkg_en.cmac_dbg)
- rtw89_debug_mac_dump_cmac_dbg(rtwdev, m);
+ p += rtw89_debug_mac_dump_cmac_dbg(rtwdev, p, end - p);
if (debugfs_priv->dbgpkg_en.dbg_port)
- rtw89_debug_mac_dump_dbg_port(rtwdev, m);
+ p += rtw89_debug_mac_dump_dbg_port(rtwdev, p, end - p);
- return 0;
+ return p - buf;
};
-static u8 *rtw89_hex2bin_user(struct rtw89_dev *rtwdev,
- const char __user *user_buf, size_t count)
+static u8 *rtw89_hex2bin(struct rtw89_dev *rtwdev, const char *buf, size_t count)
{
- char *buf;
u8 *bin;
int num;
int err = 0;
- buf = memdup_user(user_buf, count);
- if (IS_ERR(buf))
- return buf;
-
num = count / 2;
bin = kmalloc(num, GFP_KERNEL);
if (!bin) {
@@ -3297,22 +3458,18 @@ static u8 *rtw89_hex2bin_user(struct rtw89_dev *rtwdev,
}
out:
- kfree(buf);
-
return err ? ERR_PTR(err) : bin;
}
-static ssize_t rtw89_debug_priv_send_h2c_set(struct file *filp,
- const char __user *user_buf,
- size_t count, loff_t *loff)
+static ssize_t rtw89_debug_priv_send_h2c_set(struct rtw89_dev *rtwdev,
+ struct rtw89_debugfs_priv *debugfs_priv,
+ const char *buf, size_t count)
{
- struct rtw89_debugfs_priv *debugfs_priv = filp->private_data;
- struct rtw89_dev *rtwdev = debugfs_priv->rtwdev;
u8 *h2c;
int ret;
u16 h2c_len = count / 2;
- h2c = rtw89_hex2bin_user(rtwdev, user_buf, count);
+ h2c = rtw89_hex2bin(rtwdev, buf, count);
if (IS_ERR(h2c))
return -EFAULT;
@@ -3323,34 +3480,36 @@ static ssize_t rtw89_debug_priv_send_h2c_set(struct file *filp,
return ret ? ret : count;
}
-static int
-rtw89_debug_priv_early_h2c_get(struct seq_file *m, void *v)
+static ssize_t
+rtw89_debug_priv_early_h2c_get(struct rtw89_dev *rtwdev,
+ struct rtw89_debugfs_priv *debugfs_priv,
+ char *buf, size_t bufsz)
{
- struct rtw89_debugfs_priv *debugfs_priv = m->private;
- struct rtw89_dev *rtwdev = debugfs_priv->rtwdev;
struct rtw89_early_h2c *early_h2c;
+ char *p = buf, *end = buf + bufsz;
int seq = 0;
- mutex_lock(&rtwdev->mutex);
+ lockdep_assert_wiphy(rtwdev->hw->wiphy);
+
list_for_each_entry(early_h2c, &rtwdev->early_h2c_list, list)
- seq_printf(m, "%d: %*ph\n", ++seq, early_h2c->h2c_len, early_h2c->h2c);
- mutex_unlock(&rtwdev->mutex);
+ p += scnprintf(p, end - p, "%d: %*ph\n", ++seq,
+ early_h2c->h2c_len, early_h2c->h2c);
- return 0;
+ return p - buf;
}
static ssize_t
-rtw89_debug_priv_early_h2c_set(struct file *filp, const char __user *user_buf,
- size_t count, loff_t *loff)
+rtw89_debug_priv_early_h2c_set(struct rtw89_dev *rtwdev,
+ struct rtw89_debugfs_priv *debugfs_priv,
+ const char *buf, size_t count)
{
- struct seq_file *m = (struct seq_file *)filp->private_data;
- struct rtw89_debugfs_priv *debugfs_priv = m->private;
- struct rtw89_dev *rtwdev = debugfs_priv->rtwdev;
struct rtw89_early_h2c *early_h2c;
u8 *h2c;
u16 h2c_len = count / 2;
- h2c = rtw89_hex2bin_user(rtwdev, user_buf, count);
+ lockdep_assert_wiphy(rtwdev->hw->wiphy);
+
+ h2c = rtw89_hex2bin(rtwdev, buf, count);
if (IS_ERR(h2c))
return -EFAULT;
@@ -3369,9 +3528,7 @@ rtw89_debug_priv_early_h2c_set(struct file *filp, const char __user *user_buf,
early_h2c->h2c = h2c;
early_h2c->h2c_len = h2c_len;
- mutex_lock(&rtwdev->mutex);
list_add_tail(&early_h2c->list, &rtwdev->early_h2c_list);
- mutex_unlock(&rtwdev->mutex);
out:
return count;
@@ -3404,15 +3561,16 @@ static int rtw89_dbg_trigger_ctrl_error(struct rtw89_dev *rtwdev)
return 0;
}
-static int
-rtw89_debug_priv_fw_crash_get(struct seq_file *m, void *v)
+static ssize_t
+rtw89_debug_priv_fw_crash_get(struct rtw89_dev *rtwdev,
+ struct rtw89_debugfs_priv *debugfs_priv,
+ char *buf, size_t bufsz)
{
- struct rtw89_debugfs_priv *debugfs_priv = m->private;
- struct rtw89_dev *rtwdev = debugfs_priv->rtwdev;
+ char *p = buf, *end = buf + bufsz;
- seq_printf(m, "%d\n",
- test_bit(RTW89_FLAG_CRASH_SIMULATING, rtwdev->flags));
- return 0;
+ p += scnprintf(p, end - p, "%d\n",
+ test_bit(RTW89_FLAG_CRASH_SIMULATING, rtwdev->flags));
+ return p - buf;
}
enum rtw89_dbg_crash_simulation_type {
@@ -3421,17 +3579,17 @@ enum rtw89_dbg_crash_simulation_type {
};
static ssize_t
-rtw89_debug_priv_fw_crash_set(struct file *filp, const char __user *user_buf,
- size_t count, loff_t *loff)
+rtw89_debug_priv_fw_crash_set(struct rtw89_dev *rtwdev,
+ struct rtw89_debugfs_priv *debugfs_priv,
+ const char *buf, size_t count)
{
- struct seq_file *m = (struct seq_file *)filp->private_data;
- struct rtw89_debugfs_priv *debugfs_priv = m->private;
- struct rtw89_dev *rtwdev = debugfs_priv->rtwdev;
int (*sim)(struct rtw89_dev *rtwdev);
u8 crash_type;
int ret;
- ret = kstrtou8_from_user(user_buf, count, 0, &crash_type);
+ lockdep_assert_wiphy(rtwdev->hw->wiphy);
+
+ ret = kstrtou8(buf, 0, &crash_type);
if (ret)
return -EINVAL;
@@ -3448,10 +3606,8 @@ rtw89_debug_priv_fw_crash_set(struct file *filp, const char __user *user_buf,
return -EINVAL;
}
- mutex_lock(&rtwdev->mutex);
set_bit(RTW89_FLAG_CRASH_SIMULATING, rtwdev->flags);
ret = sim(rtwdev);
- mutex_unlock(&rtwdev->mutex);
if (ret)
return ret;
@@ -3459,27 +3615,22 @@ rtw89_debug_priv_fw_crash_set(struct file *filp, const char __user *user_buf,
return count;
}
-static int rtw89_debug_priv_btc_info_get(struct seq_file *m, void *v)
+static ssize_t rtw89_debug_priv_btc_info_get(struct rtw89_dev *rtwdev,
+ struct rtw89_debugfs_priv *debugfs_priv,
+ char *buf, size_t bufsz)
{
- struct rtw89_debugfs_priv *debugfs_priv = m->private;
- struct rtw89_dev *rtwdev = debugfs_priv->rtwdev;
-
- rtw89_btc_dump_info(rtwdev, m);
-
- return 0;
+ return rtw89_btc_dump_info(rtwdev, buf, bufsz);
}
-static ssize_t rtw89_debug_priv_btc_manual_set(struct file *filp,
- const char __user *user_buf,
- size_t count, loff_t *loff)
+static ssize_t rtw89_debug_priv_btc_manual_set(struct rtw89_dev *rtwdev,
+ struct rtw89_debugfs_priv *debugfs_priv,
+ const char *buf, size_t count)
{
- struct rtw89_debugfs_priv *debugfs_priv = filp->private_data;
- struct rtw89_dev *rtwdev = debugfs_priv->rtwdev;
struct rtw89_btc *btc = &rtwdev->btc;
const struct rtw89_btc_ver *ver = btc->ver;
int ret;
- ret = kstrtobool_from_user(user_buf, count, &btc->manual_ctrl);
+ ret = kstrtobool(buf, &btc->manual_ctrl);
if (ret)
return ret;
@@ -3491,31 +3642,29 @@ static ssize_t rtw89_debug_priv_btc_manual_set(struct file *filp,
return count;
}
-static ssize_t rtw89_debug_priv_fw_log_manual_set(struct file *filp,
- const char __user *user_buf,
- size_t count, loff_t *loff)
+static ssize_t rtw89_debug_priv_fw_log_manual_set(struct rtw89_dev *rtwdev,
+ struct rtw89_debugfs_priv *debugfs_priv,
+ const char *buf, size_t count)
{
- struct rtw89_debugfs_priv *debugfs_priv = filp->private_data;
- struct rtw89_dev *rtwdev = debugfs_priv->rtwdev;
struct rtw89_fw_log *log = &rtwdev->fw.log;
bool fw_log_manual;
- if (kstrtobool_from_user(user_buf, count, &fw_log_manual))
+ lockdep_assert_wiphy(rtwdev->hw->wiphy);
+
+ if (kstrtobool(buf, &fw_log_manual))
goto out;
- mutex_lock(&rtwdev->mutex);
log->enable = fw_log_manual;
if (log->enable)
rtw89_fw_log_prepare(rtwdev);
rtw89_fw_h2c_fw_log(rtwdev, fw_log_manual);
- mutex_unlock(&rtwdev->mutex);
out:
return count;
}
-static void rtw89_sta_link_info_get_iter(struct seq_file *m,
- struct rtw89_dev *rtwdev,
- struct rtw89_sta_link *rtwsta_link)
+static int rtw89_sta_link_info_get_iter(struct rtw89_dev *rtwdev,
+ char *buf, size_t bufsz,
+ struct rtw89_sta_link *rtwsta_link)
{
static const char * const he_gi_str[] = {
[NL80211_RATE_INFO_HE_GI_0_8] = "0.8",
@@ -3533,6 +3682,7 @@ static void rtw89_sta_link_info_get_iter(struct seq_file *m,
u8 ant_num = hal->ant_diversity ? 2 : rtwdev->chip->rf_path_num;
bool ant_asterisk = hal->tx_path_diversity || hal->ant_diversity;
struct ieee80211_link_sta *link_sta;
+ char *p = buf, *end = buf + bufsz;
u8 evm_min, evm_max, evm_1ss;
u16 max_rc_amsdu_len;
u8 rssi;
@@ -3546,107 +3696,136 @@ static void rtw89_sta_link_info_get_iter(struct seq_file *m,
rcu_read_unlock();
- seq_printf(m, "TX rate [%u, %u]: ", rtwsta_link->mac_id, rtwsta_link->link_id);
+ p += scnprintf(p, end - p, "TX rate [%u, %u]: ", rtwsta_link->mac_id,
+ rtwsta_link->link_id);
if (rate->flags & RATE_INFO_FLAGS_MCS)
- seq_printf(m, "HT MCS-%d%s", rate->mcs,
- rate->flags & RATE_INFO_FLAGS_SHORT_GI ? " SGI" : "");
+ p += scnprintf(p, end - p, "HT MCS-%d%s", rate->mcs,
+ rate->flags & RATE_INFO_FLAGS_SHORT_GI ? " SGI" : "");
else if (rate->flags & RATE_INFO_FLAGS_VHT_MCS)
- seq_printf(m, "VHT %dSS MCS-%d%s", rate->nss, rate->mcs,
- rate->flags & RATE_INFO_FLAGS_SHORT_GI ? " SGI" : "");
+ p += scnprintf(p, end - p, "VHT %dSS MCS-%d%s", rate->nss,
+ rate->mcs,
+ rate->flags & RATE_INFO_FLAGS_SHORT_GI ? " SGI" : "");
else if (rate->flags & RATE_INFO_FLAGS_HE_MCS)
- seq_printf(m, "HE %dSS MCS-%d GI:%s", rate->nss, rate->mcs,
- rate->he_gi <= NL80211_RATE_INFO_HE_GI_3_2 ?
- he_gi_str[rate->he_gi] : "N/A");
+ p += scnprintf(p, end - p, "HE %dSS MCS-%d GI:%s", rate->nss,
+ rate->mcs,
+ rate->he_gi <= NL80211_RATE_INFO_HE_GI_3_2 ?
+ he_gi_str[rate->he_gi] : "N/A");
else if (rate->flags & RATE_INFO_FLAGS_EHT_MCS)
- seq_printf(m, "EHT %dSS MCS-%d GI:%s", rate->nss, rate->mcs,
- rate->eht_gi < ARRAY_SIZE(eht_gi_str) ?
- eht_gi_str[rate->eht_gi] : "N/A");
+ p += scnprintf(p, end - p, "EHT %dSS MCS-%d GI:%s", rate->nss,
+ rate->mcs,
+ rate->eht_gi < ARRAY_SIZE(eht_gi_str) ?
+ eht_gi_str[rate->eht_gi] : "N/A");
else
- seq_printf(m, "Legacy %d", rate->legacy);
- seq_printf(m, "%s", rtwsta_link->ra_report.might_fallback_legacy ? " FB_G" : "");
- seq_printf(m, " BW:%u", rtw89_rate_info_bw_to_mhz(rate->bw));
- seq_printf(m, " (hw_rate=0x%x)", rtwsta_link->ra_report.hw_rate);
- seq_printf(m, " ==> agg_wait=%d (%d)\n", rtwsta_link->max_agg_wait,
- max_rc_amsdu_len);
-
- seq_printf(m, "RX rate [%u, %u]: ", rtwsta_link->mac_id, rtwsta_link->link_id);
+ p += scnprintf(p, end - p, "Legacy %d", rate->legacy);
+ p += scnprintf(p, end - p, "%s",
+ rtwsta_link->ra_report.might_fallback_legacy ? " FB_G" : "");
+ p += scnprintf(p, end - p, " BW:%u",
+ rtw89_rate_info_bw_to_mhz(rate->bw));
+ p += scnprintf(p, end - p, " (hw_rate=0x%x)",
+ rtwsta_link->ra_report.hw_rate);
+ p += scnprintf(p, end - p, " ==> agg_wait=%d (%d)\n",
+ rtwsta_link->max_agg_wait,
+ max_rc_amsdu_len);
+
+ p += scnprintf(p, end - p, "RX rate [%u, %u]: ", rtwsta_link->mac_id,
+ rtwsta_link->link_id);
switch (status->encoding) {
case RX_ENC_LEGACY:
- seq_printf(m, "Legacy %d", status->rate_idx +
- (status->band != NL80211_BAND_2GHZ ? 4 : 0));
+ p += scnprintf(p, end - p, "Legacy %d", status->rate_idx +
+ (status->band != NL80211_BAND_2GHZ ? 4 : 0));
break;
case RX_ENC_HT:
- seq_printf(m, "HT MCS-%d%s", status->rate_idx,
- status->enc_flags & RX_ENC_FLAG_SHORT_GI ? " SGI" : "");
+ p += scnprintf(p, end - p, "HT MCS-%d%s", status->rate_idx,
+ status->enc_flags & RX_ENC_FLAG_SHORT_GI ? " SGI" : "");
break;
case RX_ENC_VHT:
- seq_printf(m, "VHT %dSS MCS-%d%s", status->nss, status->rate_idx,
- status->enc_flags & RX_ENC_FLAG_SHORT_GI ? " SGI" : "");
+ p += scnprintf(p, end - p, "VHT %dSS MCS-%d%s", status->nss,
+ status->rate_idx,
+ status->enc_flags & RX_ENC_FLAG_SHORT_GI ? " SGI" : "");
break;
case RX_ENC_HE:
- seq_printf(m, "HE %dSS MCS-%d GI:%s", status->nss, status->rate_idx,
- status->he_gi <= NL80211_RATE_INFO_HE_GI_3_2 ?
- he_gi_str[status->he_gi] : "N/A");
+ p += scnprintf(p, end - p, "HE %dSS MCS-%d GI:%s",
+ status->nss, status->rate_idx,
+ status->he_gi <= NL80211_RATE_INFO_HE_GI_3_2 ?
+ he_gi_str[status->he_gi] : "N/A");
break;
case RX_ENC_EHT:
- seq_printf(m, "EHT %dSS MCS-%d GI:%s", status->nss, status->rate_idx,
- status->eht.gi < ARRAY_SIZE(eht_gi_str) ?
- eht_gi_str[status->eht.gi] : "N/A");
+ p += scnprintf(p, end - p, "EHT %dSS MCS-%d GI:%s",
+ status->nss, status->rate_idx,
+ status->eht.gi < ARRAY_SIZE(eht_gi_str) ?
+ eht_gi_str[status->eht.gi] : "N/A");
break;
}
- seq_printf(m, " BW:%u", rtw89_rate_info_bw_to_mhz(status->bw));
- seq_printf(m, " (hw_rate=0x%x)\n", rtwsta_link->rx_hw_rate);
+ p += scnprintf(p, end - p, " BW:%u",
+ rtw89_rate_info_bw_to_mhz(status->bw));
+ p += scnprintf(p, end - p, " (hw_rate=0x%x)\n",
+ rtwsta_link->rx_hw_rate);
rssi = ewma_rssi_read(&rtwsta_link->avg_rssi);
- seq_printf(m, "RSSI: %d dBm (raw=%d, prev=%d) [",
- RTW89_RSSI_RAW_TO_DBM(rssi), rssi, rtwsta_link->prev_rssi);
+ p += scnprintf(p, end - p, "RSSI: %d dBm (raw=%d, prev=%d) [",
+ RTW89_RSSI_RAW_TO_DBM(rssi), rssi,
+ rtwsta_link->prev_rssi);
for (i = 0; i < ant_num; i++) {
rssi = ewma_rssi_read(&rtwsta_link->rssi[i]);
- seq_printf(m, "%d%s%s", RTW89_RSSI_RAW_TO_DBM(rssi),
- ant_asterisk && (hal->antenna_tx & BIT(i)) ? "*" : "",
- i + 1 == ant_num ? "" : ", ");
+ p += scnprintf(p, end - p, "%d%s%s",
+ RTW89_RSSI_RAW_TO_DBM(rssi),
+ ant_asterisk && (hal->antenna_tx & BIT(i)) ? "*" : "",
+ i + 1 == ant_num ? "" : ", ");
}
- seq_puts(m, "]\n");
+ p += scnprintf(p, end - p, "]\n");
evm_1ss = ewma_evm_read(&rtwsta_link->evm_1ss);
- seq_printf(m, "EVM: [%2u.%02u, ", evm_1ss >> 2, (evm_1ss & 0x3) * 25);
+ p += scnprintf(p, end - p, "EVM: [%2u.%02u, ", evm_1ss >> 2,
+ (evm_1ss & 0x3) * 25);
for (i = 0; i < (hal->ant_diversity ? 2 : 1); i++) {
evm_min = ewma_evm_read(&rtwsta_link->evm_min[i]);
evm_max = ewma_evm_read(&rtwsta_link->evm_max[i]);
- seq_printf(m, "%s(%2u.%02u, %2u.%02u)", i == 0 ? "" : " ",
- evm_min >> 2, (evm_min & 0x3) * 25,
- evm_max >> 2, (evm_max & 0x3) * 25);
+ p += scnprintf(p, end - p, "%s(%2u.%02u, %2u.%02u)",
+ i == 0 ? "" : " ",
+ evm_min >> 2, (evm_min & 0x3) * 25,
+ evm_max >> 2, (evm_max & 0x3) * 25);
}
- seq_puts(m, "]\t");
+ p += scnprintf(p, end - p, "]\t");
snr = ewma_snr_read(&rtwsta_link->avg_snr);
- seq_printf(m, "SNR: %u\n", snr);
+ p += scnprintf(p, end - p, "SNR: %u\n", snr);
+
+ return p - buf;
}
static void rtw89_sta_info_get_iter(void *data, struct ieee80211_sta *sta)
{
- struct seq_file *m = (struct seq_file *)data;
+ struct rtw89_debugfs_iter_data *iter_data =
+ (struct rtw89_debugfs_iter_data *)data;
struct rtw89_sta *rtwsta = sta_to_rtwsta(sta);
struct rtw89_dev *rtwdev = rtwsta->rtwdev;
struct rtw89_sta_link *rtwsta_link;
+ size_t bufsz = iter_data->bufsz;
+ char *buf = iter_data->buf;
+ char *p = buf, *end = buf + bufsz;
unsigned int link_id;
rtw89_sta_for_each_link(rtwsta, rtwsta_link, link_id)
- rtw89_sta_link_info_get_iter(m, rtwdev, rtwsta_link);
+ p += rtw89_sta_link_info_get_iter(rtwdev, p, end - p, rtwsta_link);
+
+ rtw89_debugfs_iter_data_next(iter_data, p, end - p, p - buf);
}
-static void
-rtw89_debug_append_rx_rate(struct seq_file *m, struct rtw89_pkt_stat *pkt_stat,
+static int
+rtw89_debug_append_rx_rate(char *buf, size_t bufsz, struct rtw89_pkt_stat *pkt_stat,
enum rtw89_hw_rate first_rate, int len)
{
+ char *p = buf, *end = buf + bufsz;
int i;
for (i = 0; i < len; i++)
- seq_printf(m, "%s%u", i == 0 ? "" : ", ",
- pkt_stat->rx_rate_cnt[first_rate + i]);
+ p += scnprintf(p, end - p, "%s%u", i == 0 ? "" : ", ",
+ pkt_stat->rx_rate_cnt[first_rate + i]);
+
+ return p - buf;
}
#define FIRST_RATE_SAME(rate) {RTW89_HW_RATE_ ## rate, RTW89_HW_RATE_ ## rate}
@@ -3671,34 +3850,40 @@ static const struct rtw89_rx_rate_cnt_info {
{FIRST_RATE_GEV1(EHT_NSS2_MCS0), 14, 0, "EHT 2SS:"},
};
-static int rtw89_debug_priv_phy_info_get(struct seq_file *m, void *v)
+static ssize_t rtw89_debug_priv_phy_info_get(struct rtw89_dev *rtwdev,
+ struct rtw89_debugfs_priv *debugfs_priv,
+ char *buf, size_t bufsz)
{
- struct rtw89_debugfs_priv *debugfs_priv = m->private;
- struct rtw89_dev *rtwdev = debugfs_priv->rtwdev;
struct rtw89_traffic_stats *stats = &rtwdev->stats;
struct rtw89_pkt_stat *pkt_stat = &rtwdev->phystat.last_pkt_stat;
const struct rtw89_chip_info *chip = rtwdev->chip;
+ struct rtw89_debugfs_iter_data iter_data;
const struct rtw89_rx_rate_cnt_info *info;
struct rtw89_hal *hal = &rtwdev->hal;
+ char *p = buf, *end = buf + bufsz;
enum rtw89_hw_rate first_rate;
u8 rssi;
int i;
rssi = ewma_rssi_read(&rtwdev->phystat.bcn_rssi);
- seq_printf(m, "TP TX: %u [%u] Mbps (lv: %d",
- stats->tx_throughput, stats->tx_throughput_raw, stats->tx_tfc_lv);
+ p += scnprintf(p, end - p, "TP TX: %u [%u] Mbps (lv: %d",
+ stats->tx_throughput, stats->tx_throughput_raw,
+ stats->tx_tfc_lv);
if (hal->thermal_prot_lv)
- seq_printf(m, ", duty: %d%%",
- 100 - hal->thermal_prot_lv * RTW89_THERMAL_PROT_STEP);
- seq_printf(m, "), RX: %u [%u] Mbps (lv: %d)\n",
- stats->rx_throughput, stats->rx_throughput_raw, stats->rx_tfc_lv);
- seq_printf(m, "Beacon: %u (%d dBm), TF: %u\n", pkt_stat->beacon_nr,
- RTW89_RSSI_RAW_TO_DBM(rssi), stats->rx_tf_periodic);
- seq_printf(m, "Avg packet length: TX=%u, RX=%u\n", stats->tx_avg_len,
- stats->rx_avg_len);
-
- seq_puts(m, "RX count:\n");
+ p += scnprintf(p, end - p, ", duty: %d%%",
+ 100 - hal->thermal_prot_lv * RTW89_THERMAL_PROT_STEP);
+ p += scnprintf(p, end - p, "), RX: %u [%u] Mbps (lv: %d)\n",
+ stats->rx_throughput, stats->rx_throughput_raw,
+ stats->rx_tfc_lv);
+ p += scnprintf(p, end - p, "Beacon: %u (%d dBm), TF: %u\n",
+ pkt_stat->beacon_nr,
+ RTW89_RSSI_RAW_TO_DBM(rssi), stats->rx_tf_periodic);
+ p += scnprintf(p, end - p, "Avg packet length: TX=%u, RX=%u\n",
+ stats->tx_avg_len,
+ stats->rx_avg_len);
+
+ p += scnprintf(p, end - p, "RX count:\n");
for (i = 0; i < ARRAY_SIZE(rtw89_rx_rate_cnt_infos); i++) {
info = &rtw89_rx_rate_cnt_infos[i];
@@ -3706,189 +3891,279 @@ static int rtw89_debug_priv_phy_info_get(struct seq_file *m, void *v)
if (first_rate >= RTW89_HW_RATE_NR)
continue;
- seq_printf(m, "%10s [", info->rate_mode);
- rtw89_debug_append_rx_rate(m, pkt_stat,
- first_rate, info->len);
+ p += scnprintf(p, end - p, "%10s [", info->rate_mode);
+ p += rtw89_debug_append_rx_rate(p, end - p, pkt_stat,
+ first_rate, info->len);
if (info->ext) {
- seq_puts(m, "][");
- rtw89_debug_append_rx_rate(m, pkt_stat,
- first_rate + info->len, info->ext);
+ p += scnprintf(p, end - p, "][");
+ p += rtw89_debug_append_rx_rate(p, end - p, pkt_stat,
+ first_rate + info->len, info->ext);
}
- seq_puts(m, "]\n");
+ p += scnprintf(p, end - p, "]\n");
}
- ieee80211_iterate_stations_atomic(rtwdev->hw, rtw89_sta_info_get_iter, m);
+ rtw89_debugfs_iter_data_setup(&iter_data, p, end - p);
+ ieee80211_iterate_stations_atomic(rtwdev->hw, rtw89_sta_info_get_iter, &iter_data);
+ p += iter_data.written_sz;
- return 0;
+ return p - buf;
}
-static void rtw89_dump_addr_cam(struct seq_file *m,
- struct rtw89_dev *rtwdev,
- struct rtw89_addr_cam_entry *addr_cam)
+static int rtw89_dump_addr_cam(struct rtw89_dev *rtwdev,
+ char *buf, size_t bufsz,
+ struct rtw89_addr_cam_entry *addr_cam)
{
struct rtw89_cam_info *cam_info = &rtwdev->cam_info;
const struct rtw89_sec_cam_entry *sec_entry;
+ char *p = buf, *end = buf + bufsz;
u8 sec_cam_idx;
int i;
- seq_printf(m, "\taddr_cam_idx=%u\n", addr_cam->addr_cam_idx);
- seq_printf(m, "\t-> bssid_cam_idx=%u\n", addr_cam->bssid_cam_idx);
- seq_printf(m, "\tsec_cam_bitmap=%*ph\n", (int)sizeof(addr_cam->sec_cam_map),
- addr_cam->sec_cam_map);
+ p += scnprintf(p, end - p, "\taddr_cam_idx=%u\n",
+ addr_cam->addr_cam_idx);
+ p += scnprintf(p, end - p, "\t-> bssid_cam_idx=%u\n",
+ addr_cam->bssid_cam_idx);
+ p += scnprintf(p, end - p, "\tsec_cam_bitmap=%*ph\n",
+ (int)sizeof(addr_cam->sec_cam_map),
+ addr_cam->sec_cam_map);
for_each_set_bit(i, addr_cam->sec_cam_map, RTW89_SEC_CAM_IN_ADDR_CAM) {
sec_cam_idx = addr_cam->sec_ent[i];
sec_entry = cam_info->sec_entries[sec_cam_idx];
if (!sec_entry)
continue;
- seq_printf(m, "\tsec[%d]: sec_cam_idx %u", i, sec_entry->sec_cam_idx);
+ p += scnprintf(p, end - p, "\tsec[%d]: sec_cam_idx %u", i,
+ sec_entry->sec_cam_idx);
if (sec_entry->ext_key)
- seq_printf(m, ", %u", sec_entry->sec_cam_idx + 1);
- seq_puts(m, "\n");
+ p += scnprintf(p, end - p, ", %u",
+ sec_entry->sec_cam_idx + 1);
+ p += scnprintf(p, end - p, "\n");
}
+
+ return p - buf;
}
-__printf(3, 4)
-static void rtw89_dump_pkt_offload(struct seq_file *m, struct list_head *pkt_list,
- const char *fmt, ...)
+__printf(4, 5)
+static int rtw89_dump_pkt_offload(char *buf, size_t bufsz, struct list_head *pkt_list,
+ const char *fmt, ...)
{
+ char *p = buf, *end = buf + bufsz;
struct rtw89_pktofld_info *info;
struct va_format vaf;
va_list args;
if (list_empty(pkt_list))
- return;
+ return 0;
va_start(args, fmt);
vaf.va = &args;
vaf.fmt = fmt;
- seq_printf(m, "%pV", &vaf);
+ p += scnprintf(p, end - p, "%pV", &vaf);
va_end(args);
list_for_each_entry(info, pkt_list, list)
- seq_printf(m, "%d ", info->id);
+ p += scnprintf(p, end - p, "%d ", info->id);
+
+ p += scnprintf(p, end - p, "\n");
- seq_puts(m, "\n");
+ return p - buf;
}
-static void rtw89_vif_link_ids_get(struct seq_file *m, u8 *mac,
- struct rtw89_dev *rtwdev,
- struct rtw89_vif_link *rtwvif_link)
+static int rtw89_vif_link_ids_get(struct rtw89_dev *rtwdev,
+ char *buf, size_t bufsz, u8 *mac,
+ struct rtw89_vif_link *rtwvif_link,
+ bool designated)
{
struct rtw89_bssid_cam_entry *bssid_cam = &rtwvif_link->bssid_cam;
-
- seq_printf(m, " [%u] %pM\n", rtwvif_link->mac_id, rtwvif_link->mac_addr);
- seq_printf(m, "\tlink_id=%u\n", rtwvif_link->link_id);
- seq_printf(m, "\tbssid_cam_idx=%u\n", bssid_cam->bssid_cam_idx);
- rtw89_dump_addr_cam(m, rtwdev, &rtwvif_link->addr_cam);
- rtw89_dump_pkt_offload(m, &rtwvif_link->general_pkt_list,
- "\tpkt_ofld[GENERAL]: ");
+ char *p = buf, *end = buf + bufsz;
+
+ p += scnprintf(p, end - p, " [%u] %pM\n", rtwvif_link->mac_id,
+ rtwvif_link->mac_addr);
+ p += scnprintf(p, end - p, "\tlink_id=%u%s\n", rtwvif_link->link_id,
+ designated ? " (*)" : "");
+ p += scnprintf(p, end - p, "\tbssid_cam_idx=%u\n",
+ bssid_cam->bssid_cam_idx);
+ p += rtw89_dump_addr_cam(rtwdev, p, end - p, &rtwvif_link->addr_cam);
+ p += rtw89_dump_pkt_offload(p, end - p, &rtwvif_link->general_pkt_list,
+ "\tpkt_ofld[GENERAL]: ");
+
+ return p - buf;
}
static
void rtw89_vif_ids_get_iter(void *data, u8 *mac, struct ieee80211_vif *vif)
{
- struct seq_file *m = (struct seq_file *)data;
+ struct rtw89_debugfs_iter_data *iter_data =
+ (struct rtw89_debugfs_iter_data *)data;
struct rtw89_vif *rtwvif = vif_to_rtwvif(vif);
struct rtw89_dev *rtwdev = rtwvif->rtwdev;
+ struct rtw89_vif_link *designated_link;
struct rtw89_vif_link *rtwvif_link;
+ size_t bufsz = iter_data->bufsz;
+ char *buf = iter_data->buf;
+ char *p = buf, *end = buf + bufsz;
unsigned int link_id;
- seq_printf(m, "VIF %pM\n", rtwvif->mac_addr);
+ designated_link = rtw89_get_designated_link(rtwvif);
+
+ p += scnprintf(p, end - p, "VIF %pM\n", rtwvif->mac_addr);
rtw89_vif_for_each_link(rtwvif, rtwvif_link, link_id)
- rtw89_vif_link_ids_get(m, mac, rtwdev, rtwvif_link);
+ p += rtw89_vif_link_ids_get(rtwdev, p, end - p, mac, rtwvif_link,
+ rtwvif_link == designated_link);
+
+ rtw89_debugfs_iter_data_next(iter_data, p, end - p, p - buf);
}
-static void rtw89_dump_ba_cam(struct seq_file *m, struct rtw89_dev *rtwdev,
- struct rtw89_sta_link *rtwsta_link)
+static int rtw89_dump_ba_cam(struct rtw89_dev *rtwdev,
+ char *buf, size_t bufsz,
+ struct rtw89_sta_link *rtwsta_link)
{
struct rtw89_ba_cam_entry *entry;
+ char *p = buf, *end = buf + bufsz;
bool first = true;
list_for_each_entry(entry, &rtwsta_link->ba_cam_list, list) {
if (first) {
- seq_puts(m, "\tba_cam ");
+ p += scnprintf(p, end - p, "\tba_cam ");
first = false;
} else {
- seq_puts(m, ", ");
+ p += scnprintf(p, end - p, ", ");
}
- seq_printf(m, "tid[%u]=%d", entry->tid,
- (int)(entry - rtwdev->cam_info.ba_cam_entry));
+ p += scnprintf(p, end - p, "tid[%u]=%d", entry->tid,
+ (int)(entry - rtwdev->cam_info.ba_cam_entry));
}
- seq_puts(m, "\n");
+ p += scnprintf(p, end - p, "\n");
+
+ return p - buf;
}
-static void rtw89_sta_link_ids_get(struct seq_file *m,
- struct rtw89_dev *rtwdev,
- struct rtw89_sta_link *rtwsta_link)
+static int rtw89_sta_link_ids_get(struct rtw89_dev *rtwdev,
+ char *buf, size_t bufsz,
+ struct rtw89_sta_link *rtwsta_link,
+ bool designated)
{
struct ieee80211_link_sta *link_sta;
+ char *p = buf, *end = buf + bufsz;
rcu_read_lock();
link_sta = rtw89_sta_rcu_dereference_link(rtwsta_link, true);
- seq_printf(m, " [%u] %pM\n", rtwsta_link->mac_id, link_sta->addr);
+ p += scnprintf(p, end - p, " [%u] %pM\n", rtwsta_link->mac_id,
+ link_sta->addr);
rcu_read_unlock();
- seq_printf(m, "\tlink_id=%u\n", rtwsta_link->link_id);
- rtw89_dump_addr_cam(m, rtwdev, &rtwsta_link->addr_cam);
- rtw89_dump_ba_cam(m, rtwdev, rtwsta_link);
+ p += scnprintf(p, end - p, "\tlink_id=%u%s\n", rtwsta_link->link_id,
+ designated ? " (*)" : "");
+ p += rtw89_dump_addr_cam(rtwdev, p, end - p, &rtwsta_link->addr_cam);
+ p += rtw89_dump_ba_cam(rtwdev, p, end - p, rtwsta_link);
+
+ return p - buf;
}
static void rtw89_sta_ids_get_iter(void *data, struct ieee80211_sta *sta)
{
- struct seq_file *m = (struct seq_file *)data;
+ struct rtw89_debugfs_iter_data *iter_data =
+ (struct rtw89_debugfs_iter_data *)data;
struct rtw89_sta *rtwsta = sta_to_rtwsta(sta);
struct rtw89_dev *rtwdev = rtwsta->rtwdev;
+ struct rtw89_sta_link *designated_link;
struct rtw89_sta_link *rtwsta_link;
+ size_t bufsz = iter_data->bufsz;
+ char *buf = iter_data->buf;
+ char *p = buf, *end = buf + bufsz;
unsigned int link_id;
- seq_printf(m, "STA %pM %s\n", sta->addr, sta->tdls ? "(TDLS)" : "");
+ designated_link = rtw89_get_designated_link(rtwsta);
+
+ p += scnprintf(p, end - p, "STA %pM %s\n", sta->addr,
+ sta->tdls ? "(TDLS)" : "");
rtw89_sta_for_each_link(rtwsta, rtwsta_link, link_id)
- rtw89_sta_link_ids_get(m, rtwdev, rtwsta_link);
+ p += rtw89_sta_link_ids_get(rtwdev, p, end - p, rtwsta_link,
+ rtwsta_link == designated_link);
+
+ rtw89_debugfs_iter_data_next(iter_data, p, end - p, p - buf);
}
-static int rtw89_debug_priv_stations_get(struct seq_file *m, void *v)
+static ssize_t rtw89_debug_priv_stations_get(struct rtw89_dev *rtwdev,
+ struct rtw89_debugfs_priv *debugfs_priv,
+ char *buf, size_t bufsz)
{
- struct rtw89_debugfs_priv *debugfs_priv = m->private;
- struct rtw89_dev *rtwdev = debugfs_priv->rtwdev;
struct rtw89_cam_info *cam_info = &rtwdev->cam_info;
+ struct rtw89_debugfs_iter_data iter_data;
+ char *p = buf, *end = buf + bufsz;
u8 idx;
- mutex_lock(&rtwdev->mutex);
-
- seq_puts(m, "map:\n");
- seq_printf(m, "\tmac_id: %*ph\n", (int)sizeof(rtwdev->mac_id_map),
- rtwdev->mac_id_map);
- seq_printf(m, "\taddr_cam: %*ph\n", (int)sizeof(cam_info->addr_cam_map),
- cam_info->addr_cam_map);
- seq_printf(m, "\tbssid_cam: %*ph\n", (int)sizeof(cam_info->bssid_cam_map),
- cam_info->bssid_cam_map);
- seq_printf(m, "\tsec_cam: %*ph\n", (int)sizeof(cam_info->sec_cam_map),
- cam_info->sec_cam_map);
- seq_printf(m, "\tba_cam: %*ph\n", (int)sizeof(cam_info->ba_cam_map),
- cam_info->ba_cam_map);
- seq_printf(m, "\tpkt_ofld: %*ph\n", (int)sizeof(rtwdev->pkt_offload),
- rtwdev->pkt_offload);
+ lockdep_assert_wiphy(rtwdev->hw->wiphy);
+
+ p += scnprintf(p, end - p, "map:\n");
+ p += scnprintf(p, end - p, "\tmac_id: %*ph\n",
+ (int)sizeof(rtwdev->mac_id_map),
+ rtwdev->mac_id_map);
+ p += scnprintf(p, end - p, "\taddr_cam: %*ph\n",
+ (int)sizeof(cam_info->addr_cam_map),
+ cam_info->addr_cam_map);
+ p += scnprintf(p, end - p, "\tbssid_cam: %*ph\n",
+ (int)sizeof(cam_info->bssid_cam_map),
+ cam_info->bssid_cam_map);
+ p += scnprintf(p, end - p, "\tsec_cam: %*ph\n",
+ (int)sizeof(cam_info->sec_cam_map),
+ cam_info->sec_cam_map);
+ p += scnprintf(p, end - p, "\tba_cam: %*ph\n",
+ (int)sizeof(cam_info->ba_cam_map),
+ cam_info->ba_cam_map);
+ p += scnprintf(p, end - p, "\tpkt_ofld: %*ph\n",
+ (int)sizeof(rtwdev->pkt_offload),
+ rtwdev->pkt_offload);
for (idx = NL80211_BAND_2GHZ; idx < NUM_NL80211_BANDS; idx++) {
if (!(rtwdev->chip->support_bands & BIT(idx)))
continue;
- rtw89_dump_pkt_offload(m, &rtwdev->scan_info.pkt_list[idx],
- "\t\t[SCAN %u]: ", idx);
+ p += rtw89_dump_pkt_offload(p, end - p, &rtwdev->scan_info.pkt_list[idx],
+ "\t\t[SCAN %u]: ", idx);
}
+ rtw89_debugfs_iter_data_setup(&iter_data, p, end - p);
ieee80211_iterate_active_interfaces_atomic(rtwdev->hw,
- IEEE80211_IFACE_ITER_NORMAL, rtw89_vif_ids_get_iter, m);
+ IEEE80211_IFACE_ITER_NORMAL, rtw89_vif_ids_get_iter, &iter_data);
+ p += iter_data.written_sz;
+
+ rtw89_debugfs_iter_data_setup(&iter_data, p, end - p);
+ ieee80211_iterate_stations_atomic(rtwdev->hw, rtw89_sta_ids_get_iter, &iter_data);
+ p += iter_data.written_sz;
- ieee80211_iterate_stations_atomic(rtwdev->hw, rtw89_sta_ids_get_iter, m);
+ return p - buf;
+}
- mutex_unlock(&rtwdev->mutex);
+static void rtw89_debug_disable_dm_cfg_bmap(struct rtw89_dev *rtwdev, u32 new)
+{
+ struct rtw89_hal *hal = &rtwdev->hal;
+ u32 old = hal->disabled_dm_bitmap;
- return 0;
+ if (new == old)
+ return;
+
+ hal->disabled_dm_bitmap = new;
+
+ rtw89_debug(rtwdev, RTW89_DBG_STATE, "Disable DM: 0x%x -> 0x%x\n", old, new);
+}
+
+static void rtw89_debug_disable_dm_set_flag(struct rtw89_dev *rtwdev, u8 flag)
+{
+ struct rtw89_hal *hal = &rtwdev->hal;
+ u32 cur = hal->disabled_dm_bitmap;
+
+ rtw89_debug_disable_dm_cfg_bmap(rtwdev, cur | BIT(flag));
+}
+
+static void rtw89_debug_disable_dm_clr_flag(struct rtw89_dev *rtwdev, u8 flag)
+{
+ struct rtw89_hal *hal = &rtwdev->hal;
+ u32 cur = hal->disabled_dm_bitmap;
+
+ rtw89_debug_disable_dm_cfg_bmap(rtwdev, cur & ~BIT(flag));
}
#define DM_INFO(type) {RTW89_DM_ ## type, #type}
@@ -3899,92 +4174,187 @@ static const struct rtw89_disabled_dm_info {
} rtw89_disabled_dm_infos[] = {
DM_INFO(DYNAMIC_EDCCA),
DM_INFO(THERMAL_PROTECT),
+ DM_INFO(TAS),
+ DM_INFO(MLO),
};
-static int
-rtw89_debug_priv_disable_dm_get(struct seq_file *m, void *v)
+static ssize_t
+rtw89_debug_priv_disable_dm_get(struct rtw89_dev *rtwdev,
+ struct rtw89_debugfs_priv *debugfs_priv,
+ char *buf, size_t bufsz)
{
- struct rtw89_debugfs_priv *debugfs_priv = m->private;
- struct rtw89_dev *rtwdev = debugfs_priv->rtwdev;
const struct rtw89_disabled_dm_info *info;
struct rtw89_hal *hal = &rtwdev->hal;
+ char *p = buf, *end = buf + bufsz;
u32 disabled;
int i;
- seq_printf(m, "Disabled DM: 0x%x\n", hal->disabled_dm_bitmap);
+ p += scnprintf(p, end - p, "Disabled DM: 0x%x\n",
+ hal->disabled_dm_bitmap);
for (i = 0; i < ARRAY_SIZE(rtw89_disabled_dm_infos); i++) {
info = &rtw89_disabled_dm_infos[i];
disabled = BIT(info->type) & hal->disabled_dm_bitmap;
- seq_printf(m, "[%d] %s: %c\n", info->type, info->name,
- disabled ? 'X' : 'O');
+ p += scnprintf(p, end - p, "[%d] %s: %c\n", info->type,
+ info->name,
+ disabled ? 'X' : 'O');
}
- return 0;
+ return p - buf;
}
static ssize_t
-rtw89_debug_priv_disable_dm_set(struct file *filp, const char __user *user_buf,
- size_t count, loff_t *loff)
+rtw89_debug_priv_disable_dm_set(struct rtw89_dev *rtwdev,
+ struct rtw89_debugfs_priv *debugfs_priv,
+ const char *buf, size_t count)
{
- struct seq_file *m = (struct seq_file *)filp->private_data;
- struct rtw89_debugfs_priv *debugfs_priv = m->private;
- struct rtw89_dev *rtwdev = debugfs_priv->rtwdev;
- struct rtw89_hal *hal = &rtwdev->hal;
u32 conf;
int ret;
- ret = kstrtou32_from_user(user_buf, count, 0, &conf);
+ ret = kstrtou32(buf, 0, &conf);
if (ret)
return -EINVAL;
- hal->disabled_dm_bitmap = conf;
+ rtw89_debug_disable_dm_cfg_bmap(rtwdev, conf);
+
+ return count;
+}
+
+static void rtw89_debug_mlo_mode_set_mlsr(struct rtw89_dev *rtwdev,
+ unsigned int link_id)
+{
+ struct ieee80211_vif *vif;
+ struct rtw89_vif *rtwvif;
+
+ rtw89_for_each_rtwvif(rtwdev, rtwvif) {
+ vif = rtwvif_to_vif(rtwvif);
+ if (!ieee80211_vif_is_mld(vif))
+ continue;
+
+ rtw89_core_mlsr_switch(rtwdev, rtwvif, link_id);
+ }
+}
+
+static ssize_t
+rtw89_debug_priv_mlo_mode_get(struct rtw89_dev *rtwdev,
+ struct rtw89_debugfs_priv *debugfs_priv,
+ char *buf, size_t bufsz)
+{
+ bool mlo_dm_dis = rtwdev->hal.disabled_dm_bitmap & BIT(RTW89_DM_MLO);
+ char *p = buf, *end = buf + bufsz;
+ struct ieee80211_vif *vif;
+ struct rtw89_vif *rtwvif;
+ int count = 0;
+
+ p += scnprintf(p, end - p, "MLD(s) status: (MLO DM: %s)\n",
+ str_disable_enable(mlo_dm_dis));
+
+ rtw89_for_each_rtwvif(rtwdev, rtwvif) {
+ vif = rtwvif_to_vif(rtwvif);
+ if (!ieee80211_vif_is_mld(vif))
+ continue;
+
+ p += scnprintf(p, end - p,
+ "\t#%u: MLO mode %x, valid 0x%x, active 0x%x\n",
+ count++, rtwvif->mlo_mode, vif->valid_links,
+ vif->active_links);
+ }
+
+ if (count == 0)
+ p += scnprintf(p, end - p, "\t(None)\n");
+
+ return p - buf;
+}
+
+static ssize_t
+rtw89_debug_priv_mlo_mode_set(struct rtw89_dev *rtwdev,
+ struct rtw89_debugfs_priv *debugfs_priv,
+ const char *buf, size_t count)
+{
+ u8 num, mlo_mode;
+ u32 argv;
+
+ num = sscanf(buf, "%hhx %u", &mlo_mode, &argv);
+ if (num != 2)
+ return -EINVAL;
+
+ rtw89_debug_disable_dm_set_flag(rtwdev, RTW89_DM_MLO);
+
+ rtw89_debug(rtwdev, RTW89_DBG_STATE, "Set MLO mode to %x\n", mlo_mode);
+
+ switch (mlo_mode) {
+ case RTW89_MLO_MODE_MLSR:
+ rtw89_debug_mlo_mode_set_mlsr(rtwdev, argv);
+ break;
+ default:
+ rtw89_debug(rtwdev, RTW89_DBG_STATE, "Unsupported MLO mode\n");
+ rtw89_debug_disable_dm_clr_flag(rtwdev, RTW89_DM_MLO);
+
+ return -EOPNOTSUPP;
+ }
return count;
}
-#define rtw89_debug_priv_get(name) \
+#define rtw89_debug_priv_get(name, opts...) \
{ \
.cb_read = rtw89_debug_priv_ ##name## _get, \
+ .opt = { opts }, \
}
-#define rtw89_debug_priv_set(name) \
+#define rtw89_debug_priv_set(name, opts...) \
{ \
.cb_write = rtw89_debug_priv_ ##name## _set, \
+ .opt = { opts }, \
}
-#define rtw89_debug_priv_select_and_get(name) \
+#define rtw89_debug_priv_select_and_get(name, opts...) \
{ \
.cb_write = rtw89_debug_priv_ ##name## _select, \
.cb_read = rtw89_debug_priv_ ##name## _get, \
+ .opt = { opts }, \
}
-#define rtw89_debug_priv_set_and_get(name) \
+#define rtw89_debug_priv_set_and_get(name, opts...) \
{ \
.cb_write = rtw89_debug_priv_ ##name## _set, \
.cb_read = rtw89_debug_priv_ ##name## _get, \
+ .opt = { opts }, \
}
+#define RSIZE_8K .rsize = 0x2000
+#define RSIZE_12K .rsize = 0x3000
+#define RSIZE_16K .rsize = 0x4000
+#define RSIZE_20K .rsize = 0x5000
+#define RSIZE_32K .rsize = 0x8000
+#define RSIZE_64K .rsize = 0x10000
+#define RSIZE_128K .rsize = 0x20000
+#define RSIZE_1M .rsize = 0x100000
+#define RLOCK .rlock = 1
+#define WLOCK .wlock = 1
+#define RWLOCK RLOCK, WLOCK
+
static const struct rtw89_debugfs rtw89_debugfs_templ = {
.read_reg = rtw89_debug_priv_select_and_get(read_reg),
.write_reg = rtw89_debug_priv_set(write_reg),
.read_rf = rtw89_debug_priv_select_and_get(read_rf),
.write_rf = rtw89_debug_priv_set(write_rf),
- .rf_reg_dump = rtw89_debug_priv_get(rf_reg_dump),
- .txpwr_table = rtw89_debug_priv_get(txpwr_table),
- .mac_reg_dump = rtw89_debug_priv_select_and_get(mac_reg_dump),
- .mac_mem_dump = rtw89_debug_priv_select_and_get(mac_mem_dump),
- .mac_dbg_port_dump = rtw89_debug_priv_select_and_get(mac_dbg_port_dump),
+ .rf_reg_dump = rtw89_debug_priv_get(rf_reg_dump, RSIZE_8K),
+ .txpwr_table = rtw89_debug_priv_get(txpwr_table, RSIZE_20K, RLOCK),
+ .mac_reg_dump = rtw89_debug_priv_select_and_get(mac_reg_dump, RSIZE_128K),
+ .mac_mem_dump = rtw89_debug_priv_select_and_get(mac_mem_dump, RSIZE_16K, RLOCK),
+ .mac_dbg_port_dump = rtw89_debug_priv_select_and_get(mac_dbg_port_dump, RSIZE_1M),
.send_h2c = rtw89_debug_priv_set(send_h2c),
- .early_h2c = rtw89_debug_priv_set_and_get(early_h2c),
- .fw_crash = rtw89_debug_priv_set_and_get(fw_crash),
- .btc_info = rtw89_debug_priv_get(btc_info),
+ .early_h2c = rtw89_debug_priv_set_and_get(early_h2c, RWLOCK),
+ .fw_crash = rtw89_debug_priv_set_and_get(fw_crash, WLOCK),
+ .btc_info = rtw89_debug_priv_get(btc_info, RSIZE_12K),
.btc_manual = rtw89_debug_priv_set(btc_manual),
- .fw_log_manual = rtw89_debug_priv_set(fw_log_manual),
+ .fw_log_manual = rtw89_debug_priv_set(fw_log_manual, WLOCK),
.phy_info = rtw89_debug_priv_get(phy_info),
- .stations = rtw89_debug_priv_get(stations),
- .disable_dm = rtw89_debug_priv_set_and_get(disable_dm),
+ .stations = rtw89_debug_priv_get(stations, RLOCK),
+ .disable_dm = rtw89_debug_priv_set_and_get(disable_dm, RWLOCK),
+ .mlo_mode = rtw89_debug_priv_set_and_get(mlo_mode, RWLOCK),
};
#define rtw89_debugfs_add(name, mode, fopname, parent) \
@@ -4029,6 +4399,7 @@ void rtw89_debugfs_add_sec1(struct rtw89_dev *rtwdev, struct dentry *debugfs_top
rtw89_debugfs_add_r(phy_info);
rtw89_debugfs_add_r(stations);
rtw89_debugfs_add_rw(disable_dm);
+ rtw89_debugfs_add_rw(mlo_mode);
}
void rtw89_debugfs_init(struct rtw89_dev *rtwdev)
diff --git a/drivers/net/wireless/realtek/rtw89/fw.c b/drivers/net/wireless/realtek/rtw89/fw.c
index 5d4ad23cc3bd..00b65b2995cf 100644
--- a/drivers/net/wireless/realtek/rtw89/fw.c
+++ b/drivers/net/wireless/realtek/rtw89/fw.c
@@ -15,6 +15,8 @@
#include "util.h"
#include "wow.h"
+static bool rtw89_is_any_vif_connected_or_connecting(struct rtw89_dev *rtwdev);
+
struct rtw89_eapol_2_of_2 {
u8 gtkbody[14];
u8 key_des_ver;
@@ -38,6 +40,16 @@ struct rtw89_arp_rsp {
static const u8 mss_signature[] = {0x4D, 0x53, 0x53, 0x4B, 0x50, 0x4F, 0x4F, 0x4C};
+const struct rtw89_fw_blacklist rtw89_fw_blacklist_default = {
+ .ver = 0x00,
+ .list = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ },
+};
+EXPORT_SYMBOL(rtw89_fw_blacklist_default);
+
union rtw89_fw_element_arg {
size_t offset;
enum rtw89_rf_path rf_path;
@@ -314,7 +326,7 @@ static int __parse_formatted_mssc(struct rtw89_dev *rtwdev,
if (!sec->secure_boot)
goto out;
- sb_sel_ver = le32_to_cpu(section_content->sb_sel_ver.v);
+ sb_sel_ver = get_unaligned_le32(&section_content->sb_sel_ver.v);
if (sb_sel_ver && sb_sel_ver != sec->sb_sel_mgn)
goto ignore;
@@ -344,6 +356,46 @@ ignore:
return 0;
}
+static int __check_secure_blacklist(struct rtw89_dev *rtwdev,
+ struct rtw89_fw_bin_info *info,
+ struct rtw89_fw_hdr_section_info *section_info,
+ const void *content)
+{
+ const struct rtw89_fw_blacklist *chip_blacklist = rtwdev->chip->fw_blacklist;
+ const union rtw89_fw_section_mssc_content *section_content = content;
+ struct rtw89_fw_secure *sec = &rtwdev->fw.sec;
+ u8 byte_idx;
+ u8 bit_mask;
+
+ if (!sec->secure_boot)
+ return 0;
+
+ if (!info->secure_section_exist || section_info->ignore)
+ return 0;
+
+ if (!chip_blacklist) {
+ rtw89_warn(rtwdev, "chip no blacklist for secure firmware\n");
+ return -ENOENT;
+ }
+
+ byte_idx = section_content->blacklist.bit_in_chip_list >> 3;
+ bit_mask = BIT(section_content->blacklist.bit_in_chip_list & 0x7);
+
+ if (section_content->blacklist.ver > chip_blacklist->ver) {
+ rtw89_warn(rtwdev, "chip blacklist out of date (%u, %u)\n",
+ section_content->blacklist.ver, chip_blacklist->ver);
+ return -EINVAL;
+ }
+
+ if (chip_blacklist->list[byte_idx] & bit_mask) {
+ rtw89_warn(rtwdev, "firmware %u in chip blacklist\n",
+ section_content->blacklist.ver);
+ return -EPERM;
+ }
+
+ return 0;
+}
+
static int __parse_security_section(struct rtw89_dev *rtwdev,
struct rtw89_fw_bin_info *info,
struct rtw89_fw_hdr_section_info *section_info,
@@ -364,8 +416,11 @@ static int __parse_security_section(struct rtw89_dev *rtwdev,
*mssc_len += section_info->mssc * FWDL_SECURITY_CHKSUM_LEN;
if (sec->secure_boot) {
- if (sec->mss_idx >= section_info->mssc)
+ if (sec->mss_idx >= section_info->mssc) {
+ rtw89_err(rtwdev, "unexpected MSS %d >= %d\n",
+ sec->mss_idx, section_info->mssc);
return -EFAULT;
+ }
section_info->key_addr = content + section_info->len +
sec->mss_idx * FWDL_SECURITY_SIGLEN;
section_info->key_len = FWDL_SECURITY_SIGLEN;
@@ -374,6 +429,9 @@ static int __parse_security_section(struct rtw89_dev *rtwdev,
info->secure_section_exist = true;
}
+ ret = __check_secure_blacklist(rtwdev, info, section_info, content);
+ WARN_ONCE(ret, "Current firmware in blacklist. Please update firmware.\n");
+
return 0;
}
@@ -490,18 +548,61 @@ static int rtw89_fw_hdr_parser(struct rtw89_dev *rtwdev,
}
static
+const struct rtw89_mfw_hdr *rtw89_mfw_get_hdr_ptr(struct rtw89_dev *rtwdev,
+ const struct firmware *firmware)
+{
+ const struct rtw89_mfw_hdr *mfw_hdr;
+
+ if (sizeof(*mfw_hdr) > firmware->size)
+ return NULL;
+
+ mfw_hdr = (const struct rtw89_mfw_hdr *)&firmware->data[0];
+
+ if (mfw_hdr->sig != RTW89_MFW_SIG)
+ return NULL;
+
+ return mfw_hdr;
+}
+
+static int rtw89_mfw_validate_hdr(struct rtw89_dev *rtwdev,
+ const struct firmware *firmware,
+ const struct rtw89_mfw_hdr *mfw_hdr)
+{
+ const void *mfw = firmware->data;
+ u32 mfw_len = firmware->size;
+ u8 fw_nr = mfw_hdr->fw_nr;
+ const void *ptr;
+
+ if (fw_nr == 0) {
+ rtw89_err(rtwdev, "mfw header has no fw entry\n");
+ return -ENOENT;
+ }
+
+ ptr = &mfw_hdr->info[fw_nr];
+
+ if (ptr > mfw + mfw_len) {
+ rtw89_err(rtwdev, "mfw header out of address\n");
+ return -EFAULT;
+ }
+
+ return 0;
+}
+
+static
int rtw89_mfw_recognize(struct rtw89_dev *rtwdev, enum rtw89_fw_type type,
struct rtw89_fw_suit *fw_suit, bool nowarn)
{
struct rtw89_fw_info *fw_info = &rtwdev->fw;
const struct firmware *firmware = fw_info->req.firmware;
+ const struct rtw89_mfw_info *mfw_info = NULL, *tmp;
+ const struct rtw89_mfw_hdr *mfw_hdr;
const u8 *mfw = firmware->data;
u32 mfw_len = firmware->size;
- const struct rtw89_mfw_hdr *mfw_hdr = (const struct rtw89_mfw_hdr *)mfw;
- const struct rtw89_mfw_info *mfw_info = NULL, *tmp;
+ int ret;
int i;
- if (mfw_hdr->sig != RTW89_MFW_SIG) {
+ mfw_hdr = rtw89_mfw_get_hdr_ptr(rtwdev, firmware);
+ if (!mfw_hdr) {
rtw89_debug(rtwdev, RTW89_DBG_FW, "use legacy firmware\n");
/* legacy firmware support normal type only */
if (type != RTW89_FW_NORMAL)
@@ -511,6 +612,10 @@ int rtw89_mfw_recognize(struct rtw89_dev *rtwdev, enum rtw89_fw_type type,
return 0;
}
+ ret = rtw89_mfw_validate_hdr(rtwdev, firmware, mfw_hdr);
+ if (ret)
+ return ret;
+
for (i = 0; i < mfw_hdr->fw_nr; i++) {
tmp = &mfw_hdr->info[i];
if (tmp->type != type)
@@ -540,6 +645,12 @@ int rtw89_mfw_recognize(struct rtw89_dev *rtwdev, enum rtw89_fw_type type,
found:
fw_suit->data = mfw + le32_to_cpu(mfw_info->shift);
fw_suit->size = le32_to_cpu(mfw_info->size);
+
+ if (fw_suit->data + fw_suit->size > mfw + mfw_len) {
+ rtw89_err(rtwdev, "fw_suit %d out of address\n", type);
+ return -EFAULT;
+ }
+
return 0;
}
@@ -547,16 +658,21 @@ static u32 rtw89_mfw_get_size(struct rtw89_dev *rtwdev)
{
struct rtw89_fw_info *fw_info = &rtwdev->fw;
const struct firmware *firmware = fw_info->req.firmware;
- const struct rtw89_mfw_hdr *mfw_hdr =
- (const struct rtw89_mfw_hdr *)firmware->data;
const struct rtw89_mfw_info *mfw_info;
+ const struct rtw89_mfw_hdr *mfw_hdr;
u32 size;
+ int ret;
- if (mfw_hdr->sig != RTW89_MFW_SIG) {
+ mfw_hdr = rtw89_mfw_get_hdr_ptr(rtwdev, firmware);
+ if (!mfw_hdr) {
rtw89_warn(rtwdev, "not mfw format\n");
return 0;
}
+ ret = rtw89_mfw_validate_hdr(rtwdev, firmware, mfw_hdr);
+ if (ret)
+ return ret;
+
mfw_info = &mfw_hdr->info[mfw_hdr->fw_nr - 1];
size = le32_to_cpu(mfw_info->shift) + le32_to_cpu(mfw_info->size);
@@ -735,6 +851,8 @@ static const struct __fw_feat_cfg fw_feat_tbl[] = {
__CFG_FW_FEAT(RTL8922A, lt, 0, 35, 47, 0, CH_INFO_BE_V0),
__CFG_FW_FEAT(RTL8922A, lt, 0, 35, 49, 0, RFK_PRE_NOTIFY_V1),
__CFG_FW_FEAT(RTL8922A, lt, 0, 35, 51, 0, NO_PHYCAP_P1),
+ __CFG_FW_FEAT(RTL8922A, lt, 0, 35, 64, 0, NO_POWER_DIFFERENCE),
+ __CFG_FW_FEAT(RTL8922A, ge, 0, 35, 71, 0, BEACON_LOSS_COUNT_V1),
};
static void rtw89_fw_iterate_feature_cfg(struct rtw89_fw_info *fw,
@@ -903,7 +1021,7 @@ int rtw89_build_phy_tbl_from_elm(struct rtw89_dev *rtwdev,
}
n_regs = le32_to_cpu(elm->size) / sizeof(tbl->regs[0]);
- regs = kcalloc(n_regs, sizeof(tbl->regs[0]), GFP_KERNEL);
+ regs = kcalloc(n_regs, sizeof(*regs), GFP_KERNEL);
if (!regs)
goto out;
@@ -988,7 +1106,7 @@ int rtw89_build_txpwr_trk_tbl_from_elm(struct rtw89_dev *rtwdev,
bitmap = le32_to_cpu(elm->u.txpwr_trk.bitmap);
if ((bitmap & needed_bitmap) != needed_bitmap) {
- rtw89_warn(rtwdev, "needed txpwr trk bitmap %08x but %0x8x\n",
+ rtw89_warn(rtwdev, "needed txpwr trk bitmap %08x but %08x\n",
needed_bitmap, bitmap);
return -ENOENT;
}
@@ -1056,6 +1174,101 @@ allocated:
return 0;
}
+static bool rtw89_regd_entcpy(struct rtw89_regd *regd, const void *cursor,
+ u8 cursor_size)
+{
+ /* fill default values if needed for backward compatibility */
+ struct rtw89_fw_regd_entry entry = {
+ .rule_2ghz = RTW89_NA,
+ .rule_5ghz = RTW89_NA,
+ .rule_6ghz = RTW89_NA,
+ .fmap = cpu_to_le32(0x0),
+ };
+ u8 valid_size = min_t(u8, sizeof(entry), cursor_size);
+ unsigned int i;
+ u32 fmap;
+
+ memcpy(&entry, cursor, valid_size);
+ memset(regd, 0, sizeof(*regd));
+
+ regd->alpha2[0] = entry.alpha2_0;
+ regd->alpha2[1] = entry.alpha2_1;
+ regd->alpha2[2] = '\0';
+
+ /* also need to consider forward compatibility */
+ regd->txpwr_regd[RTW89_BAND_2G] = entry.rule_2ghz < RTW89_REGD_NUM ?
+ entry.rule_2ghz : RTW89_NA;
+ regd->txpwr_regd[RTW89_BAND_5G] = entry.rule_5ghz < RTW89_REGD_NUM ?
+ entry.rule_5ghz : RTW89_NA;
+ regd->txpwr_regd[RTW89_BAND_6G] = entry.rule_6ghz < RTW89_REGD_NUM ?
+ entry.rule_6ghz : RTW89_NA;
+
+ BUILD_BUG_ON(sizeof(fmap) != sizeof(entry.fmap));
+ BUILD_BUG_ON(sizeof(fmap) * 8 < NUM_OF_RTW89_REGD_FUNC);
+
+ fmap = le32_to_cpu(entry.fmap);
+ for (i = 0; i < NUM_OF_RTW89_REGD_FUNC; i++) {
+ if (fmap & BIT(i))
+ set_bit(i, regd->func_bitmap);
+ }
+
+ return true;
+}
+
+#define rtw89_for_each_in_regd_element(regd, element) \
+ for (const void *cursor = (element)->content, \
+ *end = (element)->content + \
+ le32_to_cpu((element)->num_ents) * (element)->ent_sz; \
+ cursor < end; cursor += (element)->ent_sz) \
+ if (rtw89_regd_entcpy(regd, cursor, (element)->ent_sz))
+
+static
+int rtw89_recognize_regd_from_elm(struct rtw89_dev *rtwdev,
+ const struct rtw89_fw_element_hdr *elm,
+ const union rtw89_fw_element_arg arg)
+{
+ const struct __rtw89_fw_regd_element *regd_elm = &elm->u.regd;
+ struct rtw89_fw_elm_info *elm_info = &rtwdev->fw.elm_info;
+ u32 num_ents = le32_to_cpu(regd_elm->num_ents);
+ struct rtw89_regd_data *p;
+ struct rtw89_regd regd;
+ u32 i = 0;
+
+ if (num_ents > RTW89_REGD_MAX_COUNTRY_NUM) {
+ rtw89_warn(rtwdev,
+ "regd element ents (%d) are over max num (%d)\n",
+ num_ents, RTW89_REGD_MAX_COUNTRY_NUM);
+ rtw89_warn(rtwdev,
+ "regd element ignore and take another/common\n");
+ return 1;
+ }
+
+ if (elm_info->regd) {
+ rtw89_debug(rtwdev, RTW89_DBG_REGD,
+ "regd element take the latter\n");
+ devm_kfree(rtwdev->dev, elm_info->regd);
+ elm_info->regd = NULL;
+ }
+
+ p = devm_kzalloc(rtwdev->dev, struct_size(p, map, num_ents), GFP_KERNEL);
+ if (!p)
+ return -ENOMEM;
+
+ p->nr = num_ents;
+ rtw89_for_each_in_regd_element(&regd, regd_elm)
+ p->map[i++] = regd;
+
+ if (i != num_ents) {
+ rtw89_err(rtwdev, "regd element has %d invalid ents\n",
+ num_ents - i);
+ devm_kfree(rtwdev->dev, p);
+ return -EINVAL;
+ }
+
+ elm_info->regd = p;
+ return 0;
+}
+
static const struct rtw89_fw_element_handler __fw_element_handlers[] = {
[RTW89_FW_ELEMENT_ID_BBMCU0] = {__rtw89_fw_recognize_from_elm,
{ .fw_type = RTW89_FW_BBMCU0 }, NULL},
@@ -1088,6 +1301,18 @@ static const struct rtw89_fw_element_handler __fw_element_handlers[] = {
rtw89_fw_recognize_txpwr_from_elm,
{ .offset = offsetof(struct rtw89_rfe_data, lmt_6ghz.conf) }, NULL,
},
+ [RTW89_FW_ELEMENT_ID_TXPWR_DA_LMT_2GHZ] = {
+ rtw89_fw_recognize_txpwr_from_elm,
+ { .offset = offsetof(struct rtw89_rfe_data, da_lmt_2ghz.conf) }, NULL,
+ },
+ [RTW89_FW_ELEMENT_ID_TXPWR_DA_LMT_5GHZ] = {
+ rtw89_fw_recognize_txpwr_from_elm,
+ { .offset = offsetof(struct rtw89_rfe_data, da_lmt_5ghz.conf) }, NULL,
+ },
+ [RTW89_FW_ELEMENT_ID_TXPWR_DA_LMT_6GHZ] = {
+ rtw89_fw_recognize_txpwr_from_elm,
+ { .offset = offsetof(struct rtw89_rfe_data, da_lmt_6ghz.conf) }, NULL,
+ },
[RTW89_FW_ELEMENT_ID_TXPWR_LMT_RU_2GHZ] = {
rtw89_fw_recognize_txpwr_from_elm,
{ .offset = offsetof(struct rtw89_rfe_data, lmt_ru_2ghz.conf) }, NULL,
@@ -1100,6 +1325,18 @@ static const struct rtw89_fw_element_handler __fw_element_handlers[] = {
rtw89_fw_recognize_txpwr_from_elm,
{ .offset = offsetof(struct rtw89_rfe_data, lmt_ru_6ghz.conf) }, NULL,
},
+ [RTW89_FW_ELEMENT_ID_TXPWR_DA_LMT_RU_2GHZ] = {
+ rtw89_fw_recognize_txpwr_from_elm,
+ { .offset = offsetof(struct rtw89_rfe_data, da_lmt_ru_2ghz.conf) }, NULL,
+ },
+ [RTW89_FW_ELEMENT_ID_TXPWR_DA_LMT_RU_5GHZ] = {
+ rtw89_fw_recognize_txpwr_from_elm,
+ { .offset = offsetof(struct rtw89_rfe_data, da_lmt_ru_5ghz.conf) }, NULL,
+ },
+ [RTW89_FW_ELEMENT_ID_TXPWR_DA_LMT_RU_6GHZ] = {
+ rtw89_fw_recognize_txpwr_from_elm,
+ { .offset = offsetof(struct rtw89_rfe_data, da_lmt_ru_6ghz.conf) }, NULL,
+ },
[RTW89_FW_ELEMENT_ID_TX_SHAPE_LMT] = {
rtw89_fw_recognize_txpwr_from_elm,
{ .offset = offsetof(struct rtw89_rfe_data, tx_shape_lmt.conf) }, NULL,
@@ -1114,6 +1351,9 @@ static const struct rtw89_fw_element_handler __fw_element_handlers[] = {
[RTW89_FW_ELEMENT_ID_RFKLOG_FMT] = {
rtw89_build_rfk_log_fmt_from_elm, {}, NULL,
},
+ [RTW89_FW_ELEMENT_ID_REGD] = {
+ rtw89_recognize_regd_from_elm, {}, "REGD",
+ },
};
int rtw89_fw_recognize_elements(struct rtw89_dev *rtwdev)
@@ -1322,7 +1562,6 @@ static int __rtw89_fw_download_hdr(struct rtw89_dev *rtwdev,
ret = rtw89_h2c_tx(rtwdev, skb, false);
if (ret) {
rtw89_err(rtwdev, "failed to send h2c\n");
- ret = -1;
goto fail;
}
@@ -1409,7 +1648,6 @@ static int __rtw89_fw_download_main(struct rtw89_dev *rtwdev,
ret = rtw89_h2c_tx(rtwdev, skb, true);
if (ret) {
rtw89_err(rtwdev, "failed to send h2c\n");
- ret = -1;
goto fail;
}
@@ -2272,7 +2510,7 @@ int rtw89_fw_h2c_fw_log(struct rtw89_dev *rtwdev, bool enable)
if (enable)
comp = BIT(RTW89_FW_LOG_COMP_INIT) | BIT(RTW89_FW_LOG_COMP_TASK) |
BIT(RTW89_FW_LOG_COMP_PS) | BIT(RTW89_FW_LOG_COMP_ERROR) |
- BIT(RTW89_FW_LOG_COMP_SCAN);
+ BIT(RTW89_FW_LOG_COMP_MLO) | BIT(RTW89_FW_LOG_COMP_SCAN);
skb = rtw89_fw_h2c_alloc_skb_with_hdr(rtwdev, H2C_LOG_CFG_LEN);
if (!skb) {
@@ -2697,7 +2935,9 @@ int rtw89_fw_h2c_lps_ml_cmn_info(struct rtw89_dev *rtwdev,
{
const struct rtw89_phy_bb_gain_info_be *gain = &rtwdev->bb_gain.be;
struct rtw89_pkt_stat *pkt_stat = &rtwdev->phystat.cur_pkt_stat;
+ static const u8 bcn_bw_ofst[] = {0, 0, 0, 3, 6, 9, 0, 12};
const struct rtw89_chip_info *chip = rtwdev->chip;
+ struct rtw89_efuse *efuse = &rtwdev->efuse;
struct rtw89_h2c_lps_ml_cmn_info *h2c;
struct rtw89_vif_link *rtwvif_link;
const struct rtw89_chan *chan;
@@ -2705,6 +2945,7 @@ int rtw89_fw_h2c_lps_ml_cmn_info(struct rtw89_dev *rtwdev,
u32 len = sizeof(*h2c);
unsigned int link_id;
struct sk_buff *skb;
+ u8 beacon_bw_ofst;
u8 gain_band;
u32 done;
u8 path;
@@ -2722,9 +2963,10 @@ int rtw89_fw_h2c_lps_ml_cmn_info(struct rtw89_dev *rtwdev,
skb_put(skb, len);
h2c = (struct rtw89_h2c_lps_ml_cmn_info *)skb->data;
- h2c->fmt_id = 0x1;
+ h2c->fmt_id = 0x3;
h2c->mlo_dbcc_mode = cpu_to_le32(rtwdev->mlo_dbcc_mode);
+ h2c->rfe_type = efuse->rfe_type;
rtw89_vif_for_each_link(rtwvif, rtwvif_link, link_id) {
path = rtwvif_link->phy_idx == RTW89_PHY_1 ? RF_PATH_B : RF_PATH_A;
@@ -2745,9 +2987,21 @@ int rtw89_fw_h2c_lps_ml_cmn_info(struct rtw89_dev *rtwdev,
h2c->tia_gain[rtwvif_link->phy_idx][i] =
cpu_to_le16(gain->tia_gain[gain_band][bw_idx][path][i]);
}
+
+ if (rtwvif_link->bcn_bw_idx < ARRAY_SIZE(bcn_bw_ofst)) {
+ beacon_bw_ofst = bcn_bw_ofst[rtwvif_link->bcn_bw_idx];
+ h2c->dup_bcn_ofst[rtwvif_link->phy_idx] = beacon_bw_ofst;
+ }
+
memcpy(h2c->lna_gain[rtwvif_link->phy_idx],
gain->lna_gain[gain_band][bw_idx][path],
LNA_GAIN_NUM);
+ memcpy(h2c->tia_lna_op1db[rtwvif_link->phy_idx],
+ gain->tia_lna_op1db[gain_band][bw_idx][path],
+ LNA_GAIN_NUM + 1);
+ memcpy(h2c->lna_op1db[rtwvif_link->phy_idx],
+ gain->lna_op1db[gain_band][bw_idx][path],
+ LNA_GAIN_NUM);
}
rtw89_h2c_pkt_set_hdr(rtwdev, skb, FWCMD_TYPE_H2C,
@@ -2838,8 +3092,8 @@ static void __rtw89_fw_h2c_set_tx_path(struct rtw89_dev *rtwdev,
ntx_path = RF_A;
map_b = 0;
} else {
- ntx_path = hal->antenna_tx ? hal->antenna_tx : RF_B;
- map_b = hal->antenna_tx == RF_AB ? 1 : 0;
+ ntx_path = hal->antenna_tx ? hal->antenna_tx : RF_AB;
+ map_b = ntx_path == RF_AB ? 1 : 0;
}
SET_CMC_TBL_NTX_PATH_EN(skb->data, ntx_path);
@@ -3281,9 +3535,10 @@ int rtw89_fw_h2c_assoc_cmac_tbl_g7(struct rtw89_dev *rtwdev,
CCTLINFO_G7_W5_NOMINAL_PKT_PADDING3 |
CCTLINFO_G7_W5_NOMINAL_PKT_PADDING4);
- h2c->w6 = le32_encode_bits(vif->type == NL80211_IFTYPE_STATION ? 1 : 0,
+ h2c->w6 = le32_encode_bits(vif->cfg.aid, CCTLINFO_G7_W6_AID12_PAID) |
+ le32_encode_bits(vif->type == NL80211_IFTYPE_STATION ? 1 : 0,
CCTLINFO_G7_W6_ULDL);
- h2c->m6 = cpu_to_le32(CCTLINFO_G7_W6_ULDL);
+ h2c->m6 = cpu_to_le32(CCTLINFO_G7_W6_AID12_PAID | CCTLINFO_G7_W6_ULDL);
if (rtwsta_link) {
h2c->w8 = le32_encode_bits(link_sta->he_cap.has_he,
@@ -3419,6 +3674,61 @@ fail:
return ret;
}
+EXPORT_SYMBOL(rtw89_fw_h2c_txtime_cmac_tbl);
+
+int rtw89_fw_h2c_txtime_cmac_tbl_g7(struct rtw89_dev *rtwdev,
+ struct rtw89_sta_link *rtwsta_link)
+{
+ struct rtw89_h2c_cctlinfo_ud_g7 *h2c;
+ u32 len = sizeof(*h2c);
+ struct sk_buff *skb;
+ int ret;
+
+ skb = rtw89_fw_h2c_alloc_skb_with_hdr(rtwdev, len);
+ if (!skb) {
+ rtw89_err(rtwdev, "failed to alloc skb for txtime_cmac_g7\n");
+ return -ENOMEM;
+ }
+ skb_put(skb, len);
+ h2c = (struct rtw89_h2c_cctlinfo_ud_g7 *)skb->data;
+
+ h2c->c0 = le32_encode_bits(rtwsta_link->mac_id, CCTLINFO_G7_C0_MACID) |
+ le32_encode_bits(1, CCTLINFO_G7_C0_OP);
+
+ if (rtwsta_link->cctl_tx_time) {
+ h2c->w3 |= le32_encode_bits(1, CCTLINFO_G7_W3_AMPDU_TIME_SEL);
+ h2c->m3 |= cpu_to_le32(CCTLINFO_G7_W3_AMPDU_TIME_SEL);
+
+ h2c->w2 |= le32_encode_bits(rtwsta_link->ampdu_max_time,
+ CCTLINFO_G7_W2_AMPDU_MAX_TIME);
+ h2c->m2 |= cpu_to_le32(CCTLINFO_G7_W2_AMPDU_MAX_TIME);
+ }
+ if (rtwsta_link->cctl_tx_retry_limit) {
+ h2c->w2 |= le32_encode_bits(1, CCTLINFO_G7_W2_DATA_TXCNT_LMT_SEL) |
+ le32_encode_bits(rtwsta_link->data_tx_cnt_lmt,
+ CCTLINFO_G7_W2_DATA_TX_CNT_LMT);
+ h2c->m2 |= cpu_to_le32(CCTLINFO_G7_W2_DATA_TXCNT_LMT_SEL |
+ CCTLINFO_G7_W2_DATA_TX_CNT_LMT);
+ }
+
+ rtw89_h2c_pkt_set_hdr(rtwdev, skb, FWCMD_TYPE_H2C,
+ H2C_CAT_MAC, H2C_CL_MAC_FR_EXCHG,
+ H2C_FUNC_MAC_CCTLINFO_UD_G7, 0, 1,
+ len);
+
+ ret = rtw89_h2c_tx(rtwdev, skb, false);
+ if (ret) {
+ rtw89_err(rtwdev, "failed to send h2c\n");
+ goto fail;
+ }
+
+ return 0;
+fail:
+ dev_kfree_skb_any(skb);
+
+ return ret;
+}
+EXPORT_SYMBOL(rtw89_fw_h2c_txtime_cmac_tbl_g7);
int rtw89_fw_h2c_txpath_cmac_tbl(struct rtw89_dev *rtwdev,
struct rtw89_sta_link *rtwsta_link)
@@ -3622,14 +3932,15 @@ fail:
}
EXPORT_SYMBOL(rtw89_fw_h2c_update_beacon_be);
-#define H2C_ROLE_MAINTAIN_LEN 4
int rtw89_fw_h2c_role_maintain(struct rtw89_dev *rtwdev,
struct rtw89_vif_link *rtwvif_link,
struct rtw89_sta_link *rtwsta_link,
enum rtw89_upd_mode upd_mode)
{
- struct sk_buff *skb;
u8 mac_id = rtwsta_link ? rtwsta_link->mac_id : rtwvif_link->mac_id;
+ struct rtw89_h2c_role_maintain *h2c;
+ u32 len = sizeof(*h2c);
+ struct sk_buff *skb;
u8 self_role;
int ret;
@@ -3642,21 +3953,27 @@ int rtw89_fw_h2c_role_maintain(struct rtw89_dev *rtwdev,
self_role = rtwvif_link->self_role;
}
- skb = rtw89_fw_h2c_alloc_skb_with_hdr(rtwdev, H2C_ROLE_MAINTAIN_LEN);
+ skb = rtw89_fw_h2c_alloc_skb_with_hdr(rtwdev, len);
if (!skb) {
rtw89_err(rtwdev, "failed to alloc skb for h2c join\n");
return -ENOMEM;
}
- skb_put(skb, H2C_ROLE_MAINTAIN_LEN);
- SET_FWROLE_MAINTAIN_MACID(skb->data, mac_id);
- SET_FWROLE_MAINTAIN_SELF_ROLE(skb->data, self_role);
- SET_FWROLE_MAINTAIN_UPD_MODE(skb->data, upd_mode);
- SET_FWROLE_MAINTAIN_WIFI_ROLE(skb->data, rtwvif_link->wifi_role);
+ skb_put(skb, len);
+ h2c = (struct rtw89_h2c_role_maintain *)skb->data;
+
+ h2c->w0 = le32_encode_bits(mac_id, RTW89_H2C_ROLE_MAINTAIN_W0_MACID) |
+ le32_encode_bits(self_role, RTW89_H2C_ROLE_MAINTAIN_W0_SELF_ROLE) |
+ le32_encode_bits(upd_mode, RTW89_H2C_ROLE_MAINTAIN_W0_UPD_MODE) |
+ le32_encode_bits(rtwvif_link->wifi_role,
+ RTW89_H2C_ROLE_MAINTAIN_W0_WIFI_ROLE) |
+ le32_encode_bits(rtwvif_link->mac_idx,
+ RTW89_H2C_ROLE_MAINTAIN_W0_BAND) |
+ le32_encode_bits(rtwvif_link->port, RTW89_H2C_ROLE_MAINTAIN_W0_PORT);
rtw89_h2c_pkt_set_hdr(rtwdev, skb, FWCMD_TYPE_H2C,
H2C_CAT_MAC, H2C_CL_MAC_MEDIA_RPT,
H2C_FUNC_MAC_FWROLE_MAINTAIN, 0, 1,
- H2C_ROLE_MAINTAIN_LEN);
+ len);
ret = rtw89_h2c_tx(rtwdev, skb, false);
if (ret) {
@@ -3714,8 +4031,9 @@ out:
int rtw89_fw_h2c_join_info(struct rtw89_dev *rtwdev, struct rtw89_vif_link *rtwvif_link,
struct rtw89_sta_link *rtwsta_link, bool dis_conn)
{
- struct sk_buff *skb;
u8 mac_id = rtwsta_link ? rtwsta_link->mac_id : rtwvif_link->mac_id;
+ struct ieee80211_vif *vif = rtwvif_link_to_vif(rtwvif_link);
+ bool is_mld = ieee80211_vif_is_mld(vif);
u8 self_role = rtwvif_link->self_role;
enum rtw89_fw_sta_type sta_type;
u8 net_type = rtwvif_link->net_type;
@@ -3723,6 +4041,9 @@ int rtw89_fw_h2c_join_info(struct rtw89_dev *rtwdev, struct rtw89_vif_link *rtwv
struct rtw89_h2c_join *h2c;
u32 len = sizeof(*h2c);
bool format_v1 = false;
+ struct sk_buff *skb;
+ u8 main_mac_id;
+ bool init_ps;
int ret;
if (rtwdev->chip->chip_gen == RTW89_CHIP_BE) {
@@ -3764,8 +4085,28 @@ int rtw89_fw_h2c_join_info(struct rtw89_dev *rtwdev, struct rtw89_vif_link *rtwv
h2c_v1 = (struct rtw89_h2c_join_v1 *)skb->data;
sta_type = rtw89_fw_get_sta_type(rtwdev, rtwvif_link, rtwsta_link);
+ init_ps = rtwvif_link != rtw89_get_designated_link(rtwvif_link->rtwvif);
+
+ if (rtwsta_link)
+ main_mac_id = rtw89_sta_get_main_macid(rtwsta_link->rtwsta);
+ else
+ main_mac_id = rtw89_vif_get_main_macid(rtwvif_link->rtwvif);
+
+ h2c_v1->w1 = le32_encode_bits(sta_type, RTW89_H2C_JOININFO_W1_STA_TYPE) |
+ le32_encode_bits(is_mld, RTW89_H2C_JOININFO_W1_IS_MLD) |
+ le32_encode_bits(main_mac_id, RTW89_H2C_JOININFO_W1_MAIN_MACID) |
+ le32_encode_bits(RTW89_H2C_JOININFO_MLO_MODE_MLSR,
+ RTW89_H2C_JOININFO_W1_MLO_MODE) |
+ le32_encode_bits(0, RTW89_H2C_JOININFO_W1_EMLSR_CAB) |
+ le32_encode_bits(0, RTW89_H2C_JOININFO_W1_NSTR_EN) |
+ le32_encode_bits(init_ps, RTW89_H2C_JOININFO_W1_INIT_PWR_STATE) |
+ le32_encode_bits(IEEE80211_EML_CAP_EMLSR_PADDING_DELAY_256US,
+ RTW89_H2C_JOININFO_W1_EMLSR_PADDING) |
+ le32_encode_bits(IEEE80211_EML_CAP_EMLSR_TRANSITION_DELAY_256US,
+ RTW89_H2C_JOININFO_W1_EMLSR_TRANS_DELAY) |
+ le32_encode_bits(0, RTW89_H2C_JOININFO_W2_MACID_EXT) |
+ le32_encode_bits(0, RTW89_H2C_JOININFO_W2_MAIN_MACID_EXT);
- h2c_v1->w1 = le32_encode_bits(sta_type, RTW89_H2C_JOININFO_W1_STA_TYPE);
h2c_v1->w2 = 0;
done:
@@ -4049,6 +4390,7 @@ int rtw89_fw_h2c_set_bcn_fltr_cfg(struct rtw89_dev *rtwdev,
struct rtw89_h2c_bcnfltr *h2c;
u32 len = sizeof(*h2c);
struct sk_buff *skb;
+ u8 max_cnt, cnt;
int ret;
if (!RTW89_CHK_FW_FEATURE(BEACON_FILTER, &rtwdev->fw))
@@ -4077,12 +4419,20 @@ int rtw89_fw_h2c_set_bcn_fltr_cfg(struct rtw89_dev *rtwdev,
skb_put(skb, len);
h2c = (struct rtw89_h2c_bcnfltr *)skb->data;
+ if (RTW89_CHK_FW_FEATURE(BEACON_LOSS_COUNT_V1, &rtwdev->fw))
+ max_cnt = BIT(7) - 1;
+ else
+ max_cnt = BIT(4) - 1;
+
+ cnt = min(RTW89_BCN_LOSS_CNT, max_cnt);
+
h2c->w0 = le32_encode_bits(connect, RTW89_H2C_BCNFLTR_W0_MON_RSSI) |
le32_encode_bits(connect, RTW89_H2C_BCNFLTR_W0_MON_BCN) |
le32_encode_bits(connect, RTW89_H2C_BCNFLTR_W0_MON_EN) |
le32_encode_bits(RTW89_BCN_FLTR_OFFLOAD_MODE_DEFAULT,
RTW89_H2C_BCNFLTR_W0_MODE) |
- le32_encode_bits(RTW89_BCN_LOSS_CNT, RTW89_H2C_BCNFLTR_W0_BCN_LOSS_CNT) |
+ le32_encode_bits(cnt >> 4, RTW89_H2C_BCNFLTR_W0_BCN_LOSS_CNT_H3) |
+ le32_encode_bits(cnt & 0xf, RTW89_H2C_BCNFLTR_W0_BCN_LOSS_CNT_L4) |
le32_encode_bits(hyst, RTW89_H2C_BCNFLTR_W0_RSSI_HYST) |
le32_encode_bits(thold + MAX_RSSI,
RTW89_H2C_BCNFLTR_W0_RSSI_THRESHOLD) |
@@ -5008,12 +5358,12 @@ int rtw89_fw_h2c_add_pkt_offload(struct rtw89_dev *rtwdev, u8 *id,
}
static
-int rtw89_fw_h2c_scan_list_offload(struct rtw89_dev *rtwdev, int ch_num,
- struct list_head *chan_list)
+int rtw89_fw_h2c_scan_list_offload_ax(struct rtw89_dev *rtwdev, int ch_num,
+ struct list_head *chan_list)
{
struct rtw89_wait_info *wait = &rtwdev->mac.fw_ofld_wait;
struct rtw89_h2c_chinfo_elem *elem;
- struct rtw89_mac_chinfo *ch_info;
+ struct rtw89_mac_chinfo_ax *ch_info;
struct rtw89_h2c_chinfo *h2c;
struct sk_buff *skb;
unsigned int cond;
@@ -5187,7 +5537,7 @@ int rtw89_fw_h2c_scan_list_offload_be(struct rtw89_dev *rtwdev, int ch_num,
return 0;
}
-#define RTW89_SCAN_DELAY_TSF_UNIT 104800
+#define RTW89_SCAN_DELAY_TSF_UNIT 1000000
int rtw89_fw_h2c_scan_offload_ax(struct rtw89_dev *rtwdev,
struct rtw89_scan_option *option,
struct rtw89_vif_link *rtwvif_link,
@@ -5311,6 +5661,7 @@ int rtw89_fw_h2c_scan_offload_be(struct rtw89_dev *rtwdev,
u8 macc_role_size = sizeof(*macc_role) * option->num_macc_role;
u8 opch_size = sizeof(*opch) * option->num_opch;
u8 probe_id[NUM_NL80211_BANDS];
+ u8 scan_offload_ver = U8_MAX;
u8 cfg_len = sizeof(*h2c);
unsigned int cond;
u8 ver = U8_MAX;
@@ -5321,6 +5672,11 @@ int rtw89_fw_h2c_scan_offload_be(struct rtw89_dev *rtwdev,
rtw89_scan_get_6g_disabled_chan(rtwdev, option);
+ if (RTW89_CHK_FW_FEATURE(SCAN_OFFLOAD_BE_V0, &rtwdev->fw)) {
+ cfg_len = offsetofend(typeof(*h2c), w8);
+ scan_offload_ver = 0;
+ }
+
len = cfg_len + macc_role_size + opch_size;
skb = rtw89_fw_h2c_alloc_skb_with_hdr(rtwdev, len);
if (!skb) {
@@ -5392,10 +5748,8 @@ int rtw89_fw_h2c_scan_offload_be(struct rtw89_dev *rtwdev,
RTW89_H2C_SCANOFLD_BE_W8_PROBE_RATE_6GHZ);
}
- if (RTW89_CHK_FW_FEATURE(SCAN_OFFLOAD_BE_V0, &rtwdev->fw)) {
- cfg_len = offsetofend(typeof(*h2c), w8);
+ if (scan_offload_ver == 0)
goto flex_member;
- }
h2c->w9 = le32_encode_bits(sizeof(*h2c) / sizeof(h2c->w0),
RTW89_H2C_SCANOFLD_BE_W9_SIZE_CFG) |
@@ -5447,7 +5801,7 @@ flex_member:
RTW89_H2C_SCANOFLD_BE_OPCH_W2_PKTS_CTRL) |
le32_encode_bits(0,
RTW89_H2C_SCANOFLD_BE_OPCH_W2_SW_DEF) |
- le32_encode_bits(2,
+ le32_encode_bits(rtw89_is_mlo_1_1(rtwdev) ? 1 : 2,
RTW89_H2C_SCANOFLD_BE_OPCH_W2_SS);
opch->w3 = le32_encode_bits(RTW89_SCANOFLD_PKT_NONE,
@@ -5994,24 +6348,29 @@ void rtw89_fw_send_all_early_h2c(struct rtw89_dev *rtwdev)
{
struct rtw89_early_h2c *early_h2c;
- lockdep_assert_held(&rtwdev->mutex);
+ lockdep_assert_wiphy(rtwdev->hw->wiphy);
list_for_each_entry(early_h2c, &rtwdev->early_h2c_list, list) {
rtw89_fw_h2c_raw(rtwdev, early_h2c->h2c, early_h2c->h2c_len);
}
}
-void rtw89_fw_free_all_early_h2c(struct rtw89_dev *rtwdev)
+void __rtw89_fw_free_all_early_h2c(struct rtw89_dev *rtwdev)
{
struct rtw89_early_h2c *early_h2c, *tmp;
- mutex_lock(&rtwdev->mutex);
list_for_each_entry_safe(early_h2c, tmp, &rtwdev->early_h2c_list, list) {
list_del(&early_h2c->list);
kfree(early_h2c->h2c);
kfree(early_h2c);
}
- mutex_unlock(&rtwdev->mutex);
+}
+
+void rtw89_fw_free_all_early_h2c(struct rtw89_dev *rtwdev)
+{
+ lockdep_assert_wiphy(rtwdev->hw->wiphy);
+
+ __rtw89_fw_free_all_early_h2c(rtwdev);
}
static void rtw89_fw_c2h_parse_attr(struct sk_buff *c2h)
@@ -6055,7 +6414,7 @@ void rtw89_fw_c2h_irqsafe(struct rtw89_dev *rtwdev, struct sk_buff *c2h)
enqueue:
skb_queue_tail(&rtwdev->c2h_queue, c2h);
- ieee80211_queue_work(rtwdev->hw, &rtwdev->c2h_work);
+ wiphy_work_queue(rtwdev->hw->wiphy, &rtwdev->c2h_work);
}
static void rtw89_fw_c2h_cmd_handle(struct rtw89_dev *rtwdev,
@@ -6093,17 +6452,17 @@ static void rtw89_fw_c2h_cmd_handle(struct rtw89_dev *rtwdev,
rtw89_hex_dump(rtwdev, RTW89_DBG_FW, "C2H: ", skb->data, skb->len);
}
-void rtw89_fw_c2h_work(struct work_struct *work)
+void rtw89_fw_c2h_work(struct wiphy *wiphy, struct wiphy_work *work)
{
struct rtw89_dev *rtwdev = container_of(work, struct rtw89_dev,
c2h_work);
struct sk_buff *skb, *tmp;
+ lockdep_assert_wiphy(rtwdev->hw->wiphy);
+
skb_queue_walk_safe(&rtwdev->c2h_queue, skb, tmp) {
skb_unlink(skb, &rtwdev->c2h_queue);
- mutex_lock(&rtwdev->mutex);
rtw89_fw_c2h_cmd_handle(rtwdev, skb);
- mutex_unlock(&rtwdev->mutex);
dev_kfree_skb_any(skb);
}
}
@@ -6184,7 +6543,7 @@ int rtw89_fw_msg_reg(struct rtw89_dev *rtwdev,
u32 ret;
if (h2c_info && h2c_info->id != RTW89_FWCMD_H2CREG_FUNC_GET_FEATURE)
- lockdep_assert_held(&rtwdev->mutex);
+ lockdep_assert_wiphy(rtwdev->hw->wiphy);
if (!h2c_info && !c2h_info)
return -EINVAL;
@@ -6226,7 +6585,7 @@ void rtw89_fw_st_dbg_dump(struct rtw89_dev *rtwdev)
rtw89_fw_prog_cnt_dump(rtwdev);
}
-static void rtw89_release_pkt_list(struct rtw89_dev *rtwdev)
+static void rtw89_hw_scan_release_pkt_list(struct rtw89_dev *rtwdev)
{
struct list_head *pkt_list = rtwdev->scan_info.pkt_list;
struct rtw89_pktofld_info *info, *tmp;
@@ -6245,6 +6604,23 @@ static void rtw89_release_pkt_list(struct rtw89_dev *rtwdev)
}
}
+static void rtw89_hw_scan_cleanup(struct rtw89_dev *rtwdev,
+ struct rtw89_vif_link *rtwvif_link)
+{
+ const struct rtw89_mac_gen_def *mac = rtwdev->chip->mac_def;
+ struct rtw89_hw_scan_info *scan_info = &rtwdev->scan_info;
+ struct rtw89_vif *rtwvif = rtwvif_link->rtwvif;
+
+ mac->free_chan_list(rtwdev);
+ rtw89_hw_scan_release_pkt_list(rtwdev);
+
+ rtwvif->scan_req = NULL;
+ rtwvif->scan_ies = NULL;
+ scan_info->scanning_vif = NULL;
+ scan_info->abort = false;
+ scan_info->connected = false;
+}
+
static bool rtw89_is_6ghz_wildcard_probe_req(struct rtw89_dev *rtwdev,
struct cfg80211_scan_request *req,
struct rtw89_pktofld_info *info,
@@ -6313,7 +6689,8 @@ out:
}
static int rtw89_hw_scan_update_probe_req(struct rtw89_dev *rtwdev,
- struct rtw89_vif_link *rtwvif_link)
+ struct rtw89_vif_link *rtwvif_link,
+ const u8 *mac_addr)
{
struct rtw89_vif *rtwvif = rtwvif_link->rtwvif;
struct cfg80211_scan_request *req = rtwvif->scan_req;
@@ -6322,7 +6699,7 @@ static int rtw89_hw_scan_update_probe_req(struct rtw89_dev *rtwdev,
int ret;
for (i = 0; i < num; i++) {
- skb = ieee80211_probereq_get(rtwdev->hw, rtwvif_link->mac_addr,
+ skb = ieee80211_probereq_get(rtwdev->hw, mac_addr,
req->ssids[i].ssid,
req->ssids[i].ssid_len,
req->ie_len);
@@ -6339,10 +6716,10 @@ static int rtw89_hw_scan_update_probe_req(struct rtw89_dev *rtwdev,
return 0;
}
-static int rtw89_update_6ghz_rnr_chan(struct rtw89_dev *rtwdev,
- struct ieee80211_scan_ies *ies,
- struct cfg80211_scan_request *req,
- struct rtw89_mac_chinfo *ch_info)
+static int rtw89_update_6ghz_rnr_chan_ax(struct rtw89_dev *rtwdev,
+ struct ieee80211_scan_ies *ies,
+ struct cfg80211_scan_request *req,
+ struct rtw89_mac_chinfo_ax *ch_info)
{
struct rtw89_vif_link *rtwvif_link = rtwdev->scan_info.scanning_vif;
struct list_head *pkt_list = rtwdev->scan_info.pkt_list;
@@ -6414,7 +6791,7 @@ out:
static void rtw89_pno_scan_add_chan_ax(struct rtw89_dev *rtwdev,
int chan_type, int ssid_num,
- struct rtw89_mac_chinfo *ch_info)
+ struct rtw89_mac_chinfo_ax *ch_info)
{
struct rtw89_wow_param *rtw_wow = &rtwdev->wow;
struct rtw89_pktofld_info *info;
@@ -6462,9 +6839,9 @@ static void rtw89_pno_scan_add_chan_ax(struct rtw89_dev *rtwdev,
}
}
-static void rtw89_hw_scan_add_chan(struct rtw89_dev *rtwdev, int chan_type,
- int ssid_num,
- struct rtw89_mac_chinfo *ch_info)
+static void rtw89_hw_scan_add_chan_ax(struct rtw89_dev *rtwdev, int chan_type,
+ int ssid_num,
+ struct rtw89_mac_chinfo_ax *ch_info)
{
struct rtw89_hw_scan_info *scan_info = &rtwdev->scan_info;
struct rtw89_vif_link *rtwvif_link = rtwdev->scan_info.scanning_vif;
@@ -6495,7 +6872,7 @@ static void rtw89_hw_scan_add_chan(struct rtw89_dev *rtwdev, int chan_type,
}
}
- ret = rtw89_update_6ghz_rnr_chan(rtwdev, ies, req, ch_info);
+ ret = rtw89_update_6ghz_rnr_chan_ax(rtwdev, ies, req, ch_info);
if (ret)
rtw89_warn(rtwdev, "RNR fails: %d\n", ret);
@@ -6651,7 +7028,7 @@ int rtw89_pno_scan_add_chan_list_ax(struct rtw89_dev *rtwdev,
{
struct rtw89_wow_param *rtw_wow = &rtwdev->wow;
struct cfg80211_sched_scan_request *nd_config = rtw_wow->nd_config;
- struct rtw89_mac_chinfo *ch_info, *tmp;
+ struct rtw89_mac_chinfo_ax *ch_info, *tmp;
struct ieee80211_channel *channel;
struct list_head chan_list;
int list_len;
@@ -6685,7 +7062,7 @@ int rtw89_pno_scan_add_chan_list_ax(struct rtw89_dev *rtwdev,
rtw89_pno_scan_add_chan_ax(rtwdev, type, nd_config->n_match_sets, ch_info);
list_add_tail(&ch_info->list, &chan_list);
}
- ret = rtw89_fw_h2c_scan_list_offload(rtwdev, list_len, &chan_list);
+ ret = rtw89_fw_h2c_scan_list_offload_ax(rtwdev, list_len, &chan_list);
out:
list_for_each_entry_safe(ch_info, tmp, &chan_list, list) {
@@ -6696,24 +7073,24 @@ out:
return ret;
}
-int rtw89_hw_scan_add_chan_list_ax(struct rtw89_dev *rtwdev,
- struct rtw89_vif_link *rtwvif_link, bool connected)
+int rtw89_hw_scan_prep_chan_list_ax(struct rtw89_dev *rtwdev,
+ struct rtw89_vif_link *rtwvif_link)
{
+ struct rtw89_hw_scan_info *scan_info = &rtwdev->scan_info;
struct rtw89_vif *rtwvif = rtwvif_link->rtwvif;
struct cfg80211_scan_request *req = rtwvif->scan_req;
- struct rtw89_mac_chinfo *ch_info, *tmp;
+ struct rtw89_mac_chinfo_ax *ch_info, *tmp;
struct ieee80211_channel *channel;
struct list_head chan_list;
bool random_seq = req->flags & NL80211_SCAN_FLAG_RANDOM_SN;
- int list_len, off_chan_time = 0;
enum rtw89_chan_type type;
- int ret = 0;
+ int off_chan_time = 0;
+ int ret;
u32 idx;
INIT_LIST_HEAD(&chan_list);
- for (idx = rtwdev->scan_info.last_chan_idx, list_len = 0;
- idx < req->n_channels && list_len < RTW89_SCAN_LIST_LIMIT_AX;
- idx++, list_len++) {
+
+ for (idx = 0; idx < req->n_channels; idx++) {
channel = req->channels[idx];
ch_info = kzalloc(sizeof(*ch_info), GFP_KERNEL);
if (!ch_info) {
@@ -6740,9 +7117,9 @@ int rtw89_hw_scan_add_chan_list_ax(struct rtw89_dev *rtwdev,
type = RTW89_CHAN_DFS;
else
type = RTW89_CHAN_ACTIVE;
- rtw89_hw_scan_add_chan(rtwdev, type, req->n_ssids, ch_info);
+ rtw89_hw_scan_add_chan_ax(rtwdev, type, req->n_ssids, ch_info);
- if (connected &&
+ if (scan_info->connected &&
off_chan_time + ch_info->period > RTW89_OFF_CHAN_TIME) {
tmp = kzalloc(sizeof(*tmp), GFP_KERNEL);
if (!tmp) {
@@ -6754,16 +7131,16 @@ int rtw89_hw_scan_add_chan_list_ax(struct rtw89_dev *rtwdev,
type = RTW89_CHAN_OPERATE;
tmp->period = req->duration_mandatory ?
req->duration : RTW89_CHANNEL_TIME;
- rtw89_hw_scan_add_chan(rtwdev, type, 0, tmp);
+ rtw89_hw_scan_add_chan_ax(rtwdev, type, 0, tmp);
list_add_tail(&tmp->list, &chan_list);
off_chan_time = 0;
- list_len++;
}
list_add_tail(&ch_info->list, &chan_list);
off_chan_time += ch_info->period;
}
- rtwdev->scan_info.last_chan_idx = idx;
- ret = rtw89_fw_h2c_scan_list_offload(rtwdev, list_len, &chan_list);
+
+ list_splice_tail(&chan_list, &scan_info->chan_list);
+ return 0;
out:
list_for_each_entry_safe(ch_info, tmp, &chan_list, list) {
@@ -6774,6 +7151,46 @@ out:
return ret;
}
+void rtw89_hw_scan_free_chan_list_ax(struct rtw89_dev *rtwdev)
+{
+ struct rtw89_hw_scan_info *scan_info = &rtwdev->scan_info;
+ struct rtw89_mac_chinfo_ax *ch_info, *tmp;
+
+ list_for_each_entry_safe(ch_info, tmp, &scan_info->chan_list, list) {
+ list_del(&ch_info->list);
+ kfree(ch_info);
+ }
+}
+
+int rtw89_hw_scan_add_chan_list_ax(struct rtw89_dev *rtwdev,
+ struct rtw89_vif_link *rtwvif_link)
+{
+ struct rtw89_hw_scan_info *scan_info = &rtwdev->scan_info;
+ struct rtw89_mac_chinfo_ax *ch_info, *tmp;
+ unsigned int list_len = 0;
+ struct list_head list;
+ int ret;
+
+ INIT_LIST_HEAD(&list);
+
+ list_for_each_entry_safe(ch_info, tmp, &scan_info->chan_list, list) {
+ list_move_tail(&ch_info->list, &list);
+
+ list_len++;
+ if (list_len == RTW89_SCAN_LIST_LIMIT_AX)
+ break;
+ }
+
+ ret = rtw89_fw_h2c_scan_list_offload_ax(rtwdev, list_len, &list);
+
+ list_for_each_entry_safe(ch_info, tmp, &list, list) {
+ list_del(&ch_info->list);
+ kfree(ch_info);
+ }
+
+ return ret;
+}
+
int rtw89_pno_scan_add_chan_list_be(struct rtw89_dev *rtwdev,
struct rtw89_vif_link *rtwvif_link)
{
@@ -6827,25 +7244,24 @@ out:
return ret;
}
-int rtw89_hw_scan_add_chan_list_be(struct rtw89_dev *rtwdev,
- struct rtw89_vif_link *rtwvif_link, bool connected)
+int rtw89_hw_scan_prep_chan_list_be(struct rtw89_dev *rtwdev,
+ struct rtw89_vif_link *rtwvif_link)
{
+ struct rtw89_hw_scan_info *scan_info = &rtwdev->scan_info;
struct rtw89_vif *rtwvif = rtwvif_link->rtwvif;
struct cfg80211_scan_request *req = rtwvif->scan_req;
struct rtw89_mac_chinfo_be *ch_info, *tmp;
struct ieee80211_channel *channel;
struct list_head chan_list;
enum rtw89_chan_type type;
- int list_len, ret;
bool random_seq;
+ int ret;
u32 idx;
random_seq = !!(req->flags & NL80211_SCAN_FLAG_RANDOM_SN);
INIT_LIST_HEAD(&chan_list);
- for (idx = rtwdev->scan_info.last_chan_idx, list_len = 0;
- idx < req->n_channels && list_len < RTW89_SCAN_LIST_LIMIT_BE;
- idx++, list_len++) {
+ for (idx = 0; idx < req->n_channels; idx++) {
channel = req->channels[idx];
ch_info = kzalloc(sizeof(*ch_info), GFP_KERNEL);
if (!ch_info) {
@@ -6875,9 +7291,8 @@ int rtw89_hw_scan_add_chan_list_be(struct rtw89_dev *rtwdev,
list_add_tail(&ch_info->list, &chan_list);
}
- rtwdev->scan_info.last_chan_idx = idx;
- ret = rtw89_fw_h2c_scan_list_offload_be(rtwdev, list_len, &chan_list,
- rtwvif_link);
+ list_splice_tail(&chan_list, &scan_info->chan_list);
+ return 0;
out:
list_for_each_entry_safe(ch_info, tmp, &chan_list, list) {
@@ -6888,51 +7303,182 @@ out:
return ret;
}
+void rtw89_hw_scan_free_chan_list_be(struct rtw89_dev *rtwdev)
+{
+ struct rtw89_hw_scan_info *scan_info = &rtwdev->scan_info;
+ struct rtw89_mac_chinfo_be *ch_info, *tmp;
+
+ list_for_each_entry_safe(ch_info, tmp, &scan_info->chan_list, list) {
+ list_del(&ch_info->list);
+ kfree(ch_info);
+ }
+}
+
+int rtw89_hw_scan_add_chan_list_be(struct rtw89_dev *rtwdev,
+ struct rtw89_vif_link *rtwvif_link)
+{
+ struct rtw89_hw_scan_info *scan_info = &rtwdev->scan_info;
+ struct rtw89_mac_chinfo_be *ch_info, *tmp;
+ unsigned int list_len = 0;
+ struct list_head list;
+ int ret;
+
+ INIT_LIST_HEAD(&list);
+
+ list_for_each_entry_safe(ch_info, tmp, &scan_info->chan_list, list) {
+ list_move_tail(&ch_info->list, &list);
+
+ list_len++;
+ if (list_len == RTW89_SCAN_LIST_LIMIT_BE)
+ break;
+ }
+
+ ret = rtw89_fw_h2c_scan_list_offload_be(rtwdev, list_len, &list,
+ rtwvif_link);
+
+ list_for_each_entry_safe(ch_info, tmp, &list, list) {
+ list_del(&ch_info->list);
+ kfree(ch_info);
+ }
+
+ return ret;
+}
+
static int rtw89_hw_scan_prehandle(struct rtw89_dev *rtwdev,
- struct rtw89_vif_link *rtwvif_link, bool connected)
+ struct rtw89_vif_link *rtwvif_link,
+ const u8 *mac_addr)
{
const struct rtw89_mac_gen_def *mac = rtwdev->chip->mac_def;
int ret;
- ret = rtw89_hw_scan_update_probe_req(rtwdev, rtwvif_link);
+ ret = rtw89_hw_scan_update_probe_req(rtwdev, rtwvif_link, mac_addr);
if (ret) {
rtw89_err(rtwdev, "Update probe request failed\n");
goto out;
}
- ret = mac->add_chan_list(rtwdev, rtwvif_link, connected);
+ ret = mac->prep_chan_list(rtwdev, rtwvif_link);
out:
return ret;
}
-void rtw89_hw_scan_start(struct rtw89_dev *rtwdev,
- struct rtw89_vif_link *rtwvif_link,
- struct ieee80211_scan_request *scan_req)
+static void rtw89_hw_scan_update_link_beacon_noa(struct rtw89_dev *rtwdev,
+ struct rtw89_vif_link *rtwvif_link,
+ u16 tu)
+{
+ struct ieee80211_p2p_noa_desc noa_desc = {};
+ u64 tsf;
+ int ret;
+
+ ret = rtw89_mac_port_get_tsf(rtwdev, rtwvif_link, &tsf);
+ if (ret) {
+ rtw89_warn(rtwdev, "%s: failed to get tsf\n", __func__);
+ return;
+ }
+
+ noa_desc.start_time = cpu_to_le32(tsf);
+ noa_desc.interval = cpu_to_le32(ieee80211_tu_to_usec(tu));
+ noa_desc.duration = cpu_to_le32(ieee80211_tu_to_usec(tu));
+ noa_desc.count = 1;
+
+ rtw89_p2p_noa_renew(rtwvif_link);
+ rtw89_p2p_noa_append(rtwvif_link, &noa_desc);
+ rtw89_chip_h2c_update_beacon(rtwdev, rtwvif_link);
+}
+
+static void rtw89_hw_scan_update_beacon_noa(struct rtw89_dev *rtwdev,
+ const struct cfg80211_scan_request *req)
+{
+ const struct rtw89_entity_mgnt *mgnt = &rtwdev->hal.entity_mgnt;
+ const struct rtw89_hw_scan_info *scan_info = &rtwdev->scan_info;
+ const struct rtw89_chip_info *chip = rtwdev->chip;
+ struct rtw89_mac_chinfo_ax *chinfo_ax;
+ struct rtw89_mac_chinfo_be *chinfo_be;
+ struct rtw89_vif_link *rtwvif_link;
+ struct list_head *pos, *tmp;
+ struct ieee80211_vif *vif;
+ struct rtw89_vif *rtwvif;
+ u16 tu = 0;
+
+ lockdep_assert_wiphy(rtwdev->hw->wiphy);
+
+ list_for_each_safe(pos, tmp, &scan_info->chan_list) {
+ switch (chip->chip_gen) {
+ case RTW89_CHIP_AX:
+ chinfo_ax = list_entry(pos, typeof(*chinfo_ax), list);
+ tu += chinfo_ax->period;
+ break;
+ case RTW89_CHIP_BE:
+ chinfo_be = list_entry(pos, typeof(*chinfo_be), list);
+ tu += chinfo_be->period;
+ break;
+ default:
+ rtw89_warn(rtwdev, "%s: invalid chip gen %d\n",
+ __func__, chip->chip_gen);
+ return;
+ }
+ }
+
+ if (unlikely(tu == 0)) {
+ rtw89_debug(rtwdev, RTW89_DBG_HW_SCAN,
+ "%s: cannot estimate needed TU\n", __func__);
+ return;
+ }
+
+ list_for_each_entry(rtwvif, &mgnt->active_list, mgnt_entry) {
+ unsigned int link_id;
+
+ vif = rtwvif_to_vif(rtwvif);
+ if (vif->type != NL80211_IFTYPE_AP || !vif->p2p)
+ continue;
+
+ rtw89_vif_for_each_link(rtwvif, rtwvif_link, link_id)
+ rtw89_hw_scan_update_link_beacon_noa(rtwdev, rtwvif_link, tu);
+ }
+}
+
+int rtw89_hw_scan_start(struct rtw89_dev *rtwdev,
+ struct rtw89_vif_link *rtwvif_link,
+ struct ieee80211_scan_request *scan_req)
{
const struct rtw89_mac_gen_def *mac = rtwdev->chip->mac_def;
+ enum rtw89_entity_mode mode = rtw89_get_entity_mode(rtwdev);
struct cfg80211_scan_request *req = &scan_req->req;
const struct rtw89_chan *chan = rtw89_chan_get(rtwdev,
rtwvif_link->chanctx_idx);
struct rtw89_vif *rtwvif = rtwvif_link->rtwvif;
+ struct rtw89_chanctx_pause_parm pause_parm = {
+ .rsn = RTW89_CHANCTX_PAUSE_REASON_HW_SCAN,
+ .trigger = rtwvif_link,
+ };
u32 rx_fltr = rtwdev->hal.rx_fltr;
u8 mac_addr[ETH_ALEN];
u32 reg;
+ int ret;
/* clone op and keep it during scan */
rtwdev->scan_info.op_chan = *chan;
+ rtwdev->scan_info.connected = rtw89_is_any_vif_connected_or_connecting(rtwdev);
rtwdev->scan_info.scanning_vif = rtwvif_link;
- rtwdev->scan_info.last_chan_idx = 0;
rtwdev->scan_info.abort = false;
rtwvif->scan_ies = &scan_req->ies;
rtwvif->scan_req = req;
- ieee80211_stop_queues(rtwdev->hw);
- rtw89_mac_port_cfg_rx_sync(rtwdev, rtwvif_link, false);
if (req->flags & NL80211_SCAN_FLAG_RANDOM_ADDR)
get_random_mask_addr(mac_addr, req->mac_addr,
req->mac_addr_mask);
else
ether_addr_copy(mac_addr, rtwvif_link->mac_addr);
+
+ ret = rtw89_hw_scan_prehandle(rtwdev, rtwvif_link, mac_addr);
+ if (ret) {
+ rtw89_hw_scan_cleanup(rtwdev, rtwvif_link);
+ return ret;
+ }
+
+ ieee80211_stop_queues(rtwdev->hw);
+ rtw89_mac_port_cfg_rx_sync(rtwdev, rtwvif_link, false);
+
rtw89_core_scan_start(rtwdev, rtwvif_link, mac_addr, true);
rx_fltr &= ~B_AX_A_BCN_CHK_EN;
@@ -6942,7 +7488,12 @@ void rtw89_hw_scan_start(struct rtw89_dev *rtwdev,
reg = rtw89_mac_reg_by_idx(rtwdev, mac->rx_fltr, rtwvif_link->mac_idx);
rtw89_write32_mask(rtwdev, reg, B_AX_RX_FLTR_CFG_MASK, rx_fltr);
- rtw89_chanctx_pause(rtwdev, RTW89_CHANCTX_PAUSE_REASON_HW_SCAN);
+ rtw89_chanctx_pause(rtwdev, &pause_parm);
+
+ if (mode == RTW89_ENTITY_MODE_MCC)
+ rtw89_hw_scan_update_beacon_noa(rtwdev, req);
+
+ return 0;
}
struct rtw89_hw_scan_complete_cb_data {
@@ -6953,20 +7504,16 @@ struct rtw89_hw_scan_complete_cb_data {
static int rtw89_hw_scan_complete_cb(struct rtw89_dev *rtwdev, void *data)
{
const struct rtw89_mac_gen_def *mac = rtwdev->chip->mac_def;
- struct rtw89_hw_scan_info *scan_info = &rtwdev->scan_info;
struct rtw89_hw_scan_complete_cb_data *cb_data = data;
struct rtw89_vif_link *rtwvif_link = cb_data->rtwvif_link;
struct cfg80211_scan_info info = {
.aborted = cb_data->aborted,
};
- struct rtw89_vif *rtwvif;
u32 reg;
if (!rtwvif_link)
return -EINVAL;
- rtwvif = rtwvif_link->rtwvif;
-
reg = rtw89_mac_reg_by_idx(rtwdev, mac->rx_fltr, rtwvif_link->mac_idx);
rtw89_write32_mask(rtwdev, reg, B_AX_RX_FLTR_CFG_MASK, rtwdev->hal.rx_fltr);
@@ -6976,12 +7523,7 @@ static int rtw89_hw_scan_complete_cb(struct rtw89_dev *rtwdev, void *data)
rtw89_mac_port_cfg_rx_sync(rtwdev, rtwvif_link, true);
rtw89_mac_enable_beacon_for_ap_vifs(rtwdev, true);
- rtw89_release_pkt_list(rtwdev);
- rtwvif->scan_req = NULL;
- rtwvif->scan_ies = NULL;
- scan_info->last_chan_idx = 0;
- scan_info->scanning_vif = NULL;
- scan_info->abort = false;
+ rtw89_hw_scan_cleanup(rtwdev, rtwvif_link);
return 0;
}
@@ -7056,11 +7598,11 @@ int rtw89_hw_scan_offload(struct rtw89_dev *rtwdev,
if (!rtwvif_link)
return -EINVAL;
- connected = rtw89_is_any_vif_connected_or_connecting(rtwdev);
+ connected = rtwdev->scan_info.connected;
opt.enable = enable;
opt.target_ch_mode = connected;
if (enable) {
- ret = rtw89_hw_scan_prehandle(rtwdev, rtwvif_link, connected);
+ ret = mac->add_chan_list(rtwdev, rtwvif_link);
if (ret)
goto out;
}
@@ -8420,6 +8962,47 @@ int rtw89_fw_h2c_ap_info_refcount(struct rtw89_dev *rtwdev, bool en)
return 0;
}
+int rtw89_fw_h2c_mlo_link_cfg(struct rtw89_dev *rtwdev, struct rtw89_vif_link *rtwvif_link,
+ bool enable)
+{
+ struct rtw89_wait_info *wait = &rtwdev->mlo.wait;
+ struct rtw89_h2c_mlo_link_cfg *h2c;
+ u8 mac_id = rtwvif_link->mac_id;
+ u32 len = sizeof(*h2c);
+ struct sk_buff *skb;
+ unsigned int cond;
+ int ret;
+
+ skb = rtw89_fw_h2c_alloc_skb_with_hdr(rtwdev, len);
+ if (!skb) {
+ rtw89_err(rtwdev, "failed to alloc skb for mlo link cfg\n");
+ return -ENOMEM;
+ }
+
+ skb_put(skb, len);
+ h2c = (struct rtw89_h2c_mlo_link_cfg *)skb->data;
+
+ h2c->w0 = le32_encode_bits(mac_id, RTW89_H2C_MLO_LINK_CFG_W0_MACID) |
+ le32_encode_bits(enable, RTW89_H2C_MLO_LINK_CFG_W0_OPTION);
+
+ rtw89_h2c_pkt_set_hdr(rtwdev, skb, FWCMD_TYPE_H2C,
+ H2C_CAT_MAC,
+ H2C_CL_MLO,
+ H2C_FUNC_MLO_LINK_CFG, 0, 0,
+ len);
+
+ cond = RTW89_MLO_WAIT_COND(mac_id, H2C_FUNC_MLO_LINK_CFG);
+
+ ret = rtw89_h2c_tx_and_wait(rtwdev, skb, wait, cond);
+ if (ret) {
+ rtw89_err(rtwdev, "mlo link cfg (%s link id %u) failed: %d\n",
+ str_enable_disable(enable), rtwvif_link->link_id, ret);
+ return ret;
+ }
+
+ return 0;
+}
+
static bool __fw_txpwr_entry_zero_ext(const void *ext_ptr, u8 ext_len)
{
static const u8 zeros[U8_MAX] = {};
@@ -8807,6 +9390,26 @@ void rtw89_fw_load_tx_shape_lmt_ru(struct rtw89_tx_shape_lmt_ru_data *data)
}
}
+static bool rtw89_fw_has_da_txpwr_table(struct rtw89_dev *rtwdev,
+ const struct rtw89_rfe_parms *parms)
+{
+ const struct rtw89_chip_info *chip = rtwdev->chip;
+
+ if (chip->support_bands & BIT(NL80211_BAND_2GHZ) &&
+ !(parms->rule_da_2ghz.lmt && parms->rule_da_2ghz.lmt_ru))
+ return false;
+
+ if (chip->support_bands & BIT(NL80211_BAND_5GHZ) &&
+ !(parms->rule_da_5ghz.lmt && parms->rule_da_5ghz.lmt_ru))
+ return false;
+
+ if (chip->support_bands & BIT(NL80211_BAND_6GHZ) &&
+ !(parms->rule_da_6ghz.lmt && parms->rule_da_6ghz.lmt_ru))
+ return false;
+
+ return true;
+}
+
const struct rtw89_rfe_parms *
rtw89_load_rfe_data_from_fw(struct rtw89_dev *rtwdev,
const struct rtw89_rfe_parms *init)
@@ -8843,6 +9446,21 @@ rtw89_load_rfe_data_from_fw(struct rtw89_dev *rtwdev,
parms->rule_6ghz.lmt = &rfe_data->lmt_6ghz.v;
}
+ if (rtw89_txpwr_conf_valid(&rfe_data->da_lmt_2ghz.conf)) {
+ rtw89_fw_load_txpwr_lmt_2ghz(&rfe_data->da_lmt_2ghz);
+ parms->rule_da_2ghz.lmt = &rfe_data->da_lmt_2ghz.v;
+ }
+
+ if (rtw89_txpwr_conf_valid(&rfe_data->da_lmt_5ghz.conf)) {
+ rtw89_fw_load_txpwr_lmt_5ghz(&rfe_data->da_lmt_5ghz);
+ parms->rule_da_5ghz.lmt = &rfe_data->da_lmt_5ghz.v;
+ }
+
+ if (rtw89_txpwr_conf_valid(&rfe_data->da_lmt_6ghz.conf)) {
+ rtw89_fw_load_txpwr_lmt_6ghz(&rfe_data->da_lmt_6ghz);
+ parms->rule_da_6ghz.lmt = &rfe_data->da_lmt_6ghz.v;
+ }
+
if (rtw89_txpwr_conf_valid(&rfe_data->lmt_ru_2ghz.conf)) {
rtw89_fw_load_txpwr_lmt_ru_2ghz(&rfe_data->lmt_ru_2ghz);
parms->rule_2ghz.lmt_ru = &rfe_data->lmt_ru_2ghz.v;
@@ -8858,6 +9476,21 @@ rtw89_load_rfe_data_from_fw(struct rtw89_dev *rtwdev,
parms->rule_6ghz.lmt_ru = &rfe_data->lmt_ru_6ghz.v;
}
+ if (rtw89_txpwr_conf_valid(&rfe_data->da_lmt_ru_2ghz.conf)) {
+ rtw89_fw_load_txpwr_lmt_ru_2ghz(&rfe_data->da_lmt_ru_2ghz);
+ parms->rule_da_2ghz.lmt_ru = &rfe_data->da_lmt_ru_2ghz.v;
+ }
+
+ if (rtw89_txpwr_conf_valid(&rfe_data->da_lmt_ru_5ghz.conf)) {
+ rtw89_fw_load_txpwr_lmt_ru_5ghz(&rfe_data->da_lmt_ru_5ghz);
+ parms->rule_da_5ghz.lmt_ru = &rfe_data->da_lmt_ru_5ghz.v;
+ }
+
+ if (rtw89_txpwr_conf_valid(&rfe_data->da_lmt_ru_6ghz.conf)) {
+ rtw89_fw_load_txpwr_lmt_ru_6ghz(&rfe_data->da_lmt_ru_6ghz);
+ parms->rule_da_6ghz.lmt_ru = &rfe_data->da_lmt_ru_6ghz.v;
+ }
+
if (rtw89_txpwr_conf_valid(&rfe_data->tx_shape_lmt.conf)) {
rtw89_fw_load_tx_shape_lmt(&rfe_data->tx_shape_lmt);
parms->tx_shape.lmt = &rfe_data->tx_shape_lmt.v;
@@ -8868,5 +9501,7 @@ rtw89_load_rfe_data_from_fw(struct rtw89_dev *rtwdev,
parms->tx_shape.lmt_ru = &rfe_data->tx_shape_lmt_ru.v;
}
+ parms->has_da = rtw89_fw_has_da_txpwr_table(rtwdev, parms);
+
return parms;
}
diff --git a/drivers/net/wireless/realtek/rtw89/fw.h b/drivers/net/wireless/realtek/rtw89/fw.h
index 2026bc2fd2ac..0fcc824e41be 100644
--- a/drivers/net/wireless/realtek/rtw89/fw.h
+++ b/drivers/net/wireless/realtek/rtw89/fw.h
@@ -199,6 +199,7 @@ enum rtw89_fw_log_comp {
RTW89_FW_LOG_COMP_TWT,
RTW89_FW_LOG_COMP_RF,
RTW89_FW_LOG_COMP_MCC = 20,
+ RTW89_FW_LOG_COMP_MLO = 26,
RTW89_FW_LOG_COMP_SCAN = 28,
};
@@ -333,9 +334,9 @@ struct rtw89_fw_macid_pause_sleep_grp {
#define RTW89_SCAN_LIST_LIMIT_AX RTW89_SCAN_LIST_LIMIT(RTW89_MAC_CHINFO_SIZE)
#define RTW89_SCAN_LIST_LIMIT_BE RTW89_SCAN_LIST_LIMIT(RTW89_MAC_CHINFO_SIZE_BE)
-#define RTW89_BCN_LOSS_CNT 10
+#define RTW89_BCN_LOSS_CNT 60
-struct rtw89_mac_chinfo {
+struct rtw89_mac_chinfo_ax {
u8 period;
u8 dwell_time;
u8 central_ch;
@@ -664,6 +665,11 @@ struct rtw89_fw_mss_pool_hdr {
union rtw89_fw_section_mssc_content {
struct {
+ u8 pad[0x20];
+ u8 bit_in_chip_list;
+ u8 ver;
+ } __packed blacklist;
+ struct {
u8 pad[58];
__le32 v;
} __packed sb_sel_ver;
@@ -673,6 +679,13 @@ union rtw89_fw_section_mssc_content {
} __packed key_sign_len;
} __packed;
+struct rtw89_fw_blacklist {
+ u8 ver;
+ u8 list[32];
+};
+
+extern const struct rtw89_fw_blacklist rtw89_fw_blacklist_default;
+
static inline void SET_CTRL_INFO_MACID(void *table, u32 val)
{
le32p_replace_bits((__le32 *)(table) + 0, val, GENMASK(6, 0));
@@ -1579,25 +1592,17 @@ struct rtw89_h2c_bcn_upd_be {
#define RTW89_H2C_BCN_UPD_BE_W7_ECSA_OFST GENMASK(30, 16)
#define RTW89_H2C_BCN_UPD_BE_W7_PROTECTION_KEY_ID BIT(31)
-static inline void SET_FWROLE_MAINTAIN_MACID(void *h2c, u32 val)
-{
- le32p_replace_bits((__le32 *)h2c, val, GENMASK(7, 0));
-}
-
-static inline void SET_FWROLE_MAINTAIN_SELF_ROLE(void *h2c, u32 val)
-{
- le32p_replace_bits((__le32 *)h2c, val, GENMASK(9, 8));
-}
-
-static inline void SET_FWROLE_MAINTAIN_UPD_MODE(void *h2c, u32 val)
-{
- le32p_replace_bits((__le32 *)h2c, val, GENMASK(12, 10));
-}
+struct rtw89_h2c_role_maintain {
+ __le32 w0;
+};
-static inline void SET_FWROLE_MAINTAIN_WIFI_ROLE(void *h2c, u32 val)
-{
- le32p_replace_bits((__le32 *)h2c, val, GENMASK(16, 13));
-}
+#define RTW89_H2C_ROLE_MAINTAIN_W0_MACID GENMASK(7, 0)
+#define RTW89_H2C_ROLE_MAINTAIN_W0_SELF_ROLE GENMASK(9, 8)
+#define RTW89_H2C_ROLE_MAINTAIN_W0_UPD_MODE GENMASK(12, 10)
+#define RTW89_H2C_ROLE_MAINTAIN_W0_WIFI_ROLE GENMASK(16, 13)
+#define RTW89_H2C_ROLE_MAINTAIN_W0_BAND GENMASK(18, 17)
+#define RTW89_H2C_ROLE_MAINTAIN_W0_PORT GENMASK(21, 19)
+#define RTW89_H2C_ROLE_MAINTAIN_W0_MACID_EXT GENMASK(31, 24)
enum rtw89_fw_sta_type { /* value of RTW89_H2C_JOININFO_W1_STA_TYPE */
RTW89_FW_N_AC_STA = 0,
@@ -1632,6 +1637,8 @@ struct rtw89_h2c_join_v1 {
#define RTW89_H2C_JOININFO_W1_IS_MLD BIT(3)
#define RTW89_H2C_JOININFO_W1_MAIN_MACID GENMASK(11, 4)
#define RTW89_H2C_JOININFO_W1_MLO_MODE BIT(12)
+#define RTW89_H2C_JOININFO_MLO_MODE_MLMR 0
+#define RTW89_H2C_JOININFO_MLO_MODE_MLSR 1
#define RTW89_H2C_JOININFO_W1_EMLSR_CAB BIT(13)
#define RTW89_H2C_JOININFO_W1_NSTR_EN BIT(14)
#define RTW89_H2C_JOININFO_W1_INIT_PWR_STATE BIT(15)
@@ -1801,17 +1808,21 @@ struct rtw89_h2c_lps_ch_info {
struct rtw89_h2c_lps_ml_cmn_info {
u8 fmt_id;
- u8 rsvd0[3];
+ u8 rfe_type;
+ u8 rsvd0[2];
__le32 mlo_dbcc_mode;
- u8 central_ch[RTW89_PHY_MAX];
- u8 pri_ch[RTW89_PHY_MAX];
- u8 bw[RTW89_PHY_MAX];
- u8 band[RTW89_PHY_MAX];
- u8 bcn_rate_type[RTW89_PHY_MAX];
+ u8 central_ch[RTW89_PHY_NUM];
+ u8 pri_ch[RTW89_PHY_NUM];
+ u8 bw[RTW89_PHY_NUM];
+ u8 band[RTW89_PHY_NUM];
+ u8 bcn_rate_type[RTW89_PHY_NUM];
u8 rsvd1[2];
- __le16 tia_gain[RTW89_PHY_MAX][TIA_GAIN_NUM];
- u8 lna_gain[RTW89_PHY_MAX][LNA_GAIN_NUM];
+ __le16 tia_gain[RTW89_PHY_NUM][TIA_GAIN_NUM];
+ u8 lna_gain[RTW89_PHY_NUM][LNA_GAIN_NUM];
u8 rsvd2[2];
+ u8 tia_lna_op1db[RTW89_PHY_NUM][LNA_GAIN_NUM + 1];
+ u8 lna_op1db[RTW89_PHY_NUM][LNA_GAIN_NUM];
+ u8 dup_bcn_ofst[RTW89_PHY_NUM];
} __packed;
static inline void RTW89_SET_FWCMD_CPU_EXCEPTION_TYPE(void *cmd, u32 val)
@@ -2855,6 +2866,13 @@ struct rtw89_h2c_fwips {
#define RTW89_H2C_FW_IPS_W0_MACID GENMASK(7, 0)
#define RTW89_H2C_FW_IPS_W0_ENABLE BIT(8)
+struct rtw89_h2c_mlo_link_cfg {
+ __le32 w0;
+};
+
+#define RTW89_H2C_MLO_LINK_CFG_W0_MACID GENMASK(15, 0)
+#define RTW89_H2C_MLO_LINK_CFG_W0_OPTION GENMASK(19, 16)
+
static inline void RTW89_SET_FWCMD_P2P_MACID(void *cmd, u32 val)
{
le32p_replace_bits((__le32 *)cmd, val, GENMASK(7, 0));
@@ -3554,6 +3572,7 @@ struct rtw89_c2h_done_ack {
#define RTW89_C2H_DONE_ACK_W2_CLASS GENMASK(7, 2)
#define RTW89_C2H_DONE_ACK_W2_FUNC GENMASK(15, 8)
#define RTW89_C2H_DONE_ACK_W2_H2C_RETURN GENMASK(23, 16)
+#define RTW89_C2H_SCAN_DONE_ACK_RETURN GENMASK(5, 0)
#define RTW89_C2H_DONE_ACK_W2_H2C_SEQ GENMASK(31, 24)
#define RTW89_GET_MAC_C2H_REV_ACK_CAT(c2h) \
@@ -3612,6 +3631,19 @@ struct rtw89_c2h_ra_rpt {
#define RTW89_C2H_RA_RPT_W3_MD_SEL_B2 BIT(15)
#define RTW89_C2H_RA_RPT_W3_BW_B2 BIT(16)
+struct rtw89_c2h_fw_scan_rpt {
+ struct rtw89_c2h_hdr hdr;
+ u8 phy_idx;
+ u8 band;
+ u8 center_ch;
+ u8 ofdm_pd_idx; /* in unit of 2 dBm */
+#define PD_LOWER_BOUND_BASE 102
+ s8 cck_pd_idx;
+ u8 rsvd0;
+ u8 rsvd1;
+ u8 rsvd2;
+} __packed;
+
/* For WiFi 6 chips:
* VHT, HE, HT-old: [6:4]: NSS, [3:0]: MCS
* HT-new: [6:5]: NA, [4:0]: MCS
@@ -3709,6 +3741,25 @@ static_assert(sizeof(struct rtw89_mac_mcc_tsf_rpt) <= RTW89_COMPLETION_BUF_SIZE)
#define RTW89_GET_MAC_C2H_MCC_STATUS_RPT_TSF_HIGH(c2h) \
le32_get_bits(*((const __le32 *)(c2h) + 4), GENMASK(31, 0))
+struct rtw89_c2h_mlo_link_cfg_rpt {
+ struct rtw89_c2h_hdr hdr;
+ __le32 w2;
+} __packed;
+
+#define RTW89_C2H_MLO_LINK_CFG_RPT_W2_MACID GENMASK(15, 0)
+#define RTW89_C2H_MLO_LINK_CFG_RPT_W2_STATUS GENMASK(19, 16)
+
+enum rtw89_c2h_mlo_link_status {
+ RTW89_C2H_MLO_LINK_CFG_IDLE = 0,
+ RTW89_C2H_MLO_LINK_CFG_DONE = 1,
+ RTW89_C2H_MLO_LINK_CFG_ISSUE_NULL_FAIL = 2,
+ RTW89_C2H_MLO_LINK_CFG_TX_NULL_FAIL = 3,
+ RTW89_C2H_MLO_LINK_CFG_ROLE_NOT_EXIST = 4,
+ RTW89_C2H_MLO_LINK_CFG_NULL_1_TIMEOUT = 5,
+ RTW89_C2H_MLO_LINK_CFG_NULL_0_TIMEOUT = 6,
+ RTW89_C2H_MLO_LINK_CFG_RUNNING = 0xff,
+};
+
struct rtw89_mac_mrc_tsf_rpt {
unsigned int num;
u64 tsfs[RTW89_MAC_MRC_MAX_REQ_TSF_NUM];
@@ -3805,7 +3856,8 @@ struct rtw89_h2c_bcnfltr {
#define RTW89_H2C_BCNFLTR_W0_MON_BCN BIT(1)
#define RTW89_H2C_BCNFLTR_W0_MON_EN BIT(2)
#define RTW89_H2C_BCNFLTR_W0_MODE GENMASK(4, 3)
-#define RTW89_H2C_BCNFLTR_W0_BCN_LOSS_CNT GENMASK(11, 8)
+#define RTW89_H2C_BCNFLTR_W0_BCN_LOSS_CNT_H3 GENMASK(7, 5)
+#define RTW89_H2C_BCNFLTR_W0_BCN_LOSS_CNT_L4 GENMASK(11, 8)
#define RTW89_H2C_BCNFLTR_W0_RSSI_HYST GENMASK(15, 12)
#define RTW89_H2C_BCNFLTR_W0_RSSI_THRESHOLD GENMASK(23, 16)
#define RTW89_H2C_BCNFLTR_W0_MAC_ID GENMASK(31, 24)
@@ -3882,6 +3934,13 @@ enum rtw89_fw_element_id {
RTW89_FW_ELEMENT_ID_TX_SHAPE_LMT_RU = 17,
RTW89_FW_ELEMENT_ID_TXPWR_TRK = 18,
RTW89_FW_ELEMENT_ID_RFKLOG_FMT = 19,
+ RTW89_FW_ELEMENT_ID_REGD = 20,
+ RTW89_FW_ELEMENT_ID_TXPWR_DA_LMT_2GHZ = 21,
+ RTW89_FW_ELEMENT_ID_TXPWR_DA_LMT_5GHZ = 22,
+ RTW89_FW_ELEMENT_ID_TXPWR_DA_LMT_6GHZ = 23,
+ RTW89_FW_ELEMENT_ID_TXPWR_DA_LMT_RU_2GHZ = 24,
+ RTW89_FW_ELEMENT_ID_TXPWR_DA_LMT_RU_5GHZ = 25,
+ RTW89_FW_ELEMENT_ID_TXPWR_DA_LMT_RU_6GHZ = 26,
RTW89_FW_ELEMENT_ID_NUM,
};
@@ -3925,6 +3984,15 @@ struct __rtw89_fw_txpwr_element {
u8 content[];
} __packed;
+struct __rtw89_fw_regd_element {
+ u8 rsvd0;
+ u8 rsvd1;
+ u8 rsvd2;
+ u8 ent_sz;
+ __le32 num_ents;
+ u8 content[];
+} __packed;
+
enum rtw89_fw_txpwr_trk_type {
__RTW89_FW_TXPWR_TRK_TYPE_6GHZ_START = 0,
RTW89_FW_TXPWR_TRK_TYPE_6GB_N = 0,
@@ -4016,6 +4084,7 @@ struct rtw89_fw_element_hdr {
__le16 offset[];
} __packed rfk_log_fmt;
struct __rtw89_fw_txpwr_element txpwr;
+ struct __rtw89_fw_regd_element regd;
} __packed u;
} __packed;
@@ -4210,6 +4279,26 @@ enum rtw89_mcc_h2c_func {
#define RTW89_MCC_WAIT_COND(group, func) \
((group) * NUM_OF_RTW89_MCC_H2C_FUNC + (func))
+/* CLASS 20 - MLO */
+#define H2C_CL_MLO 0x14
+enum rtw89_mlo_h2c_func {
+ H2C_FUNC_MLO_TBL_CFG = 0x0,
+ H2C_FUNC_MLO_STA_CFG = 0x1,
+ H2C_FUNC_MLO_TTLM = 0x2,
+ H2C_FUNC_MLO_DM_CFG = 0x3,
+ H2C_FUNC_MLO_EMLSR_STA_CFG = 0x4,
+ H2C_FUNC_MLO_MCMLO_RELINK_DROP = 0x5,
+ H2C_FUNC_MLO_MCMLO_SN_SYNC = 0x6,
+ H2C_FUNC_MLO_RELINK = 0x7,
+ H2C_FUNC_MLO_LINK_CFG = 0x8,
+ H2C_FUNC_MLO_DM_DBG = 0x9,
+
+ NUM_OF_RTW89_MLO_H2C_FUNC,
+};
+
+#define RTW89_MLO_WAIT_COND(macid, func) \
+ ((macid) * NUM_OF_RTW89_MLO_H2C_FUNC + (func))
+
/* CLASS 24 - MRC */
#define H2C_CL_MRC 0x18
enum rtw89_mrc_h2c_func {
@@ -4524,6 +4613,12 @@ struct rtw89_c2h_rfk_report {
u8 version;
} __packed;
+struct rtw89_c2h_rf_tas_info {
+ struct rtw89_c2h_hdr hdr;
+ __le32 cur_idx;
+ __le16 txpwr_history[20];
+} __packed;
+
#define RTW89_FW_RSVD_PLE_SIZE 0x800
#define RTW89_FW_BACKTRACE_INFO_SIZE 8
@@ -4573,6 +4668,8 @@ int rtw89_fw_h2c_ampdu_cmac_tbl_g7(struct rtw89_dev *rtwdev,
struct rtw89_sta_link *rtwsta_link);
int rtw89_fw_h2c_txtime_cmac_tbl(struct rtw89_dev *rtwdev,
struct rtw89_sta_link *rtwsta_link);
+int rtw89_fw_h2c_txtime_cmac_tbl_g7(struct rtw89_dev *rtwdev,
+ struct rtw89_sta_link *rtwsta_link);
int rtw89_fw_h2c_txpath_cmac_tbl(struct rtw89_dev *rtwdev,
struct rtw89_sta_link *rtwsta_link);
int rtw89_fw_h2c_update_beacon(struct rtw89_dev *rtwdev,
@@ -4588,7 +4685,7 @@ int rtw89_fw_h2c_dctl_sec_cam_v2(struct rtw89_dev *rtwdev,
struct rtw89_vif_link *rtwvif_link,
struct rtw89_sta_link *rtwsta_link);
void rtw89_fw_c2h_irqsafe(struct rtw89_dev *rtwdev, struct sk_buff *c2h);
-void rtw89_fw_c2h_work(struct work_struct *work);
+void rtw89_fw_c2h_work(struct wiphy *wiphy, struct wiphy_work *work);
int rtw89_fw_h2c_role_maintain(struct rtw89_dev *rtwdev,
struct rtw89_vif_link *rtwvif_link,
struct rtw89_sta_link *rtwsta_link,
@@ -4654,6 +4751,7 @@ int rtw89_fw_h2c_raw_with_hdr(struct rtw89_dev *rtwdev,
bool rack, bool dack);
int rtw89_fw_h2c_raw(struct rtw89_dev *rtwdev, const u8 *buf, u16 len);
void rtw89_fw_send_all_early_h2c(struct rtw89_dev *rtwdev);
+void __rtw89_fw_free_all_early_h2c(struct rtw89_dev *rtwdev);
void rtw89_fw_free_all_early_h2c(struct rtw89_dev *rtwdev);
int rtw89_fw_h2c_general_pkt(struct rtw89_dev *rtwdev, struct rtw89_vif_link *rtwvif_link,
u8 macid);
@@ -4687,9 +4785,9 @@ int rtw89_fw_msg_reg(struct rtw89_dev *rtwdev,
struct rtw89_mac_c2h_info *c2h_info);
int rtw89_fw_h2c_fw_log(struct rtw89_dev *rtwdev, bool enable);
void rtw89_fw_st_dbg_dump(struct rtw89_dev *rtwdev);
-void rtw89_hw_scan_start(struct rtw89_dev *rtwdev,
- struct rtw89_vif_link *rtwvif_link,
- struct ieee80211_scan_request *scan_req);
+int rtw89_hw_scan_start(struct rtw89_dev *rtwdev,
+ struct rtw89_vif_link *rtwvif_link,
+ struct ieee80211_scan_request *scan_req);
void rtw89_hw_scan_complete(struct rtw89_dev *rtwdev,
struct rtw89_vif_link *rtwvif_link,
bool aborted);
@@ -4698,12 +4796,18 @@ int rtw89_hw_scan_offload(struct rtw89_dev *rtwdev,
bool enable);
void rtw89_hw_scan_abort(struct rtw89_dev *rtwdev,
struct rtw89_vif_link *rtwvif_link);
+int rtw89_hw_scan_prep_chan_list_ax(struct rtw89_dev *rtwdev,
+ struct rtw89_vif_link *rtwvif_link);
+void rtw89_hw_scan_free_chan_list_ax(struct rtw89_dev *rtwdev);
int rtw89_hw_scan_add_chan_list_ax(struct rtw89_dev *rtwdev,
- struct rtw89_vif_link *rtwvif_link, bool connected);
+ struct rtw89_vif_link *rtwvif_link);
int rtw89_pno_scan_add_chan_list_ax(struct rtw89_dev *rtwdev,
struct rtw89_vif_link *rtwvif_link);
+int rtw89_hw_scan_prep_chan_list_be(struct rtw89_dev *rtwdev,
+ struct rtw89_vif_link *rtwvif_link);
+void rtw89_hw_scan_free_chan_list_be(struct rtw89_dev *rtwdev);
int rtw89_hw_scan_add_chan_list_be(struct rtw89_dev *rtwdev,
- struct rtw89_vif_link *rtwvif_link, bool connected);
+ struct rtw89_vif_link *rtwvif_link);
int rtw89_pno_scan_add_chan_list_be(struct rtw89_dev *rtwdev,
struct rtw89_vif_link *rtwvif_link);
int rtw89_fw_h2c_trigger_cpu_exception(struct rtw89_dev *rtwdev);
@@ -4772,6 +4876,8 @@ int rtw89_fw_h2c_mrc_sync(struct rtw89_dev *rtwdev,
int rtw89_fw_h2c_mrc_upd_duration(struct rtw89_dev *rtwdev,
const struct rtw89_fw_mrc_upd_duration_arg *arg);
int rtw89_fw_h2c_ap_info_refcount(struct rtw89_dev *rtwdev, bool en);
+int rtw89_fw_h2c_mlo_link_cfg(struct rtw89_dev *rtwdev, struct rtw89_vif_link *rtwvif_link,
+ bool enable);
static inline void rtw89_fw_h2c_init_ba_cam(struct rtw89_dev *rtwdev)
{
@@ -4854,6 +4960,15 @@ static inline int rtw89_chip_h2c_ampdu_cmac_tbl(struct rtw89_dev *rtwdev,
}
static inline
+int rtw89_chip_h2c_txtime_cmac_tbl(struct rtw89_dev *rtwdev,
+ struct rtw89_sta_link *rtwsta_link)
+{
+ const struct rtw89_chip_info *chip = rtwdev->chip;
+
+ return chip->ops->h2c_txtime_cmac_tbl(rtwdev, rtwsta_link);
+}
+
+static inline
int rtw89_chip_h2c_ba_cam(struct rtw89_dev *rtwdev, struct rtw89_sta *rtwsta,
bool valid, struct ieee80211_ampdu_params *params)
{
@@ -4874,6 +4989,18 @@ int rtw89_chip_h2c_ba_cam(struct rtw89_dev *rtwdev, struct rtw89_sta *rtwsta,
return 0;
}
+/* Must consider compatibility; don't insert new in the mid.
+ * Fill each field's default value in rtw89_regd_entcpy().
+ */
+struct rtw89_fw_regd_entry {
+ u8 alpha2_0;
+ u8 alpha2_1;
+ u8 rule_2ghz;
+ u8 rule_5ghz;
+ u8 rule_6ghz;
+ __le32 fmap;
+} __packed;
+
/* must consider compatibility; don't insert new in the mid */
struct rtw89_fw_txpwr_byrate_entry {
u8 band;
diff --git a/drivers/net/wireless/realtek/rtw89/mac.c b/drivers/net/wireless/realtek/rtw89/mac.c
index a37c6d525d6f..9f0e30e75009 100644
--- a/drivers/net/wireless/realtek/rtw89/mac.c
+++ b/drivers/net/wireless/realtek/rtw89/mac.c
@@ -1495,6 +1495,21 @@ static int rtw89_mac_power_switch(struct rtw89_dev *rtwdev, bool on)
#undef PWR_ACT
}
+int rtw89_mac_pwr_on(struct rtw89_dev *rtwdev)
+{
+ int ret;
+
+ ret = rtw89_mac_power_switch(rtwdev, true);
+ if (ret) {
+ rtw89_mac_power_switch(rtwdev, false);
+ ret = rtw89_mac_power_switch(rtwdev, true);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
void rtw89_mac_pwr_off(struct rtw89_dev *rtwdev)
{
rtw89_mac_power_switch(rtwdev, false);
@@ -3996,14 +4011,6 @@ int rtw89_mac_partial_init(struct rtw89_dev *rtwdev, bool include_bb)
{
int ret;
- ret = rtw89_mac_power_switch(rtwdev, true);
- if (ret) {
- rtw89_mac_power_switch(rtwdev, false);
- ret = rtw89_mac_power_switch(rtwdev, true);
- if (ret)
- return ret;
- }
-
rtw89_mac_ctrl_hci_dma_trx(rtwdev, true);
if (include_bb) {
@@ -4036,6 +4043,10 @@ int rtw89_mac_init(struct rtw89_dev *rtwdev)
bool include_bb = !!chip->bbmcu_nr;
int ret;
+ ret = rtw89_mac_pwr_on(rtwdev);
+ if (ret)
+ return ret;
+
ret = rtw89_mac_partial_init(rtwdev, include_bb);
if (ret)
goto fail;
@@ -4067,7 +4078,7 @@ int rtw89_mac_init(struct rtw89_dev *rtwdev)
return ret;
fail:
- rtw89_mac_power_switch(rtwdev, false);
+ rtw89_mac_pwr_off(rtwdev);
return ret;
}
@@ -4620,11 +4631,17 @@ static void rtw89_mac_port_tsf_sync_rand(struct rtw89_dev *rtwdev,
if (rtwvif_link->net_type != RTW89_NET_TYPE_AP_MODE || rtwvif_link == rtwvif_src)
return;
+ if (rtwvif_link->rand_tsf_done)
+ goto out;
+
/* adjust offset randomly to avoid beacon conflict */
offset = offset - offset / 4 + get_random_u32() % (offset / 2);
rtw89_mac_port_tsf_sync(rtwdev, rtwvif_link, rtwvif_src,
(*n_offset) * offset);
+ rtwvif_link->rand_tsf_done = true;
+
+out:
(*n_offset)++;
}
@@ -4826,9 +4843,37 @@ void rtw89_mac_set_he_obss_narrow_bw_ru(struct rtw89_dev *rtwdev,
rtw89_write32_set(rtwdev, reg, mac->narrow_bw_ru_dis.mask);
}
+void rtw89_mac_set_he_tb(struct rtw89_dev *rtwdev,
+ struct rtw89_vif_link *rtwvif_link)
+{
+ struct ieee80211_bss_conf *bss_conf;
+ bool set;
+ u32 reg;
+
+ if (rtwdev->chip->chip_gen != RTW89_CHIP_BE)
+ return;
+
+ rcu_read_lock();
+
+ bss_conf = rtw89_vif_rcu_dereference_link(rtwvif_link, true);
+ set = bss_conf->he_support && !bss_conf->eht_support;
+
+ rcu_read_unlock();
+
+ reg = rtw89_mac_reg_by_idx(rtwdev, R_BE_CLIENT_OM_CTRL,
+ rtwvif_link->mac_idx);
+
+ if (set)
+ rtw89_write32_set(rtwdev, reg, B_BE_TRIG_DIS_EHTTB);
+ else
+ rtw89_write32_clr(rtwdev, reg, B_BE_TRIG_DIS_EHTTB);
+}
+
void rtw89_mac_stop_ap(struct rtw89_dev *rtwdev, struct rtw89_vif_link *rtwvif_link)
{
rtw89_mac_port_cfg_func_sw(rtwdev, rtwvif_link);
+
+ rtwvif_link->rand_tsf_done = false;
}
int rtw89_mac_add_vif(struct rtw89_dev *rtwdev, struct rtw89_vif_link *rtwvif_link)
@@ -4862,11 +4907,11 @@ rtw89_mac_c2h_scanofld_rsp(struct rtw89_dev *rtwdev, struct sk_buff *skb,
struct rtw89_vif_link *rtwvif_link = rtwdev->scan_info.scanning_vif;
struct rtw89_vif *rtwvif;
struct rtw89_chan new;
- u32 last_chan = rtwdev->scan_info.last_chan_idx, report_tsf;
u16 actual_period, expect_period;
u8 reason, status, tx_fail, band;
u8 mac_idx, sw_def, fw_def;
u8 ver = U8_MAX;
+ u32 report_tsf;
u16 chan;
int ret;
@@ -4925,7 +4970,7 @@ rtw89_mac_c2h_scanofld_rsp(struct rtw89_dev *rtwdev, struct sk_buff *skb,
return;
if (rtwvif_link && rtwvif->scan_req &&
- last_chan < rtwvif->scan_req->n_channels) {
+ !list_empty(&rtwdev->scan_info.chan_list)) {
ret = rtw89_hw_scan_offload(rtwdev, rtwvif_link, true);
if (ret) {
rtw89_hw_scan_abort(rtwdev, rtwvif_link);
@@ -4980,7 +5025,8 @@ rtw89_mac_bcn_fltr_rpt(struct rtw89_dev *rtwdev, struct rtw89_vif_link *rtwvif_l
switch (type) {
case RTW89_BCN_FLTR_BEACON_LOSS:
- if (!rtwdev->scanning && !rtwvif->offchan)
+ if (!rtwdev->scanning && !rtwvif->offchan &&
+ !rtwvif_link->noa_once.in_duration)
ieee80211_connection_loss(vif);
else
rtw89_fw_h2c_set_bcn_fltr_cfg(rtwdev, rtwvif_link, true);
@@ -5073,12 +5119,14 @@ rtw89_mac_c2h_done_ack(struct rtw89_dev *rtwdev, struct sk_buff *skb_c2h, u32 le
return;
case H2C_FUNC_ADD_SCANOFLD_CH:
cond = RTW89_SCANOFLD_WAIT_COND_ADD_CH;
+ h2c_return &= RTW89_C2H_SCAN_DONE_ACK_RETURN;
break;
case H2C_FUNC_SCANOFLD:
cond = RTW89_SCANOFLD_WAIT_COND_START;
break;
case H2C_FUNC_SCANOFLD_BE:
cond = RTW89_SCANOFLD_BE_WAIT_COND_START;
+ h2c_return &= RTW89_C2H_SCAN_DONE_ACK_RETURN;
break;
}
@@ -5362,6 +5410,27 @@ rtw89_mac_c2h_wow_aoac_rpt(struct rtw89_dev *rtwdev, struct sk_buff *skb, u32 le
}
static void
+rtw89_mac_c2h_mlo_link_cfg_stat(struct rtw89_dev *rtwdev, struct sk_buff *c2h, u32 len)
+{
+ const struct rtw89_c2h_mlo_link_cfg_rpt *c2h_rpt;
+ struct rtw89_wait_info *wait = &rtwdev->mlo.wait;
+ struct rtw89_completion_data data = {};
+ unsigned int cond;
+ u16 mac_id;
+ u8 status;
+
+ c2h_rpt = (const struct rtw89_c2h_mlo_link_cfg_rpt *)c2h->data;
+
+ mac_id = le32_get_bits(c2h_rpt->w2, RTW89_C2H_MLO_LINK_CFG_RPT_W2_MACID);
+ status = le32_get_bits(c2h_rpt->w2, RTW89_C2H_MLO_LINK_CFG_RPT_W2_STATUS);
+
+ data.err = status == RTW89_C2H_MLO_LINK_CFG_ROLE_NOT_EXIST ||
+ status == RTW89_C2H_MLO_LINK_CFG_RUNNING;
+ cond = RTW89_MLO_WAIT_COND(mac_id, H2C_FUNC_MLO_LINK_CFG);
+ rtw89_complete_cond(wait, cond, &data);
+}
+
+static void
rtw89_mac_c2h_mrc_status_rpt(struct rtw89_dev *rtwdev, struct sk_buff *c2h, u32 len)
{
struct rtw89_wait_info *wait = &rtwdev->mcc.wait;
@@ -5516,6 +5585,18 @@ void (* const rtw89_mac_c2h_mcc_handler[])(struct rtw89_dev *rtwdev,
};
static
+void (* const rtw89_mac_c2h_mlo_handler[])(struct rtw89_dev *rtwdev,
+ struct sk_buff *c2h, u32 len) = {
+ [RTW89_MAC_C2H_FUNC_MLO_GET_TBL] = NULL,
+ [RTW89_MAC_C2H_FUNC_MLO_EMLSR_TRANS_DONE] = NULL,
+ [RTW89_MAC_C2H_FUNC_MLO_EMLSR_STA_CFG_DONE] = NULL,
+ [RTW89_MAC_C2H_FUNC_MCMLO_RELINK_RPT] = NULL,
+ [RTW89_MAC_C2H_FUNC_MCMLO_SN_SYNC_RPT] = NULL,
+ [RTW89_MAC_C2H_FUNC_MLO_LINK_CFG_STAT] = rtw89_mac_c2h_mlo_link_cfg_stat,
+ [RTW89_MAC_C2H_FUNC_MLO_DM_DBG_DUMP] = NULL,
+};
+
+static
void (* const rtw89_mac_c2h_mrc_handler[])(struct rtw89_dev *rtwdev,
struct sk_buff *c2h, u32 len) = {
[RTW89_MAC_C2H_FUNC_MRC_TSF_RPT] = rtw89_mac_c2h_mrc_tsf_rpt,
@@ -5584,6 +5665,8 @@ bool rtw89_mac_c2h_chk_atomic(struct rtw89_dev *rtwdev, struct sk_buff *c2h,
}
case RTW89_MAC_C2H_CLASS_MCC:
return true;
+ case RTW89_MAC_C2H_CLASS_MLO:
+ return true;
case RTW89_MAC_C2H_CLASS_MRC:
return true;
case RTW89_MAC_C2H_CLASS_WOW:
@@ -5617,6 +5700,10 @@ void rtw89_mac_c2h_handle(struct rtw89_dev *rtwdev, struct sk_buff *skb,
if (func < NUM_OF_RTW89_MAC_C2H_FUNC_MCC)
handler = rtw89_mac_c2h_mcc_handler[func];
break;
+ case RTW89_MAC_C2H_CLASS_MLO:
+ if (func < NUM_OF_RTW89_MAC_C2H_FUNC_MLO)
+ handler = rtw89_mac_c2h_mlo_handler[func];
+ break;
case RTW89_MAC_C2H_CLASS_MRC:
if (func < NUM_OF_RTW89_MAC_C2H_FUNC_MRC)
handler = rtw89_mac_c2h_mrc_handler[func];
@@ -6031,7 +6118,7 @@ int rtw89_mac_cfg_ctrl_path_v1(struct rtw89_dev *rtwdev, bool wl)
if (wl)
return 0;
- for (i = 0; i < RTW89_PHY_MAX; i++) {
+ for (i = 0; i < RTW89_PHY_NUM; i++) {
g[i].gnt_bt_sw_en = 1;
g[i].gnt_bt = 1;
g[i].gnt_wl_sw_en = 1;
@@ -6422,6 +6509,7 @@ __rtw89_mac_set_tx_time(struct rtw89_dev *rtwdev, struct rtw89_sta_link *rtwsta_
u32 tx_time)
{
#define MAC_AX_DFLT_TX_TIME 5280
+ const struct rtw89_mac_gen_def *mac = rtwdev->chip->mac_def;
u8 mac_idx = rtwsta_link->rtwvif_link->mac_idx;
u32 max_tx_time = tx_time == 0 ? MAC_AX_DFLT_TX_TIME : tx_time;
u32 reg;
@@ -6429,7 +6517,7 @@ __rtw89_mac_set_tx_time(struct rtw89_dev *rtwdev, struct rtw89_sta_link *rtwsta_
if (rtwsta_link->cctl_tx_time) {
rtwsta_link->ampdu_max_time = (max_tx_time - 512) >> 9;
- ret = rtw89_fw_h2c_txtime_cmac_tbl(rtwdev, rtwsta_link);
+ ret = rtw89_chip_h2c_txtime_cmac_tbl(rtwdev, rtwsta_link);
} else {
ret = rtw89_mac_check_mac_en(rtwdev, mac_idx, RTW89_CMAC_SEL);
if (ret) {
@@ -6437,8 +6525,8 @@ __rtw89_mac_set_tx_time(struct rtw89_dev *rtwdev, struct rtw89_sta_link *rtwsta_
return ret;
}
- reg = rtw89_mac_reg_by_idx(rtwdev, R_AX_AMPDU_AGG_LIMIT, mac_idx);
- rtw89_write32_mask(rtwdev, reg, B_AX_AMPDU_MAX_TIME_MASK,
+ reg = rtw89_mac_reg_by_idx(rtwdev, mac->agg_limit.addr, mac_idx);
+ rtw89_write32_mask(rtwdev, reg, mac->agg_limit.mask,
max_tx_time >> 5);
}
@@ -6464,6 +6552,7 @@ int rtw89_mac_set_tx_time(struct rtw89_dev *rtwdev, struct rtw89_sta_link *rtwst
int rtw89_mac_get_tx_time(struct rtw89_dev *rtwdev, struct rtw89_sta_link *rtwsta_link,
u32 *tx_time)
{
+ const struct rtw89_mac_gen_def *mac = rtwdev->chip->mac_def;
u8 mac_idx = rtwsta_link->rtwvif_link->mac_idx;
u32 reg;
int ret = 0;
@@ -6477,8 +6566,8 @@ int rtw89_mac_get_tx_time(struct rtw89_dev *rtwdev, struct rtw89_sta_link *rtwst
return ret;
}
- reg = rtw89_mac_reg_by_idx(rtwdev, R_AX_AMPDU_AGG_LIMIT, mac_idx);
- *tx_time = rtw89_read32_mask(rtwdev, reg, B_AX_AMPDU_MAX_TIME_MASK) << 5;
+ reg = rtw89_mac_reg_by_idx(rtwdev, mac->agg_limit.addr, mac_idx);
+ *tx_time = rtw89_read32_mask(rtwdev, reg, mac->agg_limit.mask) << 5;
}
return ret;
@@ -6494,9 +6583,9 @@ int rtw89_mac_set_tx_retry_limit(struct rtw89_dev *rtwdev,
if (!resume) {
rtwsta_link->cctl_tx_retry_limit = true;
- ret = rtw89_fw_h2c_txtime_cmac_tbl(rtwdev, rtwsta_link);
+ ret = rtw89_chip_h2c_txtime_cmac_tbl(rtwdev, rtwsta_link);
} else {
- ret = rtw89_fw_h2c_txtime_cmac_tbl(rtwdev, rtwsta_link);
+ ret = rtw89_chip_h2c_txtime_cmac_tbl(rtwdev, rtwsta_link);
rtwsta_link->cctl_tx_retry_limit = false;
}
@@ -6506,6 +6595,7 @@ int rtw89_mac_set_tx_retry_limit(struct rtw89_dev *rtwdev,
int rtw89_mac_get_tx_retry_limit(struct rtw89_dev *rtwdev,
struct rtw89_sta_link *rtwsta_link, u8 *tx_retry)
{
+ const struct rtw89_mac_gen_def *mac = rtwdev->chip->mac_def;
u8 mac_idx = rtwsta_link->rtwvif_link->mac_idx;
u32 reg;
int ret = 0;
@@ -6519,8 +6609,8 @@ int rtw89_mac_get_tx_retry_limit(struct rtw89_dev *rtwdev,
return ret;
}
- reg = rtw89_mac_reg_by_idx(rtwdev, R_AX_TXCNT, mac_idx);
- *tx_retry = rtw89_read32_mask(rtwdev, reg, B_AX_L_TXCNT_LMT_MASK);
+ reg = rtw89_mac_reg_by_idx(rtwdev, mac->txcnt_limit.addr, mac_idx);
+ *tx_retry = rtw89_read32_mask(rtwdev, reg, mac->txcnt_limit.mask);
}
return ret;
@@ -6798,6 +6888,8 @@ const struct rtw89_mac_gen_def rtw89_mac_gen_ax = {
.mask = B_AX_RXTRIG_RU26_DIS,
},
.wow_ctrl = {.addr = R_AX_WOW_CTRL, .mask = B_AX_WOW_WOWEN,},
+ .agg_limit = {.addr = R_AX_AMPDU_AGG_LIMIT, .mask = B_AX_AMPDU_MAX_TIME_MASK,},
+ .txcnt_limit = {.addr = R_AX_TXCNT, .mask = B_AX_L_TXCNT_LMT_MASK,},
.check_mac_en = rtw89_mac_check_mac_en_ax,
.sys_init = sys_init_ax,
@@ -6847,6 +6939,8 @@ const struct rtw89_mac_gen_def rtw89_mac_gen_ax = {
.is_txq_empty = mac_is_txq_empty_ax,
+ .prep_chan_list = rtw89_hw_scan_prep_chan_list_ax,
+ .free_chan_list = rtw89_hw_scan_free_chan_list_ax,
.add_chan_list = rtw89_hw_scan_add_chan_list_ax,
.add_chan_list_pno = rtw89_pno_scan_add_chan_list_ax,
.scan_offload = rtw89_fw_h2c_scan_offload_ax,
diff --git a/drivers/net/wireless/realtek/rtw89/mac.h b/drivers/net/wireless/realtek/rtw89/mac.h
index 8edea96d037f..8013c852d5be 100644
--- a/drivers/net/wireless/realtek/rtw89/mac.h
+++ b/drivers/net/wireless/realtek/rtw89/mac.h
@@ -370,6 +370,7 @@ enum rtw89_mac_mem_sel {
RTW89_MAC_MEM_TXD_FIFO_0_V1,
RTW89_MAC_MEM_TXD_FIFO_1_V1,
RTW89_MAC_MEM_WD_PAGE,
+ RTW89_MAC_MEM_MLD_TBL,
/* keep last */
RTW89_MAC_MEM_NUM,
@@ -427,6 +428,18 @@ enum rtw89_mac_c2h_mcc_func {
NUM_OF_RTW89_MAC_C2H_FUNC_MCC,
};
+enum rtw89_mac_c2h_mlo_func {
+ RTW89_MAC_C2H_FUNC_MLO_GET_TBL = 0x0,
+ RTW89_MAC_C2H_FUNC_MLO_EMLSR_TRANS_DONE = 0x1,
+ RTW89_MAC_C2H_FUNC_MLO_EMLSR_STA_CFG_DONE = 0x2,
+ RTW89_MAC_C2H_FUNC_MCMLO_RELINK_RPT = 0x3,
+ RTW89_MAC_C2H_FUNC_MCMLO_SN_SYNC_RPT = 0x4,
+ RTW89_MAC_C2H_FUNC_MLO_LINK_CFG_STAT = 0x5,
+ RTW89_MAC_C2H_FUNC_MLO_DM_DBG_DUMP = 0x6,
+
+ NUM_OF_RTW89_MAC_C2H_FUNC_MLO,
+};
+
enum rtw89_mac_c2h_mrc_func {
RTW89_MAC_C2H_FUNC_MRC_TSF_RPT = 0,
RTW89_MAC_C2H_FUNC_MRC_STATUS_RPT = 1,
@@ -453,6 +466,7 @@ enum rtw89_mac_c2h_class {
RTW89_MAC_C2H_CLASS_WOW = 0x3,
RTW89_MAC_C2H_CLASS_MCC = 0x4,
RTW89_MAC_C2H_CLASS_FWDBG = 0x5,
+ RTW89_MAC_C2H_CLASS_MLO = 0xc,
RTW89_MAC_C2H_CLASS_MRC = 0xe,
RTW89_MAC_C2H_CLASS_AP = 0x18,
RTW89_MAC_C2H_CLASS_MAX,
@@ -964,6 +978,8 @@ struct rtw89_mac_gen_def {
struct rtw89_reg_def bfee_ctrl;
struct rtw89_reg_def narrow_bw_ru_dis;
struct rtw89_reg_def wow_ctrl;
+ struct rtw89_reg_def agg_limit;
+ struct rtw89_reg_def txcnt_limit;
int (*check_mac_en)(struct rtw89_dev *rtwdev, u8 band,
enum rtw89_mac_hwmod_sel sel);
@@ -1029,8 +1045,11 @@ struct rtw89_mac_gen_def {
bool (*is_txq_empty)(struct rtw89_dev *rtwdev);
+ int (*prep_chan_list)(struct rtw89_dev *rtwdev,
+ struct rtw89_vif_link *rtwvif_link);
+ void (*free_chan_list)(struct rtw89_dev *rtwdev);
int (*add_chan_list)(struct rtw89_dev *rtwdev,
- struct rtw89_vif_link *rtwvif_link, bool connected);
+ struct rtw89_vif_link *rtwvif_link);
int (*add_chan_list_pno)(struct rtw89_dev *rtwdev,
struct rtw89_vif_link *rtwvif_link);
int (*scan_offload)(struct rtw89_dev *rtwdev,
@@ -1145,6 +1164,7 @@ rtw89_write32_port_set(struct rtw89_dev *rtwdev, struct rtw89_vif_link *rtwvif_l
rtw89_write32_set(rtwdev, reg, bit);
}
+int rtw89_mac_pwr_on(struct rtw89_dev *rtwdev);
void rtw89_mac_pwr_off(struct rtw89_dev *rtwdev);
int rtw89_mac_partial_init(struct rtw89_dev *rtwdev, bool include_bb);
int rtw89_mac_init(struct rtw89_dev *rtwdev);
@@ -1185,6 +1205,8 @@ void rtw89_mac_port_cfg_rx_sync(struct rtw89_dev *rtwdev,
struct rtw89_vif_link *rtwvif_link, bool en);
void rtw89_mac_set_he_obss_narrow_bw_ru(struct rtw89_dev *rtwdev,
struct rtw89_vif_link *rtwvif_link);
+void rtw89_mac_set_he_tb(struct rtw89_dev *rtwdev,
+ struct rtw89_vif_link *rtwvif_link);
void rtw89_mac_stop_ap(struct rtw89_dev *rtwdev, struct rtw89_vif_link *rtwvif_link);
void rtw89_mac_enable_beacon_for_ap_vifs(struct rtw89_dev *rtwdev, bool en);
int rtw89_mac_remove_vif(struct rtw89_dev *rtwdev, struct rtw89_vif_link *vif);
diff --git a/drivers/net/wireless/realtek/rtw89/mac80211.c b/drivers/net/wireless/realtek/rtw89/mac80211.c
index b3669e0074df..a47971003bd4 100644
--- a/drivers/net/wireless/realtek/rtw89/mac80211.c
+++ b/drivers/net/wireless/realtek/rtw89/mac80211.c
@@ -57,32 +57,30 @@ static void rtw89_ops_wake_tx_queue(struct ieee80211_hw *hw,
static int rtw89_ops_start(struct ieee80211_hw *hw)
{
struct rtw89_dev *rtwdev = hw->priv;
- int ret;
- mutex_lock(&rtwdev->mutex);
- ret = rtw89_core_start(rtwdev);
- mutex_unlock(&rtwdev->mutex);
+ lockdep_assert_wiphy(hw->wiphy);
- return ret;
+ return rtw89_core_start(rtwdev);
}
static void rtw89_ops_stop(struct ieee80211_hw *hw, bool suspend)
{
struct rtw89_dev *rtwdev = hw->priv;
- mutex_lock(&rtwdev->mutex);
+ lockdep_assert_wiphy(hw->wiphy);
+
rtw89_core_stop(rtwdev);
- mutex_unlock(&rtwdev->mutex);
}
static int rtw89_ops_config(struct ieee80211_hw *hw, u32 changed)
{
struct rtw89_dev *rtwdev = hw->priv;
+ lockdep_assert_wiphy(hw->wiphy);
+
/* let previous ips work finish to ensure we don't leave ips twice */
- cancel_work_sync(&rtwdev->ips_work);
+ wiphy_work_cancel(hw->wiphy, &rtwdev->ips_work);
- mutex_lock(&rtwdev->mutex);
rtw89_leave_ps_mode(rtwdev);
if ((changed & IEEE80211_CONF_CHANGE_IDLE) &&
@@ -100,8 +98,6 @@ static int rtw89_ops_config(struct ieee80211_hw *hw, u32 changed)
!rtwdev->scanning)
rtw89_enter_ips(rtwdev);
- mutex_unlock(&rtwdev->mutex);
-
return 0;
}
@@ -115,14 +111,17 @@ static int __rtw89_ops_add_iface_link(struct rtw89_dev *rtwdev,
rtw89_vif_type_mapping(rtwvif_link, false);
- INIT_WORK(&rtwvif_link->update_beacon_work, rtw89_core_update_beacon_work);
+ wiphy_work_init(&rtwvif_link->update_beacon_work, rtw89_core_update_beacon_work);
INIT_LIST_HEAD(&rtwvif_link->general_pkt_list);
+ rtw89_p2p_noa_once_init(rtwvif_link);
+
rtwvif_link->hit_rule = 0;
rtwvif_link->bcn_hit_cond = 0;
rtwvif_link->chanctx_assigned = false;
rtwvif_link->chanctx_idx = RTW89_CHANCTX_0;
rtwvif_link->reg_6ghz_power = RTW89_REG_6GHZ_POWER_DFLT;
+ rtwvif_link->rand_tsf_done = false;
rcu_read_lock();
@@ -142,9 +141,11 @@ static int __rtw89_ops_add_iface_link(struct rtw89_dev *rtwdev,
static void __rtw89_ops_remove_iface_link(struct rtw89_dev *rtwdev,
struct rtw89_vif_link *rtwvif_link)
{
- mutex_unlock(&rtwdev->mutex);
- cancel_work_sync(&rtwvif_link->update_beacon_work);
- mutex_lock(&rtwdev->mutex);
+ lockdep_assert_wiphy(rtwdev->hw->wiphy);
+
+ wiphy_work_cancel(rtwdev->hw->wiphy, &rtwvif_link->update_beacon_work);
+
+ rtw89_p2p_noa_once_deinit(rtwvif_link);
rtw89_leave_ps_mode(rtwdev);
@@ -162,11 +163,11 @@ static int rtw89_ops_add_interface(struct ieee80211_hw *hw,
u8 mac_id, port;
int ret = 0;
+ lockdep_assert_wiphy(hw->wiphy);
+
rtw89_debug(rtwdev, RTW89_DBG_STATE, "add vif %pM type %d, p2p %d\n",
vif->addr, vif->type, vif->p2p);
- mutex_lock(&rtwdev->mutex);
-
rtw89_leave_ips_by_hwflags(rtwdev);
if (RTW89_CHK_FW_FEATURE(BEACON_FILTER, &rtwdev->fw))
@@ -174,10 +175,8 @@ static int rtw89_ops_add_interface(struct ieee80211_hw *hw,
IEEE80211_VIF_SUPPORTS_CQM_RSSI;
mac_id = rtw89_acquire_mac_id(rtwdev);
- if (mac_id == RTW89_MAX_MAC_ID_NUM) {
- ret = -ENOSPC;
- goto err;
- }
+ if (mac_id == RTW89_MAX_MAC_ID_NUM)
+ return -ENOSPC;
port = rtw89_core_acquire_bit_map(rtwdev->hw_port, RTW89_PORT_NUM);
if (port == RTW89_PORT_NUM) {
@@ -192,13 +191,14 @@ static int rtw89_ops_add_interface(struct ieee80211_hw *hw,
if (!rtw89_rtwvif_in_list(rtwdev, rtwvif)) {
list_add_tail(&rtwvif->list, &rtwdev->rtwvifs_list);
INIT_LIST_HEAD(&rtwvif->mgnt_entry);
+ INIT_LIST_HEAD(&rtwvif->dlink_pool);
}
ether_addr_copy(rtwvif->mac_addr, vif->addr);
rtwvif->offchan = false;
rtwvif->roc.state = RTW89_ROC_IDLE;
- INIT_DELAYED_WORK(&rtwvif->roc.roc_work, rtw89_roc_work);
+ wiphy_delayed_work_init(&rtwvif->roc.roc_work, rtw89_roc_work);
rtw89_traffic_stats_init(rtwdev, &rtwvif->stats);
@@ -213,8 +213,6 @@ static int rtw89_ops_add_interface(struct ieee80211_hw *hw,
goto unset_link;
rtw89_recalc_lps(rtwdev);
-
- mutex_unlock(&rtwdev->mutex);
return 0;
unset_link:
@@ -224,8 +222,6 @@ release_port:
rtw89_core_release_bit_map(rtwdev->hw_port, port);
release_macid:
rtw89_release_mac_id(rtwdev, mac_id);
-err:
- mutex_unlock(&rtwdev->mutex);
return ret;
}
@@ -239,12 +235,12 @@ static void rtw89_ops_remove_interface(struct ieee80211_hw *hw,
u8 port = rtw89_vif_get_main_port(rtwvif);
struct rtw89_vif_link *rtwvif_link;
+ lockdep_assert_wiphy(hw->wiphy);
+
rtw89_debug(rtwdev, RTW89_DBG_STATE, "remove vif %pM type %d p2p %d\n",
vif->addr, vif->type, vif->p2p);
- cancel_delayed_work_sync(&rtwvif->roc.roc_work);
-
- mutex_lock(&rtwdev->mutex);
+ wiphy_delayed_work_cancel(hw->wiphy, &rtwvif->roc.roc_work);
rtwvif_link = rtwvif->links[RTW89_VIF_IDLE_LINK_ID];
if (unlikely(!rtwvif_link)) {
@@ -265,8 +261,6 @@ bottom:
rtw89_recalc_lps(rtwdev);
rtw89_enter_ips_by_hwflags(rtwdev);
-
- mutex_unlock(&rtwdev->mutex);
}
static int rtw89_ops_change_interface(struct ieee80211_hw *hw,
@@ -304,7 +298,8 @@ static void rtw89_ops_configure_filter(struct ieee80211_hw *hw,
const struct rtw89_mac_gen_def *mac = rtwdev->chip->mac_def;
u32 rx_fltr;
- mutex_lock(&rtwdev->mutex);
+ lockdep_assert_wiphy(hw->wiphy);
+
rtw89_leave_ps_mode(rtwdev);
*new_flags &= FIF_ALLMULTI | FIF_OTHER_BSS | FIF_FCSFAIL |
@@ -367,14 +362,11 @@ static void rtw89_ops_configure_filter(struct ieee80211_hw *hw,
B_AX_RX_FLTR_CFG_MASK,
rx_fltr);
if (!rtwdev->dbcc_en)
- goto out;
+ return;
rtw89_write32_mask(rtwdev,
rtw89_mac_reg_by_idx(rtwdev, mac->rx_fltr, RTW89_MAC_1),
B_AX_RX_FLTR_CFG_MASK,
rx_fltr);
-
-out:
- mutex_unlock(&rtwdev->mutex);
}
static const u8 ac_to_fw_idx[IEEE80211_NUM_ACS] = {
@@ -493,6 +485,8 @@ static int __rtw89_ops_sta_add(struct rtw89_dev *rtwdev,
int i;
if (vif->type == NL80211_IFTYPE_STATION && !sta->tdls) {
+ rtwvif->mlo_mode = RTW89_MLO_MODE_MLSR;
+
/* for station mode, assign the mac_id from itself */
macid = rtw89_vif_get_main_macid(rtwvif);
} else {
@@ -508,6 +502,8 @@ static int __rtw89_ops_sta_add(struct rtw89_dev *rtwdev,
for (i = 0; i < ARRAY_SIZE(sta->txq); i++)
rtw89_core_txq_init(rtwdev, sta->txq[i]);
+ INIT_LIST_HEAD(&rtwsta->dlink_pool);
+
skb_queue_head_init(&rtwsta->roc_queue);
bitmap_zero(rtwsta->pairwise_sec_cam_map, RTW89_MAX_SEC_CAM_NUM);
@@ -670,6 +666,7 @@ static void __rtw89_ops_bss_link_assoc(struct rtw89_dev *rtwdev,
rtw89_chip_cfg_txpwr_ul_tb_offset(rtwdev, rtwvif_link);
rtw89_mac_port_update(rtwdev, rtwvif_link);
rtw89_mac_set_he_obss_narrow_bw_ru(rtwdev, rtwvif_link);
+ rtw89_mac_set_he_tb(rtwdev, rtwvif_link);
}
static void __rtw89_ops_bss_assoc(struct rtw89_dev *rtwdev,
@@ -689,7 +686,8 @@ static void rtw89_ops_vif_cfg_changed(struct ieee80211_hw *hw,
struct rtw89_dev *rtwdev = hw->priv;
struct rtw89_vif *rtwvif = vif_to_rtwvif(vif);
- mutex_lock(&rtwdev->mutex);
+ lockdep_assert_wiphy(hw->wiphy);
+
rtw89_leave_ps_mode(rtwdev);
if (changed & BSS_CHANGED_ASSOC) {
@@ -712,8 +710,6 @@ static void rtw89_ops_vif_cfg_changed(struct ieee80211_hw *hw,
if (changed & BSS_CHANGED_ARP_FILTER)
rtwvif->ip_addr = vif->cfg.arp_addr_list[0];
-
- mutex_unlock(&rtwdev->mutex);
}
static void rtw89_ops_link_info_changed(struct ieee80211_hw *hw,
@@ -725,7 +721,8 @@ static void rtw89_ops_link_info_changed(struct ieee80211_hw *hw,
struct rtw89_vif *rtwvif = vif_to_rtwvif(vif);
struct rtw89_vif_link *rtwvif_link;
- mutex_lock(&rtwdev->mutex);
+ lockdep_assert_wiphy(hw->wiphy);
+
rtw89_leave_ps_mode(rtwdev);
rtwvif_link = rtwvif->links[conf->link_id];
@@ -733,7 +730,7 @@ static void rtw89_ops_link_info_changed(struct ieee80211_hw *hw,
rtw89_err(rtwdev,
"%s: rtwvif link (link_id %u) is not active\n",
__func__, conf->link_id);
- goto out;
+ return;
}
if (changed & BSS_CHANGED_BSSID) {
@@ -763,9 +760,6 @@ static void rtw89_ops_link_info_changed(struct ieee80211_hw *hw,
if (changed & BSS_CHANGED_TPE)
rtw89_reg_6ghz_recalc(rtwdev, rtwvif_link, true);
-
-out:
- mutex_unlock(&rtwdev->mutex);
}
static int rtw89_ops_start_ap(struct ieee80211_hw *hw,
@@ -778,22 +772,19 @@ static int rtw89_ops_start_ap(struct ieee80211_hw *hw,
const struct rtw89_chan *chan;
int ret = 0;
- mutex_lock(&rtwdev->mutex);
+ lockdep_assert_wiphy(hw->wiphy);
rtwvif_link = rtwvif->links[link_conf->link_id];
if (unlikely(!rtwvif_link)) {
rtw89_err(rtwdev,
"%s: rtwvif link (link_id %u) is not active\n",
__func__, link_conf->link_id);
- ret = -ENOLINK;
- goto out;
+ return -ENOLINK;
}
chan = rtw89_chan_get(rtwdev, rtwvif_link->chanctx_idx);
- if (chan->band_type == RTW89_BAND_6G) {
- mutex_unlock(&rtwdev->mutex);
+ if (chan->band_type == RTW89_BAND_6G)
return -EOPNOTSUPP;
- }
if (rtwdev->scanning)
rtw89_hw_scan_abort(rtwdev, rtwdev->scan_info.scanning_vif);
@@ -810,15 +801,12 @@ static int rtw89_ops_start_ap(struct ieee80211_hw *hw,
if (RTW89_CHK_FW_FEATURE(NOTIFY_AP_INFO, &rtwdev->fw)) {
ret = rtw89_fw_h2c_ap_info_refcount(rtwdev, true);
if (ret)
- goto out;
+ return ret;
}
rtw89_queue_chanctx_work(rtwdev);
-out:
- mutex_unlock(&rtwdev->mutex);
-
- return ret;
+ return 0;
}
static
@@ -829,14 +817,14 @@ void rtw89_ops_stop_ap(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
struct rtw89_vif *rtwvif = vif_to_rtwvif(vif);
struct rtw89_vif_link *rtwvif_link;
- mutex_lock(&rtwdev->mutex);
+ lockdep_assert_wiphy(hw->wiphy);
rtwvif_link = rtwvif->links[link_conf->link_id];
if (unlikely(!rtwvif_link)) {
rtw89_err(rtwdev,
"%s: rtwvif link (link_id %u) is not active\n",
__func__, link_conf->link_id);
- goto out;
+ return;
}
if (RTW89_CHK_FW_FEATURE(NOTIFY_AP_INFO, &rtwdev->fw))
@@ -845,22 +833,18 @@ void rtw89_ops_stop_ap(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
rtw89_mac_stop_ap(rtwdev, rtwvif_link);
rtw89_chip_h2c_assoc_cmac_tbl(rtwdev, rtwvif_link, NULL);
rtw89_fw_h2c_join_info(rtwdev, rtwvif_link, NULL, true);
-
-out:
- mutex_unlock(&rtwdev->mutex);
}
static int rtw89_ops_set_tim(struct ieee80211_hw *hw, struct ieee80211_sta *sta,
bool set)
{
- struct rtw89_dev *rtwdev = hw->priv;
struct rtw89_sta *rtwsta = sta_to_rtwsta(sta);
struct rtw89_vif *rtwvif = rtwsta->rtwvif;
struct rtw89_vif_link *rtwvif_link;
unsigned int link_id;
rtw89_vif_for_each_link(rtwvif, rtwvif_link, link_id)
- ieee80211_queue_work(rtwdev->hw, &rtwvif_link->update_beacon_work);
+ wiphy_work_queue(hw->wiphy, &rtwvif_link->update_beacon_work);
return 0;
}
@@ -873,9 +857,9 @@ static int rtw89_ops_conf_tx(struct ieee80211_hw *hw,
struct rtw89_dev *rtwdev = hw->priv;
struct rtw89_vif *rtwvif = vif_to_rtwvif(vif);
struct rtw89_vif_link *rtwvif_link;
- int ret = 0;
- mutex_lock(&rtwdev->mutex);
+ lockdep_assert_wiphy(hw->wiphy);
+
rtw89_leave_ps_mode(rtwdev);
rtwvif_link = rtwvif->links[link_id];
@@ -883,17 +867,13 @@ static int rtw89_ops_conf_tx(struct ieee80211_hw *hw,
rtw89_err(rtwdev,
"%s: rtwvif link (link_id %u) is not active\n",
__func__, link_id);
- ret = -ENOLINK;
- goto out;
+ return -ENOLINK;
}
rtwvif_link->tx_params[ac] = *params;
__rtw89_conf_tx(rtwdev, rtwvif_link, ac);
-out:
- mutex_unlock(&rtwdev->mutex);
-
- return ret;
+ return 0;
}
static int __rtw89_ops_sta_state(struct ieee80211_hw *hw,
@@ -937,14 +917,11 @@ static int rtw89_ops_sta_state(struct ieee80211_hw *hw,
enum ieee80211_sta_state new_state)
{
struct rtw89_dev *rtwdev = hw->priv;
- int ret;
- mutex_lock(&rtwdev->mutex);
- rtw89_leave_ps_mode(rtwdev);
- ret = __rtw89_ops_sta_state(hw, vif, sta, old_state, new_state);
- mutex_unlock(&rtwdev->mutex);
+ lockdep_assert_wiphy(hw->wiphy);
- return ret;
+ rtw89_leave_ps_mode(rtwdev);
+ return __rtw89_ops_sta_state(hw, vif, sta, old_state, new_state);
}
static int rtw89_ops_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
@@ -953,9 +930,10 @@ static int rtw89_ops_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
struct ieee80211_key_conf *key)
{
struct rtw89_dev *rtwdev = hw->priv;
- int ret = 0;
+ int ret;
+
+ lockdep_assert_wiphy(hw->wiphy);
- mutex_lock(&rtwdev->mutex);
rtw89_leave_ps_mode(rtwdev);
switch (cmd) {
@@ -964,7 +942,7 @@ static int rtw89_ops_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
ret = rtw89_cam_sec_key_add(rtwdev, vif, sta, key);
if (ret && ret != -EOPNOTSUPP) {
rtw89_err(rtwdev, "failed to add key to sec cam\n");
- goto out;
+ return ret;
}
break;
case DISABLE_KEY:
@@ -974,14 +952,11 @@ static int rtw89_ops_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
ret = rtw89_cam_sec_key_del(rtwdev, vif, sta, key, true);
if (ret) {
rtw89_err(rtwdev, "failed to remove key from sec cam\n");
- goto out;
+ return ret;
}
break;
}
-out:
- mutex_unlock(&rtwdev->mutex);
-
return ret;
}
@@ -997,38 +972,32 @@ static int rtw89_ops_ampdu_action(struct ieee80211_hw *hw,
struct ieee80211_txq *txq = sta->txq[tid];
struct rtw89_txq *rtwtxq = (struct rtw89_txq *)txq->drv_priv;
+ lockdep_assert_wiphy(rtwdev->hw->wiphy);
+
switch (params->action) {
case IEEE80211_AMPDU_TX_START:
return IEEE80211_AMPDU_TX_START_IMMEDIATE;
case IEEE80211_AMPDU_TX_STOP_CONT:
case IEEE80211_AMPDU_TX_STOP_FLUSH:
case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT:
- mutex_lock(&rtwdev->mutex);
clear_bit(RTW89_TXQ_F_AMPDU, &rtwtxq->flags);
clear_bit(tid, rtwsta->ampdu_map);
rtw89_chip_h2c_ampdu_cmac_tbl(rtwdev, rtwvif, rtwsta);
- mutex_unlock(&rtwdev->mutex);
ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid);
break;
case IEEE80211_AMPDU_TX_OPERATIONAL:
- mutex_lock(&rtwdev->mutex);
set_bit(RTW89_TXQ_F_AMPDU, &rtwtxq->flags);
rtwsta->ampdu_params[tid].agg_num = params->buf_size;
rtwsta->ampdu_params[tid].amsdu = params->amsdu;
set_bit(tid, rtwsta->ampdu_map);
rtw89_leave_ps_mode(rtwdev);
rtw89_chip_h2c_ampdu_cmac_tbl(rtwdev, rtwvif, rtwsta);
- mutex_unlock(&rtwdev->mutex);
break;
case IEEE80211_AMPDU_RX_START:
- mutex_lock(&rtwdev->mutex);
rtw89_chip_h2c_ba_cam(rtwdev, rtwsta, true, params);
- mutex_unlock(&rtwdev->mutex);
break;
case IEEE80211_AMPDU_RX_STOP:
- mutex_lock(&rtwdev->mutex);
rtw89_chip_h2c_ba_cam(rtwdev, rtwsta, false, params);
- mutex_unlock(&rtwdev->mutex);
break;
default:
WARN_ON(1);
@@ -1042,11 +1011,11 @@ static int rtw89_ops_set_rts_threshold(struct ieee80211_hw *hw, u32 value)
{
struct rtw89_dev *rtwdev = hw->priv;
- mutex_lock(&rtwdev->mutex);
+ lockdep_assert_wiphy(hw->wiphy);
+
rtw89_leave_ps_mode(rtwdev);
if (test_bit(RTW89_FLAG_POWERON, rtwdev->flags))
rtw89_mac_update_rts_threshold(rtwdev);
- mutex_unlock(&rtwdev->mutex);
return 0;
}
@@ -1059,7 +1028,7 @@ static void rtw89_ops_sta_statistics(struct ieee80211_hw *hw,
struct rtw89_sta *rtwsta = sta_to_rtwsta(sta);
struct rtw89_sta_link *rtwsta_link;
- rtwsta_link = rtw89_sta_get_link_inst(rtwsta, 0);
+ rtwsta_link = rtw89_get_designated_link(rtwsta);
if (unlikely(!rtwsta_link))
return;
@@ -1086,7 +1055,8 @@ static void rtw89_ops_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
{
struct rtw89_dev *rtwdev = hw->priv;
- mutex_lock(&rtwdev->mutex);
+ lockdep_assert_wiphy(hw->wiphy);
+
rtw89_leave_lps(rtwdev);
rtw89_hci_flush_queues(rtwdev, queues, drop);
@@ -1094,8 +1064,6 @@ static void rtw89_ops_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
__rtw89_drop_packets(rtwdev, vif);
else
rtw89_mac_flush_txq(rtwdev, queues, drop);
-
- mutex_unlock(&rtwdev->mutex);
}
struct rtw89_iter_bitrate_mask_data {
@@ -1142,10 +1110,10 @@ static int rtw89_ops_set_bitrate_mask(struct ieee80211_hw *hw,
{
struct rtw89_dev *rtwdev = hw->priv;
- mutex_lock(&rtwdev->mutex);
+ lockdep_assert_wiphy(hw->wiphy);
+
rtw89_phy_rate_pattern_vif(rtwdev, vif, mask);
rtw89_ra_mask_info_update(rtwdev, vif, mask);
- mutex_unlock(&rtwdev->mutex);
return 0;
}
@@ -1156,6 +1124,8 @@ int rtw89_ops_set_antenna(struct ieee80211_hw *hw, u32 tx_ant, u32 rx_ant)
struct rtw89_dev *rtwdev = hw->priv;
struct rtw89_hal *hal = &rtwdev->hal;
+ lockdep_assert_wiphy(hw->wiphy);
+
if (hal->ant_diversity) {
if (tx_ant != rx_ant || hweight32(tx_ant) != 1)
return -EINVAL;
@@ -1163,12 +1133,10 @@ int rtw89_ops_set_antenna(struct ieee80211_hw *hw, u32 tx_ant, u32 rx_ant)
return -EINVAL;
}
- mutex_lock(&rtwdev->mutex);
hal->antenna_tx = tx_ant;
hal->antenna_rx = rx_ant;
hal->tx_path_diversity = false;
hal->ant_diversity_fixed = true;
- mutex_unlock(&rtwdev->mutex);
return 0;
}
@@ -1193,18 +1161,17 @@ static void rtw89_ops_sw_scan_start(struct ieee80211_hw *hw,
struct rtw89_vif *rtwvif = vif_to_rtwvif(vif);
struct rtw89_vif_link *rtwvif_link;
- mutex_lock(&rtwdev->mutex);
+ lockdep_assert_wiphy(hw->wiphy);
- rtwvif_link = rtw89_vif_get_link_inst(rtwvif, 0);
+ rtwvif_link = rtw89_get_designated_link(rtwvif);
if (unlikely(!rtwvif_link)) {
- rtw89_err(rtwdev, "sw scan start: find no link on HW-0\n");
- goto out;
+ rtw89_err(rtwdev, "sw scan start: find no designated link\n");
+ return;
}
- rtw89_core_scan_start(rtwdev, rtwvif_link, mac_addr, false);
+ rtw89_leave_lps(rtwdev);
-out:
- mutex_unlock(&rtwdev->mutex);
+ rtw89_core_scan_start(rtwdev, rtwvif_link, mac_addr, false);
}
static void rtw89_ops_sw_scan_complete(struct ieee80211_hw *hw,
@@ -1214,18 +1181,15 @@ static void rtw89_ops_sw_scan_complete(struct ieee80211_hw *hw,
struct rtw89_vif *rtwvif = vif_to_rtwvif(vif);
struct rtw89_vif_link *rtwvif_link;
- mutex_lock(&rtwdev->mutex);
+ lockdep_assert_wiphy(hw->wiphy);
- rtwvif_link = rtw89_vif_get_link_inst(rtwvif, 0);
+ rtwvif_link = rtw89_get_designated_link(rtwvif);
if (unlikely(!rtwvif_link)) {
- rtw89_err(rtwdev, "sw scan complete: find no link on HW-0\n");
- goto out;
+ rtw89_err(rtwdev, "sw scan complete: find no designated link\n");
+ return;
}
rtw89_core_scan_complete(rtwdev, rtwvif_link, false);
-
-out:
- mutex_unlock(&rtwdev->mutex);
}
static void rtw89_ops_reconfig_complete(struct ieee80211_hw *hw,
@@ -1245,33 +1209,33 @@ static int rtw89_ops_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
struct rtw89_vif_link *rtwvif_link;
int ret;
+ lockdep_assert_wiphy(hw->wiphy);
+
if (!RTW89_CHK_FW_FEATURE(SCAN_OFFLOAD, &rtwdev->fw))
return 1;
- mutex_lock(&rtwdev->mutex);
-
- if (rtwdev->scanning || rtwvif->offchan) {
- ret = -EBUSY;
- goto out;
- }
+ if (rtwdev->scanning || rtwvif->offchan)
+ return -EBUSY;
- rtwvif_link = rtw89_vif_get_link_inst(rtwvif, 0);
+ rtwvif_link = rtw89_get_designated_link(rtwvif);
if (unlikely(!rtwvif_link)) {
- rtw89_err(rtwdev, "hw scan: find no link on HW-0\n");
- ret = -ENOLINK;
- goto out;
+ rtw89_err(rtwdev, "hw scan: find no designated link\n");
+ return -ENOLINK;
}
- rtw89_hw_scan_start(rtwdev, rtwvif_link, req);
+ rtw89_leave_lps(rtwdev);
+ rtw89_leave_ips_by_hwflags(rtwdev);
+
+ ret = rtw89_hw_scan_start(rtwdev, rtwvif_link, req);
+ if (ret)
+ return ret;
+
ret = rtw89_hw_scan_offload(rtwdev, rtwvif_link, true);
if (ret) {
rtw89_hw_scan_abort(rtwdev, rtwvif_link);
rtw89_err(rtwdev, "HW scan failed with status: %d\n", ret);
}
-out:
- mutex_unlock(&rtwdev->mutex);
-
return ret;
}
@@ -1282,24 +1246,21 @@ static void rtw89_ops_cancel_hw_scan(struct ieee80211_hw *hw,
struct rtw89_vif *rtwvif = vif_to_rtwvif(vif);
struct rtw89_vif_link *rtwvif_link;
+ lockdep_assert_wiphy(hw->wiphy);
+
if (!RTW89_CHK_FW_FEATURE(SCAN_OFFLOAD, &rtwdev->fw))
return;
- mutex_lock(&rtwdev->mutex);
-
if (!rtwdev->scanning)
- goto out;
+ return;
- rtwvif_link = rtw89_vif_get_link_inst(rtwvif, 0);
+ rtwvif_link = rtw89_get_designated_link(rtwvif);
if (unlikely(!rtwvif_link)) {
- rtw89_err(rtwdev, "cancel hw scan: find no link on HW-0\n");
- goto out;
+ rtw89_err(rtwdev, "cancel hw scan: find no designated link\n");
+ return;
}
rtw89_hw_scan_abort(rtwdev, rtwvif_link);
-
-out:
- mutex_unlock(&rtwdev->mutex);
}
static void rtw89_ops_sta_rc_update(struct ieee80211_hw *hw,
@@ -1322,13 +1283,10 @@ static int rtw89_ops_add_chanctx(struct ieee80211_hw *hw,
struct ieee80211_chanctx_conf *ctx)
{
struct rtw89_dev *rtwdev = hw->priv;
- int ret;
- mutex_lock(&rtwdev->mutex);
- ret = rtw89_chanctx_ops_add(rtwdev, ctx);
- mutex_unlock(&rtwdev->mutex);
+ lockdep_assert_wiphy(hw->wiphy);
- return ret;
+ return rtw89_chanctx_ops_add(rtwdev, ctx);
}
static void rtw89_ops_remove_chanctx(struct ieee80211_hw *hw,
@@ -1336,9 +1294,9 @@ static void rtw89_ops_remove_chanctx(struct ieee80211_hw *hw,
{
struct rtw89_dev *rtwdev = hw->priv;
- mutex_lock(&rtwdev->mutex);
+ lockdep_assert_wiphy(hw->wiphy);
+
rtw89_chanctx_ops_remove(rtwdev, ctx);
- mutex_unlock(&rtwdev->mutex);
}
static void rtw89_ops_change_chanctx(struct ieee80211_hw *hw,
@@ -1347,9 +1305,9 @@ static void rtw89_ops_change_chanctx(struct ieee80211_hw *hw,
{
struct rtw89_dev *rtwdev = hw->priv;
- mutex_lock(&rtwdev->mutex);
+ lockdep_assert_wiphy(hw->wiphy);
+
rtw89_chanctx_ops_change(rtwdev, ctx, changed);
- mutex_unlock(&rtwdev->mutex);
}
static int rtw89_ops_assign_vif_chanctx(struct ieee80211_hw *hw,
@@ -1360,25 +1318,18 @@ static int rtw89_ops_assign_vif_chanctx(struct ieee80211_hw *hw,
struct rtw89_dev *rtwdev = hw->priv;
struct rtw89_vif *rtwvif = vif_to_rtwvif(vif);
struct rtw89_vif_link *rtwvif_link;
- int ret;
- mutex_lock(&rtwdev->mutex);
+ lockdep_assert_wiphy(hw->wiphy);
rtwvif_link = rtwvif->links[link_conf->link_id];
if (unlikely(!rtwvif_link)) {
rtw89_err(rtwdev,
"%s: rtwvif link (link_id %u) is not active\n",
__func__, link_conf->link_id);
- ret = -ENOLINK;
- goto out;
+ return -ENOLINK;
}
- ret = rtw89_chanctx_ops_assign_vif(rtwdev, rtwvif_link, ctx);
-
-out:
- mutex_unlock(&rtwdev->mutex);
-
- return ret;
+ return rtw89_chanctx_ops_assign_vif(rtwdev, rtwvif_link, ctx);
}
static void rtw89_ops_unassign_vif_chanctx(struct ieee80211_hw *hw,
@@ -1390,11 +1341,10 @@ static void rtw89_ops_unassign_vif_chanctx(struct ieee80211_hw *hw,
struct rtw89_vif *rtwvif = vif_to_rtwvif(vif);
struct rtw89_vif_link *rtwvif_link;
- mutex_lock(&rtwdev->mutex);
+ lockdep_assert_wiphy(hw->wiphy);
rtwvif_link = rtwvif->links[link_conf->link_id];
if (unlikely(!rtwvif_link)) {
- mutex_unlock(&rtwdev->mutex);
rtw89_err(rtwdev,
"%s: rtwvif link (link_id %u) is not active\n",
__func__, link_conf->link_id);
@@ -1402,7 +1352,6 @@ static void rtw89_ops_unassign_vif_chanctx(struct ieee80211_hw *hw,
}
rtw89_chanctx_ops_unassign_vif(rtwdev, rtwvif_link, ctx);
- mutex_unlock(&rtwdev->mutex);
}
static int rtw89_ops_remain_on_channel(struct ieee80211_hw *hw,
@@ -1415,13 +1364,12 @@ static int rtw89_ops_remain_on_channel(struct ieee80211_hw *hw,
struct rtw89_vif *rtwvif = vif_to_rtwvif_safe(vif);
struct rtw89_roc *roc = &rtwvif->roc;
+ lockdep_assert_wiphy(hw->wiphy);
+
if (!rtwvif)
return -EINVAL;
- mutex_lock(&rtwdev->mutex);
-
if (roc->state != RTW89_ROC_IDLE) {
- mutex_unlock(&rtwdev->mutex);
return -EBUSY;
}
@@ -1439,8 +1387,6 @@ static int rtw89_ops_remain_on_channel(struct ieee80211_hw *hw,
rtw89_roc_start(rtwdev, rtwvif);
- mutex_unlock(&rtwdev->mutex);
-
return 0;
}
@@ -1450,14 +1396,14 @@ static int rtw89_ops_cancel_remain_on_channel(struct ieee80211_hw *hw,
struct rtw89_dev *rtwdev = hw->priv;
struct rtw89_vif *rtwvif = vif_to_rtwvif_safe(vif);
+ lockdep_assert_wiphy(hw->wiphy);
+
if (!rtwvif)
return -EINVAL;
- cancel_delayed_work_sync(&rtwvif->roc.roc_work);
+ wiphy_delayed_work_cancel(hw->wiphy, &rtwvif->roc.roc_work);
- mutex_lock(&rtwdev->mutex);
rtw89_roc_end(rtwdev, rtwvif);
- mutex_unlock(&rtwdev->mutex);
return 0;
}
@@ -1478,14 +1424,14 @@ static int rtw89_ops_set_tid_config(struct ieee80211_hw *hw,
{
struct rtw89_dev *rtwdev = hw->priv;
- mutex_lock(&rtwdev->mutex);
+ lockdep_assert_wiphy(hw->wiphy);
+
if (sta)
rtw89_core_set_tid_config(rtwdev, sta, tid_config);
else
ieee80211_iterate_stations_atomic(rtwdev->hw,
rtw89_set_tid_config_iter,
tid_config);
- mutex_unlock(&rtwdev->mutex);
return 0;
}
@@ -1509,7 +1455,7 @@ static bool rtw89_ops_can_activate_links(struct ieee80211_hw *hw,
{
struct rtw89_dev *rtwdev = hw->priv;
- guard(mutex)(&rtwdev->mutex);
+ lockdep_assert_wiphy(hw->wiphy);
return rtw89_can_work_on_links(rtwdev, vif, active_links);
}
@@ -1571,7 +1517,7 @@ int rtw89_ops_change_vif_links(struct ieee80211_hw *hw,
int ret = 0;
int i;
- guard(mutex)(&rtwdev->mutex);
+ lockdep_assert_wiphy(hw->wiphy);
rtw89_debug(rtwdev, RTW89_DBG_STATE,
"%s: old_links (0x%08x) -> new_links (0x%08x)\n",
@@ -1720,7 +1666,7 @@ int rtw89_ops_change_sta_links(struct ieee80211_hw *hw,
unsigned long set_links = new_links & ~old_links;
int ret = 0;
- guard(mutex)(&rtwdev->mutex);
+ lockdep_assert_wiphy(hw->wiphy);
rtw89_debug(rtwdev, RTW89_DBG_STATE,
"%s: old_links (0x%08x) -> new_links (0x%08x)\n",
@@ -1750,13 +1696,12 @@ static int rtw89_ops_suspend(struct ieee80211_hw *hw,
struct rtw89_dev *rtwdev = hw->priv;
int ret;
+ lockdep_assert_wiphy(hw->wiphy);
+
set_bit(RTW89_FLAG_FORBIDDEN_TRACK_WROK, rtwdev->flags);
- cancel_delayed_work_sync(&rtwdev->track_work);
+ wiphy_delayed_work_cancel(hw->wiphy, &rtwdev->track_work);
- mutex_lock(&rtwdev->mutex);
ret = rtw89_wow_suspend(rtwdev, wowlan);
- mutex_unlock(&rtwdev->mutex);
-
if (ret) {
rtw89_warn(rtwdev, "failed to suspend for wow %d\n", ret);
clear_bit(RTW89_FLAG_FORBIDDEN_TRACK_WROK, rtwdev->flags);
@@ -1771,15 +1716,15 @@ static int rtw89_ops_resume(struct ieee80211_hw *hw)
struct rtw89_dev *rtwdev = hw->priv;
int ret;
- mutex_lock(&rtwdev->mutex);
+ lockdep_assert_wiphy(hw->wiphy);
+
ret = rtw89_wow_resume(rtwdev);
if (ret)
rtw89_warn(rtwdev, "failed to resume for wow %d\n", ret);
- mutex_unlock(&rtwdev->mutex);
clear_bit(RTW89_FLAG_FORBIDDEN_TRACK_WROK, rtwdev->flags);
- ieee80211_queue_delayed_work(rtwdev->hw, &rtwdev->track_work,
- RTW89_TRACK_WORK_PERIOD);
+ wiphy_delayed_work_queue(hw->wiphy, &rtwdev->track_work,
+ RTW89_TRACK_WORK_PERIOD);
return ret ? 1 : 0;
}
@@ -1799,18 +1744,16 @@ static void rtw89_set_rekey_data(struct ieee80211_hw *hw,
struct rtw89_wow_param *rtw_wow = &rtwdev->wow;
struct rtw89_wow_gtk_info *gtk_info = &rtw_wow->gtk_info;
+ lockdep_assert_wiphy(hw->wiphy);
+
if (data->kek_len > sizeof(gtk_info->kek) ||
data->kck_len > sizeof(gtk_info->kck)) {
rtw89_warn(rtwdev, "kek or kck length over fw limit\n");
return;
}
- mutex_lock(&rtwdev->mutex);
-
memcpy(gtk_info->kek, data->kek, data->kek_len);
memcpy(gtk_info->kck, data->kck, data->kck_len);
-
- mutex_unlock(&rtwdev->mutex);
}
#endif
@@ -1818,16 +1761,13 @@ static void rtw89_ops_rfkill_poll(struct ieee80211_hw *hw)
{
struct rtw89_dev *rtwdev = hw->priv;
- mutex_lock(&rtwdev->mutex);
+ lockdep_assert_wiphy(hw->wiphy);
/* wl_disable GPIO get floating when entering LPS */
if (test_bit(RTW89_FLAG_RUNNING, rtwdev->flags))
- goto out;
+ return;
rtw89_core_rfkill_poll(rtwdev, false);
-
-out:
- mutex_unlock(&rtwdev->mutex);
}
const struct ieee80211_ops rtw89_ops = {
diff --git a/drivers/net/wireless/realtek/rtw89/mac_be.c b/drivers/net/wireless/realtek/rtw89/mac_be.c
index 2dbdeae904ad..8c9d326dc907 100644
--- a/drivers/net/wireless/realtek/rtw89/mac_be.c
+++ b/drivers/net/wireless/realtek/rtw89/mac_be.c
@@ -29,6 +29,7 @@ static const u32 rtw89_mac_mem_base_addrs_be[RTW89_MAC_MEM_NUM] = {
[RTW89_MAC_MEM_CPU_LOCAL] = CPU_LOCAL_BASE_ADDR_BE,
[RTW89_MAC_MEM_BSSID_CAM] = BSSID_CAM_BASE_ADDR_BE,
[RTW89_MAC_MEM_WD_PAGE] = WD_PAGE_BASE_ADDR_BE,
+ [RTW89_MAC_MEM_MLD_TBL] = MLD_TBL_BASE_ADDR_BE,
};
static const struct rtw89_port_reg rtw89_port_base_be = {
@@ -708,8 +709,8 @@ static int sec_eng_init_be(struct rtw89_dev *rtwdev)
val32 |= B_BE_CLK_EN_CGCMP | B_BE_CLK_EN_WAPI | B_BE_CLK_EN_WEP_TKIP |
B_BE_SEC_TX_ENC | B_BE_SEC_RX_DEC |
B_BE_MC_DEC | B_BE_BC_DEC |
- B_BE_BMC_MGNT_DEC | B_BE_UC_MGNT_DEC;
- val32 &= ~B_BE_SEC_PRE_ENQUE_TX;
+ B_BE_BMC_MGNT_DEC | B_BE_UC_MGNT_DEC |
+ B_BE_SEC_PRE_ENQUE_TX;
rtw89_write32(rtwdev, R_BE_SEC_ENG_CTRL, val32);
rtw89_write32_set(rtwdev, R_BE_SEC_MPDU_PROC, B_BE_APPEND_ICV | B_BE_APPEND_MIC);
@@ -1865,7 +1866,7 @@ int rtw89_mac_cfg_ctrl_path_v2(struct rtw89_dev *rtwdev, bool wl)
if (wl)
return 0;
- for (i = 0; i < RTW89_PHY_MAX; i++) {
+ for (i = 0; i < RTW89_PHY_NUM; i++) {
g[i].gnt_bt_sw_en = 1;
g[i].gnt_bt = 1;
g[i].gnt_wl_sw_en = 1;
@@ -2585,6 +2586,8 @@ const struct rtw89_mac_gen_def rtw89_mac_gen_be = {
.mask = B_BE_RXTRIG_RU26_DIS,
},
.wow_ctrl = {.addr = R_BE_WOW_CTRL, .mask = B_BE_WOW_WOWEN,},
+ .agg_limit = {.addr = R_BE_AMPDU_AGG_LIMIT, .mask = B_BE_AMPDU_MAX_TIME_MASK,},
+ .txcnt_limit = {.addr = R_BE_TXCNT, .mask = B_BE_L_TXCNT_LMT_MASK,},
.check_mac_en = rtw89_mac_check_mac_en_be,
.sys_init = sys_init_be,
@@ -2634,6 +2637,8 @@ const struct rtw89_mac_gen_def rtw89_mac_gen_be = {
.is_txq_empty = mac_is_txq_empty_be,
+ .prep_chan_list = rtw89_hw_scan_prep_chan_list_be,
+ .free_chan_list = rtw89_hw_scan_free_chan_list_be,
.add_chan_list = rtw89_hw_scan_add_chan_list_be,
.add_chan_list_pno = rtw89_pno_scan_add_chan_list_be,
.scan_offload = rtw89_fw_h2c_scan_offload_be,
diff --git a/drivers/net/wireless/realtek/rtw89/pci.c b/drivers/net/wireless/realtek/rtw89/pci.c
index c2fe5a898dc7..064f6a940107 100644
--- a/drivers/net/wireless/realtek/rtw89/pci.c
+++ b/drivers/net/wireless/realtek/rtw89/pci.c
@@ -228,7 +228,7 @@ int rtw89_pci_sync_skb_for_device_and_validate_rx_info(struct rtw89_dev *rtwdev,
struct sk_buff *skb)
{
struct rtw89_pci_rx_info *rx_info = RTW89_PCI_RX_SKB_CB(skb);
- int rx_tag_retry = 100;
+ int rx_tag_retry = 1000;
int ret;
do {
@@ -3105,17 +3105,26 @@ static bool rtw89_pci_is_dac_compatible_bridge(struct rtw89_dev *rtwdev)
return false;
}
-static void rtw89_pci_cfg_dac(struct rtw89_dev *rtwdev)
+static int rtw89_pci_cfg_dac(struct rtw89_dev *rtwdev, bool force)
{
struct rtw89_pci *rtwpci = (struct rtw89_pci *)rtwdev->priv;
+ struct pci_dev *pdev = rtwpci->pdev;
+ int ret;
+ u8 val;
- if (!rtwpci->enable_dac)
- return;
+ if (!rtwpci->enable_dac && !force)
+ return 0;
if (!rtw89_pci_chip_is_manual_dac(rtwdev))
- return;
+ return 0;
- rtw89_pci_config_byte_set(rtwdev, RTW89_PCIE_L1_CTRL, RTW89_PCIE_BIT_EN_64BITS);
+ /* Configure DAC only via PCI config API, not DBI interfaces */
+ ret = pci_read_config_byte(pdev, RTW89_PCIE_L1_CTRL, &val);
+ if (ret)
+ return ret;
+
+ val |= RTW89_PCIE_BIT_EN_64BITS;
+ return pci_write_config_byte(pdev, RTW89_PCIE_L1_CTRL, val);
}
static int rtw89_pci_setup_mapping(struct rtw89_dev *rtwdev,
@@ -3133,13 +3142,16 @@ static int rtw89_pci_setup_mapping(struct rtw89_dev *rtwdev,
}
if (!rtw89_pci_is_dac_compatible_bridge(rtwdev))
- goto no_dac;
+ goto try_dac_done;
ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(36));
if (!ret) {
- rtwpci->enable_dac = true;
- rtw89_pci_cfg_dac(rtwdev);
- } else {
+ ret = rtw89_pci_cfg_dac(rtwdev, true);
+ if (!ret) {
+ rtwpci->enable_dac = true;
+ goto try_dac_done;
+ }
+
ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32));
if (ret) {
rtw89_err(rtwdev,
@@ -3147,7 +3159,7 @@ static int rtw89_pci_setup_mapping(struct rtw89_dev *rtwdev,
goto err_release_regions;
}
}
-no_dac:
+try_dac_done:
resource_len = pci_resource_len(pdev, bar_id);
rtwpci->mmap = pci_iomap(pdev, bar_id, resource_len);
@@ -4302,7 +4314,7 @@ static void rtw89_pci_l2_hci_ldo(struct rtw89_dev *rtwdev)
void rtw89_pci_basic_cfg(struct rtw89_dev *rtwdev, bool resume)
{
if (resume)
- rtw89_pci_cfg_dac(rtwdev);
+ rtw89_pci_cfg_dac(rtwdev, false);
rtw89_pci_disable_eq(rtwdev);
rtw89_pci_filter_out(rtwdev);
diff --git a/drivers/net/wireless/realtek/rtw89/pci.h b/drivers/net/wireless/realtek/rtw89/pci.h
index 4d11c3dd60a5..79fef5f90140 100644
--- a/drivers/net/wireless/realtek/rtw89/pci.h
+++ b/drivers/net/wireless/realtek/rtw89/pci.h
@@ -455,34 +455,36 @@
#define B_BE_RX0DMA_INT_EN BIT(0)
#define R_BE_HAXI_HISR00 0xB0B4
-#define B_BE_RDU_CH6_INT BIT(28)
-#define B_BE_RDU_CH5_INT BIT(27)
-#define B_BE_RDU_CH4_INT BIT(26)
-#define B_BE_RDU_CH2_INT BIT(25)
-#define B_BE_RDU_CH1_INT BIT(24)
-#define B_BE_RDU_CH0_INT BIT(23)
-#define B_BE_RXDMA_STUCK_INT BIT(22)
-#define B_BE_TXDMA_STUCK_INT BIT(21)
-#define B_BE_TXDMA_CH14_INT BIT(20)
-#define B_BE_TXDMA_CH13_INT BIT(19)
-#define B_BE_TXDMA_CH12_INT BIT(18)
-#define B_BE_TXDMA_CH11_INT BIT(17)
-#define B_BE_TXDMA_CH10_INT BIT(16)
-#define B_BE_TXDMA_CH9_INT BIT(15)
-#define B_BE_TXDMA_CH8_INT BIT(14)
-#define B_BE_TXDMA_CH7_INT BIT(13)
-#define B_BE_TXDMA_CH6_INT BIT(12)
-#define B_BE_TXDMA_CH5_INT BIT(11)
-#define B_BE_TXDMA_CH4_INT BIT(10)
-#define B_BE_TXDMA_CH3_INT BIT(9)
-#define B_BE_TXDMA_CH2_INT BIT(8)
-#define B_BE_TXDMA_CH1_INT BIT(7)
-#define B_BE_TXDMA_CH0_INT BIT(6)
-#define B_BE_RPQ1DMA_INT BIT(5)
-#define B_BE_RX1P1DMA_INT BIT(4)
+#define B_BE_RDU_CH5_INT_V1 BIT(30)
+#define B_BE_RDU_CH4_INT_V1 BIT(29)
+#define B_BE_RDU_CH3_INT_V1 BIT(28)
+#define B_BE_RDU_CH2_INT_V1 BIT(27)
+#define B_BE_RDU_CH1_INT_V1 BIT(26)
+#define B_BE_RDU_CH0_INT_V1 BIT(25)
+#define B_BE_RXDMA_STUCK_INT_V1 BIT(24)
+#define B_BE_TXDMA_STUCK_INT_V1 BIT(23)
+#define B_BE_TXDMA_CH14_INT_V1 BIT(22)
+#define B_BE_TXDMA_CH13_INT_V1 BIT(21)
+#define B_BE_TXDMA_CH12_INT_V1 BIT(20)
+#define B_BE_TXDMA_CH11_INT_V1 BIT(19)
+#define B_BE_TXDMA_CH10_INT_V1 BIT(18)
+#define B_BE_TXDMA_CH9_INT_V1 BIT(17)
+#define B_BE_TXDMA_CH8_INT_V1 BIT(16)
+#define B_BE_TXDMA_CH7_INT_V1 BIT(15)
+#define B_BE_TXDMA_CH6_INT_V1 BIT(14)
+#define B_BE_TXDMA_CH5_INT_V1 BIT(13)
+#define B_BE_TXDMA_CH4_INT_V1 BIT(12)
+#define B_BE_TXDMA_CH3_INT_V1 BIT(11)
+#define B_BE_TXDMA_CH2_INT_V1 BIT(10)
+#define B_BE_TXDMA_CH1_INT_V1 BIT(9)
+#define B_BE_TXDMA_CH0_INT_V1 BIT(8)
+#define B_BE_RX1P1DMA_INT_V1 BIT(7)
+#define B_BE_RX0P1DMA_INT_V1 BIT(6)
+#define B_BE_RO1DMA_INT BIT(5)
+#define B_BE_RP1DMA_INT BIT(4)
#define B_BE_RX1DMA_INT BIT(3)
-#define B_BE_RPQ0DMA_INT BIT(2)
-#define B_BE_RX0P1DMA_INT BIT(1)
+#define B_BE_RO0DMA_INT BIT(2)
+#define B_BE_RP0DMA_INT BIT(1)
#define B_BE_RX0DMA_INT BIT(0)
/* TX/RX */
diff --git a/drivers/net/wireless/realtek/rtw89/pci_be.c b/drivers/net/wireless/realtek/rtw89/pci_be.c
index cd39eebe8186..12e6a0cbb889 100644
--- a/drivers/net/wireless/realtek/rtw89/pci_be.c
+++ b/drivers/net/wireless/realtek/rtw89/pci_be.c
@@ -666,7 +666,7 @@ SIMPLE_DEV_PM_OPS(rtw89_pm_ops_be, rtw89_pci_suspend_be, rtw89_pci_resume_be);
EXPORT_SYMBOL(rtw89_pm_ops_be);
const struct rtw89_pci_gen_def rtw89_pci_gen_be = {
- .isr_rdu = B_BE_RDU_CH1_INT | B_BE_RDU_CH0_INT,
+ .isr_rdu = B_BE_RDU_CH1_INT_V1 | B_BE_RDU_CH0_INT_V1,
.isr_halt_c2h = B_BE_HALT_C2H_INT,
.isr_wdt_timeout = B_BE_WDT_TIMEOUT_INT,
.isr_clear_rpq = {R_BE_PCIE_DMA_ISR, B_BE_PCIE_RX_RPQ0_ISR_V1},
diff --git a/drivers/net/wireless/realtek/rtw89/phy.c b/drivers/net/wireless/realtek/rtw89/phy.c
index c7c05f7fda1d..76a2e26d4a10 100644
--- a/drivers/net/wireless/realtek/rtw89/phy.c
+++ b/drivers/net/wireless/realtek/rtw89/phy.c
@@ -2034,33 +2034,47 @@ static s8 rtw89_phy_ant_gain_query(struct rtw89_dev *rtwdev,
ant_gain->offset[path][subband_h]);
}
-static s8 rtw89_phy_ant_gain_offset(struct rtw89_dev *rtwdev, u8 band, u32 center_freq)
+static s8 rtw89_phy_ant_gain_offset(struct rtw89_dev *rtwdev, u32 center_freq)
{
+ s8 offset_patha, offset_pathb;
+
+ offset_patha = rtw89_phy_ant_gain_query(rtwdev, RF_PATH_A, center_freq);
+ offset_pathb = rtw89_phy_ant_gain_query(rtwdev, RF_PATH_B, center_freq);
+
+ if (RTW89_CHK_FW_FEATURE(NO_POWER_DIFFERENCE, &rtwdev->fw))
+ return min(offset_patha, offset_pathb);
+
+ return max(offset_patha, offset_pathb);
+}
+
+static bool rtw89_can_apply_ant_gain(struct rtw89_dev *rtwdev, u8 band)
+{
+ const struct rtw89_rfe_parms *rfe_parms = rtwdev->rfe_parms;
struct rtw89_ant_gain_info *ant_gain = &rtwdev->ant_gain;
const struct rtw89_chip_info *chip = rtwdev->chip;
u8 regd = rtw89_regd_get(rtwdev, band);
- s8 offset_patha, offset_pathb;
if (!chip->support_ant_gain)
- return 0;
+ return false;
- if (!(ant_gain->regd_enabled & BIT(regd)))
- return 0;
+ if (ant_gain->block_country || !(ant_gain->regd_enabled & BIT(regd)))
+ return false;
- offset_patha = rtw89_phy_ant_gain_query(rtwdev, RF_PATH_A, center_freq);
- offset_pathb = rtw89_phy_ant_gain_query(rtwdev, RF_PATH_B, center_freq);
+ if (!rfe_parms->has_da)
+ return false;
- return max(offset_patha, offset_pathb);
+ return true;
}
s16 rtw89_phy_ant_gain_pwr_offset(struct rtw89_dev *rtwdev,
const struct rtw89_chan *chan)
{
- struct rtw89_ant_gain_info *ant_gain = &rtwdev->ant_gain;
- u8 regd = rtw89_regd_get(rtwdev, chan->band_type);
s8 offset_patha, offset_pathb;
- if (!(ant_gain->regd_enabled & BIT(regd)))
+ if (!rtw89_can_apply_ant_gain(rtwdev, chan->band_type))
+ return 0;
+
+ if (RTW89_CHK_FW_FEATURE(NO_POWER_DIFFERENCE, &rtwdev->fw))
return 0;
offset_patha = rtw89_phy_ant_gain_query(rtwdev, RF_PATH_A, chan->freq);
@@ -2070,24 +2084,25 @@ s16 rtw89_phy_ant_gain_pwr_offset(struct rtw89_dev *rtwdev,
}
EXPORT_SYMBOL(rtw89_phy_ant_gain_pwr_offset);
-void rtw89_print_ant_gain(struct seq_file *m, struct rtw89_dev *rtwdev,
- const struct rtw89_chan *chan)
+int rtw89_print_ant_gain(struct rtw89_dev *rtwdev, char *buf, size_t bufsz,
+ const struct rtw89_chan *chan)
{
- struct rtw89_ant_gain_info *ant_gain = &rtwdev->ant_gain;
- const struct rtw89_chip_info *chip = rtwdev->chip;
- u8 regd = rtw89_regd_get(rtwdev, chan->band_type);
+ char *p = buf, *end = buf + bufsz;
s8 offset_patha, offset_pathb;
- if (!chip->support_ant_gain || !(ant_gain->regd_enabled & BIT(regd))) {
- seq_puts(m, "no DAG is applied\n");
- return;
+ if (!rtw89_can_apply_ant_gain(rtwdev, chan->band_type)) {
+ p += scnprintf(p, end - p, "no DAG is applied\n");
+ goto out;
}
offset_patha = rtw89_phy_ant_gain_query(rtwdev, RF_PATH_A, chan->freq);
offset_pathb = rtw89_phy_ant_gain_query(rtwdev, RF_PATH_B, chan->freq);
- seq_printf(m, "ChainA offset: %d dBm\n", offset_patha);
- seq_printf(m, "ChainB offset: %d dBm\n", offset_pathb);
+ p += scnprintf(p, end - p, "ChainA offset: %d dBm\n", offset_patha);
+ p += scnprintf(p, end - p, "ChainB offset: %d dBm\n", offset_pathb);
+
+out:
+ return p - buf;
}
static const u8 rtw89_rs_idx_num_ax[] = {
@@ -2240,20 +2255,31 @@ s8 rtw89_phy_read_txpwr_limit(struct rtw89_dev *rtwdev, u8 band,
u8 bw, u8 ntx, u8 rs, u8 bf, u8 ch)
{
const struct rtw89_rfe_parms *rfe_parms = rtwdev->rfe_parms;
+ const struct rtw89_txpwr_rule_2ghz *rule_da_2ghz = &rfe_parms->rule_da_2ghz;
+ const struct rtw89_txpwr_rule_5ghz *rule_da_5ghz = &rfe_parms->rule_da_5ghz;
+ const struct rtw89_txpwr_rule_6ghz *rule_da_6ghz = &rfe_parms->rule_da_6ghz;
const struct rtw89_txpwr_rule_2ghz *rule_2ghz = &rfe_parms->rule_2ghz;
const struct rtw89_txpwr_rule_5ghz *rule_5ghz = &rfe_parms->rule_5ghz;
const struct rtw89_txpwr_rule_6ghz *rule_6ghz = &rfe_parms->rule_6ghz;
struct rtw89_regulatory_info *regulatory = &rtwdev->regulatory;
enum nl80211_band nl_band = rtw89_hw_to_nl80211_band(band);
+ bool has_ant_gain = rtw89_can_apply_ant_gain(rtwdev, band);
u32 freq = ieee80211_channel_to_frequency(ch, nl_band);
u8 ch_idx = rtw89_channel_to_idx(rtwdev, band, ch);
+ s8 lmt = 0, da_lmt = S8_MAX, sar, offset = 0;
u8 regd = rtw89_regd_get(rtwdev, band);
u8 reg6 = regulatory->reg_6ghz_power;
- s8 lmt = 0, sar, offset;
+ struct rtw89_sar_parm sar_parm = {
+ .center_freq = freq,
+ .ntx = ntx,
+ };
s8 cstr;
switch (band) {
case RTW89_BAND_2G:
+ if (has_ant_gain)
+ da_lmt = (*rule_da_2ghz->lmt)[bw][ntx][rs][bf][regd][ch_idx];
+
lmt = (*rule_2ghz->lmt)[bw][ntx][rs][bf][regd][ch_idx];
if (lmt)
break;
@@ -2261,6 +2287,9 @@ s8 rtw89_phy_read_txpwr_limit(struct rtw89_dev *rtwdev, u8 band,
lmt = (*rule_2ghz->lmt)[bw][ntx][rs][bf][RTW89_WW][ch_idx];
break;
case RTW89_BAND_5G:
+ if (has_ant_gain)
+ da_lmt = (*rule_da_5ghz->lmt)[bw][ntx][rs][bf][regd][ch_idx];
+
lmt = (*rule_5ghz->lmt)[bw][ntx][rs][bf][regd][ch_idx];
if (lmt)
break;
@@ -2268,6 +2297,9 @@ s8 rtw89_phy_read_txpwr_limit(struct rtw89_dev *rtwdev, u8 band,
lmt = (*rule_5ghz->lmt)[bw][ntx][rs][bf][RTW89_WW][ch_idx];
break;
case RTW89_BAND_6G:
+ if (has_ant_gain)
+ da_lmt = (*rule_da_6ghz->lmt)[bw][ntx][rs][bf][regd][reg6][ch_idx];
+
lmt = (*rule_6ghz->lmt)[bw][ntx][rs][bf][regd][reg6][ch_idx];
if (lmt)
break;
@@ -2281,9 +2313,12 @@ s8 rtw89_phy_read_txpwr_limit(struct rtw89_dev *rtwdev, u8 band,
return 0;
}
- offset = rtw89_phy_ant_gain_offset(rtwdev, band, freq);
- lmt = rtw89_phy_txpwr_rf_to_mac(rtwdev, lmt + offset);
- sar = rtw89_query_sar(rtwdev, freq);
+ da_lmt = da_lmt ?: S8_MAX;
+ if (da_lmt != S8_MAX)
+ offset = rtw89_phy_ant_gain_offset(rtwdev, freq);
+
+ lmt = rtw89_phy_txpwr_rf_to_mac(rtwdev, min(lmt + offset, da_lmt));
+ sar = rtw89_query_sar(rtwdev, &sar_parm);
cstr = rtw89_phy_get_tpe_constraint(rtwdev, band);
return min3(lmt, sar, cstr);
@@ -2500,20 +2535,31 @@ s8 rtw89_phy_read_txpwr_limit_ru(struct rtw89_dev *rtwdev, u8 band,
u8 ru, u8 ntx, u8 ch)
{
const struct rtw89_rfe_parms *rfe_parms = rtwdev->rfe_parms;
+ const struct rtw89_txpwr_rule_2ghz *rule_da_2ghz = &rfe_parms->rule_da_2ghz;
+ const struct rtw89_txpwr_rule_5ghz *rule_da_5ghz = &rfe_parms->rule_da_5ghz;
+ const struct rtw89_txpwr_rule_6ghz *rule_da_6ghz = &rfe_parms->rule_da_6ghz;
const struct rtw89_txpwr_rule_2ghz *rule_2ghz = &rfe_parms->rule_2ghz;
const struct rtw89_txpwr_rule_5ghz *rule_5ghz = &rfe_parms->rule_5ghz;
const struct rtw89_txpwr_rule_6ghz *rule_6ghz = &rfe_parms->rule_6ghz;
struct rtw89_regulatory_info *regulatory = &rtwdev->regulatory;
enum nl80211_band nl_band = rtw89_hw_to_nl80211_band(band);
+ bool has_ant_gain = rtw89_can_apply_ant_gain(rtwdev, band);
u32 freq = ieee80211_channel_to_frequency(ch, nl_band);
u8 ch_idx = rtw89_channel_to_idx(rtwdev, band, ch);
+ s8 lmt_ru = 0, da_lmt_ru = S8_MAX, sar, offset = 0;
u8 regd = rtw89_regd_get(rtwdev, band);
u8 reg6 = regulatory->reg_6ghz_power;
- s8 lmt_ru = 0, sar, offset;
+ struct rtw89_sar_parm sar_parm = {
+ .center_freq = freq,
+ .ntx = ntx,
+ };
s8 cstr;
switch (band) {
case RTW89_BAND_2G:
+ if (has_ant_gain)
+ da_lmt_ru = (*rule_da_2ghz->lmt_ru)[ru][ntx][regd][ch_idx];
+
lmt_ru = (*rule_2ghz->lmt_ru)[ru][ntx][regd][ch_idx];
if (lmt_ru)
break;
@@ -2521,6 +2567,9 @@ s8 rtw89_phy_read_txpwr_limit_ru(struct rtw89_dev *rtwdev, u8 band,
lmt_ru = (*rule_2ghz->lmt_ru)[ru][ntx][RTW89_WW][ch_idx];
break;
case RTW89_BAND_5G:
+ if (has_ant_gain)
+ da_lmt_ru = (*rule_da_5ghz->lmt_ru)[ru][ntx][regd][ch_idx];
+
lmt_ru = (*rule_5ghz->lmt_ru)[ru][ntx][regd][ch_idx];
if (lmt_ru)
break;
@@ -2528,6 +2577,9 @@ s8 rtw89_phy_read_txpwr_limit_ru(struct rtw89_dev *rtwdev, u8 band,
lmt_ru = (*rule_5ghz->lmt_ru)[ru][ntx][RTW89_WW][ch_idx];
break;
case RTW89_BAND_6G:
+ if (has_ant_gain)
+ da_lmt_ru = (*rule_da_6ghz->lmt_ru)[ru][ntx][regd][reg6][ch_idx];
+
lmt_ru = (*rule_6ghz->lmt_ru)[ru][ntx][regd][reg6][ch_idx];
if (lmt_ru)
break;
@@ -2541,9 +2593,12 @@ s8 rtw89_phy_read_txpwr_limit_ru(struct rtw89_dev *rtwdev, u8 band,
return 0;
}
- offset = rtw89_phy_ant_gain_offset(rtwdev, band, freq);
- lmt_ru = rtw89_phy_txpwr_rf_to_mac(rtwdev, lmt_ru + offset);
- sar = rtw89_query_sar(rtwdev, freq);
+ da_lmt_ru = da_lmt_ru ?: S8_MAX;
+ if (da_lmt_ru != S8_MAX)
+ offset = rtw89_phy_ant_gain_offset(rtwdev, freq);
+
+ lmt_ru = rtw89_phy_txpwr_rf_to_mac(rtwdev, min(lmt_ru + offset, da_lmt_ru));
+ sar = rtw89_query_sar(rtwdev, &sar_parm);
cstr = rtw89_phy_get_tpe_constraint(rtwdev, band);
return min3(lmt_ru, sar, cstr);
@@ -3000,6 +3055,35 @@ void (* const rtw89_phy_c2h_ra_handler[])(struct rtw89_dev *rtwdev,
[RTW89_PHY_C2H_FUNC_TXSTS] = NULL,
};
+static void
+rtw89_phy_c2h_lowrt_rty(struct rtw89_dev *rtwdev, struct sk_buff *c2h, u32 len)
+{
+}
+
+static void
+rtw89_phy_c2h_fw_scan_rpt(struct rtw89_dev *rtwdev, struct sk_buff *c2h, u32 len)
+{
+ const struct rtw89_c2h_fw_scan_rpt *c2h_rpt =
+ (const struct rtw89_c2h_fw_scan_rpt *)c2h->data;
+
+ rtw89_debug(rtwdev, RTW89_DBG_DIG,
+ "%s: band: %u, op_chan: %u, PD_low_bd(ofdm, cck): (-%d, %d), phy_idx: %u\n",
+ __func__, c2h_rpt->band, c2h_rpt->center_ch,
+ PD_LOWER_BOUND_BASE - (c2h_rpt->ofdm_pd_idx << 1),
+ c2h_rpt->cck_pd_idx, c2h_rpt->phy_idx);
+}
+
+static
+void (* const rtw89_phy_c2h_dm_handler[])(struct rtw89_dev *rtwdev,
+ struct sk_buff *c2h, u32 len) = {
+ [RTW89_PHY_C2H_DM_FUNC_FW_TEST] = NULL,
+ [RTW89_PHY_C2H_DM_FUNC_FW_TRIG_TX_RPT] = NULL,
+ [RTW89_PHY_C2H_DM_FUNC_SIGB] = NULL,
+ [RTW89_PHY_C2H_DM_FUNC_LOWRT_RTY] = rtw89_phy_c2h_lowrt_rty,
+ [RTW89_PHY_C2H_DM_FUNC_MCC_DIG] = NULL,
+ [RTW89_PHY_C2H_DM_FUNC_FW_SCAN] = rtw89_phy_c2h_fw_scan_rpt,
+};
+
static void rtw89_phy_c2h_rfk_rpt_log(struct rtw89_dev *rtwdev,
enum rtw89_phy_c2h_rfk_log_func func,
void *content, u16 len)
@@ -3455,6 +3539,30 @@ rtw89_phy_c2h_rfk_report_state(struct rtw89_dev *rtwdev, struct sk_buff *c2h, u3
static void
rtw89_phy_c2h_rfk_log_tas_pwr(struct rtw89_dev *rtwdev, struct sk_buff *c2h, u32 len)
{
+ const struct rtw89_c2h_rf_tas_info *rf_tas =
+ (const struct rtw89_c2h_rf_tas_info *)c2h->data;
+ const enum rtw89_sar_sources src = rtwdev->sar.src;
+ struct rtw89_tas_info *tas = &rtwdev->tas;
+ u64 linear = 0;
+ u32 i, cur_idx;
+ s16 txpwr;
+
+ if (!tas->enable || src == RTW89_SAR_SOURCE_NONE)
+ return;
+
+ cur_idx = le32_to_cpu(rf_tas->cur_idx);
+ for (i = 0; i < cur_idx; i++) {
+ txpwr = (s16)le16_to_cpu(rf_tas->txpwr_history[i]);
+ linear += rtw89_db_quarter_to_linear(txpwr);
+
+ rtw89_debug(rtwdev, RTW89_DBG_SAR,
+ "tas: index: %u, txpwr: %d\n", i, txpwr);
+ }
+
+ if (cur_idx == 0)
+ tas->instant_txpwr = rtw89_db_to_linear(0);
+ else
+ tas->instant_txpwr = DIV_ROUND_DOWN_ULL(linear, cur_idx);
}
static
@@ -3511,9 +3619,9 @@ void rtw89_phy_c2h_handle(struct rtw89_dev *rtwdev, struct sk_buff *skb,
handler = rtw89_phy_c2h_rfk_report_handler[func];
break;
case RTW89_PHY_C2H_CLASS_DM:
- if (func == RTW89_PHY_C2H_DM_FUNC_LOWRT_RTY)
- return;
- fallthrough;
+ if (func < ARRAY_SIZE(rtw89_phy_c2h_dm_handler))
+ handler = rtw89_phy_c2h_dm_handler[func];
+ break;
default:
rtw89_info(rtwdev, "PHY c2h class %d not support\n", class);
return;
@@ -4600,7 +4708,7 @@ static void rtw89_phy_cfo_dm(struct rtw89_dev *rtwdev)
cfo->dcfo_avg = 0;
rtw89_debug(rtwdev, RTW89_DBG_CFO, "CFO:total_sta_assoc=%d\n",
rtwdev->total_sta_assoc);
- if (rtwdev->total_sta_assoc == 0) {
+ if (rtwdev->total_sta_assoc == 0 || rtw89_is_mlo_1_1(rtwdev)) {
rtw89_phy_cfo_reset(rtwdev);
return;
}
@@ -4651,29 +4759,28 @@ static void rtw89_phy_cfo_dm(struct rtw89_dev *rtwdev)
rtw89_phy_cfo_statistics_reset(rtwdev);
}
-void rtw89_phy_cfo_track_work(struct work_struct *work)
+void rtw89_phy_cfo_track_work(struct wiphy *wiphy, struct wiphy_work *work)
{
struct rtw89_dev *rtwdev = container_of(work, struct rtw89_dev,
cfo_track_work.work);
struct rtw89_cfo_tracking_info *cfo = &rtwdev->cfo_tracking;
- mutex_lock(&rtwdev->mutex);
+ lockdep_assert_wiphy(wiphy);
+
if (!cfo->cfo_trig_by_timer_en)
- goto out;
+ return;
rtw89_leave_ps_mode(rtwdev);
rtw89_phy_cfo_dm(rtwdev);
- ieee80211_queue_delayed_work(rtwdev->hw, &rtwdev->cfo_track_work,
- msecs_to_jiffies(cfo->cfo_timer_ms));
-out:
- mutex_unlock(&rtwdev->mutex);
+ wiphy_delayed_work_queue(wiphy, &rtwdev->cfo_track_work,
+ msecs_to_jiffies(cfo->cfo_timer_ms));
}
static void rtw89_phy_cfo_start_work(struct rtw89_dev *rtwdev)
{
struct rtw89_cfo_tracking_info *cfo = &rtwdev->cfo_tracking;
- ieee80211_queue_delayed_work(rtwdev->hw, &rtwdev->cfo_track_work,
- msecs_to_jiffies(cfo->cfo_timer_ms));
+ wiphy_delayed_work_queue(rtwdev->hw->wiphy, &rtwdev->cfo_track_work,
+ msecs_to_jiffies(cfo->cfo_timer_ms));
}
void rtw89_phy_cfo_track(struct rtw89_dev *rtwdev)
@@ -5115,7 +5222,6 @@ static void rtw89_phy_stat_thermal_update(struct rtw89_dev *rtwdev)
struct rtw89_phy_iter_rssi_data {
struct rtw89_dev *rtwdev;
- struct rtw89_phy_ch_info *ch_info;
bool rssi_changed;
};
@@ -5123,10 +5229,15 @@ static
void __rtw89_phy_stat_rssi_update_iter(struct rtw89_sta_link *rtwsta_link,
struct rtw89_phy_iter_rssi_data *rssi_data)
{
- struct rtw89_phy_ch_info *ch_info = rssi_data->ch_info;
+ struct rtw89_vif_link *rtwvif_link = rtwsta_link->rtwvif_link;
+ struct rtw89_dev *rtwdev = rssi_data->rtwdev;
+ struct rtw89_phy_ch_info *ch_info;
+ struct rtw89_bb_ctx *bb;
unsigned long rssi_curr;
rssi_curr = ewma_rssi_read(&rtwsta_link->avg_rssi);
+ bb = rtw89_get_bb_ctx(rtwdev, rtwvif_link->phy_idx);
+ ch_info = &bb->ch_info;
if (rssi_curr < ch_info->rssi_min) {
ch_info->rssi_min = rssi_curr;
@@ -5157,11 +5268,13 @@ static void rtw89_phy_stat_rssi_update_iter(void *data,
static void rtw89_phy_stat_rssi_update(struct rtw89_dev *rtwdev)
{
- struct rtw89_phy_iter_rssi_data rssi_data = {0};
+ struct rtw89_phy_iter_rssi_data rssi_data = {};
+ struct rtw89_bb_ctx *bb;
rssi_data.rtwdev = rtwdev;
- rssi_data.ch_info = &rtwdev->ch_info;
- rssi_data.ch_info->rssi_min = U8_MAX;
+ rtw89_for_each_active_bb(rtwdev, bb)
+ bb->ch_info.rssi_min = U8_MAX;
+
ieee80211_iterate_stations_atomic(rtwdev->hw,
rtw89_phy_stat_rssi_update_iter,
&rssi_data);
@@ -5199,24 +5312,27 @@ void rtw89_phy_stat_track(struct rtw89_dev *rtwdev)
memset(&phystat->cur_pkt_stat, 0, sizeof(phystat->cur_pkt_stat));
}
-static u16 rtw89_phy_ccx_us_to_idx(struct rtw89_dev *rtwdev, u32 time_us)
+static u16 rtw89_phy_ccx_us_to_idx(struct rtw89_dev *rtwdev,
+ struct rtw89_bb_ctx *bb, u32 time_us)
{
- struct rtw89_env_monitor_info *env = &rtwdev->env_monitor;
+ struct rtw89_env_monitor_info *env = &bb->env_monitor;
return time_us >> (ilog2(CCX_US_BASE_RATIO) + env->ccx_unit_idx);
}
-static u32 rtw89_phy_ccx_idx_to_us(struct rtw89_dev *rtwdev, u16 idx)
+static u32 rtw89_phy_ccx_idx_to_us(struct rtw89_dev *rtwdev,
+ struct rtw89_bb_ctx *bb, u16 idx)
{
- struct rtw89_env_monitor_info *env = &rtwdev->env_monitor;
+ struct rtw89_env_monitor_info *env = &bb->env_monitor;
return idx << (ilog2(CCX_US_BASE_RATIO) + env->ccx_unit_idx);
}
-static void rtw89_phy_ccx_top_setting_init(struct rtw89_dev *rtwdev)
+static void rtw89_phy_ccx_top_setting_init(struct rtw89_dev *rtwdev,
+ struct rtw89_bb_ctx *bb)
{
const struct rtw89_phy_gen_def *phy = rtwdev->chip->phy_def;
- struct rtw89_env_monitor_info *env = &rtwdev->env_monitor;
+ struct rtw89_env_monitor_info *env = &bb->env_monitor;
const struct rtw89_ccx_regs *ccx = phy->ccx;
env->ccx_manual_ctrl = false;
@@ -5225,17 +5341,20 @@ static void rtw89_phy_ccx_top_setting_init(struct rtw89_dev *rtwdev)
env->ccx_period = 0;
env->ccx_unit_idx = RTW89_CCX_32_US;
- rtw89_phy_set_phy_regs(rtwdev, ccx->setting_addr, ccx->en_mask, 1);
- rtw89_phy_set_phy_regs(rtwdev, ccx->setting_addr, ccx->trig_opt_mask, 1);
- rtw89_phy_set_phy_regs(rtwdev, ccx->setting_addr, ccx->measurement_trig_mask, 1);
- rtw89_phy_set_phy_regs(rtwdev, ccx->setting_addr, ccx->edcca_opt_mask,
- RTW89_CCX_EDCCA_BW20_0);
+ rtw89_phy_write32_idx(rtwdev, ccx->setting_addr, ccx->en_mask, 1, bb->phy_idx);
+ rtw89_phy_write32_idx(rtwdev, ccx->setting_addr, ccx->trig_opt_mask, 1,
+ bb->phy_idx);
+ rtw89_phy_write32_idx(rtwdev, ccx->setting_addr, ccx->measurement_trig_mask, 1,
+ bb->phy_idx);
+ rtw89_phy_write32_idx(rtwdev, ccx->setting_addr, ccx->edcca_opt_mask,
+ RTW89_CCX_EDCCA_BW20_0, bb->phy_idx);
}
-static u16 rtw89_phy_ccx_get_report(struct rtw89_dev *rtwdev, u16 report,
- u16 score)
+static u16 rtw89_phy_ccx_get_report(struct rtw89_dev *rtwdev,
+ struct rtw89_bb_ctx *bb,
+ u16 report, u16 score)
{
- struct rtw89_env_monitor_info *env = &rtwdev->env_monitor;
+ struct rtw89_env_monitor_info *env = &bb->env_monitor;
u32 numer = 0;
u16 ret = 0;
@@ -5275,9 +5394,10 @@ static void rtw89_phy_ccx_ms_to_period_unit(struct rtw89_dev *rtwdev,
*period, *unit_idx);
}
-static void rtw89_phy_ccx_racing_release(struct rtw89_dev *rtwdev)
+static void rtw89_phy_ccx_racing_release(struct rtw89_dev *rtwdev,
+ struct rtw89_bb_ctx *bb)
{
- struct rtw89_env_monitor_info *env = &rtwdev->env_monitor;
+ struct rtw89_env_monitor_info *env = &bb->env_monitor;
rtw89_debug(rtwdev, RTW89_DBG_PHY_TRACK,
"lv:(%d)->(0)\n", env->ccx_rac_lv);
@@ -5288,9 +5408,10 @@ static void rtw89_phy_ccx_racing_release(struct rtw89_dev *rtwdev)
}
static bool rtw89_phy_ifs_clm_th_update_check(struct rtw89_dev *rtwdev,
+ struct rtw89_bb_ctx *bb,
struct rtw89_ccx_para_info *para)
{
- struct rtw89_env_monitor_info *env = &rtwdev->env_monitor;
+ struct rtw89_env_monitor_info *env = &bb->env_monitor;
bool is_update = env->ifs_clm_app != para->ifs_clm_app;
u8 i = 0;
u16 *ifs_th_l = env->ifs_clm_th_l;
@@ -5325,12 +5446,12 @@ static bool rtw89_phy_ifs_clm_th_update_check(struct rtw89_dev *rtwdev,
*/
ifs_th_l[IFS_CLM_TH_START_IDX] = 0;
ifs_th_h_us[IFS_CLM_TH_START_IDX] = ifs_th0_us;
- ifs_th_h[IFS_CLM_TH_START_IDX] = rtw89_phy_ccx_us_to_idx(rtwdev,
+ ifs_th_h[IFS_CLM_TH_START_IDX] = rtw89_phy_ccx_us_to_idx(rtwdev, bb,
ifs_th0_us);
for (i = 1; i < RTW89_IFS_CLM_NUM; i++) {
ifs_th_l[i] = ifs_th_h[i - 1] + 1;
ifs_th_h_us[i] = ifs_th_h_us[i - 1] * ifs_th_times;
- ifs_th_h[i] = rtw89_phy_ccx_us_to_idx(rtwdev, ifs_th_h_us[i]);
+ ifs_th_h[i] = rtw89_phy_ccx_us_to_idx(rtwdev, bb, ifs_th_h_us[i]);
}
ifs_update_finished:
@@ -5341,30 +5462,31 @@ ifs_update_finished:
return is_update;
}
-static void rtw89_phy_ifs_clm_set_th_reg(struct rtw89_dev *rtwdev)
+static void rtw89_phy_ifs_clm_set_th_reg(struct rtw89_dev *rtwdev,
+ struct rtw89_bb_ctx *bb)
{
const struct rtw89_phy_gen_def *phy = rtwdev->chip->phy_def;
- struct rtw89_env_monitor_info *env = &rtwdev->env_monitor;
+ struct rtw89_env_monitor_info *env = &bb->env_monitor;
const struct rtw89_ccx_regs *ccx = phy->ccx;
u8 i = 0;
- rtw89_phy_set_phy_regs(rtwdev, ccx->ifs_t1_addr, ccx->ifs_t1_th_l_mask,
- env->ifs_clm_th_l[0]);
- rtw89_phy_set_phy_regs(rtwdev, ccx->ifs_t2_addr, ccx->ifs_t2_th_l_mask,
- env->ifs_clm_th_l[1]);
- rtw89_phy_set_phy_regs(rtwdev, ccx->ifs_t3_addr, ccx->ifs_t3_th_l_mask,
- env->ifs_clm_th_l[2]);
- rtw89_phy_set_phy_regs(rtwdev, ccx->ifs_t4_addr, ccx->ifs_t4_th_l_mask,
- env->ifs_clm_th_l[3]);
-
- rtw89_phy_set_phy_regs(rtwdev, ccx->ifs_t1_addr, ccx->ifs_t1_th_h_mask,
- env->ifs_clm_th_h[0]);
- rtw89_phy_set_phy_regs(rtwdev, ccx->ifs_t2_addr, ccx->ifs_t2_th_h_mask,
- env->ifs_clm_th_h[1]);
- rtw89_phy_set_phy_regs(rtwdev, ccx->ifs_t3_addr, ccx->ifs_t3_th_h_mask,
- env->ifs_clm_th_h[2]);
- rtw89_phy_set_phy_regs(rtwdev, ccx->ifs_t4_addr, ccx->ifs_t4_th_h_mask,
- env->ifs_clm_th_h[3]);
+ rtw89_phy_write32_idx(rtwdev, ccx->ifs_t1_addr, ccx->ifs_t1_th_l_mask,
+ env->ifs_clm_th_l[0], bb->phy_idx);
+ rtw89_phy_write32_idx(rtwdev, ccx->ifs_t2_addr, ccx->ifs_t2_th_l_mask,
+ env->ifs_clm_th_l[1], bb->phy_idx);
+ rtw89_phy_write32_idx(rtwdev, ccx->ifs_t3_addr, ccx->ifs_t3_th_l_mask,
+ env->ifs_clm_th_l[2], bb->phy_idx);
+ rtw89_phy_write32_idx(rtwdev, ccx->ifs_t4_addr, ccx->ifs_t4_th_l_mask,
+ env->ifs_clm_th_l[3], bb->phy_idx);
+
+ rtw89_phy_write32_idx(rtwdev, ccx->ifs_t1_addr, ccx->ifs_t1_th_h_mask,
+ env->ifs_clm_th_h[0], bb->phy_idx);
+ rtw89_phy_write32_idx(rtwdev, ccx->ifs_t2_addr, ccx->ifs_t2_th_h_mask,
+ env->ifs_clm_th_h[1], bb->phy_idx);
+ rtw89_phy_write32_idx(rtwdev, ccx->ifs_t3_addr, ccx->ifs_t3_th_h_mask,
+ env->ifs_clm_th_h[2], bb->phy_idx);
+ rtw89_phy_write32_idx(rtwdev, ccx->ifs_t4_addr, ccx->ifs_t4_th_h_mask,
+ env->ifs_clm_th_h[3], bb->phy_idx);
for (i = 0; i < RTW89_IFS_CLM_NUM; i++)
rtw89_debug(rtwdev, RTW89_DBG_PHY_TRACK,
@@ -5372,31 +5494,38 @@ static void rtw89_phy_ifs_clm_set_th_reg(struct rtw89_dev *rtwdev)
i + 1, env->ifs_clm_th_l[i], env->ifs_clm_th_h[i]);
}
-static void rtw89_phy_ifs_clm_setting_init(struct rtw89_dev *rtwdev)
+static void rtw89_phy_ifs_clm_setting_init(struct rtw89_dev *rtwdev,
+ struct rtw89_bb_ctx *bb)
{
const struct rtw89_phy_gen_def *phy = rtwdev->chip->phy_def;
- struct rtw89_env_monitor_info *env = &rtwdev->env_monitor;
+ struct rtw89_env_monitor_info *env = &bb->env_monitor;
const struct rtw89_ccx_regs *ccx = phy->ccx;
- struct rtw89_ccx_para_info para = {0};
+ struct rtw89_ccx_para_info para = {};
env->ifs_clm_app = RTW89_IFS_CLM_BACKGROUND;
env->ifs_clm_mntr_time = 0;
para.ifs_clm_app = RTW89_IFS_CLM_INIT;
- if (rtw89_phy_ifs_clm_th_update_check(rtwdev, &para))
- rtw89_phy_ifs_clm_set_th_reg(rtwdev);
+ if (rtw89_phy_ifs_clm_th_update_check(rtwdev, bb, &para))
+ rtw89_phy_ifs_clm_set_th_reg(rtwdev, bb);
- rtw89_phy_set_phy_regs(rtwdev, ccx->ifs_cnt_addr, ccx->ifs_collect_en_mask, true);
- rtw89_phy_set_phy_regs(rtwdev, ccx->ifs_t1_addr, ccx->ifs_t1_en_mask, true);
- rtw89_phy_set_phy_regs(rtwdev, ccx->ifs_t2_addr, ccx->ifs_t2_en_mask, true);
- rtw89_phy_set_phy_regs(rtwdev, ccx->ifs_t3_addr, ccx->ifs_t3_en_mask, true);
- rtw89_phy_set_phy_regs(rtwdev, ccx->ifs_t4_addr, ccx->ifs_t4_en_mask, true);
+ rtw89_phy_write32_idx(rtwdev, ccx->ifs_cnt_addr, ccx->ifs_collect_en_mask, true,
+ bb->phy_idx);
+ rtw89_phy_write32_idx(rtwdev, ccx->ifs_t1_addr, ccx->ifs_t1_en_mask, true,
+ bb->phy_idx);
+ rtw89_phy_write32_idx(rtwdev, ccx->ifs_t2_addr, ccx->ifs_t2_en_mask, true,
+ bb->phy_idx);
+ rtw89_phy_write32_idx(rtwdev, ccx->ifs_t3_addr, ccx->ifs_t3_en_mask, true,
+ bb->phy_idx);
+ rtw89_phy_write32_idx(rtwdev, ccx->ifs_t4_addr, ccx->ifs_t4_en_mask, true,
+ bb->phy_idx);
}
static int rtw89_phy_ccx_racing_ctrl(struct rtw89_dev *rtwdev,
+ struct rtw89_bb_ctx *bb,
enum rtw89_env_racing_lv level)
{
- struct rtw89_env_monitor_info *env = &rtwdev->env_monitor;
+ struct rtw89_env_monitor_info *env = &bb->env_monitor;
int ret = 0;
if (level >= RTW89_RAC_MAX_NUM) {
@@ -5425,56 +5554,62 @@ static int rtw89_phy_ccx_racing_ctrl(struct rtw89_dev *rtwdev,
return ret;
}
-static void rtw89_phy_ccx_trigger(struct rtw89_dev *rtwdev)
+static void rtw89_phy_ccx_trigger(struct rtw89_dev *rtwdev,
+ struct rtw89_bb_ctx *bb)
{
const struct rtw89_phy_gen_def *phy = rtwdev->chip->phy_def;
- struct rtw89_env_monitor_info *env = &rtwdev->env_monitor;
+ struct rtw89_env_monitor_info *env = &bb->env_monitor;
const struct rtw89_ccx_regs *ccx = phy->ccx;
- rtw89_phy_set_phy_regs(rtwdev, ccx->ifs_cnt_addr, ccx->ifs_clm_cnt_clear_mask, 0);
- rtw89_phy_set_phy_regs(rtwdev, ccx->setting_addr, ccx->measurement_trig_mask, 0);
- rtw89_phy_set_phy_regs(rtwdev, ccx->ifs_cnt_addr, ccx->ifs_clm_cnt_clear_mask, 1);
- rtw89_phy_set_phy_regs(rtwdev, ccx->setting_addr, ccx->measurement_trig_mask, 1);
+ rtw89_phy_write32_idx(rtwdev, ccx->ifs_cnt_addr, ccx->ifs_clm_cnt_clear_mask, 0,
+ bb->phy_idx);
+ rtw89_phy_write32_idx(rtwdev, ccx->setting_addr, ccx->measurement_trig_mask, 0,
+ bb->phy_idx);
+ rtw89_phy_write32_idx(rtwdev, ccx->ifs_cnt_addr, ccx->ifs_clm_cnt_clear_mask, 1,
+ bb->phy_idx);
+ rtw89_phy_write32_idx(rtwdev, ccx->setting_addr, ccx->measurement_trig_mask, 1,
+ bb->phy_idx);
env->ccx_ongoing = true;
}
-static void rtw89_phy_ifs_clm_get_utility(struct rtw89_dev *rtwdev)
+static void rtw89_phy_ifs_clm_get_utility(struct rtw89_dev *rtwdev,
+ struct rtw89_bb_ctx *bb)
{
- struct rtw89_env_monitor_info *env = &rtwdev->env_monitor;
+ struct rtw89_env_monitor_info *env = &bb->env_monitor;
u8 i = 0;
u32 res = 0;
env->ifs_clm_tx_ratio =
- rtw89_phy_ccx_get_report(rtwdev, env->ifs_clm_tx, PERCENT);
+ rtw89_phy_ccx_get_report(rtwdev, bb, env->ifs_clm_tx, PERCENT);
env->ifs_clm_edcca_excl_cca_ratio =
- rtw89_phy_ccx_get_report(rtwdev, env->ifs_clm_edcca_excl_cca,
+ rtw89_phy_ccx_get_report(rtwdev, bb, env->ifs_clm_edcca_excl_cca,
PERCENT);
env->ifs_clm_cck_fa_ratio =
- rtw89_phy_ccx_get_report(rtwdev, env->ifs_clm_cckfa, PERCENT);
+ rtw89_phy_ccx_get_report(rtwdev, bb, env->ifs_clm_cckfa, PERCENT);
env->ifs_clm_ofdm_fa_ratio =
- rtw89_phy_ccx_get_report(rtwdev, env->ifs_clm_ofdmfa, PERCENT);
+ rtw89_phy_ccx_get_report(rtwdev, bb, env->ifs_clm_ofdmfa, PERCENT);
env->ifs_clm_cck_cca_excl_fa_ratio =
- rtw89_phy_ccx_get_report(rtwdev, env->ifs_clm_cckcca_excl_fa,
+ rtw89_phy_ccx_get_report(rtwdev, bb, env->ifs_clm_cckcca_excl_fa,
PERCENT);
env->ifs_clm_ofdm_cca_excl_fa_ratio =
- rtw89_phy_ccx_get_report(rtwdev, env->ifs_clm_ofdmcca_excl_fa,
+ rtw89_phy_ccx_get_report(rtwdev, bb, env->ifs_clm_ofdmcca_excl_fa,
PERCENT);
env->ifs_clm_cck_fa_permil =
- rtw89_phy_ccx_get_report(rtwdev, env->ifs_clm_cckfa, PERMIL);
+ rtw89_phy_ccx_get_report(rtwdev, bb, env->ifs_clm_cckfa, PERMIL);
env->ifs_clm_ofdm_fa_permil =
- rtw89_phy_ccx_get_report(rtwdev, env->ifs_clm_ofdmfa, PERMIL);
+ rtw89_phy_ccx_get_report(rtwdev, bb, env->ifs_clm_ofdmfa, PERMIL);
for (i = 0; i < RTW89_IFS_CLM_NUM; i++) {
if (env->ifs_clm_his[i] > ENV_MNTR_IFSCLM_HIS_MAX) {
env->ifs_clm_ifs_avg[i] = ENV_MNTR_FAIL_DWORD;
} else {
env->ifs_clm_ifs_avg[i] =
- rtw89_phy_ccx_idx_to_us(rtwdev,
+ rtw89_phy_ccx_idx_to_us(rtwdev, bb,
env->ifs_clm_avg[i]);
}
- res = rtw89_phy_ccx_idx_to_us(rtwdev, env->ifs_clm_cca[i]);
+ res = rtw89_phy_ccx_idx_to_us(rtwdev, bb, env->ifs_clm_cca[i]);
res += env->ifs_clm_his[i] >> 1;
if (env->ifs_clm_his[i])
res /= env->ifs_clm_his[i];
@@ -5504,81 +5639,82 @@ static void rtw89_phy_ifs_clm_get_utility(struct rtw89_dev *rtwdev)
env->ifs_clm_cca_avg[i]);
}
-static bool rtw89_phy_ifs_clm_get_result(struct rtw89_dev *rtwdev)
+static bool rtw89_phy_ifs_clm_get_result(struct rtw89_dev *rtwdev,
+ struct rtw89_bb_ctx *bb)
{
const struct rtw89_phy_gen_def *phy = rtwdev->chip->phy_def;
- struct rtw89_env_monitor_info *env = &rtwdev->env_monitor;
+ struct rtw89_env_monitor_info *env = &bb->env_monitor;
const struct rtw89_ccx_regs *ccx = phy->ccx;
u8 i = 0;
- if (rtw89_phy_read32_mask(rtwdev, ccx->ifs_total_addr,
- ccx->ifs_cnt_done_mask) == 0) {
+ if (rtw89_phy_read32_idx(rtwdev, ccx->ifs_total_addr,
+ ccx->ifs_cnt_done_mask, bb->phy_idx) == 0) {
rtw89_debug(rtwdev, RTW89_DBG_PHY_TRACK,
"Get IFS_CLM report Fail\n");
return false;
}
env->ifs_clm_tx =
- rtw89_phy_read32_mask(rtwdev, ccx->ifs_clm_tx_cnt_addr,
- ccx->ifs_clm_tx_cnt_msk);
+ rtw89_phy_read32_idx(rtwdev, ccx->ifs_clm_tx_cnt_addr,
+ ccx->ifs_clm_tx_cnt_msk, bb->phy_idx);
env->ifs_clm_edcca_excl_cca =
- rtw89_phy_read32_mask(rtwdev, ccx->ifs_clm_tx_cnt_addr,
- ccx->ifs_clm_edcca_excl_cca_fa_mask);
+ rtw89_phy_read32_idx(rtwdev, ccx->ifs_clm_tx_cnt_addr,
+ ccx->ifs_clm_edcca_excl_cca_fa_mask, bb->phy_idx);
env->ifs_clm_cckcca_excl_fa =
- rtw89_phy_read32_mask(rtwdev, ccx->ifs_clm_cca_addr,
- ccx->ifs_clm_cckcca_excl_fa_mask);
+ rtw89_phy_read32_idx(rtwdev, ccx->ifs_clm_cca_addr,
+ ccx->ifs_clm_cckcca_excl_fa_mask, bb->phy_idx);
env->ifs_clm_ofdmcca_excl_fa =
- rtw89_phy_read32_mask(rtwdev, ccx->ifs_clm_cca_addr,
- ccx->ifs_clm_ofdmcca_excl_fa_mask);
+ rtw89_phy_read32_idx(rtwdev, ccx->ifs_clm_cca_addr,
+ ccx->ifs_clm_ofdmcca_excl_fa_mask, bb->phy_idx);
env->ifs_clm_cckfa =
- rtw89_phy_read32_mask(rtwdev, ccx->ifs_clm_fa_addr,
- ccx->ifs_clm_cck_fa_mask);
+ rtw89_phy_read32_idx(rtwdev, ccx->ifs_clm_fa_addr,
+ ccx->ifs_clm_cck_fa_mask, bb->phy_idx);
env->ifs_clm_ofdmfa =
- rtw89_phy_read32_mask(rtwdev, ccx->ifs_clm_fa_addr,
- ccx->ifs_clm_ofdm_fa_mask);
+ rtw89_phy_read32_idx(rtwdev, ccx->ifs_clm_fa_addr,
+ ccx->ifs_clm_ofdm_fa_mask, bb->phy_idx);
env->ifs_clm_his[0] =
- rtw89_phy_read32_mask(rtwdev, ccx->ifs_his_addr,
- ccx->ifs_t1_his_mask);
+ rtw89_phy_read32_idx(rtwdev, ccx->ifs_his_addr,
+ ccx->ifs_t1_his_mask, bb->phy_idx);
env->ifs_clm_his[1] =
- rtw89_phy_read32_mask(rtwdev, ccx->ifs_his_addr,
- ccx->ifs_t2_his_mask);
+ rtw89_phy_read32_idx(rtwdev, ccx->ifs_his_addr,
+ ccx->ifs_t2_his_mask, bb->phy_idx);
env->ifs_clm_his[2] =
- rtw89_phy_read32_mask(rtwdev, ccx->ifs_his_addr,
- ccx->ifs_t3_his_mask);
+ rtw89_phy_read32_idx(rtwdev, ccx->ifs_his_addr,
+ ccx->ifs_t3_his_mask, bb->phy_idx);
env->ifs_clm_his[3] =
- rtw89_phy_read32_mask(rtwdev, ccx->ifs_his_addr,
- ccx->ifs_t4_his_mask);
+ rtw89_phy_read32_idx(rtwdev, ccx->ifs_his_addr,
+ ccx->ifs_t4_his_mask, bb->phy_idx);
env->ifs_clm_avg[0] =
- rtw89_phy_read32_mask(rtwdev, ccx->ifs_avg_l_addr,
- ccx->ifs_t1_avg_mask);
+ rtw89_phy_read32_idx(rtwdev, ccx->ifs_avg_l_addr,
+ ccx->ifs_t1_avg_mask, bb->phy_idx);
env->ifs_clm_avg[1] =
- rtw89_phy_read32_mask(rtwdev, ccx->ifs_avg_l_addr,
- ccx->ifs_t2_avg_mask);
+ rtw89_phy_read32_idx(rtwdev, ccx->ifs_avg_l_addr,
+ ccx->ifs_t2_avg_mask, bb->phy_idx);
env->ifs_clm_avg[2] =
- rtw89_phy_read32_mask(rtwdev, ccx->ifs_avg_h_addr,
- ccx->ifs_t3_avg_mask);
+ rtw89_phy_read32_idx(rtwdev, ccx->ifs_avg_h_addr,
+ ccx->ifs_t3_avg_mask, bb->phy_idx);
env->ifs_clm_avg[3] =
- rtw89_phy_read32_mask(rtwdev, ccx->ifs_avg_h_addr,
- ccx->ifs_t4_avg_mask);
+ rtw89_phy_read32_idx(rtwdev, ccx->ifs_avg_h_addr,
+ ccx->ifs_t4_avg_mask, bb->phy_idx);
env->ifs_clm_cca[0] =
- rtw89_phy_read32_mask(rtwdev, ccx->ifs_cca_l_addr,
- ccx->ifs_t1_cca_mask);
+ rtw89_phy_read32_idx(rtwdev, ccx->ifs_cca_l_addr,
+ ccx->ifs_t1_cca_mask, bb->phy_idx);
env->ifs_clm_cca[1] =
- rtw89_phy_read32_mask(rtwdev, ccx->ifs_cca_l_addr,
- ccx->ifs_t2_cca_mask);
+ rtw89_phy_read32_idx(rtwdev, ccx->ifs_cca_l_addr,
+ ccx->ifs_t2_cca_mask, bb->phy_idx);
env->ifs_clm_cca[2] =
- rtw89_phy_read32_mask(rtwdev, ccx->ifs_cca_h_addr,
- ccx->ifs_t3_cca_mask);
+ rtw89_phy_read32_idx(rtwdev, ccx->ifs_cca_h_addr,
+ ccx->ifs_t3_cca_mask, bb->phy_idx);
env->ifs_clm_cca[3] =
- rtw89_phy_read32_mask(rtwdev, ccx->ifs_cca_h_addr,
- ccx->ifs_t4_cca_mask);
+ rtw89_phy_read32_idx(rtwdev, ccx->ifs_cca_h_addr,
+ ccx->ifs_t4_cca_mask, bb->phy_idx);
env->ifs_clm_total_ifs =
- rtw89_phy_read32_mask(rtwdev, ccx->ifs_total_addr,
- ccx->ifs_total_mask);
+ rtw89_phy_read32_idx(rtwdev, ccx->ifs_total_addr,
+ ccx->ifs_total_mask, bb->phy_idx);
rtw89_debug(rtwdev, RTW89_DBG_PHY_TRACK, "IFS-CLM total_ifs = %d\n",
env->ifs_clm_total_ifs);
@@ -5598,16 +5734,17 @@ static bool rtw89_phy_ifs_clm_get_result(struct rtw89_dev *rtwdev)
"T%d:[%d, %d, %d]\n", i + 1, env->ifs_clm_his[i],
env->ifs_clm_avg[i], env->ifs_clm_cca[i]);
- rtw89_phy_ifs_clm_get_utility(rtwdev);
+ rtw89_phy_ifs_clm_get_utility(rtwdev, bb);
return true;
}
static int rtw89_phy_ifs_clm_set(struct rtw89_dev *rtwdev,
+ struct rtw89_bb_ctx *bb,
struct rtw89_ccx_para_info *para)
{
const struct rtw89_phy_gen_def *phy = rtwdev->chip->phy_def;
- struct rtw89_env_monitor_info *env = &rtwdev->env_monitor;
+ struct rtw89_env_monitor_info *env = &bb->env_monitor;
const struct rtw89_ccx_regs *ccx = phy->ccx;
u32 period = 0;
u32 unit_idx = 0;
@@ -5618,17 +5755,17 @@ static int rtw89_phy_ifs_clm_set(struct rtw89_dev *rtwdev,
return -EINVAL;
}
- if (rtw89_phy_ccx_racing_ctrl(rtwdev, para->rac_lv))
+ if (rtw89_phy_ccx_racing_ctrl(rtwdev, bb, para->rac_lv))
return -EINVAL;
if (para->mntr_time != env->ifs_clm_mntr_time) {
rtw89_phy_ccx_ms_to_period_unit(rtwdev, para->mntr_time,
&period, &unit_idx);
- rtw89_phy_set_phy_regs(rtwdev, ccx->ifs_cnt_addr,
- ccx->ifs_clm_period_mask, period);
- rtw89_phy_set_phy_regs(rtwdev, ccx->ifs_cnt_addr,
- ccx->ifs_clm_cnt_unit_mask,
- unit_idx);
+ rtw89_phy_write32_idx(rtwdev, ccx->ifs_cnt_addr,
+ ccx->ifs_clm_period_mask, period, bb->phy_idx);
+ rtw89_phy_write32_idx(rtwdev, ccx->ifs_cnt_addr,
+ ccx->ifs_clm_cnt_unit_mask,
+ unit_idx, bb->phy_idx);
rtw89_debug(rtwdev, RTW89_DBG_PHY_TRACK,
"Update IFS-CLM time ((%d)) -> ((%d))\n",
@@ -5639,18 +5776,19 @@ static int rtw89_phy_ifs_clm_set(struct rtw89_dev *rtwdev,
env->ccx_unit_idx = (u8)unit_idx;
}
- if (rtw89_phy_ifs_clm_th_update_check(rtwdev, para)) {
+ if (rtw89_phy_ifs_clm_th_update_check(rtwdev, bb, para)) {
env->ifs_clm_app = para->ifs_clm_app;
- rtw89_phy_ifs_clm_set_th_reg(rtwdev);
+ rtw89_phy_ifs_clm_set_th_reg(rtwdev, bb);
}
return 0;
}
-void rtw89_phy_env_monitor_track(struct rtw89_dev *rtwdev)
+static void __rtw89_phy_env_monitor_track(struct rtw89_dev *rtwdev,
+ struct rtw89_bb_ctx *bb)
{
- struct rtw89_env_monitor_info *env = &rtwdev->env_monitor;
- struct rtw89_ccx_para_info para = {0};
+ struct rtw89_env_monitor_info *env = &bb->env_monitor;
+ struct rtw89_ccx_para_info para = {};
u8 chk_result = RTW89_PHY_ENV_MON_CCX_FAIL;
env->ccx_watchdog_result = RTW89_PHY_ENV_MON_CCX_FAIL;
@@ -5660,25 +5798,36 @@ void rtw89_phy_env_monitor_track(struct rtw89_dev *rtwdev)
return;
}
+ rtw89_debug(rtwdev, RTW89_DBG_PHY_TRACK,
+ "BB-%d env_monitor track\n", bb->phy_idx);
+
/* only ifs_clm for now */
- if (rtw89_phy_ifs_clm_get_result(rtwdev))
+ if (rtw89_phy_ifs_clm_get_result(rtwdev, bb))
env->ccx_watchdog_result |= RTW89_PHY_ENV_MON_IFS_CLM;
- rtw89_phy_ccx_racing_release(rtwdev);
+ rtw89_phy_ccx_racing_release(rtwdev, bb);
para.mntr_time = 1900;
para.rac_lv = RTW89_RAC_LV_1;
para.ifs_clm_app = RTW89_IFS_CLM_BACKGROUND;
- if (rtw89_phy_ifs_clm_set(rtwdev, &para) == 0)
+ if (rtw89_phy_ifs_clm_set(rtwdev, bb, &para) == 0)
chk_result |= RTW89_PHY_ENV_MON_IFS_CLM;
if (chk_result)
- rtw89_phy_ccx_trigger(rtwdev);
+ rtw89_phy_ccx_trigger(rtwdev, bb);
rtw89_debug(rtwdev, RTW89_DBG_PHY_TRACK,
"get_result=0x%x, chk_result:0x%x\n",
env->ccx_watchdog_result, chk_result);
}
+void rtw89_phy_env_monitor_track(struct rtw89_dev *rtwdev)
+{
+ struct rtw89_bb_ctx *bb;
+
+ rtw89_for_each_active_bb(rtwdev, bb)
+ __rtw89_phy_env_monitor_track(rtwdev, bb);
+}
+
static bool rtw89_physts_ie_page_valid(enum rtw89_phy_status_bitmap *ie_page)
{
if (*ie_page >= RTW89_PHYSTS_BITMAP_NUM ||
@@ -5799,11 +5948,12 @@ static void rtw89_physts_parsing_init(struct rtw89_dev *rtwdev)
__rtw89_physts_parsing_init(rtwdev, RTW89_PHY_1);
}
-static void rtw89_phy_dig_read_gain_table(struct rtw89_dev *rtwdev, int type)
+static void rtw89_phy_dig_read_gain_table(struct rtw89_dev *rtwdev,
+ struct rtw89_bb_ctx *bb, int type)
{
const struct rtw89_chip_info *chip = rtwdev->chip;
- struct rtw89_dig_info *dig = &rtwdev->dig;
const struct rtw89_phy_dig_gain_cfg *cfg;
+ struct rtw89_dig_info *dig = &bb->dig;
const char *msg;
u8 i;
s8 gain_base;
@@ -5840,8 +5990,8 @@ static void rtw89_phy_dig_read_gain_table(struct rtw89_dev *rtwdev, int type)
}
for (i = 0; i < cfg->size; i++) {
- tmp = rtw89_phy_read32_mask(rtwdev, cfg->table[i].addr,
- cfg->table[i].mask);
+ tmp = rtw89_phy_read32_idx(rtwdev, cfg->table[i].addr,
+ cfg->table[i].mask, bb->phy_idx);
tmp >>= DIG_GAIN_SHIFT;
gain_arr[i] = sign_extend32(tmp, U4_MAX_BIT) + gain_base;
gain_base += DIG_GAIN;
@@ -5851,25 +6001,26 @@ static void rtw89_phy_dig_read_gain_table(struct rtw89_dev *rtwdev, int type)
}
}
-static void rtw89_phy_dig_update_gain_para(struct rtw89_dev *rtwdev)
+static void rtw89_phy_dig_update_gain_para(struct rtw89_dev *rtwdev,
+ struct rtw89_bb_ctx *bb)
{
- struct rtw89_dig_info *dig = &rtwdev->dig;
+ struct rtw89_dig_info *dig = &bb->dig;
u32 tmp;
u8 i;
if (!rtwdev->hal.support_igi)
return;
- tmp = rtw89_phy_read32_mask(rtwdev, R_PATH0_IB_PKPW,
- B_PATH0_IB_PKPW_MSK);
+ tmp = rtw89_phy_read32_idx(rtwdev, R_PATH0_IB_PKPW,
+ B_PATH0_IB_PKPW_MSK, bb->phy_idx);
dig->ib_pkpwr = sign_extend32(tmp >> DIG_GAIN_SHIFT, U8_MAX_BIT);
- dig->ib_pbk = rtw89_phy_read32_mask(rtwdev, R_PATH0_IB_PBK,
- B_PATH0_IB_PBK_MSK);
+ dig->ib_pbk = rtw89_phy_read32_idx(rtwdev, R_PATH0_IB_PBK,
+ B_PATH0_IB_PBK_MSK, bb->phy_idx);
rtw89_debug(rtwdev, RTW89_DBG_DIG, "ib_pkpwr=%d, ib_pbk=%d\n",
dig->ib_pkpwr, dig->ib_pbk);
for (i = RTW89_DIG_GAIN_LNA_G; i < RTW89_DIG_GAIN_MAX; i++)
- rtw89_phy_dig_read_gain_table(rtwdev, i);
+ rtw89_phy_dig_read_gain_table(rtwdev, bb, i);
}
static const u8 rssi_nolink = 22;
@@ -5878,10 +6029,11 @@ static const u16 fa_th_2g[FA_TH_NUM] = {22, 44, 66, 88};
static const u16 fa_th_5g[FA_TH_NUM] = {4, 8, 12, 16};
static const u16 fa_th_nolink[FA_TH_NUM] = {196, 352, 440, 528};
-static void rtw89_phy_dig_update_rssi_info(struct rtw89_dev *rtwdev)
+static void rtw89_phy_dig_update_rssi_info(struct rtw89_dev *rtwdev,
+ struct rtw89_bb_ctx *bb)
{
- struct rtw89_phy_ch_info *ch_info = &rtwdev->ch_info;
- struct rtw89_dig_info *dig = &rtwdev->dig;
+ struct rtw89_phy_ch_info *ch_info = &bb->ch_info;
+ struct rtw89_dig_info *dig = &bb->dig;
bool is_linked = rtwdev->total_sta_assoc > 0;
if (is_linked) {
@@ -5892,10 +6044,11 @@ static void rtw89_phy_dig_update_rssi_info(struct rtw89_dev *rtwdev)
}
}
-static void rtw89_phy_dig_update_para(struct rtw89_dev *rtwdev)
+static void rtw89_phy_dig_update_para(struct rtw89_dev *rtwdev,
+ struct rtw89_bb_ctx *bb)
{
- struct rtw89_dig_info *dig = &rtwdev->dig;
- const struct rtw89_chan *chan = rtw89_chan_get(rtwdev, RTW89_CHANCTX_0);
+ const struct rtw89_chan *chan = rtw89_mgnt_chan_get(rtwdev, bb->phy_idx);
+ struct rtw89_dig_info *dig = &bb->dig;
bool is_linked = rtwdev->total_sta_assoc > 0;
const u16 *fa_th_src = NULL;
@@ -5924,9 +6077,10 @@ static const u8 pd_low_th_offset = 16, dynamic_igi_min = 0x20;
static const u8 igi_max_performance_mode = 0x5a;
static const u8 dynamic_pd_threshold_max;
-static void rtw89_phy_dig_para_reset(struct rtw89_dev *rtwdev)
+static void rtw89_phy_dig_para_reset(struct rtw89_dev *rtwdev,
+ struct rtw89_bb_ctx *bb)
{
- struct rtw89_dig_info *dig = &rtwdev->dig;
+ struct rtw89_dig_info *dig = &bb->dig;
dig->cur_gaincode.lna_idx = LNA_IDX_MAX;
dig->cur_gaincode.tia_idx = TIA_IDX_MAX;
@@ -5942,15 +6096,27 @@ static void rtw89_phy_dig_para_reset(struct rtw89_dev *rtwdev)
dig->is_linked_pre = false;
}
+static void __rtw89_phy_dig_init(struct rtw89_dev *rtwdev,
+ struct rtw89_bb_ctx *bb)
+{
+ rtw89_debug(rtwdev, RTW89_DBG_DIG, "BB-%d dig_init\n", bb->phy_idx);
+
+ rtw89_phy_dig_update_gain_para(rtwdev, bb);
+ rtw89_phy_dig_reset(rtwdev, bb);
+}
+
static void rtw89_phy_dig_init(struct rtw89_dev *rtwdev)
{
- rtw89_phy_dig_update_gain_para(rtwdev);
- rtw89_phy_dig_reset(rtwdev);
+ struct rtw89_bb_ctx *bb;
+
+ rtw89_for_each_capab_bb(rtwdev, bb)
+ __rtw89_phy_dig_init(rtwdev, bb);
}
-static u8 rtw89_phy_dig_lna_idx_by_rssi(struct rtw89_dev *rtwdev, u8 rssi)
+static u8 rtw89_phy_dig_lna_idx_by_rssi(struct rtw89_dev *rtwdev,
+ struct rtw89_bb_ctx *bb, u8 rssi)
{
- struct rtw89_dig_info *dig = &rtwdev->dig;
+ struct rtw89_dig_info *dig = &bb->dig;
u8 lna_idx;
if (rssi < dig->igi_rssi_th[0])
@@ -5969,9 +6135,10 @@ static u8 rtw89_phy_dig_lna_idx_by_rssi(struct rtw89_dev *rtwdev, u8 rssi)
return lna_idx;
}
-static u8 rtw89_phy_dig_tia_idx_by_rssi(struct rtw89_dev *rtwdev, u8 rssi)
+static u8 rtw89_phy_dig_tia_idx_by_rssi(struct rtw89_dev *rtwdev,
+ struct rtw89_bb_ctx *bb, u8 rssi)
{
- struct rtw89_dig_info *dig = &rtwdev->dig;
+ struct rtw89_dig_info *dig = &bb->dig;
u8 tia_idx;
if (rssi < dig->igi_rssi_th[0])
@@ -5984,10 +6151,11 @@ static u8 rtw89_phy_dig_tia_idx_by_rssi(struct rtw89_dev *rtwdev, u8 rssi)
#define IB_PBK_BASE 110
#define WB_RSSI_BASE 10
-static u8 rtw89_phy_dig_rxb_idx_by_rssi(struct rtw89_dev *rtwdev, u8 rssi,
+static u8 rtw89_phy_dig_rxb_idx_by_rssi(struct rtw89_dev *rtwdev,
+ struct rtw89_bb_ctx *bb, u8 rssi,
struct rtw89_agc_gaincode_set *set)
{
- struct rtw89_dig_info *dig = &rtwdev->dig;
+ struct rtw89_dig_info *dig = &bb->dig;
s8 lna_gain = dig->lna_gain[set->lna_idx];
s8 tia_gain = dig->tia_gain[set->tia_idx];
s32 wb_rssi = rssi + lna_gain + tia_gain;
@@ -6003,12 +6171,13 @@ static u8 rtw89_phy_dig_rxb_idx_by_rssi(struct rtw89_dev *rtwdev, u8 rssi,
return rxb_idx;
}
-static void rtw89_phy_dig_gaincode_by_rssi(struct rtw89_dev *rtwdev, u8 rssi,
+static void rtw89_phy_dig_gaincode_by_rssi(struct rtw89_dev *rtwdev,
+ struct rtw89_bb_ctx *bb, u8 rssi,
struct rtw89_agc_gaincode_set *set)
{
- set->lna_idx = rtw89_phy_dig_lna_idx_by_rssi(rtwdev, rssi);
- set->tia_idx = rtw89_phy_dig_tia_idx_by_rssi(rtwdev, rssi);
- set->rxb_idx = rtw89_phy_dig_rxb_idx_by_rssi(rtwdev, rssi, set);
+ set->lna_idx = rtw89_phy_dig_lna_idx_by_rssi(rtwdev, bb, rssi);
+ set->tia_idx = rtw89_phy_dig_tia_idx_by_rssi(rtwdev, bb, rssi);
+ set->rxb_idx = rtw89_phy_dig_rxb_idx_by_rssi(rtwdev, bb, rssi, set);
rtw89_debug(rtwdev, RTW89_DBG_DIG,
"final_rssi=%03d, (lna,tia,rab)=(%d,%d,%02d)\n",
@@ -6017,10 +6186,11 @@ static void rtw89_phy_dig_gaincode_by_rssi(struct rtw89_dev *rtwdev, u8 rssi,
#define IGI_OFFSET_MAX 25
#define IGI_OFFSET_MUL 2
-static void rtw89_phy_dig_igi_offset_by_env(struct rtw89_dev *rtwdev)
+static void rtw89_phy_dig_igi_offset_by_env(struct rtw89_dev *rtwdev,
+ struct rtw89_bb_ctx *bb)
{
- struct rtw89_dig_info *dig = &rtwdev->dig;
- struct rtw89_env_monitor_info *env = &rtwdev->env_monitor;
+ struct rtw89_dig_info *dig = &bb->dig;
+ struct rtw89_env_monitor_info *env = &bb->env_monitor;
enum rtw89_dig_noisy_level noisy_lv;
u8 igi_offset = dig->fa_rssi_ofst;
u16 fa_ratio = 0;
@@ -6057,92 +6227,99 @@ static void rtw89_phy_dig_igi_offset_by_env(struct rtw89_dev *rtwdev)
noisy_lv, igi_offset);
}
-static void rtw89_phy_dig_set_lna_idx(struct rtw89_dev *rtwdev, u8 lna_idx)
+static void rtw89_phy_dig_set_lna_idx(struct rtw89_dev *rtwdev,
+ struct rtw89_bb_ctx *bb, u8 lna_idx)
{
const struct rtw89_dig_regs *dig_regs = rtwdev->chip->dig_regs;
- rtw89_phy_write32_mask(rtwdev, dig_regs->p0_lna_init.addr,
- dig_regs->p0_lna_init.mask, lna_idx);
- rtw89_phy_write32_mask(rtwdev, dig_regs->p1_lna_init.addr,
- dig_regs->p1_lna_init.mask, lna_idx);
+ rtw89_phy_write32_idx(rtwdev, dig_regs->p0_lna_init.addr,
+ dig_regs->p0_lna_init.mask, lna_idx, bb->phy_idx);
+ rtw89_phy_write32_idx(rtwdev, dig_regs->p1_lna_init.addr,
+ dig_regs->p1_lna_init.mask, lna_idx, bb->phy_idx);
}
-static void rtw89_phy_dig_set_tia_idx(struct rtw89_dev *rtwdev, u8 tia_idx)
+static void rtw89_phy_dig_set_tia_idx(struct rtw89_dev *rtwdev,
+ struct rtw89_bb_ctx *bb, u8 tia_idx)
{
const struct rtw89_dig_regs *dig_regs = rtwdev->chip->dig_regs;
- rtw89_phy_write32_mask(rtwdev, dig_regs->p0_tia_init.addr,
- dig_regs->p0_tia_init.mask, tia_idx);
- rtw89_phy_write32_mask(rtwdev, dig_regs->p1_tia_init.addr,
- dig_regs->p1_tia_init.mask, tia_idx);
+ rtw89_phy_write32_idx(rtwdev, dig_regs->p0_tia_init.addr,
+ dig_regs->p0_tia_init.mask, tia_idx, bb->phy_idx);
+ rtw89_phy_write32_idx(rtwdev, dig_regs->p1_tia_init.addr,
+ dig_regs->p1_tia_init.mask, tia_idx, bb->phy_idx);
}
-static void rtw89_phy_dig_set_rxb_idx(struct rtw89_dev *rtwdev, u8 rxb_idx)
+static void rtw89_phy_dig_set_rxb_idx(struct rtw89_dev *rtwdev,
+ struct rtw89_bb_ctx *bb, u8 rxb_idx)
{
const struct rtw89_dig_regs *dig_regs = rtwdev->chip->dig_regs;
- rtw89_phy_write32_mask(rtwdev, dig_regs->p0_rxb_init.addr,
- dig_regs->p0_rxb_init.mask, rxb_idx);
- rtw89_phy_write32_mask(rtwdev, dig_regs->p1_rxb_init.addr,
- dig_regs->p1_rxb_init.mask, rxb_idx);
+ rtw89_phy_write32_idx(rtwdev, dig_regs->p0_rxb_init.addr,
+ dig_regs->p0_rxb_init.mask, rxb_idx, bb->phy_idx);
+ rtw89_phy_write32_idx(rtwdev, dig_regs->p1_rxb_init.addr,
+ dig_regs->p1_rxb_init.mask, rxb_idx, bb->phy_idx);
}
static void rtw89_phy_dig_set_igi_cr(struct rtw89_dev *rtwdev,
+ struct rtw89_bb_ctx *bb,
const struct rtw89_agc_gaincode_set set)
{
if (!rtwdev->hal.support_igi)
return;
- rtw89_phy_dig_set_lna_idx(rtwdev, set.lna_idx);
- rtw89_phy_dig_set_tia_idx(rtwdev, set.tia_idx);
- rtw89_phy_dig_set_rxb_idx(rtwdev, set.rxb_idx);
+ rtw89_phy_dig_set_lna_idx(rtwdev, bb, set.lna_idx);
+ rtw89_phy_dig_set_tia_idx(rtwdev, bb, set.tia_idx);
+ rtw89_phy_dig_set_rxb_idx(rtwdev, bb, set.rxb_idx);
rtw89_debug(rtwdev, RTW89_DBG_DIG, "Set (lna,tia,rxb)=((%d,%d,%02d))\n",
set.lna_idx, set.tia_idx, set.rxb_idx);
}
static void rtw89_phy_dig_sdagc_follow_pagc_config(struct rtw89_dev *rtwdev,
+ struct rtw89_bb_ctx *bb,
bool enable)
{
const struct rtw89_dig_regs *dig_regs = rtwdev->chip->dig_regs;
- rtw89_phy_write32_mask(rtwdev, dig_regs->p0_p20_pagcugc_en.addr,
- dig_regs->p0_p20_pagcugc_en.mask, enable);
- rtw89_phy_write32_mask(rtwdev, dig_regs->p0_s20_pagcugc_en.addr,
- dig_regs->p0_s20_pagcugc_en.mask, enable);
- rtw89_phy_write32_mask(rtwdev, dig_regs->p1_p20_pagcugc_en.addr,
- dig_regs->p1_p20_pagcugc_en.mask, enable);
- rtw89_phy_write32_mask(rtwdev, dig_regs->p1_s20_pagcugc_en.addr,
- dig_regs->p1_s20_pagcugc_en.mask, enable);
+ rtw89_phy_write32_idx(rtwdev, dig_regs->p0_p20_pagcugc_en.addr,
+ dig_regs->p0_p20_pagcugc_en.mask, enable, bb->phy_idx);
+ rtw89_phy_write32_idx(rtwdev, dig_regs->p0_s20_pagcugc_en.addr,
+ dig_regs->p0_s20_pagcugc_en.mask, enable, bb->phy_idx);
+ rtw89_phy_write32_idx(rtwdev, dig_regs->p1_p20_pagcugc_en.addr,
+ dig_regs->p1_p20_pagcugc_en.mask, enable, bb->phy_idx);
+ rtw89_phy_write32_idx(rtwdev, dig_regs->p1_s20_pagcugc_en.addr,
+ dig_regs->p1_s20_pagcugc_en.mask, enable, bb->phy_idx);
rtw89_debug(rtwdev, RTW89_DBG_DIG, "sdagc_follow_pagc=%d\n", enable);
}
-static void rtw89_phy_dig_config_igi(struct rtw89_dev *rtwdev)
+static void rtw89_phy_dig_config_igi(struct rtw89_dev *rtwdev,
+ struct rtw89_bb_ctx *bb)
{
- struct rtw89_dig_info *dig = &rtwdev->dig;
+ struct rtw89_dig_info *dig = &bb->dig;
if (!rtwdev->hal.support_igi)
return;
if (dig->force_gaincode_idx_en) {
- rtw89_phy_dig_set_igi_cr(rtwdev, dig->force_gaincode);
+ rtw89_phy_dig_set_igi_cr(rtwdev, bb, dig->force_gaincode);
rtw89_debug(rtwdev, RTW89_DBG_DIG,
"Force gaincode index enabled.\n");
} else {
- rtw89_phy_dig_gaincode_by_rssi(rtwdev, dig->igi_fa_rssi,
+ rtw89_phy_dig_gaincode_by_rssi(rtwdev, bb, dig->igi_fa_rssi,
&dig->cur_gaincode);
- rtw89_phy_dig_set_igi_cr(rtwdev, dig->cur_gaincode);
+ rtw89_phy_dig_set_igi_cr(rtwdev, bb, dig->cur_gaincode);
}
}
-static void rtw89_phy_dig_dyn_pd_th(struct rtw89_dev *rtwdev, u8 rssi,
- bool enable)
+static void rtw89_phy_dig_dyn_pd_th(struct rtw89_dev *rtwdev,
+ struct rtw89_bb_ctx *bb,
+ u8 rssi, bool enable)
{
- const struct rtw89_chan *chan = rtw89_chan_get(rtwdev, RTW89_CHANCTX_0);
+ const struct rtw89_chan *chan = rtw89_mgnt_chan_get(rtwdev, bb->phy_idx);
const struct rtw89_dig_regs *dig_regs = rtwdev->chip->dig_regs;
enum rtw89_bandwidth cbw = chan->band_width;
- struct rtw89_dig_info *dig = &rtwdev->dig;
+ struct rtw89_dig_info *dig = &bb->dig;
u8 final_rssi = 0, under_region = dig->pd_low_th_ofst;
u8 ofdm_cca_th;
s8 cck_cca_th;
@@ -6184,10 +6361,10 @@ static void rtw89_phy_dig_dyn_pd_th(struct rtw89_dev *rtwdev, u8 rssi,
"Dynamic PD th disabled, Set PD_low_bd=0\n");
}
- rtw89_phy_write32_mask(rtwdev, dig_regs->seg0_pd_reg,
- dig_regs->pd_lower_bound_mask, pd_val);
- rtw89_phy_write32_mask(rtwdev, dig_regs->seg0_pd_reg,
- dig_regs->pd_spatial_reuse_en, enable);
+ rtw89_phy_write32_idx(rtwdev, dig_regs->seg0_pd_reg,
+ dig_regs->pd_lower_bound_mask, pd_val, bb->phy_idx);
+ rtw89_phy_write32_idx(rtwdev, dig_regs->seg0_pd_reg,
+ dig_regs->pd_spatial_reuse_en, enable, bb->phy_idx);
if (!rtwdev->hal.support_cckpd)
return;
@@ -6199,29 +6376,29 @@ static void rtw89_phy_dig_dyn_pd_th(struct rtw89_dev *rtwdev, u8 rssi,
"igi=%d, cck_ccaTH=%d, backoff=%d, cck_PD_low=((%d))dB\n",
final_rssi, cck_cca_th, under_region, pd_val);
- rtw89_phy_write32_mask(rtwdev, dig_regs->bmode_pd_reg,
- dig_regs->bmode_cca_rssi_limit_en, enable);
- rtw89_phy_write32_mask(rtwdev, dig_regs->bmode_pd_lower_bound_reg,
- dig_regs->bmode_rssi_nocca_low_th_mask, pd_val);
+ rtw89_phy_write32_idx(rtwdev, dig_regs->bmode_pd_reg,
+ dig_regs->bmode_cca_rssi_limit_en, enable, bb->phy_idx);
+ rtw89_phy_write32_idx(rtwdev, dig_regs->bmode_pd_lower_bound_reg,
+ dig_regs->bmode_rssi_nocca_low_th_mask, pd_val, bb->phy_idx);
}
-void rtw89_phy_dig_reset(struct rtw89_dev *rtwdev)
+void rtw89_phy_dig_reset(struct rtw89_dev *rtwdev, struct rtw89_bb_ctx *bb)
{
- struct rtw89_dig_info *dig = &rtwdev->dig;
+ struct rtw89_dig_info *dig = &bb->dig;
dig->bypass_dig = false;
- rtw89_phy_dig_para_reset(rtwdev);
- rtw89_phy_dig_set_igi_cr(rtwdev, dig->force_gaincode);
- rtw89_phy_dig_dyn_pd_th(rtwdev, rssi_nolink, false);
- rtw89_phy_dig_sdagc_follow_pagc_config(rtwdev, false);
- rtw89_phy_dig_update_para(rtwdev);
+ rtw89_phy_dig_para_reset(rtwdev, bb);
+ rtw89_phy_dig_set_igi_cr(rtwdev, bb, dig->force_gaincode);
+ rtw89_phy_dig_dyn_pd_th(rtwdev, bb, rssi_nolink, false);
+ rtw89_phy_dig_sdagc_follow_pagc_config(rtwdev, bb, false);
+ rtw89_phy_dig_update_para(rtwdev, bb);
}
#define IGI_RSSI_MIN 10
#define ABS_IGI_MIN 0xc
-void rtw89_phy_dig(struct rtw89_dev *rtwdev)
+static void __rtw89_phy_dig(struct rtw89_dev *rtwdev, struct rtw89_bb_ctx *bb)
{
- struct rtw89_dig_info *dig = &rtwdev->dig;
+ struct rtw89_dig_info *dig = &bb->dig;
bool is_linked = rtwdev->total_sta_assoc > 0;
u8 igi_min;
@@ -6230,20 +6407,22 @@ void rtw89_phy_dig(struct rtw89_dev *rtwdev)
return;
}
- rtw89_phy_dig_update_rssi_info(rtwdev);
+ rtw89_debug(rtwdev, RTW89_DBG_DIG, "BB-%d dig track\n", bb->phy_idx);
+
+ rtw89_phy_dig_update_rssi_info(rtwdev, bb);
if (!dig->is_linked_pre && is_linked) {
rtw89_debug(rtwdev, RTW89_DBG_DIG, "First connected\n");
- rtw89_phy_dig_update_para(rtwdev);
+ rtw89_phy_dig_update_para(rtwdev, bb);
dig->igi_fa_rssi = dig->igi_rssi;
} else if (dig->is_linked_pre && !is_linked) {
rtw89_debug(rtwdev, RTW89_DBG_DIG, "First disconnected\n");
- rtw89_phy_dig_update_para(rtwdev);
+ rtw89_phy_dig_update_para(rtwdev, bb);
dig->igi_fa_rssi = dig->igi_rssi;
}
dig->is_linked_pre = is_linked;
- rtw89_phy_dig_igi_offset_by_env(rtwdev);
+ rtw89_phy_dig_igi_offset_by_env(rtwdev, bb);
igi_min = max_t(int, dig->igi_rssi - IGI_RSSI_MIN, 0);
dig->dyn_igi_max = min(igi_min + IGI_OFFSET_MAX, igi_max_performance_mode);
@@ -6262,14 +6441,22 @@ void rtw89_phy_dig(struct rtw89_dev *rtwdev)
dig->igi_rssi, dig->dyn_igi_max, dig->dyn_igi_min,
dig->igi_fa_rssi);
- rtw89_phy_dig_config_igi(rtwdev);
+ rtw89_phy_dig_config_igi(rtwdev, bb);
- rtw89_phy_dig_dyn_pd_th(rtwdev, dig->igi_fa_rssi, dig->dyn_pd_th_en);
+ rtw89_phy_dig_dyn_pd_th(rtwdev, bb, dig->igi_fa_rssi, dig->dyn_pd_th_en);
if (dig->dyn_pd_th_en && dig->igi_fa_rssi > dig->dyn_pd_th_max)
- rtw89_phy_dig_sdagc_follow_pagc_config(rtwdev, true);
+ rtw89_phy_dig_sdagc_follow_pagc_config(rtwdev, bb, true);
else
- rtw89_phy_dig_sdagc_follow_pagc_config(rtwdev, false);
+ rtw89_phy_dig_sdagc_follow_pagc_config(rtwdev, bb, false);
+}
+
+void rtw89_phy_dig(struct rtw89_dev *rtwdev)
+{
+ struct rtw89_bb_ctx *bb;
+
+ rtw89_for_each_active_bb(rtwdev, bb)
+ __rtw89_phy_dig(rtwdev, bb);
}
static void __rtw89_phy_tx_path_div_sta_iter(struct rtw89_dev *rtwdev,
@@ -6443,17 +6630,17 @@ static void rtw89_phy_antdiv_training_state(struct rtw89_dev *rtwdev)
}
antdiv->training_count++;
- ieee80211_queue_delayed_work(rtwdev->hw, &rtwdev->antdiv_work,
- state_period);
+ wiphy_delayed_work_queue(rtwdev->hw->wiphy, &rtwdev->antdiv_work,
+ state_period);
}
-void rtw89_phy_antdiv_work(struct work_struct *work)
+void rtw89_phy_antdiv_work(struct wiphy *wiphy, struct wiphy_work *work)
{
struct rtw89_dev *rtwdev = container_of(work, struct rtw89_dev,
antdiv_work.work);
struct rtw89_antdiv_info *antdiv = &rtwdev->antdiv;
- mutex_lock(&rtwdev->mutex);
+ lockdep_assert_wiphy(wiphy);
if (antdiv->training_count <= ANTDIV_TRAINNING_CNT) {
rtw89_phy_antdiv_training_state(rtwdev);
@@ -6461,8 +6648,6 @@ void rtw89_phy_antdiv_work(struct work_struct *work)
rtw89_phy_antdiv_decision_state(rtwdev);
rtw89_phy_antdiv_set_ant(rtwdev);
}
-
- mutex_unlock(&rtwdev->mutex);
}
void rtw89_phy_antdiv_track(struct rtw89_dev *rtwdev)
@@ -6483,19 +6668,34 @@ void rtw89_phy_antdiv_track(struct rtw89_dev *rtwdev)
return;
antdiv->training_count = 0;
- ieee80211_queue_delayed_work(rtwdev->hw, &rtwdev->antdiv_work, 0);
+ wiphy_delayed_work_queue(rtwdev->hw->wiphy, &rtwdev->antdiv_work, 0);
+}
+
+static void __rtw89_phy_env_monitor_init(struct rtw89_dev *rtwdev,
+ struct rtw89_bb_ctx *bb)
+{
+ rtw89_debug(rtwdev, RTW89_DBG_PHY_TRACK,
+ "BB-%d env_monitor init\n", bb->phy_idx);
+
+ rtw89_phy_ccx_top_setting_init(rtwdev, bb);
+ rtw89_phy_ifs_clm_setting_init(rtwdev, bb);
}
static void rtw89_phy_env_monitor_init(struct rtw89_dev *rtwdev)
{
- rtw89_phy_ccx_top_setting_init(rtwdev);
- rtw89_phy_ifs_clm_setting_init(rtwdev);
+ struct rtw89_bb_ctx *bb;
+
+ rtw89_for_each_capab_bb(rtwdev, bb)
+ __rtw89_phy_env_monitor_init(rtwdev, bb);
}
-static void rtw89_phy_edcca_init(struct rtw89_dev *rtwdev)
+static void __rtw89_phy_edcca_init(struct rtw89_dev *rtwdev,
+ struct rtw89_bb_ctx *bb)
{
const struct rtw89_edcca_regs *edcca_regs = rtwdev->chip->edcca_regs;
- struct rtw89_edcca_bak *edcca_bak = &rtwdev->hal.edcca_bak;
+ struct rtw89_edcca_bak *edcca_bak = &bb->edcca_bak;
+
+ rtw89_debug(rtwdev, RTW89_DBG_EDCCA, "BB-%d edcca init\n", bb->phy_idx);
memset(edcca_bak, 0, sizeof(*edcca_bak));
@@ -6511,8 +6711,16 @@ static void rtw89_phy_edcca_init(struct rtw89_dev *rtwdev)
rtw89_phy_set_phy_regs(rtwdev, R_DFS_FFT_CG, B_DFS_FFT_EN, 1);
}
- rtw89_phy_write32_mask(rtwdev, edcca_regs->tx_collision_t2r_st,
- edcca_regs->tx_collision_t2r_st_mask, 0x29);
+ rtw89_phy_write32_idx(rtwdev, edcca_regs->tx_collision_t2r_st,
+ edcca_regs->tx_collision_t2r_st_mask, 0x29, bb->phy_idx);
+}
+
+static void rtw89_phy_edcca_init(struct rtw89_dev *rtwdev)
+{
+ struct rtw89_bb_ctx *bb;
+
+ rtw89_for_each_capab_bb(rtwdev, bb)
+ __rtw89_phy_edcca_init(rtwdev, bb);
}
void rtw89_phy_dm_init(struct rtw89_dev *rtwdev)
@@ -6875,44 +7083,46 @@ void rtw89_decode_chan_idx(struct rtw89_dev *rtwdev, u8 chan_idx,
}
EXPORT_SYMBOL(rtw89_decode_chan_idx);
-void rtw89_phy_config_edcca(struct rtw89_dev *rtwdev, bool scan)
+void rtw89_phy_config_edcca(struct rtw89_dev *rtwdev,
+ struct rtw89_bb_ctx *bb, bool scan)
{
const struct rtw89_edcca_regs *edcca_regs = rtwdev->chip->edcca_regs;
- struct rtw89_edcca_bak *edcca_bak = &rtwdev->hal.edcca_bak;
+ struct rtw89_edcca_bak *edcca_bak = &bb->edcca_bak;
if (scan) {
edcca_bak->a =
- rtw89_phy_read32_mask(rtwdev, edcca_regs->edcca_level,
- edcca_regs->edcca_mask);
+ rtw89_phy_read32_idx(rtwdev, edcca_regs->edcca_level,
+ edcca_regs->edcca_mask, bb->phy_idx);
edcca_bak->p =
- rtw89_phy_read32_mask(rtwdev, edcca_regs->edcca_level,
- edcca_regs->edcca_p_mask);
+ rtw89_phy_read32_idx(rtwdev, edcca_regs->edcca_level,
+ edcca_regs->edcca_p_mask, bb->phy_idx);
edcca_bak->ppdu =
- rtw89_phy_read32_mask(rtwdev, edcca_regs->ppdu_level,
- edcca_regs->ppdu_mask);
-
- rtw89_phy_write32_mask(rtwdev, edcca_regs->edcca_level,
- edcca_regs->edcca_mask, EDCCA_MAX);
- rtw89_phy_write32_mask(rtwdev, edcca_regs->edcca_level,
- edcca_regs->edcca_p_mask, EDCCA_MAX);
- rtw89_phy_write32_mask(rtwdev, edcca_regs->ppdu_level,
- edcca_regs->ppdu_mask, EDCCA_MAX);
+ rtw89_phy_read32_idx(rtwdev, edcca_regs->ppdu_level,
+ edcca_regs->ppdu_mask, bb->phy_idx);
+
+ rtw89_phy_write32_idx(rtwdev, edcca_regs->edcca_level,
+ edcca_regs->edcca_mask, EDCCA_MAX, bb->phy_idx);
+ rtw89_phy_write32_idx(rtwdev, edcca_regs->edcca_level,
+ edcca_regs->edcca_p_mask, EDCCA_MAX, bb->phy_idx);
+ rtw89_phy_write32_idx(rtwdev, edcca_regs->ppdu_level,
+ edcca_regs->ppdu_mask, EDCCA_MAX, bb->phy_idx);
} else {
- rtw89_phy_write32_mask(rtwdev, edcca_regs->edcca_level,
- edcca_regs->edcca_mask,
- edcca_bak->a);
- rtw89_phy_write32_mask(rtwdev, edcca_regs->edcca_level,
- edcca_regs->edcca_p_mask,
- edcca_bak->p);
- rtw89_phy_write32_mask(rtwdev, edcca_regs->ppdu_level,
- edcca_regs->ppdu_mask,
- edcca_bak->ppdu);
+ rtw89_phy_write32_idx(rtwdev, edcca_regs->edcca_level,
+ edcca_regs->edcca_mask,
+ edcca_bak->a, bb->phy_idx);
+ rtw89_phy_write32_idx(rtwdev, edcca_regs->edcca_level,
+ edcca_regs->edcca_p_mask,
+ edcca_bak->p, bb->phy_idx);
+ rtw89_phy_write32_idx(rtwdev, edcca_regs->ppdu_level,
+ edcca_regs->ppdu_mask,
+ edcca_bak->ppdu, bb->phy_idx);
}
}
-static void rtw89_phy_edcca_log(struct rtw89_dev *rtwdev)
+static void rtw89_phy_edcca_log(struct rtw89_dev *rtwdev, struct rtw89_bb_ctx *bb)
{
const struct rtw89_edcca_regs *edcca_regs = rtwdev->chip->edcca_regs;
+ const struct rtw89_edcca_p_regs *edcca_p_regs;
bool flag_fb, flag_p20, flag_s20, flag_s40, flag_s80;
s8 pwdb_fb, pwdb_p20, pwdb_s20, pwdb_s40, pwdb_s80;
u8 path, per20_bitmap;
@@ -6922,13 +7132,18 @@ static void rtw89_phy_edcca_log(struct rtw89_dev *rtwdev)
if (!rtw89_debug_is_enabled(rtwdev, RTW89_DBG_EDCCA))
return;
+ if (bb->phy_idx == RTW89_PHY_1)
+ edcca_p_regs = &edcca_regs->p[RTW89_PHY_1];
+ else
+ edcca_p_regs = &edcca_regs->p[RTW89_PHY_0];
+
if (rtwdev->chip->chip_id == RTL8922A)
rtw89_phy_write32_mask(rtwdev, edcca_regs->rpt_sel_be,
edcca_regs->rpt_sel_be_mask, 0);
- rtw89_phy_write32_mask(rtwdev, edcca_regs->rpt_sel,
- edcca_regs->rpt_sel_mask, 0);
- tmp = rtw89_phy_read32(rtwdev, edcca_regs->rpt_b);
+ rtw89_phy_write32_mask(rtwdev, edcca_p_regs->rpt_sel,
+ edcca_p_regs->rpt_sel_mask, 0);
+ tmp = rtw89_phy_read32(rtwdev, edcca_p_regs->rpt_b);
path = u32_get_bits(tmp, B_EDCCA_RPT_B_PATH_MASK);
flag_s80 = u32_get_bits(tmp, B_EDCCA_RPT_B_S80);
flag_s40 = u32_get_bits(tmp, B_EDCCA_RPT_B_S40);
@@ -6939,19 +7154,19 @@ static void rtw89_phy_edcca_log(struct rtw89_dev *rtwdev)
pwdb_p20 = u32_get_bits(tmp, MASKBYTE2);
pwdb_fb = u32_get_bits(tmp, MASKBYTE3);
- rtw89_phy_write32_mask(rtwdev, edcca_regs->rpt_sel,
- edcca_regs->rpt_sel_mask, 4);
- tmp = rtw89_phy_read32(rtwdev, edcca_regs->rpt_b);
+ rtw89_phy_write32_mask(rtwdev, edcca_p_regs->rpt_sel,
+ edcca_p_regs->rpt_sel_mask, 4);
+ tmp = rtw89_phy_read32(rtwdev, edcca_p_regs->rpt_b);
pwdb_s80 = u32_get_bits(tmp, MASKBYTE1);
pwdb_s40 = u32_get_bits(tmp, MASKBYTE2);
- per20_bitmap = rtw89_phy_read32_mask(rtwdev, edcca_regs->rpt_a,
+ per20_bitmap = rtw89_phy_read32_mask(rtwdev, edcca_p_regs->rpt_a,
MASKBYTE0);
if (rtwdev->chip->chip_id == RTL8922A) {
rtw89_phy_write32_mask(rtwdev, edcca_regs->rpt_sel_be,
edcca_regs->rpt_sel_be_mask, 4);
- tmp = rtw89_phy_read32(rtwdev, edcca_regs->rpt_b);
+ tmp = rtw89_phy_read32(rtwdev, edcca_p_regs->rpt_b);
pwdb[0] = u32_get_bits(tmp, MASKBYTE3);
pwdb[1] = u32_get_bits(tmp, MASKBYTE2);
pwdb[2] = u32_get_bits(tmp, MASKBYTE1);
@@ -6959,33 +7174,33 @@ static void rtw89_phy_edcca_log(struct rtw89_dev *rtwdev)
rtw89_phy_write32_mask(rtwdev, edcca_regs->rpt_sel_be,
edcca_regs->rpt_sel_be_mask, 5);
- tmp = rtw89_phy_read32(rtwdev, edcca_regs->rpt_b);
+ tmp = rtw89_phy_read32(rtwdev, edcca_p_regs->rpt_b);
pwdb[4] = u32_get_bits(tmp, MASKBYTE3);
pwdb[5] = u32_get_bits(tmp, MASKBYTE2);
pwdb[6] = u32_get_bits(tmp, MASKBYTE1);
pwdb[7] = u32_get_bits(tmp, MASKBYTE0);
} else {
- rtw89_phy_write32_mask(rtwdev, edcca_regs->rpt_sel,
- edcca_regs->rpt_sel_mask, 0);
- tmp = rtw89_phy_read32(rtwdev, edcca_regs->rpt_a);
+ rtw89_phy_write32_mask(rtwdev, edcca_p_regs->rpt_sel,
+ edcca_p_regs->rpt_sel_mask, 0);
+ tmp = rtw89_phy_read32(rtwdev, edcca_p_regs->rpt_a);
pwdb[0] = u32_get_bits(tmp, MASKBYTE3);
pwdb[1] = u32_get_bits(tmp, MASKBYTE2);
- rtw89_phy_write32_mask(rtwdev, edcca_regs->rpt_sel,
- edcca_regs->rpt_sel_mask, 1);
- tmp = rtw89_phy_read32(rtwdev, edcca_regs->rpt_a);
+ rtw89_phy_write32_mask(rtwdev, edcca_p_regs->rpt_sel,
+ edcca_p_regs->rpt_sel_mask, 1);
+ tmp = rtw89_phy_read32(rtwdev, edcca_p_regs->rpt_a);
pwdb[2] = u32_get_bits(tmp, MASKBYTE3);
pwdb[3] = u32_get_bits(tmp, MASKBYTE2);
- rtw89_phy_write32_mask(rtwdev, edcca_regs->rpt_sel,
- edcca_regs->rpt_sel_mask, 2);
- tmp = rtw89_phy_read32(rtwdev, edcca_regs->rpt_a);
+ rtw89_phy_write32_mask(rtwdev, edcca_p_regs->rpt_sel,
+ edcca_p_regs->rpt_sel_mask, 2);
+ tmp = rtw89_phy_read32(rtwdev, edcca_p_regs->rpt_a);
pwdb[4] = u32_get_bits(tmp, MASKBYTE3);
pwdb[5] = u32_get_bits(tmp, MASKBYTE2);
- rtw89_phy_write32_mask(rtwdev, edcca_regs->rpt_sel,
- edcca_regs->rpt_sel_mask, 3);
- tmp = rtw89_phy_read32(rtwdev, edcca_regs->rpt_a);
+ rtw89_phy_write32_mask(rtwdev, edcca_p_regs->rpt_sel,
+ edcca_p_regs->rpt_sel_mask, 3);
+ tmp = rtw89_phy_read32(rtwdev, edcca_p_regs->rpt_a);
pwdb[6] = u32_get_bits(tmp, MASKBYTE3);
pwdb[7] = u32_get_bits(tmp, MASKBYTE2);
}
@@ -7007,9 +7222,10 @@ static void rtw89_phy_edcca_log(struct rtw89_dev *rtwdev)
pwdb_fb, pwdb_p20, pwdb_s20, pwdb_s40, pwdb_s80);
}
-static u8 rtw89_phy_edcca_get_thre_by_rssi(struct rtw89_dev *rtwdev)
+static u8 rtw89_phy_edcca_get_thre_by_rssi(struct rtw89_dev *rtwdev,
+ struct rtw89_bb_ctx *bb)
{
- struct rtw89_phy_ch_info *ch_info = &rtwdev->ch_info;
+ struct rtw89_phy_ch_info *ch_info = &bb->ch_info;
bool is_linked = rtwdev->total_sta_assoc > 0;
u8 rssi_min = ch_info->rssi_min >> 1;
u8 edcca_thre;
@@ -7025,13 +7241,13 @@ static u8 rtw89_phy_edcca_get_thre_by_rssi(struct rtw89_dev *rtwdev)
return edcca_thre;
}
-void rtw89_phy_edcca_thre_calc(struct rtw89_dev *rtwdev)
+void rtw89_phy_edcca_thre_calc(struct rtw89_dev *rtwdev, struct rtw89_bb_ctx *bb)
{
const struct rtw89_edcca_regs *edcca_regs = rtwdev->chip->edcca_regs;
- struct rtw89_edcca_bak *edcca_bak = &rtwdev->hal.edcca_bak;
+ struct rtw89_edcca_bak *edcca_bak = &bb->edcca_bak;
u8 th;
- th = rtw89_phy_edcca_get_thre_by_rssi(rtwdev);
+ th = rtw89_phy_edcca_get_thre_by_rssi(rtwdev, bb);
if (th == edcca_bak->th_old)
return;
@@ -7040,23 +7256,33 @@ void rtw89_phy_edcca_thre_calc(struct rtw89_dev *rtwdev)
rtw89_debug(rtwdev, RTW89_DBG_EDCCA,
"[EDCCA]: Normal Mode, EDCCA_th = %d\n", th);
- rtw89_phy_write32_mask(rtwdev, edcca_regs->edcca_level,
- edcca_regs->edcca_mask, th);
- rtw89_phy_write32_mask(rtwdev, edcca_regs->edcca_level,
- edcca_regs->edcca_p_mask, th);
- rtw89_phy_write32_mask(rtwdev, edcca_regs->ppdu_level,
- edcca_regs->ppdu_mask, th);
+ rtw89_phy_write32_idx(rtwdev, edcca_regs->edcca_level,
+ edcca_regs->edcca_mask, th, bb->phy_idx);
+ rtw89_phy_write32_idx(rtwdev, edcca_regs->edcca_level,
+ edcca_regs->edcca_p_mask, th, bb->phy_idx);
+ rtw89_phy_write32_idx(rtwdev, edcca_regs->ppdu_level,
+ edcca_regs->ppdu_mask, th, bb->phy_idx);
+}
+
+static
+void __rtw89_phy_edcca_track(struct rtw89_dev *rtwdev, struct rtw89_bb_ctx *bb)
+{
+ rtw89_debug(rtwdev, RTW89_DBG_EDCCA, "BB-%d edcca track\n", bb->phy_idx);
+
+ rtw89_phy_edcca_thre_calc(rtwdev, bb);
+ rtw89_phy_edcca_log(rtwdev, bb);
}
void rtw89_phy_edcca_track(struct rtw89_dev *rtwdev)
{
struct rtw89_hal *hal = &rtwdev->hal;
+ struct rtw89_bb_ctx *bb;
if (hal->disabled_dm_bitmap & BIT(RTW89_DM_DYNAMIC_EDCCA))
return;
- rtw89_phy_edcca_thre_calc(rtwdev);
- rtw89_phy_edcca_log(rtwdev);
+ rtw89_for_each_active_bb(rtwdev, bb)
+ __rtw89_phy_edcca_track(rtwdev, bb);
}
enum rtw89_rf_path_bit rtw89_phy_get_kpath(struct rtw89_dev *rtwdev,
diff --git a/drivers/net/wireless/realtek/rtw89/phy.h b/drivers/net/wireless/realtek/rtw89/phy.h
index 08b635c93ac3..5b451f1cfaac 100644
--- a/drivers/net/wireless/realtek/rtw89/phy.h
+++ b/drivers/net/wireless/realtek/rtw89/phy.h
@@ -164,6 +164,7 @@ enum rtw89_phy_c2h_dm_func {
RTW89_PHY_C2H_DM_FUNC_SIGB,
RTW89_PHY_C2H_DM_FUNC_LOWRT_RTY,
RTW89_PHY_C2H_DM_FUNC_MCC_DIG,
+ RTW89_PHY_C2H_DM_FUNC_FW_SCAN = 0xc,
RTW89_PHY_C2H_DM_FUNC_NUM,
};
@@ -835,8 +836,8 @@ s8 rtw89_phy_read_txpwr_byrate(struct rtw89_dev *rtwdev, u8 band, u8 bw,
void rtw89_phy_ant_gain_init(struct rtw89_dev *rtwdev);
s16 rtw89_phy_ant_gain_pwr_offset(struct rtw89_dev *rtwdev,
const struct rtw89_chan *chan);
-void rtw89_print_ant_gain(struct seq_file *m, struct rtw89_dev *rtwdev,
- const struct rtw89_chan *chan);
+int rtw89_print_ant_gain(struct rtw89_dev *rtwdev, char *buf, size_t bufsz,
+ const struct rtw89_chan *chan);
void rtw89_phy_load_txpwr_byrate(struct rtw89_dev *rtwdev,
const struct rtw89_txpwr_table *tbl);
s8 rtw89_phy_read_txpwr_limit(struct rtw89_dev *rtwdev, u8 band,
@@ -914,6 +915,13 @@ static inline s8 rtw89_phy_txpwr_rf_to_bb(struct rtw89_dev *rtwdev, s8 txpwr_rf)
return txpwr_rf << (chip->txpwr_factor_bb - chip->txpwr_factor_rf);
}
+static inline s8 rtw89_phy_txpwr_bb_to_rf(struct rtw89_dev *rtwdev, s8 txpwr_bb)
+{
+ const struct rtw89_chip_info *chip = rtwdev->chip;
+
+ return txpwr_bb >> (chip->txpwr_factor_bb - chip->txpwr_factor_rf);
+}
+
static inline s8 rtw89_phy_txpwr_rf_to_mac(struct rtw89_dev *rtwdev, s8 txpwr_rf)
{
const struct rtw89_chip_info *chip = rtwdev->chip;
@@ -928,6 +936,20 @@ static inline s8 rtw89_phy_txpwr_dbm_to_mac(struct rtw89_dev *rtwdev, s8 dbm)
return clamp_t(s16, dbm << chip->txpwr_factor_mac, -64, 63);
}
+static inline s16 rtw89_phy_txpwr_mac_to_rf(struct rtw89_dev *rtwdev, s8 txpwr_mac)
+{
+ const struct rtw89_chip_info *chip = rtwdev->chip;
+
+ return txpwr_mac << (chip->txpwr_factor_rf - chip->txpwr_factor_mac);
+}
+
+static inline s16 rtw89_phy_txpwr_mac_to_bb(struct rtw89_dev *rtwdev, s8 txpwr_mac)
+{
+ const struct rtw89_chip_info *chip = rtwdev->chip;
+
+ return txpwr_mac << (chip->txpwr_factor_bb - chip->txpwr_factor_mac);
+}
+
void rtw89_phy_ra_assoc(struct rtw89_dev *rtwdev, struct rtw89_sta_link *rtwsta_link);
void rtw89_phy_ra_update(struct rtw89_dev *rtwdev);
void rtw89_phy_ra_update_sta(struct rtw89_dev *rtwdev, struct ieee80211_sta *sta,
@@ -978,20 +1000,20 @@ void rtw89_phy_rfk_tssi_fill_fwcmd_tmeter_tbl(struct rtw89_dev *rtwdev,
const struct rtw89_chan *chan,
struct rtw89_h2c_rf_tssi *h2c);
void rtw89_phy_cfo_track(struct rtw89_dev *rtwdev);
-void rtw89_phy_cfo_track_work(struct work_struct *work);
+void rtw89_phy_cfo_track_work(struct wiphy *wiphy, struct wiphy_work *work);
void rtw89_phy_cfo_parse(struct rtw89_dev *rtwdev, s16 cfo_val,
struct rtw89_rx_phy_ppdu *phy_ppdu);
void rtw89_phy_stat_track(struct rtw89_dev *rtwdev);
void rtw89_phy_env_monitor_track(struct rtw89_dev *rtwdev);
void rtw89_phy_set_phy_regs(struct rtw89_dev *rtwdev, u32 addr, u32 mask,
u32 val);
-void rtw89_phy_dig_reset(struct rtw89_dev *rtwdev);
+void rtw89_phy_dig_reset(struct rtw89_dev *rtwdev, struct rtw89_bb_ctx *bb);
void rtw89_phy_dig(struct rtw89_dev *rtwdev);
void rtw89_phy_tx_path_div_track(struct rtw89_dev *rtwdev);
void rtw89_phy_antdiv_parse(struct rtw89_dev *rtwdev,
struct rtw89_rx_phy_ppdu *phy_ppdu);
void rtw89_phy_antdiv_track(struct rtw89_dev *rtwdev);
-void rtw89_phy_antdiv_work(struct work_struct *work);
+void rtw89_phy_antdiv_work(struct wiphy *wiphy, struct wiphy_work *work);
void rtw89_phy_set_bss_color(struct rtw89_dev *rtwdev,
struct rtw89_vif_link *rtwvif_link);
void rtw89_phy_tssi_ctrl_set_bandedge_cfg(struct rtw89_dev *rtwdev,
@@ -1002,9 +1024,10 @@ void rtw89_phy_ul_tb_ctrl_track(struct rtw89_dev *rtwdev);
u8 rtw89_encode_chan_idx(struct rtw89_dev *rtwdev, u8 central_ch, u8 band);
void rtw89_decode_chan_idx(struct rtw89_dev *rtwdev, u8 chan_idx,
u8 *ch, enum nl80211_band *band);
-void rtw89_phy_config_edcca(struct rtw89_dev *rtwdev, bool scan);
+void rtw89_phy_config_edcca(struct rtw89_dev *rtwdev,
+ struct rtw89_bb_ctx *bb, bool scan);
void rtw89_phy_edcca_track(struct rtw89_dev *rtwdev);
-void rtw89_phy_edcca_thre_calc(struct rtw89_dev *rtwdev);
+void rtw89_phy_edcca_thre_calc(struct rtw89_dev *rtwdev, struct rtw89_bb_ctx *bb);
enum rtw89_rf_path_bit rtw89_phy_get_kpath(struct rtw89_dev *rtwdev,
enum rtw89_phy_idx phy_idx);
enum rtw89_rf_path rtw89_phy_get_syn_sel(struct rtw89_dev *rtwdev,
diff --git a/drivers/net/wireless/realtek/rtw89/phy_be.c b/drivers/net/wireless/realtek/rtw89/phy_be.c
index 37d8f254ae32..d321cf1fc485 100644
--- a/drivers/net/wireless/realtek/rtw89/phy_be.c
+++ b/drivers/net/wireless/realtek/rtw89/phy_be.c
@@ -362,7 +362,7 @@ static void rtw89_phy_bb_wrap_force_cr_init(struct rtw89_dev *rtwdev,
rtw89_write32_mask(rtwdev, addr, B_BE_PWR_FORCE_RU_ENON, 0);
rtw89_write32_mask(rtwdev, addr, B_BE_PWR_FORCE_RU_ON, 0);
addr = rtw89_mac_reg_by_idx(rtwdev, R_BE_PWR_FORCE_MACID, mac_idx);
- rtw89_write32_mask(rtwdev, addr, B_BE_PWR_FORCE_MACID_ON, 0);
+ rtw89_write32_mask(rtwdev, addr, B_BE_PWR_FORCE_MACID_ALL, 0);
addr = rtw89_mac_reg_by_idx(rtwdev, R_BE_PWR_COEX_CTRL, mac_idx);
rtw89_write32_mask(rtwdev, addr, B_BE_PWR_FORCE_COEX_ON, 0);
addr = rtw89_mac_reg_by_idx(rtwdev, R_BE_PWR_RATE_CTRL, mac_idx);
diff --git a/drivers/net/wireless/realtek/rtw89/ps.c b/drivers/net/wireless/realtek/rtw89/ps.c
index 96ea04d90cd3..8e4fe73e7d77 100644
--- a/drivers/net/wireless/realtek/rtw89/ps.c
+++ b/drivers/net/wireless/realtek/rtw89/ps.c
@@ -113,7 +113,7 @@ static void __rtw89_leave_lps(struct rtw89_dev *rtwdev,
void rtw89_leave_ps_mode(struct rtw89_dev *rtwdev)
{
- lockdep_assert_held(&rtwdev->mutex);
+ lockdep_assert_wiphy(rtwdev->hw->wiphy);
__rtw89_leave_ps_mode(rtwdev);
}
@@ -125,7 +125,7 @@ void rtw89_enter_lps(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif,
bool can_ps_mode = true;
unsigned int link_id;
- lockdep_assert_held(&rtwdev->mutex);
+ lockdep_assert_wiphy(rtwdev->hw->wiphy);
if (test_and_set_bit(RTW89_FLAG_LEISURE_PS, rtwdev->flags))
return;
@@ -162,7 +162,7 @@ void rtw89_leave_lps(struct rtw89_dev *rtwdev)
struct rtw89_vif *rtwvif;
unsigned int link_id;
- lockdep_assert_held(&rtwdev->mutex);
+ lockdep_assert_wiphy(rtwdev->hw->wiphy);
if (!test_and_clear_bit(RTW89_FLAG_LEISURE_PS, rtwdev->flags))
return;
@@ -382,3 +382,150 @@ u8 rtw89_p2p_noa_fetch(struct rtw89_vif_link *rtwvif_link, void **data)
tail = ie->noa_desc + setter->noa_count;
return tail - *data;
}
+
+static void rtw89_ps_noa_once_set_work(struct wiphy *wiphy, struct wiphy_work *work)
+{
+ struct rtw89_ps_noa_once_handler *noa_once =
+ container_of(work, struct rtw89_ps_noa_once_handler, set_work.work);
+
+ lockdep_assert_wiphy(wiphy);
+
+ noa_once->in_duration = true;
+}
+
+static void rtw89_ps_noa_once_clr_work(struct wiphy *wiphy, struct wiphy_work *work)
+{
+ struct rtw89_ps_noa_once_handler *noa_once =
+ container_of(work, struct rtw89_ps_noa_once_handler, clr_work.work);
+ struct rtw89_vif_link *rtwvif_link =
+ container_of(noa_once, struct rtw89_vif_link, noa_once);
+ struct rtw89_dev *rtwdev = rtwvif_link->rtwvif->rtwdev;
+
+ lockdep_assert_wiphy(wiphy);
+
+ rtw89_fw_h2c_set_bcn_fltr_cfg(rtwdev, rtwvif_link, true);
+ noa_once->in_duration = false;
+}
+
+void rtw89_p2p_noa_once_init(struct rtw89_vif_link *rtwvif_link)
+{
+ struct rtw89_ps_noa_once_handler *noa_once = &rtwvif_link->noa_once;
+
+ noa_once->in_duration = false;
+ noa_once->tsf_begin = 0;
+ noa_once->tsf_end = 0;
+
+ wiphy_delayed_work_init(&noa_once->set_work, rtw89_ps_noa_once_set_work);
+ wiphy_delayed_work_init(&noa_once->clr_work, rtw89_ps_noa_once_clr_work);
+}
+
+static void rtw89_p2p_noa_once_cancel(struct rtw89_vif_link *rtwvif_link)
+{
+ struct rtw89_ps_noa_once_handler *noa_once = &rtwvif_link->noa_once;
+ struct rtw89_dev *rtwdev = rtwvif_link->rtwvif->rtwdev;
+ struct wiphy *wiphy = rtwdev->hw->wiphy;
+
+ wiphy_delayed_work_cancel(wiphy, &noa_once->set_work);
+ wiphy_delayed_work_cancel(wiphy, &noa_once->clr_work);
+}
+
+void rtw89_p2p_noa_once_deinit(struct rtw89_vif_link *rtwvif_link)
+{
+ rtw89_p2p_noa_once_cancel(rtwvif_link);
+ rtw89_p2p_noa_once_init(rtwvif_link);
+}
+
+void rtw89_p2p_noa_once_recalc(struct rtw89_vif_link *rtwvif_link)
+{
+ struct rtw89_ps_noa_once_handler *noa_once = &rtwvif_link->noa_once;
+ struct rtw89_dev *rtwdev = rtwvif_link->rtwvif->rtwdev;
+ const struct ieee80211_p2p_noa_desc *noa_desc;
+ struct wiphy *wiphy = rtwdev->hw->wiphy;
+ struct ieee80211_bss_conf *bss_conf;
+ u64 tsf_begin = U64_MAX, tsf_end;
+ u64 set_delay_us = 0;
+ u64 clr_delay_us = 0;
+ u32 start_time;
+ u32 interval;
+ u32 duration;
+ u64 tsf;
+ int ret;
+ int i;
+
+ lockdep_assert_wiphy(wiphy);
+
+ ret = rtw89_mac_port_get_tsf(rtwdev, rtwvif_link, &tsf);
+ if (ret) {
+ rtw89_warn(rtwdev, "%s: failed to get tsf\n", __func__);
+ return;
+ }
+
+ rcu_read_lock();
+
+ bss_conf = rtw89_vif_rcu_dereference_link(rtwvif_link, true);
+
+ for (i = 0; i < ARRAY_SIZE(bss_conf->p2p_noa_attr.desc); i++) {
+ bool first = tsf_begin == U64_MAX;
+ u64 tmp;
+
+ noa_desc = &bss_conf->p2p_noa_attr.desc[i];
+ if (noa_desc->count == 0 || noa_desc->count == 255)
+ continue;
+
+ start_time = le32_to_cpu(noa_desc->start_time);
+ interval = le32_to_cpu(noa_desc->interval);
+ duration = le32_to_cpu(noa_desc->duration);
+
+ if (unlikely(duration == 0 ||
+ (noa_desc->count > 1 && interval == 0)))
+ continue;
+
+ tmp = start_time + interval * (noa_desc->count - 1) + duration;
+ tmp = (tsf & GENMASK_ULL(63, 32)) + tmp;
+ if (unlikely(tmp <= tsf))
+ continue;
+ tsf_end = first ? tmp : max(tsf_end, tmp);
+
+ tmp = (tsf & GENMASK_ULL(63, 32)) | start_time;
+ tsf_begin = first ? tmp : min(tsf_begin, tmp);
+ }
+
+ rcu_read_unlock();
+
+ if (tsf_begin == U64_MAX)
+ return;
+
+ rtw89_p2p_noa_once_cancel(rtwvif_link);
+
+ if (noa_once->tsf_end > tsf) {
+ tsf_begin = min(tsf_begin, noa_once->tsf_begin);
+ tsf_end = max(tsf_end, noa_once->tsf_end);
+ }
+
+ clr_delay_us = min_t(u64, tsf_end - tsf, UINT_MAX);
+
+ if (tsf_begin <= tsf) {
+ noa_once->in_duration = true;
+ goto out;
+ }
+
+ set_delay_us = tsf_begin - tsf;
+ if (unlikely(set_delay_us > UINT_MAX)) {
+ rtw89_warn(rtwdev, "%s: unhandled begin\n", __func__);
+ set_delay_us = 0;
+ clr_delay_us = 0;
+ rtw89_fw_h2c_set_bcn_fltr_cfg(rtwdev, rtwvif_link, true);
+ noa_once->in_duration = false;
+ }
+
+out:
+ if (set_delay_us)
+ wiphy_delayed_work_queue(wiphy, &noa_once->set_work,
+ usecs_to_jiffies(set_delay_us));
+ if (clr_delay_us)
+ wiphy_delayed_work_queue(wiphy, &noa_once->clr_work,
+ usecs_to_jiffies(clr_delay_us));
+
+ noa_once->tsf_begin = tsf_begin;
+ noa_once->tsf_end = tsf_end;
+}
diff --git a/drivers/net/wireless/realtek/rtw89/ps.h b/drivers/net/wireless/realtek/rtw89/ps.h
index 2b88f254a32d..b2c43d44820d 100644
--- a/drivers/net/wireless/realtek/rtw89/ps.h
+++ b/drivers/net/wireless/realtek/rtw89/ps.h
@@ -22,6 +22,9 @@ void rtw89_p2p_noa_renew(struct rtw89_vif_link *rtwvif_link);
void rtw89_p2p_noa_append(struct rtw89_vif_link *rtwvif_link,
const struct ieee80211_p2p_noa_desc *desc);
u8 rtw89_p2p_noa_fetch(struct rtw89_vif_link *rtwvif_link, void **data);
+void rtw89_p2p_noa_once_init(struct rtw89_vif_link *rtwvif_link);
+void rtw89_p2p_noa_once_deinit(struct rtw89_vif_link *rtwvif_link);
+void rtw89_p2p_noa_once_recalc(struct rtw89_vif_link *rtwvif_link);
static inline void rtw89_leave_ips_by_hwflags(struct rtw89_dev *rtwdev)
{
diff --git a/drivers/net/wireless/realtek/rtw89/reg.h b/drivers/net/wireless/realtek/rtw89/reg.h
index 10d0efa7a58e..f05c81ae5869 100644
--- a/drivers/net/wireless/realtek/rtw89/reg.h
+++ b/drivers/net/wireless/realtek/rtw89/reg.h
@@ -6618,6 +6618,13 @@
#define B_BE_RTS_LIMIT_IN_OFDM6 BIT(1)
#define B_BE_CHECK_CCK_EN BIT(0)
+#define R_BE_TXCNT 0x1082C
+#define R_BE_TXCNT_C1 0x1482C
+#define B_BE_ADD_TXCNT_BY BIT(31)
+#define B_BE_TOTAL_TC_OPT BIT(30)
+#define B_BE_S_TXCNT_LMT_MASK GENMASK(29, 24)
+#define B_BE_L_TXCNT_LMT_MASK GENMASK(21, 16)
+
#define R_BE_MBSSID_DROP_0 0x1083C
#define R_BE_MBSSID_DROP_0_C1 0x1483C
#define B_BE_GI_LTF_FB_SEL BIT(30)
@@ -7095,6 +7102,10 @@
#define B_BE_MACLBK_RDY_NUM_MASK GENMASK(7, 3)
#define B_BE_MACLBK_EN BIT(0)
+#define R_BE_CLIENT_OM_CTRL 0x11040
+#define R_BE_CLIENT_OM_CTRL_C1 0x15040
+#define B_BE_TRIG_DIS_EHTTB BIT(24)
+
#define R_BE_WMAC_NAV_CTL 0x11080
#define R_BE_WMAC_NAV_CTL_C1 0x15080
#define B_BE_WMAC_NAV_UPPER_EN BIT(26)
@@ -7590,7 +7601,15 @@
#define B_BE_PWR_FORCE_RU_ON BIT(18)
#define B_BE_PWR_FORCE_RU_ENON BIT(28)
#define R_BE_PWR_FORCE_MACID 0x11A48
-#define B_BE_PWR_FORCE_MACID_ON BIT(9)
+#define B_BE_PWR_FORCE_MACID_DBM_ON BIT(9)
+#define B_BE_PWR_FORCE_MACID_DBM_VAL GENMASK(17, 10)
+#define B_BE_PWR_FORCE_MACID_EN_VAL BIT(18)
+#define B_BE_PWR_FORCE_MACID_EN_ON BIT(19)
+#define B_BE_PWR_FORCE_MACID_ALL \
+ (B_BE_PWR_FORCE_MACID_DBM_ON | \
+ B_BE_PWR_FORCE_MACID_DBM_VAL | \
+ B_BE_PWR_FORCE_MACID_EN_VAL | \
+ B_BE_PWR_FORCE_MACID_EN_ON)
#define R_BE_PWR_REG_CTRL 0x11A50
#define B_BE_PWR_BT_EN BIT(23)
@@ -8157,6 +8176,8 @@
#define B_EDCCA_RPT_B_S40 BIT(4)
#define B_EDCCA_RPT_B_S80 BIT(3)
#define B_EDCCA_RPT_B_PATH_MASK GENMASK(2, 1)
+#define R_EDCCA_RPT_P1_A 0x1740
+#define R_EDCCA_RPT_P1_B 0x1744
#define R_SWSI_V1 0x174C
#define B_SWSI_W_BUSY_V1 BIT(24)
#define B_SWSI_R_BUSY_V1 BIT(25)
@@ -8222,6 +8243,7 @@
#define B_TXCKEN_FORCE_ALL GENMASK(24, 0)
#define R_EDCCA_RPT_SEL 0x20CC
#define B_EDCCA_RPT_SEL_MSK GENMASK(2, 0)
+#define B_EDCCA_RPT_SEL_P1_MSK GENMASK(5, 3)
#define R_ADC_FIFO 0x20fc
#define B_ADC_FIFO_RST GENMASK(31, 24)
#define B_ADC_FIFO_RXK GENMASK(31, 16)
@@ -8291,6 +8313,8 @@
#define B_P1_EN_SOUND_WO_NDP BIT(1)
#define R_EDCCA_RPT_A_BE 0x2E38
#define R_EDCCA_RPT_B_BE 0x2E3C
+#define R_EDCCA_RPT_P1_A_BE 0x2E40
+#define R_EDCCA_RPT_P1_B_BE 0x2E44
#define R_S1_HW_SI_DIS 0x3200
#define B_S1_HW_SI_DIS_W_R_TRIG GENMASK(30, 28)
#define R_P1_RXCK 0x32A0
@@ -8721,8 +8745,10 @@
#define B_DPD_GDIS BIT(13)
#define B_IQK_RFC_ON BIT(1)
#define R_TXPWRB 0x56CC
+#define R_P1_TXPWRB 0x76CC
#define B_TXPWRB_ON BIT(28)
#define B_TXPWRB_VAL GENMASK(27, 19)
+#define B_TXPWRB_MAX GENMASK(8, 0)
#define R_DPD_OFT_EN 0x5800
#define B_DPD_OFT_EN BIT(28)
#define B_DPD_TSSI_CW GENMASK(26, 18)
@@ -9169,6 +9195,16 @@
#define B_IQKINF2_FCNT GENMASK(23, 16)
#define B_IQKINF2_KCNT GENMASK(15, 8)
#define B_IQKINF2_NCTLV GENMASK(7, 0)
+#define R_TXAGC_REF_DBM_RF1_P0 0xBC04
+#define B_TXAGC_OFDM_REF_DBM_RF1_P0 GENMASK(10, 2)
+#define B_TXAGC_CCK_REF_DBM_RF1_P0 GENMASK(19, 11)
+#define R_TSSI_K_RF1_P0 0xBC28
+#define B_TSSI_K_OFDM_RF1_P0 GENMASK(9, 0)
+#define R_TXAGC_REF_DBM_RF1_P1 0xBD04
+#define B_TXAGC_OFDM_REF_DBM_RF1_P1 GENMASK(10, 2)
+#define B_TXAGC_CCK_REF_DBM_RF1_P1 GENMASK(19, 11)
+#define R_TSSI_K_RF1_P1 0xBD28
+#define B_TSSI_K_OFDM_RF1_P1 GENMASK(9, 0)
#define R_RFK_ST 0xBFF8
#define R_DCOF0 0xC000
#define B_DCOF0_RST BIT(17)
@@ -9334,20 +9370,25 @@
#define R_TSSI_PWR_P0 0xE610
#define R_TSSI_PWR_P1 0xE710
#define B_TSSI_CONT_EN BIT(3)
+#define R_P0_TXPWRB_BE 0xE61C
+#define R_P1_TXPWRB_BE 0xE71C
+#define B_TXPWRB_MAX_BE GENMASK(20, 12)
#define R_TSSI_MAP_OFST_P0 0xE620
#define R_TSSI_MAP_OFST_P1 0xE720
#define B_TSSI_MAP_OFST_OFDM GENMASK(17, 9)
#define B_TSSI_MAP_OFST_CCK GENMASK(26, 18)
-#define R_TXAGC_REF0_P0 0xE628
-#define R_TXAGC_REF0_P1 0xE728
-#define B_TXAGC_REF0_OFDM_DBM GENMASK(8, 0)
-#define B_TXAGC_REF0_CCK_DBM GENMASK(17, 9)
-#define B_TXAGC_REF0_OFDM_CW GENMASK(26, 18)
-#define R_TXAGC_REF1_P0 0xE62C
-#define R_TXAGC_REF1_P1 0xE72C
-#define B_TXAGC_REF1_CCK_CW GENMASK(8, 0)
+#define R_TXAGC_REF_DBM_P0 0xE628
+#define B_TXAGC_OFDM_REF_DBM_P0 GENMASK(8, 0)
+#define B_TXAGC_CCK_REF_DBM_P0 GENMASK(17, 9)
+#define R_TSSI_K_P0 0xE6A0
+#define B_TSSI_K_OFDM_P0 GENMASK(29, 20)
#define R_TXPWR_RSTB 0xE70C
#define B_TXPWR_RSTB BIT(16)
+#define R_TXAGC_REF_DBM_P1 0xE728
+#define B_TXAGC_OFDM_REF_DBM_P1 GENMASK(8, 0)
+#define B_TXAGC_CCK_REF_DBM_P1 GENMASK(17, 9)
+#define R_TSSI_K_P1 0xE7A0
+#define B_TSSI_K_OFDM_P1 GENMASK(29, 20)
/* WiFi CPU local domain */
#define R_AX_WDT_CTRL 0x0040
diff --git a/drivers/net/wireless/realtek/rtw89/regd.c b/drivers/net/wireless/realtek/rtw89/regd.c
index 80b2f74589eb..3ad14cab1f58 100644
--- a/drivers/net/wireless/realtek/rtw89/regd.c
+++ b/drivers/net/wireless/realtek/rtw89/regd.c
@@ -7,257 +7,266 @@
#include "ps.h"
#include "util.h"
-#define COUNTRY_REGD(_alpha2, _txpwr_regd...) \
- {.alpha2 = (_alpha2), \
- .txpwr_regd = {_txpwr_regd}, \
+static
+void rtw89_regd_notifier(struct wiphy *wiphy, struct regulatory_request *request);
+
+#define COUNTRY_REGD(_alpha2, _rule_2ghz, _rule_5ghz, _rule_6ghz, _fmap) \
+ { \
+ .alpha2 = _alpha2, \
+ .txpwr_regd[RTW89_BAND_2G] = _rule_2ghz, \
+ .txpwr_regd[RTW89_BAND_5G] = _rule_5ghz, \
+ .txpwr_regd[RTW89_BAND_6G] = _rule_6ghz, \
+ .func_bitmap = { _fmap, }, \
}
+static_assert(BITS_PER_TYPE(unsigned long) >= NUM_OF_RTW89_REGD_FUNC);
+
static const struct rtw89_regd rtw89_ww_regd =
- COUNTRY_REGD("00", RTW89_WW, RTW89_WW, RTW89_WW);
+ COUNTRY_REGD("00", RTW89_WW, RTW89_WW, RTW89_WW, 0x0);
static const struct rtw89_regd rtw89_regd_map[] = {
- COUNTRY_REGD("AR", RTW89_MEXICO, RTW89_MEXICO, RTW89_FCC),
- COUNTRY_REGD("BO", RTW89_FCC, RTW89_FCC, RTW89_NA),
- COUNTRY_REGD("BR", RTW89_FCC, RTW89_FCC, RTW89_FCC),
- COUNTRY_REGD("CL", RTW89_CHILE, RTW89_CHILE, RTW89_CHILE),
- COUNTRY_REGD("CO", RTW89_FCC, RTW89_FCC, RTW89_FCC),
- COUNTRY_REGD("CR", RTW89_FCC, RTW89_FCC, RTW89_FCC),
- COUNTRY_REGD("EC", RTW89_FCC, RTW89_FCC, RTW89_NA),
- COUNTRY_REGD("SV", RTW89_FCC, RTW89_FCC, RTW89_FCC),
- COUNTRY_REGD("GT", RTW89_FCC, RTW89_FCC, RTW89_FCC),
- COUNTRY_REGD("HN", RTW89_FCC, RTW89_FCC, RTW89_FCC),
- COUNTRY_REGD("MX", RTW89_MEXICO, RTW89_MEXICO, RTW89_FCC),
- COUNTRY_REGD("NI", RTW89_FCC, RTW89_FCC, RTW89_NA),
- COUNTRY_REGD("PA", RTW89_FCC, RTW89_FCC, RTW89_NA),
- COUNTRY_REGD("PY", RTW89_FCC, RTW89_FCC, RTW89_NA),
- COUNTRY_REGD("PE", RTW89_FCC, RTW89_FCC, RTW89_FCC),
- COUNTRY_REGD("US", RTW89_FCC, RTW89_FCC, RTW89_FCC),
- COUNTRY_REGD("UY", RTW89_FCC, RTW89_FCC, RTW89_NA),
- COUNTRY_REGD("VE", RTW89_FCC, RTW89_FCC, RTW89_NA),
- COUNTRY_REGD("PR", RTW89_FCC, RTW89_FCC, RTW89_NA),
- COUNTRY_REGD("DO", RTW89_FCC, RTW89_FCC, RTW89_FCC),
- COUNTRY_REGD("AT", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI),
- COUNTRY_REGD("BE", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI),
- COUNTRY_REGD("CY", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI),
- COUNTRY_REGD("CZ", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI),
- COUNTRY_REGD("DK", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI),
- COUNTRY_REGD("EE", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI),
- COUNTRY_REGD("FI", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI),
- COUNTRY_REGD("FR", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI),
- COUNTRY_REGD("DE", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI),
- COUNTRY_REGD("GR", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI),
- COUNTRY_REGD("HU", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI),
- COUNTRY_REGD("IS", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI),
- COUNTRY_REGD("IE", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI),
- COUNTRY_REGD("IT", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI),
- COUNTRY_REGD("LV", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI),
- COUNTRY_REGD("LI", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI),
- COUNTRY_REGD("LT", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI),
- COUNTRY_REGD("LU", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI),
- COUNTRY_REGD("MT", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI),
- COUNTRY_REGD("MC", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI),
- COUNTRY_REGD("NL", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI),
- COUNTRY_REGD("NO", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI),
- COUNTRY_REGD("PL", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI),
- COUNTRY_REGD("PT", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI),
- COUNTRY_REGD("SK", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI),
- COUNTRY_REGD("SI", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI),
- COUNTRY_REGD("ES", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI),
- COUNTRY_REGD("SE", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI),
- COUNTRY_REGD("CH", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI),
- COUNTRY_REGD("GB", RTW89_UK, RTW89_UK, RTW89_UK),
- COUNTRY_REGD("AL", RTW89_ETSI, RTW89_ETSI, RTW89_NA),
- COUNTRY_REGD("AZ", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI),
- COUNTRY_REGD("BH", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI),
- COUNTRY_REGD("BA", RTW89_ETSI, RTW89_ETSI, RTW89_NA),
- COUNTRY_REGD("BG", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI),
- COUNTRY_REGD("HR", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI),
- COUNTRY_REGD("EG", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI),
- COUNTRY_REGD("GH", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI),
- COUNTRY_REGD("IQ", RTW89_ETSI, RTW89_ETSI, RTW89_NA),
- COUNTRY_REGD("IL", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI),
- COUNTRY_REGD("JO", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI),
- COUNTRY_REGD("KZ", RTW89_ETSI, RTW89_ETSI, RTW89_NA),
- COUNTRY_REGD("KE", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI),
- COUNTRY_REGD("KW", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI),
- COUNTRY_REGD("KG", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI),
- COUNTRY_REGD("LB", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI),
- COUNTRY_REGD("LS", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI),
- COUNTRY_REGD("MK", RTW89_ETSI, RTW89_ETSI, RTW89_NA),
- COUNTRY_REGD("MA", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI),
- COUNTRY_REGD("MZ", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI),
- COUNTRY_REGD("NA", RTW89_ETSI, RTW89_ETSI, RTW89_NA),
- COUNTRY_REGD("NG", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI),
- COUNTRY_REGD("OM", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI),
- COUNTRY_REGD("QA", RTW89_QATAR, RTW89_QATAR, RTW89_QATAR),
- COUNTRY_REGD("RO", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI),
- COUNTRY_REGD("RU", RTW89_ETSI, RTW89_ETSI, RTW89_NA),
- COUNTRY_REGD("SA", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI),
- COUNTRY_REGD("SN", RTW89_ETSI, RTW89_ETSI, RTW89_NA),
- COUNTRY_REGD("RS", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI),
- COUNTRY_REGD("ME", RTW89_ETSI, RTW89_ETSI, RTW89_NA),
- COUNTRY_REGD("ZA", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI),
- COUNTRY_REGD("TR", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI),
- COUNTRY_REGD("UA", RTW89_UKRAINE, RTW89_UKRAINE, RTW89_UKRAINE),
- COUNTRY_REGD("AE", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI),
- COUNTRY_REGD("YE", RTW89_ETSI, RTW89_ETSI, RTW89_NA),
- COUNTRY_REGD("ZW", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI),
- COUNTRY_REGD("BD", RTW89_ETSI, RTW89_ETSI, RTW89_NA),
- COUNTRY_REGD("KH", RTW89_ETSI, RTW89_ETSI, RTW89_NA),
- COUNTRY_REGD("CN", RTW89_CN, RTW89_CN, RTW89_CN),
- COUNTRY_REGD("HK", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI),
- COUNTRY_REGD("IN", RTW89_ETSI, RTW89_ETSI, RTW89_NA),
- COUNTRY_REGD("ID", RTW89_ETSI, RTW89_ETSI, RTW89_NA),
- COUNTRY_REGD("KR", RTW89_KCC, RTW89_KCC, RTW89_KCC),
- COUNTRY_REGD("MY", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI),
- COUNTRY_REGD("PK", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI),
- COUNTRY_REGD("PH", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI),
- COUNTRY_REGD("SG", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI),
- COUNTRY_REGD("LK", RTW89_ETSI, RTW89_ETSI, RTW89_NA),
- COUNTRY_REGD("TW", RTW89_FCC, RTW89_FCC, RTW89_ETSI),
- COUNTRY_REGD("TH", RTW89_THAILAND, RTW89_THAILAND, RTW89_THAILAND),
- COUNTRY_REGD("VN", RTW89_ETSI, RTW89_ETSI, RTW89_NA),
- COUNTRY_REGD("AU", RTW89_ACMA, RTW89_ACMA, RTW89_ACMA),
- COUNTRY_REGD("NZ", RTW89_ACMA, RTW89_ACMA, RTW89_ACMA),
- COUNTRY_REGD("PG", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI),
- COUNTRY_REGD("CA", RTW89_IC, RTW89_IC, RTW89_IC),
- COUNTRY_REGD("JP", RTW89_MKK, RTW89_MKK, RTW89_MKK),
- COUNTRY_REGD("JM", RTW89_FCC, RTW89_FCC, RTW89_FCC),
- COUNTRY_REGD("AN", RTW89_FCC, RTW89_FCC, RTW89_FCC),
- COUNTRY_REGD("TT", RTW89_FCC, RTW89_FCC, RTW89_NA),
- COUNTRY_REGD("TN", RTW89_ETSI, RTW89_ETSI, RTW89_NA),
- COUNTRY_REGD("AF", RTW89_ETSI, RTW89_ETSI, RTW89_NA),
- COUNTRY_REGD("DZ", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI),
- COUNTRY_REGD("AS", RTW89_FCC, RTW89_FCC, RTW89_NA),
- COUNTRY_REGD("AD", RTW89_ETSI, RTW89_ETSI, RTW89_NA),
- COUNTRY_REGD("AO", RTW89_ETSI, RTW89_ETSI, RTW89_NA),
- COUNTRY_REGD("AI", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI),
- COUNTRY_REGD("AQ", RTW89_ETSI, RTW89_ETSI, RTW89_NA),
- COUNTRY_REGD("AG", RTW89_FCC, RTW89_FCC, RTW89_FCC),
- COUNTRY_REGD("AM", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI),
- COUNTRY_REGD("AW", RTW89_FCC, RTW89_FCC, RTW89_FCC),
- COUNTRY_REGD("BS", RTW89_FCC, RTW89_FCC, RTW89_FCC),
- COUNTRY_REGD("BB", RTW89_FCC, RTW89_FCC, RTW89_FCC),
- COUNTRY_REGD("BY", RTW89_ETSI, RTW89_ETSI, RTW89_NA),
- COUNTRY_REGD("BZ", RTW89_FCC, RTW89_FCC, RTW89_NA),
- COUNTRY_REGD("BJ", RTW89_ETSI, RTW89_ETSI, RTW89_NA),
- COUNTRY_REGD("BM", RTW89_FCC, RTW89_FCC, RTW89_FCC),
- COUNTRY_REGD("BT", RTW89_ETSI, RTW89_ETSI, RTW89_NA),
- COUNTRY_REGD("BW", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI),
- COUNTRY_REGD("BV", RTW89_ETSI, RTW89_ETSI, RTW89_NA),
- COUNTRY_REGD("IO", RTW89_ETSI, RTW89_ETSI, RTW89_NA),
- COUNTRY_REGD("VG", RTW89_FCC, RTW89_FCC, RTW89_FCC),
- COUNTRY_REGD("BN", RTW89_ETSI, RTW89_ETSI, RTW89_NA),
- COUNTRY_REGD("BF", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI),
- COUNTRY_REGD("MM", RTW89_ETSI, RTW89_ETSI, RTW89_NA),
- COUNTRY_REGD("BI", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI),
- COUNTRY_REGD("CM", RTW89_ETSI, RTW89_ETSI, RTW89_NA),
- COUNTRY_REGD("CV", RTW89_ETSI, RTW89_ETSI, RTW89_NA),
- COUNTRY_REGD("KY", RTW89_FCC, RTW89_FCC, RTW89_FCC),
- COUNTRY_REGD("CF", RTW89_ETSI, RTW89_ETSI, RTW89_NA),
- COUNTRY_REGD("TD", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI),
- COUNTRY_REGD("CX", RTW89_ACMA, RTW89_ACMA, RTW89_NA),
- COUNTRY_REGD("CC", RTW89_ACMA, RTW89_ACMA, RTW89_NA),
- COUNTRY_REGD("KM", RTW89_ETSI, RTW89_ETSI, RTW89_NA),
- COUNTRY_REGD("CG", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI),
- COUNTRY_REGD("CD", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI),
- COUNTRY_REGD("CK", RTW89_ETSI, RTW89_ETSI, RTW89_NA),
- COUNTRY_REGD("CI", RTW89_ETSI, RTW89_ETSI, RTW89_NA),
- COUNTRY_REGD("DJ", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI),
- COUNTRY_REGD("DM", RTW89_FCC, RTW89_FCC, RTW89_NA),
- COUNTRY_REGD("GQ", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI),
- COUNTRY_REGD("ER", RTW89_ETSI, RTW89_ETSI, RTW89_NA),
- COUNTRY_REGD("ET", RTW89_ETSI, RTW89_ETSI, RTW89_NA),
- COUNTRY_REGD("FK", RTW89_ETSI, RTW89_ETSI, RTW89_NA),
- COUNTRY_REGD("FO", RTW89_ETSI, RTW89_ETSI, RTW89_NA),
- COUNTRY_REGD("FJ", RTW89_FCC, RTW89_FCC, RTW89_NA),
- COUNTRY_REGD("GF", RTW89_ETSI, RTW89_ETSI, RTW89_NA),
- COUNTRY_REGD("PF", RTW89_ETSI, RTW89_ETSI, RTW89_NA),
- COUNTRY_REGD("TF", RTW89_ETSI, RTW89_ETSI, RTW89_NA),
- COUNTRY_REGD("GA", RTW89_ETSI, RTW89_ETSI, RTW89_NA),
- COUNTRY_REGD("GM", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI),
- COUNTRY_REGD("GE", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI),
- COUNTRY_REGD("GI", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI),
- COUNTRY_REGD("GL", RTW89_ETSI, RTW89_ETSI, RTW89_NA),
- COUNTRY_REGD("GD", RTW89_FCC, RTW89_FCC, RTW89_FCC),
- COUNTRY_REGD("GP", RTW89_ETSI, RTW89_ETSI, RTW89_NA),
- COUNTRY_REGD("GU", RTW89_FCC, RTW89_FCC, RTW89_FCC),
- COUNTRY_REGD("GG", RTW89_ETSI, RTW89_ETSI, RTW89_NA),
- COUNTRY_REGD("GN", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI),
- COUNTRY_REGD("GW", RTW89_ETSI, RTW89_ETSI, RTW89_NA),
- COUNTRY_REGD("GY", RTW89_FCC, RTW89_FCC, RTW89_NA),
- COUNTRY_REGD("HT", RTW89_FCC, RTW89_FCC, RTW89_FCC),
- COUNTRY_REGD("HM", RTW89_ACMA, RTW89_ACMA, RTW89_NA),
- COUNTRY_REGD("VA", RTW89_ETSI, RTW89_ETSI, RTW89_NA),
- COUNTRY_REGD("IM", RTW89_ETSI, RTW89_ETSI, RTW89_NA),
- COUNTRY_REGD("JE", RTW89_ETSI, RTW89_ETSI, RTW89_NA),
- COUNTRY_REGD("KI", RTW89_ETSI, RTW89_ETSI, RTW89_NA),
- COUNTRY_REGD("XK", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI),
- COUNTRY_REGD("LA", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI),
- COUNTRY_REGD("LR", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI),
- COUNTRY_REGD("LY", RTW89_ETSI, RTW89_ETSI, RTW89_NA),
- COUNTRY_REGD("MO", RTW89_ETSI, RTW89_ETSI, RTW89_NA),
- COUNTRY_REGD("MG", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI),
- COUNTRY_REGD("MW", RTW89_ETSI, RTW89_ETSI, RTW89_NA),
- COUNTRY_REGD("MV", RTW89_ETSI, RTW89_ETSI, RTW89_NA),
- COUNTRY_REGD("ML", RTW89_ETSI, RTW89_ETSI, RTW89_NA),
- COUNTRY_REGD("MH", RTW89_FCC, RTW89_FCC, RTW89_FCC),
- COUNTRY_REGD("MQ", RTW89_ETSI, RTW89_ETSI, RTW89_NA),
- COUNTRY_REGD("MR", RTW89_ETSI, RTW89_ETSI, RTW89_NA),
- COUNTRY_REGD("MU", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI),
- COUNTRY_REGD("YT", RTW89_ETSI, RTW89_ETSI, RTW89_NA),
- COUNTRY_REGD("FM", RTW89_FCC, RTW89_FCC, RTW89_FCC),
- COUNTRY_REGD("MD", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI),
- COUNTRY_REGD("MN", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI),
- COUNTRY_REGD("MS", RTW89_ETSI, RTW89_ETSI, RTW89_NA),
- COUNTRY_REGD("NR", RTW89_ETSI, RTW89_ETSI, RTW89_NA),
- COUNTRY_REGD("NP", RTW89_ETSI, RTW89_ETSI, RTW89_NA),
- COUNTRY_REGD("NC", RTW89_ETSI, RTW89_ETSI, RTW89_NA),
- COUNTRY_REGD("NE", RTW89_ETSI, RTW89_ETSI, RTW89_NA),
- COUNTRY_REGD("NU", RTW89_ACMA, RTW89_ACMA, RTW89_NA),
- COUNTRY_REGD("NF", RTW89_ACMA, RTW89_ACMA, RTW89_NA),
- COUNTRY_REGD("MP", RTW89_FCC, RTW89_FCC, RTW89_FCC),
- COUNTRY_REGD("PW", RTW89_FCC, RTW89_FCC, RTW89_FCC),
- COUNTRY_REGD("RE", RTW89_ETSI, RTW89_ETSI, RTW89_NA),
- COUNTRY_REGD("RW", RTW89_ETSI, RTW89_ETSI, RTW89_NA),
- COUNTRY_REGD("SH", RTW89_ETSI, RTW89_ETSI, RTW89_NA),
- COUNTRY_REGD("KN", RTW89_FCC, RTW89_FCC, RTW89_FCC),
- COUNTRY_REGD("LC", RTW89_FCC, RTW89_FCC, RTW89_FCC),
- COUNTRY_REGD("MF", RTW89_FCC, RTW89_FCC, RTW89_FCC),
- COUNTRY_REGD("SX", RTW89_FCC, RTW89_FCC, RTW89_FCC),
- COUNTRY_REGD("PM", RTW89_ETSI, RTW89_ETSI, RTW89_NA),
- COUNTRY_REGD("VC", RTW89_FCC, RTW89_FCC, RTW89_NA),
- COUNTRY_REGD("WS", RTW89_FCC, RTW89_FCC, RTW89_NA),
- COUNTRY_REGD("SM", RTW89_ETSI, RTW89_ETSI, RTW89_NA),
- COUNTRY_REGD("ST", RTW89_ETSI, RTW89_ETSI, RTW89_NA),
- COUNTRY_REGD("SC", RTW89_FCC, RTW89_FCC, RTW89_NA),
- COUNTRY_REGD("SL", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI),
- COUNTRY_REGD("SB", RTW89_ETSI, RTW89_ETSI, RTW89_NA),
- COUNTRY_REGD("SO", RTW89_ETSI, RTW89_ETSI, RTW89_NA),
- COUNTRY_REGD("GS", RTW89_ETSI, RTW89_ETSI, RTW89_NA),
- COUNTRY_REGD("SR", RTW89_FCC, RTW89_FCC, RTW89_FCC),
- COUNTRY_REGD("SJ", RTW89_ETSI, RTW89_ETSI, RTW89_NA),
- COUNTRY_REGD("SZ", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI),
- COUNTRY_REGD("TJ", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI),
- COUNTRY_REGD("TZ", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI),
- COUNTRY_REGD("TG", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI),
- COUNTRY_REGD("TK", RTW89_ACMA, RTW89_ACMA, RTW89_NA),
- COUNTRY_REGD("TO", RTW89_ETSI, RTW89_ETSI, RTW89_NA),
- COUNTRY_REGD("TM", RTW89_ETSI, RTW89_ETSI, RTW89_NA),
- COUNTRY_REGD("TC", RTW89_ETSI, RTW89_ETSI, RTW89_NA),
- COUNTRY_REGD("TV", RTW89_ETSI, RTW89_NA, RTW89_NA),
- COUNTRY_REGD("UG", RTW89_ETSI, RTW89_ETSI, RTW89_NA),
- COUNTRY_REGD("VI", RTW89_FCC, RTW89_FCC, RTW89_FCC),
- COUNTRY_REGD("UZ", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI),
- COUNTRY_REGD("VU", RTW89_ETSI, RTW89_ETSI, RTW89_NA),
- COUNTRY_REGD("WF", RTW89_ETSI, RTW89_ETSI, RTW89_NA),
- COUNTRY_REGD("EH", RTW89_ETSI, RTW89_ETSI, RTW89_NA),
- COUNTRY_REGD("ZM", RTW89_ETSI, RTW89_ETSI, RTW89_NA),
- COUNTRY_REGD("CU", RTW89_ETSI, RTW89_ETSI, RTW89_NA),
- COUNTRY_REGD("IR", RTW89_ETSI, RTW89_ETSI, RTW89_NA),
- COUNTRY_REGD("SY", RTW89_ETSI, RTW89_NA, RTW89_NA),
- COUNTRY_REGD("SD", RTW89_ETSI, RTW89_ETSI, RTW89_NA),
- COUNTRY_REGD("PS", RTW89_ETSI, RTW89_ETSI, RTW89_NA),
+ COUNTRY_REGD("AR", RTW89_MEXICO, RTW89_MEXICO, RTW89_FCC, 0x0),
+ COUNTRY_REGD("BO", RTW89_FCC, RTW89_FCC, RTW89_NA, 0x0),
+ COUNTRY_REGD("BR", RTW89_FCC, RTW89_FCC, RTW89_FCC, 0x0),
+ COUNTRY_REGD("CL", RTW89_CHILE, RTW89_CHILE, RTW89_CHILE, 0x0),
+ COUNTRY_REGD("CO", RTW89_FCC, RTW89_FCC, RTW89_FCC, 0x0),
+ COUNTRY_REGD("CR", RTW89_FCC, RTW89_FCC, RTW89_FCC, 0x0),
+ COUNTRY_REGD("EC", RTW89_FCC, RTW89_FCC, RTW89_NA, 0x0),
+ COUNTRY_REGD("SV", RTW89_FCC, RTW89_FCC, RTW89_FCC, 0x0),
+ COUNTRY_REGD("GT", RTW89_FCC, RTW89_FCC, RTW89_FCC, 0x0),
+ COUNTRY_REGD("HN", RTW89_FCC, RTW89_FCC, RTW89_FCC, 0x0),
+ COUNTRY_REGD("MX", RTW89_MEXICO, RTW89_MEXICO, RTW89_FCC, 0x0),
+ COUNTRY_REGD("NI", RTW89_FCC, RTW89_FCC, RTW89_NA, 0x0),
+ COUNTRY_REGD("PA", RTW89_FCC, RTW89_FCC, RTW89_NA, 0x0),
+ COUNTRY_REGD("PY", RTW89_FCC, RTW89_FCC, RTW89_NA, 0x0),
+ COUNTRY_REGD("PE", RTW89_FCC, RTW89_FCC, RTW89_FCC, 0x0),
+ COUNTRY_REGD("US", RTW89_FCC, RTW89_FCC, RTW89_FCC, 0x1),
+ COUNTRY_REGD("UY", RTW89_FCC, RTW89_FCC, RTW89_NA, 0x0),
+ COUNTRY_REGD("VE", RTW89_FCC, RTW89_FCC, RTW89_NA, 0x0),
+ COUNTRY_REGD("PR", RTW89_FCC, RTW89_FCC, RTW89_NA, 0x0),
+ COUNTRY_REGD("DO", RTW89_FCC, RTW89_FCC, RTW89_FCC, 0x0),
+ COUNTRY_REGD("AT", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x2),
+ COUNTRY_REGD("BE", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x2),
+ COUNTRY_REGD("CY", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x2),
+ COUNTRY_REGD("CZ", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x2),
+ COUNTRY_REGD("DK", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x2),
+ COUNTRY_REGD("EE", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x2),
+ COUNTRY_REGD("FI", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x2),
+ COUNTRY_REGD("FR", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x2),
+ COUNTRY_REGD("DE", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x2),
+ COUNTRY_REGD("GR", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x2),
+ COUNTRY_REGD("HU", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x2),
+ COUNTRY_REGD("IS", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x2),
+ COUNTRY_REGD("IE", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x2),
+ COUNTRY_REGD("IT", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x2),
+ COUNTRY_REGD("LV", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x2),
+ COUNTRY_REGD("LI", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x2),
+ COUNTRY_REGD("LT", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x2),
+ COUNTRY_REGD("LU", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x2),
+ COUNTRY_REGD("MT", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x2),
+ COUNTRY_REGD("MC", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x2),
+ COUNTRY_REGD("NL", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x2),
+ COUNTRY_REGD("NO", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x2),
+ COUNTRY_REGD("PL", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x2),
+ COUNTRY_REGD("PT", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x2),
+ COUNTRY_REGD("SK", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x2),
+ COUNTRY_REGD("SI", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x2),
+ COUNTRY_REGD("ES", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x2),
+ COUNTRY_REGD("SE", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x2),
+ COUNTRY_REGD("CH", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x2),
+ COUNTRY_REGD("GB", RTW89_UK, RTW89_UK, RTW89_UK, 0x0),
+ COUNTRY_REGD("AL", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0),
+ COUNTRY_REGD("AZ", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0),
+ COUNTRY_REGD("BH", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0),
+ COUNTRY_REGD("BA", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0),
+ COUNTRY_REGD("BG", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x2),
+ COUNTRY_REGD("HR", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x2),
+ COUNTRY_REGD("EG", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0),
+ COUNTRY_REGD("GH", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0),
+ COUNTRY_REGD("IQ", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0),
+ COUNTRY_REGD("IL", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0),
+ COUNTRY_REGD("JO", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0),
+ COUNTRY_REGD("KZ", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0),
+ COUNTRY_REGD("KE", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0),
+ COUNTRY_REGD("KW", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0),
+ COUNTRY_REGD("KG", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0),
+ COUNTRY_REGD("LB", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0),
+ COUNTRY_REGD("LS", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0),
+ COUNTRY_REGD("MK", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0),
+ COUNTRY_REGD("MA", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0),
+ COUNTRY_REGD("MZ", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0),
+ COUNTRY_REGD("NA", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0),
+ COUNTRY_REGD("NG", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0),
+ COUNTRY_REGD("OM", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0),
+ COUNTRY_REGD("QA", RTW89_QATAR, RTW89_QATAR, RTW89_QATAR, 0x0),
+ COUNTRY_REGD("RO", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x2),
+ COUNTRY_REGD("RU", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0),
+ COUNTRY_REGD("SA", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0),
+ COUNTRY_REGD("SN", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0),
+ COUNTRY_REGD("RS", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0),
+ COUNTRY_REGD("ME", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0),
+ COUNTRY_REGD("ZA", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0),
+ COUNTRY_REGD("TR", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0),
+ COUNTRY_REGD("UA", RTW89_UKRAINE, RTW89_UKRAINE, RTW89_UKRAINE, 0x0),
+ COUNTRY_REGD("AE", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0),
+ COUNTRY_REGD("YE", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0),
+ COUNTRY_REGD("ZW", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0),
+ COUNTRY_REGD("BD", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0),
+ COUNTRY_REGD("KH", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0),
+ COUNTRY_REGD("CN", RTW89_CN, RTW89_CN, RTW89_CN, 0x0),
+ COUNTRY_REGD("HK", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0),
+ COUNTRY_REGD("IN", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0),
+ COUNTRY_REGD("ID", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0),
+ COUNTRY_REGD("KR", RTW89_KCC, RTW89_KCC, RTW89_KCC, 0x1),
+ COUNTRY_REGD("MY", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0),
+ COUNTRY_REGD("PK", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0),
+ COUNTRY_REGD("PH", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0),
+ COUNTRY_REGD("SG", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0),
+ COUNTRY_REGD("LK", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0),
+ COUNTRY_REGD("TW", RTW89_FCC, RTW89_FCC, RTW89_ETSI, 0x0),
+ COUNTRY_REGD("TH", RTW89_THAILAND, RTW89_THAILAND, RTW89_THAILAND, 0x0),
+ COUNTRY_REGD("VN", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0),
+ COUNTRY_REGD("AU", RTW89_ACMA, RTW89_ACMA, RTW89_ACMA, 0x0),
+ COUNTRY_REGD("NZ", RTW89_ACMA, RTW89_ACMA, RTW89_ACMA, 0x0),
+ COUNTRY_REGD("PG", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0),
+ COUNTRY_REGD("CA", RTW89_IC, RTW89_IC, RTW89_IC, 0x1),
+ COUNTRY_REGD("JP", RTW89_MKK, RTW89_MKK, RTW89_MKK, 0x0),
+ COUNTRY_REGD("JM", RTW89_FCC, RTW89_FCC, RTW89_FCC, 0x0),
+ COUNTRY_REGD("AN", RTW89_FCC, RTW89_FCC, RTW89_FCC, 0x0),
+ COUNTRY_REGD("TT", RTW89_FCC, RTW89_FCC, RTW89_NA, 0x0),
+ COUNTRY_REGD("TN", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0),
+ COUNTRY_REGD("AF", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0),
+ COUNTRY_REGD("DZ", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0),
+ COUNTRY_REGD("AS", RTW89_FCC, RTW89_FCC, RTW89_NA, 0x0),
+ COUNTRY_REGD("AD", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0),
+ COUNTRY_REGD("AO", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0),
+ COUNTRY_REGD("AI", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0),
+ COUNTRY_REGD("AQ", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0),
+ COUNTRY_REGD("AG", RTW89_FCC, RTW89_FCC, RTW89_FCC, 0x0),
+ COUNTRY_REGD("AM", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0),
+ COUNTRY_REGD("AW", RTW89_FCC, RTW89_FCC, RTW89_FCC, 0x0),
+ COUNTRY_REGD("BS", RTW89_FCC, RTW89_FCC, RTW89_FCC, 0x0),
+ COUNTRY_REGD("BB", RTW89_FCC, RTW89_FCC, RTW89_FCC, 0x0),
+ COUNTRY_REGD("BY", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0),
+ COUNTRY_REGD("BZ", RTW89_FCC, RTW89_FCC, RTW89_NA, 0x0),
+ COUNTRY_REGD("BJ", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0),
+ COUNTRY_REGD("BM", RTW89_FCC, RTW89_FCC, RTW89_FCC, 0x0),
+ COUNTRY_REGD("BT", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0),
+ COUNTRY_REGD("BW", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0),
+ COUNTRY_REGD("BV", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0),
+ COUNTRY_REGD("IO", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0),
+ COUNTRY_REGD("VG", RTW89_FCC, RTW89_FCC, RTW89_FCC, 0x0),
+ COUNTRY_REGD("BN", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0),
+ COUNTRY_REGD("BF", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0),
+ COUNTRY_REGD("MM", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0),
+ COUNTRY_REGD("BI", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0),
+ COUNTRY_REGD("CM", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0),
+ COUNTRY_REGD("CV", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0),
+ COUNTRY_REGD("KY", RTW89_FCC, RTW89_FCC, RTW89_FCC, 0x0),
+ COUNTRY_REGD("CF", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0),
+ COUNTRY_REGD("TD", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0),
+ COUNTRY_REGD("CX", RTW89_ACMA, RTW89_ACMA, RTW89_NA, 0x0),
+ COUNTRY_REGD("CC", RTW89_ACMA, RTW89_ACMA, RTW89_NA, 0x0),
+ COUNTRY_REGD("KM", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0),
+ COUNTRY_REGD("CG", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0),
+ COUNTRY_REGD("CD", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0),
+ COUNTRY_REGD("CK", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0),
+ COUNTRY_REGD("CI", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0),
+ COUNTRY_REGD("DJ", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0),
+ COUNTRY_REGD("DM", RTW89_FCC, RTW89_FCC, RTW89_NA, 0x0),
+ COUNTRY_REGD("GQ", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0),
+ COUNTRY_REGD("ER", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0),
+ COUNTRY_REGD("ET", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0),
+ COUNTRY_REGD("FK", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0),
+ COUNTRY_REGD("FO", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0),
+ COUNTRY_REGD("FJ", RTW89_FCC, RTW89_FCC, RTW89_NA, 0x0),
+ COUNTRY_REGD("GF", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0),
+ COUNTRY_REGD("PF", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0),
+ COUNTRY_REGD("TF", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0),
+ COUNTRY_REGD("GA", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0),
+ COUNTRY_REGD("GM", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0),
+ COUNTRY_REGD("GE", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0),
+ COUNTRY_REGD("GI", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0),
+ COUNTRY_REGD("GL", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0),
+ COUNTRY_REGD("GD", RTW89_FCC, RTW89_FCC, RTW89_FCC, 0x0),
+ COUNTRY_REGD("GP", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0),
+ COUNTRY_REGD("GU", RTW89_FCC, RTW89_FCC, RTW89_FCC, 0x0),
+ COUNTRY_REGD("GG", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0),
+ COUNTRY_REGD("GN", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0),
+ COUNTRY_REGD("GW", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0),
+ COUNTRY_REGD("GY", RTW89_FCC, RTW89_FCC, RTW89_NA, 0x0),
+ COUNTRY_REGD("HT", RTW89_FCC, RTW89_FCC, RTW89_FCC, 0x0),
+ COUNTRY_REGD("HM", RTW89_ACMA, RTW89_ACMA, RTW89_NA, 0x0),
+ COUNTRY_REGD("VA", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0),
+ COUNTRY_REGD("IM", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0),
+ COUNTRY_REGD("JE", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0),
+ COUNTRY_REGD("KI", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0),
+ COUNTRY_REGD("XK", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0),
+ COUNTRY_REGD("LA", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0),
+ COUNTRY_REGD("LR", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0),
+ COUNTRY_REGD("LY", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0),
+ COUNTRY_REGD("MO", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0),
+ COUNTRY_REGD("MG", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0),
+ COUNTRY_REGD("MW", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0),
+ COUNTRY_REGD("MV", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0),
+ COUNTRY_REGD("ML", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0),
+ COUNTRY_REGD("MH", RTW89_FCC, RTW89_FCC, RTW89_FCC, 0x0),
+ COUNTRY_REGD("MQ", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0),
+ COUNTRY_REGD("MR", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0),
+ COUNTRY_REGD("MU", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0),
+ COUNTRY_REGD("YT", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0),
+ COUNTRY_REGD("FM", RTW89_FCC, RTW89_FCC, RTW89_FCC, 0x0),
+ COUNTRY_REGD("MD", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0),
+ COUNTRY_REGD("MN", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0),
+ COUNTRY_REGD("MS", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0),
+ COUNTRY_REGD("NR", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0),
+ COUNTRY_REGD("NP", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0),
+ COUNTRY_REGD("NC", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0),
+ COUNTRY_REGD("NE", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0),
+ COUNTRY_REGD("NU", RTW89_ACMA, RTW89_ACMA, RTW89_NA, 0x0),
+ COUNTRY_REGD("NF", RTW89_ACMA, RTW89_ACMA, RTW89_NA, 0x0),
+ COUNTRY_REGD("MP", RTW89_FCC, RTW89_FCC, RTW89_FCC, 0x0),
+ COUNTRY_REGD("PW", RTW89_FCC, RTW89_FCC, RTW89_FCC, 0x0),
+ COUNTRY_REGD("RE", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0),
+ COUNTRY_REGD("RW", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0),
+ COUNTRY_REGD("SH", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0),
+ COUNTRY_REGD("KN", RTW89_FCC, RTW89_FCC, RTW89_FCC, 0x0),
+ COUNTRY_REGD("LC", RTW89_FCC, RTW89_FCC, RTW89_FCC, 0x0),
+ COUNTRY_REGD("MF", RTW89_FCC, RTW89_FCC, RTW89_FCC, 0x0),
+ COUNTRY_REGD("SX", RTW89_FCC, RTW89_FCC, RTW89_FCC, 0x0),
+ COUNTRY_REGD("PM", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0),
+ COUNTRY_REGD("VC", RTW89_FCC, RTW89_FCC, RTW89_NA, 0x0),
+ COUNTRY_REGD("WS", RTW89_FCC, RTW89_FCC, RTW89_NA, 0x0),
+ COUNTRY_REGD("SM", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0),
+ COUNTRY_REGD("ST", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0),
+ COUNTRY_REGD("SC", RTW89_FCC, RTW89_FCC, RTW89_NA, 0x0),
+ COUNTRY_REGD("SL", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0),
+ COUNTRY_REGD("SB", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0),
+ COUNTRY_REGD("SO", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0),
+ COUNTRY_REGD("GS", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0),
+ COUNTRY_REGD("SR", RTW89_FCC, RTW89_FCC, RTW89_FCC, 0x0),
+ COUNTRY_REGD("SJ", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0),
+ COUNTRY_REGD("SZ", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0),
+ COUNTRY_REGD("TJ", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0),
+ COUNTRY_REGD("TZ", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0),
+ COUNTRY_REGD("TG", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0),
+ COUNTRY_REGD("TK", RTW89_ACMA, RTW89_ACMA, RTW89_NA, 0x0),
+ COUNTRY_REGD("TO", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0),
+ COUNTRY_REGD("TM", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0),
+ COUNTRY_REGD("TC", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0),
+ COUNTRY_REGD("TV", RTW89_ETSI, RTW89_NA, RTW89_NA, 0x0),
+ COUNTRY_REGD("UG", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0),
+ COUNTRY_REGD("VI", RTW89_FCC, RTW89_FCC, RTW89_FCC, 0x0),
+ COUNTRY_REGD("UZ", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0),
+ COUNTRY_REGD("VU", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0),
+ COUNTRY_REGD("WF", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0),
+ COUNTRY_REGD("EH", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0),
+ COUNTRY_REGD("ZM", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0),
+ COUNTRY_REGD("CU", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0),
+ COUNTRY_REGD("IR", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0),
+ COUNTRY_REGD("SY", RTW89_ETSI, RTW89_NA, RTW89_NA, 0x0),
+ COUNTRY_REGD("SD", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0),
+ COUNTRY_REGD("PS", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0),
};
static const char rtw89_alpha2_list_eu[][3] = {
@@ -295,13 +304,16 @@ static const char rtw89_alpha2_list_eu[][3] = {
"RO",
};
-static const struct rtw89_regd *rtw89_regd_find_reg_by_name(const char *alpha2)
+static const struct rtw89_regd *rtw89_regd_find_reg_by_name(struct rtw89_dev *rtwdev,
+ const char *alpha2)
{
+ struct rtw89_regulatory_info *regulatory = &rtwdev->regulatory;
+ const struct rtw89_regd_ctrl *regd_ctrl = &regulatory->ctrl;
u32 i;
- for (i = 0; i < ARRAY_SIZE(rtw89_regd_map); i++) {
- if (!memcmp(rtw89_regd_map[i].alpha2, alpha2, 2))
- return &rtw89_regd_map[i];
+ for (i = 0; i < regd_ctrl->nr; i++) {
+ if (!memcmp(regd_ctrl->map[i].alpha2, alpha2, 2))
+ return &regd_ctrl->map[i];
}
return &rtw89_ww_regd;
@@ -312,22 +324,25 @@ static bool rtw89_regd_is_ww(const struct rtw89_regd *regd)
return regd == &rtw89_ww_regd;
}
-static u8 rtw89_regd_get_index(const struct rtw89_regd *regd)
+static u8 rtw89_regd_get_index(struct rtw89_dev *rtwdev, const struct rtw89_regd *regd)
{
+ struct rtw89_regulatory_info *regulatory = &rtwdev->regulatory;
+ const struct rtw89_regd_ctrl *regd_ctrl = &regulatory->ctrl;
+
BUILD_BUG_ON(ARRAY_SIZE(rtw89_regd_map) > RTW89_REGD_MAX_COUNTRY_NUM);
if (rtw89_regd_is_ww(regd))
return RTW89_REGD_MAX_COUNTRY_NUM;
- return regd - rtw89_regd_map;
+ return regd - regd_ctrl->map;
}
-static u8 rtw89_regd_get_index_by_name(const char *alpha2)
+static u8 rtw89_regd_get_index_by_name(struct rtw89_dev *rtwdev, const char *alpha2)
{
const struct rtw89_regd *regd;
- regd = rtw89_regd_find_reg_by_name(alpha2);
- return rtw89_regd_get_index(regd);
+ regd = rtw89_regd_find_reg_by_name(rtwdev, alpha2);
+ return rtw89_regd_get_index(rtwdev, regd);
}
#define rtw89_debug_regd(_dev, _regd, _desc, _argv...) \
@@ -345,6 +360,7 @@ static void rtw89_regd_setup_unii4(struct rtw89_dev *rtwdev,
struct wiphy *wiphy)
{
struct rtw89_regulatory_info *regulatory = &rtwdev->regulatory;
+ const struct rtw89_regd_ctrl *regd_ctrl = &regulatory->ctrl;
const struct rtw89_chip_info *chip = rtwdev->chip;
struct ieee80211_supported_band *sband;
struct rtw89_acpi_dsm_result res = {};
@@ -382,8 +398,8 @@ static void rtw89_regd_setup_unii4(struct rtw89_dev *rtwdev,
"acpi: eval if allow unii-4: 0x%x\n", val);
bottom:
- for (i = 0; i < ARRAY_SIZE(rtw89_regd_map); i++) {
- const struct rtw89_regd *regd = &rtw89_regd_map[i];
+ for (i = 0; i < regd_ctrl->nr; i++) {
+ const struct rtw89_regd *regd = &regd_ctrl->map[i];
switch (regd->txpwr_regd[RTW89_BAND_5G]) {
case RTW89_FCC:
@@ -406,7 +422,7 @@ static void __rtw89_regd_setup_policy_6ghz(struct rtw89_dev *rtwdev, bool block,
struct rtw89_regulatory_info *regulatory = &rtwdev->regulatory;
u8 index;
- index = rtw89_regd_get_index_by_name(alpha2);
+ index = rtw89_regd_get_index_by_name(rtwdev, alpha2);
if (index == RTW89_REGD_MAX_COUNTRY_NUM) {
rtw89_debug(rtwdev, RTW89_DBG_REGD, "%s: unknown alpha2 %c%c\n",
__func__, alpha2[0], alpha2[1]);
@@ -474,6 +490,7 @@ out:
static void rtw89_regd_setup_policy_6ghz_sp(struct rtw89_dev *rtwdev)
{
struct rtw89_regulatory_info *regulatory = &rtwdev->regulatory;
+ const struct rtw89_regd_ctrl *regd_ctrl = &regulatory->ctrl;
const struct rtw89_acpi_policy_6ghz_sp *ptr;
struct rtw89_acpi_dsm_result res = {};
bool enable_by_us;
@@ -505,8 +522,8 @@ static void rtw89_regd_setup_policy_6ghz_sp(struct rtw89_dev *rtwdev)
enable_by_us = u8_get_bits(ptr->conf, RTW89_ACPI_CONF_6GHZ_SP_US);
- for (i = 0; i < ARRAY_SIZE(rtw89_regd_map); i++) {
- const struct rtw89_regd *tmp = &rtw89_regd_map[i];
+ for (i = 0; i < regd_ctrl->nr; i++) {
+ const struct rtw89_regd *tmp = &regd_ctrl->map[i];
if (enable_by_us && memcmp(tmp->alpha2, "US", 2) == 0)
clear_bit(i, regulatory->block_6ghz_sp);
@@ -571,10 +588,56 @@ bottom:
kfree(sband);
}
+#define RTW89_DEF_REGD_STR(regd) \
+ [RTW89_ ## regd] = #regd
+
+static const char * const rtw89_regd_string[] = {
+ RTW89_DEF_REGD_STR(WW),
+ RTW89_DEF_REGD_STR(ETSI),
+ RTW89_DEF_REGD_STR(FCC),
+ RTW89_DEF_REGD_STR(MKK),
+ RTW89_DEF_REGD_STR(NA),
+ RTW89_DEF_REGD_STR(IC),
+ RTW89_DEF_REGD_STR(KCC),
+ RTW89_DEF_REGD_STR(ACMA),
+ RTW89_DEF_REGD_STR(NCC),
+ RTW89_DEF_REGD_STR(MEXICO),
+ RTW89_DEF_REGD_STR(CHILE),
+ RTW89_DEF_REGD_STR(UKRAINE),
+ RTW89_DEF_REGD_STR(CN),
+ RTW89_DEF_REGD_STR(QATAR),
+ RTW89_DEF_REGD_STR(UK),
+ RTW89_DEF_REGD_STR(THAILAND),
+};
+
+static_assert(ARRAY_SIZE(rtw89_regd_string) == RTW89_REGD_NUM);
+
+const char *rtw89_regd_get_string(enum rtw89_regulation_type regd)
+{
+ if (regd < 0 || regd >= RTW89_REGD_NUM)
+ return "(unknown)";
+
+ return rtw89_regd_string[regd];
+}
+
int rtw89_regd_setup(struct rtw89_dev *rtwdev)
{
+ struct rtw89_regulatory_info *regulatory = &rtwdev->regulatory;
+ struct rtw89_fw_elm_info *elm_info = &rtwdev->fw.elm_info;
+ const struct rtw89_regd_data *regd_data = elm_info->regd;
struct wiphy *wiphy = rtwdev->hw->wiphy;
+ if (regd_data) {
+ regulatory->ctrl.nr = regd_data->nr;
+ regulatory->ctrl.map = regd_data->map;
+ } else {
+ regulatory->ctrl.nr = ARRAY_SIZE(rtw89_regd_map);
+ regulatory->ctrl.map = rtw89_regd_map;
+ }
+
+ regulatory->reg_6ghz_power = RTW89_REG_6GHZ_POWER_DFLT;
+ regulatory->txpwr_uk_follow_etsi = true;
+
if (!wiphy)
return -EINVAL;
@@ -585,21 +648,16 @@ int rtw89_regd_setup(struct rtw89_dev *rtwdev)
return 0;
}
-int rtw89_regd_init(struct rtw89_dev *rtwdev,
- void (*reg_notifier)(struct wiphy *wiphy,
- struct regulatory_request *request))
+int rtw89_regd_init_hint(struct rtw89_dev *rtwdev)
{
- struct rtw89_regulatory_info *regulatory = &rtwdev->regulatory;
const struct rtw89_regd *chip_regd;
struct wiphy *wiphy = rtwdev->hw->wiphy;
int ret;
- regulatory->reg_6ghz_power = RTW89_REG_6GHZ_POWER_DFLT;
-
if (!wiphy)
return -EINVAL;
- chip_regd = rtw89_regd_find_reg_by_name(rtwdev->efuse.country_code);
+ chip_regd = rtw89_regd_find_reg_by_name(rtwdev, rtwdev->efuse.country_code);
if (!rtw89_regd_is_ww(chip_regd)) {
rtwdev->regulatory.regd = chip_regd;
/* Ignore country ie if there is a country domain programmed in chip */
@@ -637,7 +695,7 @@ static void rtw89_regd_apply_policy_unii4(struct rtw89_dev *rtwdev,
if (!chip->support_unii4)
return;
- index = rtw89_regd_get_index(regd);
+ index = rtw89_regd_get_index(rtwdev, regd);
if (index != RTW89_REGD_MAX_COUNTRY_NUM &&
!test_bit(index, regulatory->block_unii4))
return;
@@ -655,7 +713,7 @@ static bool regd_is_6ghz_blocked(struct rtw89_dev *rtwdev)
const struct rtw89_regd *regd = regulatory->regd;
u8 index;
- index = rtw89_regd_get_index(regd);
+ index = rtw89_regd_get_index(rtwdev, regd);
if (index != RTW89_REGD_MAX_COUNTRY_NUM &&
!test_bit(index, regulatory->block_6ghz))
return false;
@@ -696,11 +754,47 @@ static void rtw89_regd_apply_policy_6ghz(struct rtw89_dev *rtwdev,
sband->channels[i].flags |= IEEE80211_CHAN_DISABLED;
}
+static void rtw89_regd_apply_policy_tas(struct rtw89_dev *rtwdev)
+{
+ struct rtw89_regulatory_info *regulatory = &rtwdev->regulatory;
+ const struct rtw89_regd *regd = regulatory->regd;
+ struct rtw89_tas_info *tas = &rtwdev->tas;
+ u8 tas_country;
+
+ if (!tas->enable)
+ return;
+
+ if (memcmp("US", regd->alpha2, 2) == 0)
+ tas_country = RTW89_ACPI_CONF_TAS_US;
+ else if (memcmp("CA", regd->alpha2, 2) == 0)
+ tas_country = RTW89_ACPI_CONF_TAS_CA;
+ else if (memcmp("KR", regd->alpha2, 2) == 0)
+ tas_country = RTW89_ACPI_CONF_TAS_KR;
+ else
+ tas_country = RTW89_ACPI_CONF_TAS_OTHERS;
+
+ tas->block_regd = !(tas->enabled_countries & tas_country &&
+ test_bit(RTW89_REGD_FUNC_TAS, regd->func_bitmap));
+}
+
+static void rtw89_regd_apply_policy_ant_gain(struct rtw89_dev *rtwdev)
+{
+ struct rtw89_regulatory_info *regulatory = &rtwdev->regulatory;
+ struct rtw89_ant_gain_info *ant_gain = &rtwdev->ant_gain;
+ const struct rtw89_chip_info *chip = rtwdev->chip;
+ const struct rtw89_regd *regd = regulatory->regd;
+
+ if (!chip->support_ant_gain)
+ return;
+
+ ant_gain->block_country = !test_bit(RTW89_REGD_FUNC_DAG, regd->func_bitmap);
+}
+
static void rtw89_regd_notifier_apply(struct rtw89_dev *rtwdev,
struct wiphy *wiphy,
struct regulatory_request *request)
{
- rtwdev->regulatory.regd = rtw89_regd_find_reg_by_name(request->alpha2);
+ rtwdev->regulatory.regd = rtw89_regd_find_reg_by_name(rtwdev, request->alpha2);
/* This notification might be set from the system of distros,
* and it does not expect the regulatory will be modified by
* connecting to an AP (i.e. country ie).
@@ -713,14 +807,17 @@ static void rtw89_regd_notifier_apply(struct rtw89_dev *rtwdev,
rtw89_regd_apply_policy_unii4(rtwdev, wiphy);
rtw89_regd_apply_policy_6ghz(rtwdev, wiphy);
+ rtw89_regd_apply_policy_tas(rtwdev);
+ rtw89_regd_apply_policy_ant_gain(rtwdev);
}
+static
void rtw89_regd_notifier(struct wiphy *wiphy, struct regulatory_request *request)
{
struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
struct rtw89_dev *rtwdev = hw->priv;
- mutex_lock(&rtwdev->mutex);
+ wiphy_lock(wiphy);
rtw89_leave_ps_mode(rtwdev);
if (wiphy->regd) {
@@ -736,7 +833,7 @@ void rtw89_regd_notifier(struct wiphy *wiphy, struct regulatory_request *request
rtw89_core_set_chip_txpwr(rtwdev);
exit:
- mutex_unlock(&rtwdev->mutex);
+ wiphy_unlock(wiphy);
}
/* Maximum Transmit Power field (@raw) can be EIRP or PSD.
@@ -925,7 +1022,7 @@ static bool __rtw89_reg_6ghz_power_recalc(struct rtw89_dev *rtwdev)
sel = RTW89_REG_6GHZ_POWER_DFLT;
if (sel == RTW89_REG_6GHZ_POWER_STD) {
- index = rtw89_regd_get_index(regd);
+ index = rtw89_regd_get_index(rtwdev, regd);
if (index == RTW89_REGD_MAX_COUNTRY_NUM ||
test_bit(index, regulatory->block_6ghz_sp)) {
rtw89_debug(rtwdev, RTW89_DBG_REGD,
@@ -986,7 +1083,7 @@ int rtw89_reg_6ghz_recalc(struct rtw89_dev *rtwdev, struct rtw89_vif_link *rtwvi
unsigned int changed = 0;
int ret;
- lockdep_assert_held(&rtwdev->mutex);
+ lockdep_assert_wiphy(rtwdev->hw->wiphy);
/* The result of reg_6ghz_tpe may depend on reg_6ghz_power type,
* so must do reg_6ghz_tpe_recalc() after reg_6ghz_power_recalc().
diff --git a/drivers/net/wireless/realtek/rtw89/rtw8851b.c b/drivers/net/wireless/realtek/rtw89/rtw8851b.c
index c56f70267882..fafa200a9c8d 100644
--- a/drivers/net/wireless/realtek/rtw89/rtw8851b.c
+++ b/drivers/net/wireless/realtek/rtw89/rtw8851b.c
@@ -224,10 +224,17 @@ static const struct rtw89_edcca_regs rtw8851b_edcca_regs = {
.edcca_p_mask = B_EDCCA_LVL_MSK1,
.ppdu_level = R_SEG0R_EDCCA_LVL_V1,
.ppdu_mask = B_EDCCA_LVL_MSK3,
- .rpt_a = R_EDCCA_RPT_A,
- .rpt_b = R_EDCCA_RPT_B,
- .rpt_sel = R_EDCCA_RPT_SEL,
- .rpt_sel_mask = B_EDCCA_RPT_SEL_MSK,
+ .p = {{
+ .rpt_a = R_EDCCA_RPT_A,
+ .rpt_b = R_EDCCA_RPT_B,
+ .rpt_sel = R_EDCCA_RPT_SEL,
+ .rpt_sel_mask = B_EDCCA_RPT_SEL_MSK,
+ }, {
+ .rpt_a = R_EDCCA_RPT_P1_A,
+ .rpt_b = R_EDCCA_RPT_P1_B,
+ .rpt_sel = R_EDCCA_RPT_SEL,
+ .rpt_sel_mask = B_EDCCA_RPT_SEL_P1_MSK,
+ }},
.tx_collision_t2r_st = R_TX_COLLISION_T2R_ST,
.tx_collision_t2r_st_mask = B_TX_COLLISION_T2R_ST_M,
};
@@ -1596,10 +1603,16 @@ static void rtw8851b_rfk_channel(struct rtw89_dev *rtwdev,
enum rtw89_chanctx_idx chanctx_idx = rtwvif_link->chanctx_idx;
enum rtw89_phy_idx phy_idx = rtwvif_link->phy_idx;
+ rtw89_btc_ntfy_conn_rfk(rtwdev, true);
+
rtw8851b_rx_dck(rtwdev, phy_idx, chanctx_idx);
rtw8851b_iqk(rtwdev, phy_idx, chanctx_idx);
+ rtw89_btc_ntfy_preserve_bt_time(rtwdev, 30);
rtw8851b_tssi(rtwdev, phy_idx, true, chanctx_idx);
+ rtw89_btc_ntfy_preserve_bt_time(rtwdev, 30);
rtw8851b_dpk(rtwdev, phy_idx, chanctx_idx);
+
+ rtw89_btc_ntfy_conn_rfk(rtwdev, false);
}
static void rtw8851b_rfk_band_changed(struct rtw89_dev *rtwdev,
@@ -2410,6 +2423,7 @@ static const struct rtw89_chip_ops rtw8851b_chip_ops = {
.h2c_default_cmac_tbl = rtw89_fw_h2c_default_cmac_tbl,
.h2c_assoc_cmac_tbl = rtw89_fw_h2c_assoc_cmac_tbl,
.h2c_ampdu_cmac_tbl = NULL,
+ .h2c_txtime_cmac_tbl = rtw89_fw_h2c_txtime_cmac_tbl,
.h2c_default_dmac_tbl = NULL,
.h2c_update_beacon = rtw89_fw_h2c_update_beacon,
.h2c_ba_cam = rtw89_fw_h2c_ba_cam,
@@ -2445,6 +2459,7 @@ const struct rtw89_chip_info rtw8851b_chip_info = {
.try_ce_fw = true,
.bbmcu_nr = 0,
.needed_fw_elms = 0,
+ .fw_blacklist = NULL,
.fifo_size = 196608,
.small_fifo_size = true,
.dle_scc_rsvd_size = 98304,
@@ -2483,10 +2498,15 @@ const struct rtw89_chip_info rtw8851b_chip_info = {
BIT(NL80211_CHAN_WIDTH_80),
.support_unii4 = true,
.support_ant_gain = false,
+ .support_tas = false,
+ .support_sar_by_ant = false,
.ul_tb_waveform_ctrl = true,
.ul_tb_pwr_diff = false,
+ .rx_freq_frome_ie = true,
.hw_sec_hdr = false,
.hw_mgmt_tx_encrypt = false,
+ .hw_tkip_crypto = false,
+ .hw_mlo_bmc_crypto = false,
.rf_path_num = 1,
.tx_nss = 1,
.rx_nss = 1,
diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852a.c b/drivers/net/wireless/realtek/rtw89/rtw8852a.c
index 9bd2842c27d5..cd5987fc52d7 100644
--- a/drivers/net/wireless/realtek/rtw89/rtw8852a.c
+++ b/drivers/net/wireless/realtek/rtw89/rtw8852a.c
@@ -522,10 +522,17 @@ static const struct rtw89_edcca_regs rtw8852a_edcca_regs = {
.edcca_p_mask = B_EDCCA_LVL_MSK1,
.ppdu_level = R_SEG0R_EDCCA_LVL,
.ppdu_mask = B_EDCCA_LVL_MSK3,
- .rpt_a = R_EDCCA_RPT_A,
- .rpt_b = R_EDCCA_RPT_B,
- .rpt_sel = R_EDCCA_RPT_SEL,
- .rpt_sel_mask = B_EDCCA_RPT_SEL_MSK,
+ .p = {{
+ .rpt_a = R_EDCCA_RPT_A,
+ .rpt_b = R_EDCCA_RPT_B,
+ .rpt_sel = R_EDCCA_RPT_SEL,
+ .rpt_sel_mask = B_EDCCA_RPT_SEL_MSK,
+ }, {
+ .rpt_a = R_EDCCA_RPT_P1_A,
+ .rpt_b = R_EDCCA_RPT_P1_B,
+ .rpt_sel = R_EDCCA_RPT_SEL,
+ .rpt_sel_mask = B_EDCCA_RPT_SEL_P1_MSK,
+ }},
.tx_collision_t2r_st = R_TX_COLLISION_T2R_ST,
.tx_collision_t2r_st_mask = B_TX_COLLISION_T2R_ST_M,
};
@@ -1356,10 +1363,16 @@ static void rtw8852a_rfk_channel(struct rtw89_dev *rtwdev,
enum rtw89_chanctx_idx chanctx_idx = rtwvif_link->chanctx_idx;
enum rtw89_phy_idx phy_idx = rtwvif_link->phy_idx;
+ rtw89_btc_ntfy_conn_rfk(rtwdev, true);
+
rtw8852a_rx_dck(rtwdev, phy_idx, true, chanctx_idx);
rtw8852a_iqk(rtwdev, phy_idx, chanctx_idx);
+ rtw89_btc_ntfy_preserve_bt_time(rtwdev, 30);
rtw8852a_tssi(rtwdev, phy_idx, chanctx_idx);
+ rtw89_btc_ntfy_preserve_bt_time(rtwdev, 30);
rtw8852a_dpk(rtwdev, phy_idx, chanctx_idx);
+
+ rtw89_btc_ntfy_conn_rfk(rtwdev, false);
}
static void rtw8852a_rfk_band_changed(struct rtw89_dev *rtwdev,
@@ -2136,6 +2149,7 @@ static const struct rtw89_chip_ops rtw8852a_chip_ops = {
.h2c_default_cmac_tbl = rtw89_fw_h2c_default_cmac_tbl,
.h2c_assoc_cmac_tbl = rtw89_fw_h2c_assoc_cmac_tbl,
.h2c_ampdu_cmac_tbl = NULL,
+ .h2c_txtime_cmac_tbl = rtw89_fw_h2c_txtime_cmac_tbl,
.h2c_default_dmac_tbl = NULL,
.h2c_update_beacon = rtw89_fw_h2c_update_beacon,
.h2c_ba_cam = rtw89_fw_h2c_ba_cam,
@@ -2162,6 +2176,7 @@ const struct rtw89_chip_info rtw8852a_chip_info = {
.try_ce_fw = false,
.bbmcu_nr = 0,
.needed_fw_elms = 0,
+ .fw_blacklist = NULL,
.fifo_size = 458752,
.small_fifo_size = false,
.dle_scc_rsvd_size = 0,
@@ -2201,10 +2216,15 @@ const struct rtw89_chip_info rtw8852a_chip_info = {
BIT(NL80211_CHAN_WIDTH_80),
.support_unii4 = false,
.support_ant_gain = false,
+ .support_tas = false,
+ .support_sar_by_ant = false,
.ul_tb_waveform_ctrl = false,
.ul_tb_pwr_diff = false,
+ .rx_freq_frome_ie = true,
.hw_sec_hdr = false,
.hw_mgmt_tx_encrypt = false,
+ .hw_tkip_crypto = false,
+ .hw_mlo_bmc_crypto = false,
.rf_path_num = 2,
.tx_nss = 2,
.rx_nss = 2,
diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852b.c b/drivers/net/wireless/realtek/rtw89/rtw8852b.c
index dfb2bf61b0b8..dacdb384de2c 100644
--- a/drivers/net/wireless/realtek/rtw89/rtw8852b.c
+++ b/drivers/net/wireless/realtek/rtw89/rtw8852b.c
@@ -189,10 +189,17 @@ static const struct rtw89_edcca_regs rtw8852b_edcca_regs = {
.edcca_p_mask = B_EDCCA_LVL_MSK1,
.ppdu_level = R_SEG0R_EDCCA_LVL_V1,
.ppdu_mask = B_EDCCA_LVL_MSK3,
- .rpt_a = R_EDCCA_RPT_A,
- .rpt_b = R_EDCCA_RPT_B,
- .rpt_sel = R_EDCCA_RPT_SEL,
- .rpt_sel_mask = B_EDCCA_RPT_SEL_MSK,
+ .p = {{
+ .rpt_a = R_EDCCA_RPT_A,
+ .rpt_b = R_EDCCA_RPT_B,
+ .rpt_sel = R_EDCCA_RPT_SEL,
+ .rpt_sel_mask = B_EDCCA_RPT_SEL_MSK,
+ }, {
+ .rpt_a = R_EDCCA_RPT_P1_A,
+ .rpt_b = R_EDCCA_RPT_P1_B,
+ .rpt_sel = R_EDCCA_RPT_SEL,
+ .rpt_sel_mask = B_EDCCA_RPT_SEL_P1_MSK,
+ }},
.tx_collision_t2r_st = R_TX_COLLISION_T2R_ST,
.tx_collision_t2r_st_mask = B_TX_COLLISION_T2R_ST_M,
};
@@ -568,10 +575,16 @@ static void rtw8852b_rfk_channel(struct rtw89_dev *rtwdev,
enum rtw89_chanctx_idx chanctx_idx = rtwvif_link->chanctx_idx;
enum rtw89_phy_idx phy_idx = rtwvif_link->phy_idx;
+ rtw89_btc_ntfy_conn_rfk(rtwdev, true);
+
rtw8852b_rx_dck(rtwdev, phy_idx, chanctx_idx);
rtw8852b_iqk(rtwdev, phy_idx, chanctx_idx);
+ rtw89_btc_ntfy_preserve_bt_time(rtwdev, 30);
rtw8852b_tssi(rtwdev, phy_idx, true, chanctx_idx);
+ rtw89_btc_ntfy_preserve_bt_time(rtwdev, 30);
rtw8852b_dpk(rtwdev, phy_idx, chanctx_idx);
+
+ rtw89_btc_ntfy_conn_rfk(rtwdev, false);
}
static void rtw8852b_rfk_band_changed(struct rtw89_dev *rtwdev,
@@ -763,6 +776,7 @@ static const struct rtw89_chip_ops rtw8852b_chip_ops = {
.h2c_default_cmac_tbl = rtw89_fw_h2c_default_cmac_tbl,
.h2c_assoc_cmac_tbl = rtw89_fw_h2c_assoc_cmac_tbl,
.h2c_ampdu_cmac_tbl = NULL,
+ .h2c_txtime_cmac_tbl = rtw89_fw_h2c_txtime_cmac_tbl,
.h2c_default_dmac_tbl = NULL,
.h2c_update_beacon = rtw89_fw_h2c_update_beacon,
.h2c_ba_cam = rtw89_fw_h2c_ba_cam,
@@ -798,6 +812,7 @@ const struct rtw89_chip_info rtw8852b_chip_info = {
.try_ce_fw = true,
.bbmcu_nr = 0,
.needed_fw_elms = 0,
+ .fw_blacklist = &rtw89_fw_blacklist_default,
.fifo_size = 196608,
.small_fifo_size = true,
.dle_scc_rsvd_size = 98304,
@@ -837,10 +852,15 @@ const struct rtw89_chip_info rtw8852b_chip_info = {
BIT(NL80211_CHAN_WIDTH_80),
.support_unii4 = true,
.support_ant_gain = true,
+ .support_tas = false,
+ .support_sar_by_ant = true,
.ul_tb_waveform_ctrl = true,
.ul_tb_pwr_diff = false,
+ .rx_freq_frome_ie = true,
.hw_sec_hdr = false,
.hw_mgmt_tx_encrypt = false,
+ .hw_tkip_crypto = false,
+ .hw_mlo_bmc_crypto = false,
.rf_path_num = 2,
.tx_nss = 2,
.rx_nss = 2,
diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852b_common.c b/drivers/net/wireless/realtek/rtw89/rtw8852b_common.c
index 0e094ce9c9b0..0cf03f18cbb1 100644
--- a/drivers/net/wireless/realtek/rtw89/rtw8852b_common.c
+++ b/drivers/net/wireless/realtek/rtw89/rtw8852b_common.c
@@ -8,6 +8,7 @@
#include "phy.h"
#include "reg.h"
#include "rtw8852b_common.h"
+#include "sar.h"
#include "util.h"
static const struct rtw89_reg3_def rtw8852bx_pmac_ht20_mcs7_tbl[] = {
@@ -621,9 +622,9 @@ static void rtw8852bt_ext_loss_avg_update(struct rtw89_dev *rtwdev,
if (ext_loss_a == ext_loss_b) {
ext_loss_avg = ext_loss_a;
} else {
- linear = rtw89_db_2_linear(abs(ext_loss_a - ext_loss_b)) + 1;
- linear = DIV_ROUND_CLOSEST_ULL(linear / 2, 1 << RTW89_LINEAR_FRAC_BITS);
- ext_loss_avg = rtw89_linear_2_db(linear);
+ linear = rtw89_db_to_linear(abs(ext_loss_a - ext_loss_b)) + 1;
+ linear /= 2;
+ ext_loss_avg = rtw89_linear_to_db(linear);
ext_loss_avg += min(ext_loss_a, ext_loss_b);
}
@@ -1234,6 +1235,7 @@ static u32 rtw8852bx_bb_cal_txpwr_ref(struct rtw89_dev *rtwdev,
u32_encode_bits(ref, B_DPD_REF);
}
+/* @pwr_ofst (unit: 1/8 dBm): power of path A minus power of path B */
static void rtw8852bx_set_txpwr_ref(struct rtw89_dev *rtwdev,
enum rtw89_phy_idx phy_idx, s16 pwr_ofst)
{
@@ -1336,6 +1338,27 @@ static void rtw8852bx_set_tx_shape(struct rtw89_dev *rtwdev,
tx_shape_ofdm);
}
+static s16 rtw8852bx_get_txpwr_sar_diff(struct rtw89_dev *rtwdev,
+ const struct rtw89_chan *chan)
+{
+ struct rtw89_sar_parm sar_parm = {
+ .center_freq = chan->freq,
+ .force_path = true,
+ };
+ s16 sar_bb_a, sar_bb_b;
+ s8 sar_mac;
+
+ sar_parm.path = RF_PATH_A;
+ sar_mac = rtw89_query_sar(rtwdev, &sar_parm);
+ sar_bb_a = rtw89_phy_txpwr_mac_to_bb(rtwdev, sar_mac);
+
+ sar_parm.path = RF_PATH_B;
+ sar_mac = rtw89_query_sar(rtwdev, &sar_parm);
+ sar_bb_b = rtw89_phy_txpwr_mac_to_bb(rtwdev, sar_mac);
+
+ return sar_bb_a - sar_bb_b;
+}
+
static void rtw8852bx_set_txpwr_diff(struct rtw89_dev *rtwdev,
const struct rtw89_chan *chan,
enum rtw89_phy_idx phy_idx)
@@ -1343,6 +1366,7 @@ static void rtw8852bx_set_txpwr_diff(struct rtw89_dev *rtwdev,
s16 pwr_ofst;
pwr_ofst = rtw89_phy_ant_gain_pwr_offset(rtwdev, chan);
+ pwr_ofst += rtw8852bx_get_txpwr_sar_diff(rtwdev, chan);
rtw8852bx_set_txpwr_ref(rtwdev, phy_idx, pwr_ofst);
}
diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852b_rfk.c b/drivers/net/wireless/realtek/rtw89/rtw8852b_rfk.c
index ef47a5facc83..fbf82d42687b 100644
--- a/drivers/net/wireless/realtek/rtw89/rtw8852b_rfk.c
+++ b/drivers/net/wireless/realtek/rtw89/rtw8852b_rfk.c
@@ -3585,9 +3585,10 @@ static void _tssi_alimentk(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy,
u8 ch_idx = _tssi_ch_to_idx(rtwdev, channel);
struct rtw8852bx_bb_tssi_bak tssi_bak;
s32 aliment_diff, tssi_cw_default;
- u32 start_time, finish_time;
u32 bb_reg_backup[8] = {0};
+ ktime_t start_time;
const s16 *power;
+ s64 this_time;
u8 band;
bool ok;
u32 tmp;
@@ -3613,7 +3614,7 @@ static void _tssi_alimentk(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy,
return;
}
- start_time = ktime_get_ns();
+ start_time = ktime_get();
if (chan->band_type == RTW89_BAND_2G)
power = power_2g;
@@ -3738,12 +3739,12 @@ out:
rtw8852bx_bb_restore_tssi(rtwdev, phy, &tssi_bak);
rtw8852bx_bb_tx_mode_switch(rtwdev, phy, 0);
- finish_time = ktime_get_ns();
- tssi_info->tssi_alimk_time += finish_time - start_time;
+ this_time = ktime_us_delta(ktime_get(), start_time);
+ tssi_info->tssi_alimk_time += this_time;
rtw89_debug(rtwdev, RTW89_DBG_RFK,
- "[TSSI PA K] %s processing time = %d ms\n", __func__,
- tssi_info->tssi_alimk_time);
+ "[TSSI PA K] %s processing time = %lld us (acc = %llu us)\n",
+ __func__, this_time, tssi_info->tssi_alimk_time);
}
void rtw8852b_dpk_init(struct rtw89_dev *rtwdev)
diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852bt.c b/drivers/net/wireless/realtek/rtw89/rtw8852bt.c
index bde3e1fb7ca6..289dce688d72 100644
--- a/drivers/net/wireless/realtek/rtw89/rtw8852bt.c
+++ b/drivers/net/wireless/realtek/rtw89/rtw8852bt.c
@@ -187,10 +187,17 @@ static const struct rtw89_edcca_regs rtw8852bt_edcca_regs = {
.edcca_p_mask = B_EDCCA_LVL_MSK1,
.ppdu_level = R_SEG0R_EDCCA_LVL_V1,
.ppdu_mask = B_EDCCA_LVL_MSK3,
- .rpt_a = R_EDCCA_RPT_A,
- .rpt_b = R_EDCCA_RPT_B,
- .rpt_sel = R_EDCCA_RPT_SEL,
- .rpt_sel_mask = B_EDCCA_RPT_SEL_MSK,
+ .p = {{
+ .rpt_a = R_EDCCA_RPT_A,
+ .rpt_b = R_EDCCA_RPT_B,
+ .rpt_sel = R_EDCCA_RPT_SEL,
+ .rpt_sel_mask = B_EDCCA_RPT_SEL_MSK,
+ }, {
+ .rpt_a = R_EDCCA_RPT_P1_A,
+ .rpt_b = R_EDCCA_RPT_P1_B,
+ .rpt_sel = R_EDCCA_RPT_SEL,
+ .rpt_sel_mask = B_EDCCA_RPT_SEL_P1_MSK,
+ }},
.tx_collision_t2r_st = R_TX_COLLISION_T2R_ST,
.tx_collision_t2r_st_mask = B_TX_COLLISION_T2R_ST_M,
};
@@ -541,10 +548,16 @@ static void rtw8852bt_rfk_channel(struct rtw89_dev *rtwdev,
enum rtw89_chanctx_idx chanctx_idx = rtwvif_link->chanctx_idx;
enum rtw89_phy_idx phy_idx = rtwvif_link->phy_idx;
+ rtw89_btc_ntfy_conn_rfk(rtwdev, true);
+
rtw8852bt_rx_dck(rtwdev, phy_idx, chanctx_idx);
rtw8852bt_iqk(rtwdev, phy_idx, chanctx_idx);
+ rtw89_btc_ntfy_preserve_bt_time(rtwdev, 30);
rtw8852bt_tssi(rtwdev, phy_idx, true, chanctx_idx);
+ rtw89_btc_ntfy_preserve_bt_time(rtwdev, 30);
rtw8852bt_dpk(rtwdev, phy_idx, chanctx_idx);
+
+ rtw89_btc_ntfy_conn_rfk(rtwdev, false);
}
static void rtw8852bt_rfk_band_changed(struct rtw89_dev *rtwdev,
@@ -697,6 +710,7 @@ static const struct rtw89_chip_ops rtw8852bt_chip_ops = {
.h2c_default_cmac_tbl = rtw89_fw_h2c_default_cmac_tbl,
.h2c_assoc_cmac_tbl = rtw89_fw_h2c_assoc_cmac_tbl,
.h2c_ampdu_cmac_tbl = NULL,
+ .h2c_txtime_cmac_tbl = rtw89_fw_h2c_txtime_cmac_tbl,
.h2c_default_dmac_tbl = NULL,
.h2c_update_beacon = rtw89_fw_h2c_update_beacon,
.h2c_ba_cam = rtw89_fw_h2c_ba_cam,
@@ -732,6 +746,7 @@ const struct rtw89_chip_info rtw8852bt_chip_info = {
.try_ce_fw = true,
.bbmcu_nr = 0,
.needed_fw_elms = RTW89_AX_GEN_DEF_NEEDED_FW_ELEMENTS_NO_6GHZ,
+ .fw_blacklist = &rtw89_fw_blacklist_default,
.fifo_size = 458752,
.small_fifo_size = true,
.dle_scc_rsvd_size = 98304,
@@ -770,10 +785,15 @@ const struct rtw89_chip_info rtw8852bt_chip_info = {
BIT(NL80211_CHAN_WIDTH_80),
.support_unii4 = true,
.support_ant_gain = true,
+ .support_tas = false,
+ .support_sar_by_ant = true,
.ul_tb_waveform_ctrl = true,
.ul_tb_pwr_diff = false,
+ .rx_freq_frome_ie = true,
.hw_sec_hdr = false,
.hw_mgmt_tx_encrypt = false,
+ .hw_tkip_crypto = true,
+ .hw_mlo_bmc_crypto = false,
.rf_path_num = 2,
.tx_nss = 2,
.rx_nss = 2,
diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852bt_rfk.c b/drivers/net/wireless/realtek/rtw89/rtw8852bt_rfk.c
index 336a83e1d46b..6e6889eea9a0 100644
--- a/drivers/net/wireless/realtek/rtw89/rtw8852bt_rfk.c
+++ b/drivers/net/wireless/realtek/rtw89/rtw8852bt_rfk.c
@@ -3663,9 +3663,10 @@ static void _tssi_alimentk(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy,
u8 ch_idx = _tssi_ch_to_idx(rtwdev, channel);
struct rtw8852bx_bb_tssi_bak tssi_bak;
s32 aliment_diff, tssi_cw_default;
- u32 start_time, finish_time;
u32 bb_reg_backup[8] = {};
+ ktime_t start_time;
const s16 *power;
+ s64 this_time;
u8 band;
bool ok;
u32 tmp;
@@ -3675,7 +3676,7 @@ static void _tssi_alimentk(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy,
"======> %s channel=%d path=%d\n", __func__, channel,
path);
- start_time = ktime_get_ns();
+ start_time = ktime_get();
if (chan->band_type == RTW89_BAND_2G)
power = power_2g;
@@ -3802,12 +3803,12 @@ out:
rtw8852bx_bb_restore_tssi(rtwdev, phy, &tssi_bak);
rtw8852bx_bb_tx_mode_switch(rtwdev, phy, 0);
- finish_time = ktime_get_ns();
- tssi_info->tssi_alimk_time += finish_time - start_time;
+ this_time = ktime_us_delta(ktime_get(), start_time);
+ tssi_info->tssi_alimk_time += this_time;
rtw89_debug(rtwdev, RTW89_DBG_RFK,
- "[TSSI PA K] %s processing time = %d ms\n", __func__,
- tssi_info->tssi_alimk_time);
+ "[TSSI PA K] %s processing time = %lld us (acc = %llu us)\n",
+ __func__, this_time, tssi_info->tssi_alimk_time);
}
void rtw8852bt_dpk_init(struct rtw89_dev *rtwdev)
diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852c.c b/drivers/net/wireless/realtek/rtw89/rtw8852c.c
index bc84b15e7826..2a6143a8d256 100644
--- a/drivers/net/wireless/realtek/rtw89/rtw8852c.c
+++ b/drivers/net/wireless/realtek/rtw89/rtw8852c.c
@@ -12,9 +12,10 @@
#include "rtw8852c.h"
#include "rtw8852c_rfk.h"
#include "rtw8852c_table.h"
+#include "sar.h"
#include "util.h"
-#define RTW8852C_FW_FORMAT_MAX 1
+#define RTW8852C_FW_FORMAT_MAX 2
#define RTW8852C_FW_BASENAME "rtw89/rtw8852c_fw"
#define RTW8852C_MODULE_FIRMWARE \
RTW8852C_FW_BASENAME "-" __stringify(RTW8852C_FW_FORMAT_MAX) ".bin"
@@ -186,10 +187,17 @@ static const struct rtw89_edcca_regs rtw8852c_edcca_regs = {
.edcca_p_mask = B_EDCCA_LVL_MSK1,
.ppdu_level = R_SEG0R_EDCCA_LVL,
.ppdu_mask = B_EDCCA_LVL_MSK3,
- .rpt_a = R_EDCCA_RPT_A,
- .rpt_b = R_EDCCA_RPT_B,
- .rpt_sel = R_EDCCA_RPT_SEL,
- .rpt_sel_mask = B_EDCCA_RPT_SEL_MSK,
+ .p = {{
+ .rpt_a = R_EDCCA_RPT_A,
+ .rpt_b = R_EDCCA_RPT_B,
+ .rpt_sel = R_EDCCA_RPT_SEL,
+ .rpt_sel_mask = B_EDCCA_RPT_SEL_MSK,
+ }, {
+ .rpt_a = R_EDCCA_RPT_P1_A,
+ .rpt_b = R_EDCCA_RPT_P1_B,
+ .rpt_sel = R_EDCCA_RPT_SEL,
+ .rpt_sel_mask = B_EDCCA_RPT_SEL_P1_MSK,
+ }},
.tx_collision_t2r_st = R_TX_COLLISION_T2R_ST,
.tx_collision_t2r_st_mask = B_TX_COLLISION_T2R_ST_M,
};
@@ -1853,10 +1861,16 @@ static void rtw8852c_rfk_channel(struct rtw89_dev *rtwdev,
enum rtw89_phy_idx phy_idx = rtwvif_link->phy_idx;
rtw8852c_mcc_get_ch_info(rtwdev, phy_idx);
+ rtw89_btc_ntfy_conn_rfk(rtwdev, true);
+
rtw8852c_rx_dck(rtwdev, phy_idx, false);
rtw8852c_iqk(rtwdev, phy_idx, chanctx_idx);
+ rtw89_btc_ntfy_preserve_bt_time(rtwdev, 30);
rtw8852c_tssi(rtwdev, phy_idx, chanctx_idx);
+ rtw89_btc_ntfy_preserve_bt_time(rtwdev, 30);
rtw8852c_dpk(rtwdev, phy_idx, chanctx_idx);
+
+ rtw89_btc_ntfy_conn_rfk(rtwdev, false);
rtw89_fw_h2c_rf_ntfy_mcc(rtwdev);
}
@@ -2065,6 +2079,31 @@ static void rtw8852c_set_txpwr_diff(struct rtw89_dev *rtwdev,
rtw8852c_set_txpwr_ref(rtwdev, phy_idx, pwr_ofst);
}
+static void rtw8852c_set_txpwr_sar_diff(struct rtw89_dev *rtwdev,
+ const struct rtw89_chan *chan,
+ enum rtw89_phy_idx phy_idx)
+{
+ struct rtw89_sar_parm sar_parm = {
+ .center_freq = chan->freq,
+ .force_path = true,
+ };
+ s16 sar_rf;
+ s8 sar_mac;
+
+ if (phy_idx != RTW89_PHY_0)
+ return;
+
+ sar_parm.path = RF_PATH_A;
+ sar_mac = rtw89_query_sar(rtwdev, &sar_parm);
+ sar_rf = rtw89_phy_txpwr_mac_to_rf(rtwdev, sar_mac);
+ rtw89_phy_write32_mask(rtwdev, R_TXPWRB, B_TXPWRB_MAX, sar_rf);
+
+ sar_parm.path = RF_PATH_B;
+ sar_mac = rtw89_query_sar(rtwdev, &sar_parm);
+ sar_rf = rtw89_phy_txpwr_mac_to_rf(rtwdev, sar_mac);
+ rtw89_phy_write32_mask(rtwdev, R_P1_TXPWRB, B_TXPWRB_MAX, sar_rf);
+}
+
static void rtw8852c_set_txpwr(struct rtw89_dev *rtwdev,
const struct rtw89_chan *chan,
enum rtw89_phy_idx phy_idx)
@@ -2075,6 +2114,7 @@ static void rtw8852c_set_txpwr(struct rtw89_dev *rtwdev,
rtw89_phy_set_txpwr_limit(rtwdev, chan, phy_idx);
rtw89_phy_set_txpwr_limit_ru(rtwdev, chan, phy_idx);
rtw8852c_set_txpwr_diff(rtwdev, chan, phy_idx);
+ rtw8852c_set_txpwr_sar_diff(rtwdev, chan, phy_idx);
}
static void rtw8852c_set_txpwr_ctrl(struct rtw89_dev *rtwdev,
@@ -2867,6 +2907,7 @@ static int rtw8852c_mac_disable_bb_rf(struct rtw89_dev *rtwdev)
static const struct rtw89_chanctx_listener rtw8852c_chanctx_listener = {
.callbacks[RTW89_CHANCTX_CALLBACK_RFK] = rtw8852c_rfk_chanctx_cb,
+ .callbacks[RTW89_CHANCTX_CALLBACK_TAS] = rtw89_tas_chanctx_cb,
};
#ifdef CONFIG_PM
@@ -2928,6 +2969,7 @@ static const struct rtw89_chip_ops rtw8852c_chip_ops = {
.h2c_default_cmac_tbl = rtw89_fw_h2c_default_cmac_tbl,
.h2c_assoc_cmac_tbl = rtw89_fw_h2c_assoc_cmac_tbl,
.h2c_ampdu_cmac_tbl = NULL,
+ .h2c_txtime_cmac_tbl = rtw89_fw_h2c_txtime_cmac_tbl,
.h2c_default_dmac_tbl = NULL,
.h2c_update_beacon = rtw89_fw_h2c_update_beacon,
.h2c_ba_cam = rtw89_fw_h2c_ba_cam,
@@ -2954,6 +2996,7 @@ const struct rtw89_chip_info rtw8852c_chip_info = {
.try_ce_fw = false,
.bbmcu_nr = 0,
.needed_fw_elms = 0,
+ .fw_blacklist = &rtw89_fw_blacklist_default,
.fifo_size = 458752,
.small_fifo_size = false,
.dle_scc_rsvd_size = 0,
@@ -2996,10 +3039,15 @@ const struct rtw89_chip_info rtw8852c_chip_info = {
BIT(NL80211_CHAN_WIDTH_160),
.support_unii4 = true,
.support_ant_gain = true,
+ .support_tas = true,
+ .support_sar_by_ant = true,
.ul_tb_waveform_ctrl = false,
.ul_tb_pwr_diff = true,
+ .rx_freq_frome_ie = false,
.hw_sec_hdr = true,
.hw_mgmt_tx_encrypt = true,
+ .hw_tkip_crypto = true,
+ .hw_mlo_bmc_crypto = false,
.rf_path_num = 2,
.tx_nss = 2,
.rx_nss = 2,
diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922a.c b/drivers/net/wireless/realtek/rtw89/rtw8922a.c
index 11d66bfceb15..1d0f6e7df497 100644
--- a/drivers/net/wireless/realtek/rtw89/rtw8922a.c
+++ b/drivers/net/wireless/realtek/rtw89/rtw8922a.c
@@ -12,6 +12,7 @@
#include "reg.h"
#include "rtw8922a.h"
#include "rtw8922a_rfk.h"
+#include "sar.h"
#include "util.h"
#define RTW8922A_FW_FORMAT_MAX 3
@@ -205,10 +206,17 @@ static const struct rtw89_edcca_regs rtw8922a_edcca_regs = {
.edcca_p_mask = B_EDCCA_LVL_MSK1,
.ppdu_level = R_SEG0R_PPDU_LVL_BE,
.ppdu_mask = B_EDCCA_LVL_MSK1,
- .rpt_a = R_EDCCA_RPT_A_BE,
- .rpt_b = R_EDCCA_RPT_B_BE,
- .rpt_sel = R_EDCCA_RPT_SEL_BE,
- .rpt_sel_mask = B_EDCCA_RPT_SEL_MSK,
+ .p = {{
+ .rpt_a = R_EDCCA_RPT_A_BE,
+ .rpt_b = R_EDCCA_RPT_B_BE,
+ .rpt_sel = R_EDCCA_RPT_SEL_BE,
+ .rpt_sel_mask = B_EDCCA_RPT_SEL_MSK,
+ }, {
+ .rpt_a = R_EDCCA_RPT_P1_A_BE,
+ .rpt_b = R_EDCCA_RPT_P1_B_BE,
+ .rpt_sel = R_EDCCA_RPT_SEL_BE,
+ .rpt_sel_mask = B_EDCCA_RPT_SEL_P1_MSK,
+ }},
.rpt_sel_be = R_EDCCA_RPTREG_SEL_BE,
.rpt_sel_be_mask = B_EDCCA_RPTREG_SEL_BE_MSK,
.tx_collision_t2r_st = R_TX_COLLISION_T2R_ST_BE,
@@ -2063,7 +2071,8 @@ static void __rtw8922a_rfk_init_late(struct rtw89_dev *rtwdev,
rtw89_phy_rfk_pre_ntfy_and_wait(rtwdev, phy_idx, 5);
rtw89_phy_rfk_dack_and_wait(rtwdev, phy_idx, chan, 58);
- rtw89_phy_rfk_rxdck_and_wait(rtwdev, phy_idx, chan, false, 32);
+ if (!test_bit(RTW89_FLAG_SER_HANDLING, rtwdev->flags))
+ rtw89_phy_rfk_rxdck_and_wait(rtwdev, phy_idx, chan, false, 128);
}
static void rtw8922a_rfk_init_late(struct rtw89_dev *rtwdev)
@@ -2149,6 +2158,56 @@ static void rtw8922a_set_txpwr_ref(struct rtw89_dev *rtwdev,
B_BE_PWR_REF_CTRL_CCK, ref_cck);
}
+static const struct rtw89_reg_def rtw8922a_txpwr_ref[][3] = {
+ {{ .addr = R_TXAGC_REF_DBM_P0, .mask = B_TXAGC_OFDM_REF_DBM_P0},
+ { .addr = R_TXAGC_REF_DBM_P0, .mask = B_TXAGC_CCK_REF_DBM_P0},
+ { .addr = R_TSSI_K_P0, .mask = B_TSSI_K_OFDM_P0}
+ },
+ {{ .addr = R_TXAGC_REF_DBM_RF1_P0, .mask = B_TXAGC_OFDM_REF_DBM_RF1_P0},
+ { .addr = R_TXAGC_REF_DBM_RF1_P0, .mask = B_TXAGC_CCK_REF_DBM_RF1_P0},
+ { .addr = R_TSSI_K_RF1_P0, .mask = B_TSSI_K_OFDM_RF1_P0}
+ },
+};
+
+static void rtw8922a_set_txpwr_diff(struct rtw89_dev *rtwdev,
+ const struct rtw89_chan *chan,
+ enum rtw89_phy_idx phy_idx)
+{
+ s16 pwr_ofst = rtw89_phy_ant_gain_pwr_offset(rtwdev, chan);
+ const struct rtw89_chip_info *chip = rtwdev->chip;
+ static const u32 path_ofst[] = {0x0, 0x100};
+ const struct rtw89_reg_def *txpwr_ref;
+ static const s16 tssi_k_base = 0x12;
+ s16 tssi_k_ofst = abs(pwr_ofst) + tssi_k_base;
+ s16 ofst_dec[RF_PATH_NUM_8922A];
+ s16 tssi_k[RF_PATH_NUM_8922A];
+ s16 pwr_ref_ofst;
+ s16 pwr_ref = 0;
+ u8 i;
+
+ if (rtwdev->hal.cv == CHIP_CAV)
+ pwr_ref = 16;
+
+ pwr_ref <<= chip->txpwr_factor_rf;
+ pwr_ref_ofst = pwr_ref - rtw89_phy_txpwr_bb_to_rf(rtwdev, abs(pwr_ofst));
+
+ ofst_dec[RF_PATH_A] = pwr_ofst > 0 ? pwr_ref : pwr_ref_ofst;
+ ofst_dec[RF_PATH_B] = pwr_ofst > 0 ? pwr_ref_ofst : pwr_ref;
+ tssi_k[RF_PATH_A] = pwr_ofst > 0 ? tssi_k_base : tssi_k_ofst;
+ tssi_k[RF_PATH_B] = pwr_ofst > 0 ? tssi_k_ofst : tssi_k_base;
+
+ for (i = 0; i < RF_PATH_NUM_8922A; i++) {
+ txpwr_ref = rtw8922a_txpwr_ref[phy_idx];
+
+ rtw89_phy_write32_mask(rtwdev, txpwr_ref[0].addr + path_ofst[i],
+ txpwr_ref[0].mask, ofst_dec[i]);
+ rtw89_phy_write32_mask(rtwdev, txpwr_ref[1].addr + path_ofst[i],
+ txpwr_ref[1].mask, ofst_dec[i]);
+ rtw89_phy_write32_mask(rtwdev, txpwr_ref[2].addr + path_ofst[i],
+ txpwr_ref[2].mask, tssi_k[i]);
+ }
+}
+
static void rtw8922a_bb_tx_triangular(struct rtw89_dev *rtwdev, bool en,
enum rtw89_phy_idx phy_idx)
{
@@ -2176,6 +2235,31 @@ static void rtw8922a_set_tx_shape(struct rtw89_dev *rtwdev,
rtw8922a_bb_tx_triangular(rtwdev, true, phy_idx);
}
+static void rtw8922a_set_txpwr_sar_diff(struct rtw89_dev *rtwdev,
+ const struct rtw89_chan *chan,
+ enum rtw89_phy_idx phy_idx)
+{
+ struct rtw89_sar_parm sar_parm = {
+ .center_freq = chan->freq,
+ .force_path = true,
+ };
+ s16 sar_rf;
+ s8 sar_mac;
+
+ if (phy_idx != RTW89_PHY_0)
+ return;
+
+ sar_parm.path = RF_PATH_A;
+ sar_mac = rtw89_query_sar(rtwdev, &sar_parm);
+ sar_rf = rtw89_phy_txpwr_mac_to_rf(rtwdev, sar_mac);
+ rtw89_phy_write32_mask(rtwdev, R_P0_TXPWRB_BE, B_TXPWRB_MAX_BE, sar_rf);
+
+ sar_parm.path = RF_PATH_B;
+ sar_mac = rtw89_query_sar(rtwdev, &sar_parm);
+ sar_rf = rtw89_phy_txpwr_mac_to_rf(rtwdev, sar_mac);
+ rtw89_phy_write32_mask(rtwdev, R_P1_TXPWRB_BE, B_TXPWRB_MAX_BE, sar_rf);
+}
+
static void rtw8922a_set_txpwr(struct rtw89_dev *rtwdev,
const struct rtw89_chan *chan,
enum rtw89_phy_idx phy_idx)
@@ -2185,6 +2269,9 @@ static void rtw8922a_set_txpwr(struct rtw89_dev *rtwdev,
rtw8922a_set_tx_shape(rtwdev, chan, phy_idx);
rtw89_phy_set_txpwr_limit(rtwdev, chan, phy_idx);
rtw89_phy_set_txpwr_limit_ru(rtwdev, chan, phy_idx);
+ rtw8922a_set_txpwr_diff(rtwdev, chan, phy_idx);
+ rtw8922a_set_txpwr_ref(rtwdev, phy_idx);
+ rtw8922a_set_txpwr_sar_diff(rtwdev, chan, phy_idx);
}
static void rtw8922a_set_txpwr_ctrl(struct rtw89_dev *rtwdev,
@@ -2695,6 +2782,7 @@ static const struct rtw89_chip_ops rtw8922a_chip_ops = {
.h2c_default_cmac_tbl = rtw89_fw_h2c_default_cmac_tbl_g7,
.h2c_assoc_cmac_tbl = rtw89_fw_h2c_assoc_cmac_tbl_g7,
.h2c_ampdu_cmac_tbl = rtw89_fw_h2c_ampdu_cmac_tbl_g7,
+ .h2c_txtime_cmac_tbl = rtw89_fw_h2c_txtime_cmac_tbl_g7,
.h2c_default_dmac_tbl = rtw89_fw_h2c_default_dmac_tbl_v2,
.h2c_update_beacon = rtw89_fw_h2c_update_beacon_be,
.h2c_ba_cam = rtw89_fw_h2c_ba_cam_v1,
@@ -2721,6 +2809,7 @@ const struct rtw89_chip_info rtw8922a_chip_info = {
.try_ce_fw = false,
.bbmcu_nr = 1,
.needed_fw_elms = RTW89_BE_GEN_DEF_NEEDED_FW_ELEMENTS,
+ .fw_blacklist = &rtw89_fw_blacklist_default,
.fifo_size = 589824,
.small_fifo_size = false,
.dle_scc_rsvd_size = 0,
@@ -2760,11 +2849,16 @@ const struct rtw89_chip_info rtw8922a_chip_info = {
BIT(NL80211_CHAN_WIDTH_80) |
BIT(NL80211_CHAN_WIDTH_160),
.support_unii4 = true,
- .support_ant_gain = false,
+ .support_ant_gain = true,
+ .support_tas = false,
+ .support_sar_by_ant = true,
.ul_tb_waveform_ctrl = false,
.ul_tb_pwr_diff = false,
+ .rx_freq_frome_ie = false,
.hw_sec_hdr = true,
.hw_mgmt_tx_encrypt = true,
+ .hw_tkip_crypto = true,
+ .hw_mlo_bmc_crypto = false,
.rf_path_num = 2,
.tx_nss = 2,
.rx_nss = 2,
diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922a_rfk.c b/drivers/net/wireless/realtek/rtw89/rtw8922a_rfk.c
index c4c93f836a2f..1659ea64ade1 100644
--- a/drivers/net/wireless/realtek/rtw89/rtw8922a_rfk.c
+++ b/drivers/net/wireless/realtek/rtw89/rtw8922a_rfk.c
@@ -77,11 +77,6 @@ void rtw8922a_ctl_band_ch_bw(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy,
RR_CFGCH_BAND0 | RR_CFGCH_CH);
rf_reg[path][i] |= u32_encode_bits(central_ch, RR_CFGCH_CH);
- if (band == RTW89_BAND_2G)
- rtw89_write_rf(rtwdev, path, RR_SMD, RR_VCO2, 0x0);
- else
- rtw89_write_rf(rtwdev, path, RR_SMD, RR_VCO2, 0x1);
-
switch (band) {
case RTW89_BAND_2G:
default:
diff --git a/drivers/net/wireless/realtek/rtw89/sar.c b/drivers/net/wireless/realtek/rtw89/sar.c
index 871f45a6508c..517b66022f18 100644
--- a/drivers/net/wireless/realtek/rtw89/sar.c
+++ b/drivers/net/wireless/realtek/rtw89/sar.c
@@ -7,10 +7,16 @@
#include "phy.h"
#include "reg.h"
#include "sar.h"
+#include "util.h"
#define RTW89_TAS_FACTOR 2 /* unit: 0.25 dBm */
+#define RTW89_TAS_SAR_GAP (1 << RTW89_TAS_FACTOR)
#define RTW89_TAS_DPR_GAP (1 << RTW89_TAS_FACTOR)
#define RTW89_TAS_DELTA (2 << RTW89_TAS_FACTOR)
+#define RTW89_TAS_TX_RATIO_THRESHOLD 70
+#define RTW89_TAS_DFLT_TX_RATIO 80
+#define RTW89_TAS_DPR_ON_OFFSET (RTW89_TAS_DELTA + RTW89_TAS_SAR_GAP)
+#define RTW89_TAS_DPR_OFF_OFFSET (4 << RTW89_TAS_FACTOR)
static enum rtw89_sar_subband rtw89_sar_get_subband(struct rtw89_dev *rtwdev,
u32 center_freq)
@@ -51,10 +57,12 @@ static enum rtw89_sar_subband rtw89_sar_get_subband(struct rtw89_dev *rtwdev,
}
static int rtw89_query_sar_config_common(struct rtw89_dev *rtwdev,
- u32 center_freq, s32 *cfg)
+ const struct rtw89_sar_parm *sar_parm,
+ s32 *cfg)
{
struct rtw89_sar_cfg_common *rtwsar = &rtwdev->sar.cfg_common;
enum rtw89_sar_subband subband_l, subband_h;
+ u32 center_freq = sar_parm->center_freq;
const struct rtw89_6ghz_span *span;
span = rtw89_get_6ghz_span(rtwdev, center_freq);
@@ -84,6 +92,93 @@ static int rtw89_query_sar_config_common(struct rtw89_dev *rtwdev,
return 0;
}
+static const struct rtw89_sar_entry_from_acpi *
+rtw89_sar_cfg_acpi_get_ent(const struct rtw89_sar_cfg_acpi *rtwsar,
+ enum rtw89_rf_path path,
+ enum rtw89_regulation_type regd)
+{
+ const struct rtw89_sar_indicator_from_acpi *ind = &rtwsar->indicator;
+ const struct rtw89_sar_table_from_acpi *tbl;
+ u8 sel;
+
+ sel = ind->tblsel[path];
+ tbl = &rtwsar->tables[sel];
+
+ return &tbl->entries[regd];
+}
+
+static
+s32 rtw89_sar_cfg_acpi_get_min(const struct rtw89_sar_entry_from_acpi *ent,
+ enum rtw89_rf_path path,
+ enum rtw89_acpi_sar_subband subband_low,
+ enum rtw89_acpi_sar_subband subband_high)
+{
+ return min(ent->v[subband_low][path], ent->v[subband_high][path]);
+}
+
+static int rtw89_query_sar_config_acpi(struct rtw89_dev *rtwdev,
+ const struct rtw89_sar_parm *sar_parm,
+ s32 *cfg)
+{
+ const struct rtw89_chip_info *chip = rtwdev->chip;
+ const struct rtw89_sar_cfg_acpi *rtwsar = &rtwdev->sar.cfg_acpi;
+ const struct rtw89_sar_entry_from_acpi *ent_a, *ent_b;
+ enum rtw89_acpi_sar_subband subband_l, subband_h;
+ u32 center_freq = sar_parm->center_freq;
+ const struct rtw89_6ghz_span *span;
+ enum rtw89_regulation_type regd;
+ enum rtw89_band band;
+ s32 cfg_a, cfg_b;
+
+ span = rtw89_get_6ghz_span(rtwdev, center_freq);
+
+ if (span && RTW89_ACPI_SAR_SPAN_VALID(span)) {
+ subband_l = span->acpi_sar_subband_low;
+ subband_h = span->acpi_sar_subband_high;
+ } else {
+ subband_l = rtw89_acpi_sar_get_subband(rtwdev, center_freq);
+ subband_h = subband_l;
+ }
+
+ band = rtw89_acpi_sar_subband_to_band(rtwdev, subband_l);
+ regd = rtw89_regd_get(rtwdev, band);
+
+ ent_a = rtw89_sar_cfg_acpi_get_ent(rtwsar, RF_PATH_A, regd);
+ ent_b = rtw89_sar_cfg_acpi_get_ent(rtwsar, RF_PATH_B, regd);
+
+ cfg_a = rtw89_sar_cfg_acpi_get_min(ent_a, RF_PATH_A, subband_l, subband_h);
+ cfg_b = rtw89_sar_cfg_acpi_get_min(ent_b, RF_PATH_B, subband_l, subband_h);
+
+ if (chip->support_sar_by_ant) {
+ /* With declaration of support_sar_by_ant, relax the general
+ * SAR querying to return the maximum between paths. However,
+ * expect chip has dealt with the corresponding SAR settings
+ * by path. (To get SAR for a given path, chip can then query
+ * with force_path.)
+ */
+ if (sar_parm->force_path) {
+ switch (sar_parm->path) {
+ default:
+ case RF_PATH_A:
+ *cfg = cfg_a;
+ break;
+ case RF_PATH_B:
+ *cfg = cfg_b;
+ break;
+ }
+ } else {
+ *cfg = max(cfg_a, cfg_b);
+ }
+ } else {
+ *cfg = min(cfg_a, cfg_b);
+ }
+
+ if (sar_parm->ntx == RTW89_2TX)
+ *cfg -= rtwsar->downgrade_2tx;
+
+ return 0;
+}
+
static const
struct rtw89_sar_handler rtw89_sar_handlers[RTW89_SAR_SOURCE_NR] = {
[RTW89_SAR_SOURCE_COMMON] = {
@@ -91,6 +186,11 @@ struct rtw89_sar_handler rtw89_sar_handlers[RTW89_SAR_SOURCE_NR] = {
.txpwr_factor_sar = 2,
.query_sar_config = rtw89_query_sar_config_common,
},
+ [RTW89_SAR_SOURCE_ACPI] = {
+ .descr_sar_source = "RTW89_SAR_SOURCE_ACPI",
+ .txpwr_factor_sar = TXPWR_FACTOR_OF_RTW89_ACPI_SAR,
+ .query_sar_config = rtw89_query_sar_config_acpi,
+ },
};
#define rtw89_sar_set_src(_dev, _src, _cfg_name, _cfg_data) \
@@ -99,7 +199,7 @@ struct rtw89_sar_handler rtw89_sar_handlers[RTW89_SAR_SOURCE_NR] = {
typeof(_dev) _d = (_dev); \
BUILD_BUG_ON(!rtw89_sar_handlers[_s].descr_sar_source); \
BUILD_BUG_ON(!rtw89_sar_handlers[_s].query_sar_config); \
- lockdep_assert_held(&_d->mutex); \
+ lockdep_assert_wiphy(_d->hw->wiphy); \
_d->sar._cfg_name = *(_cfg_data); \
_d->sar.src = _s; \
} while (0)
@@ -117,8 +217,8 @@ static s8 rtw89_txpwr_sar_to_mac(struct rtw89_dev *rtwdev, u8 fct, s32 cfg)
RTW89_SAR_TXPWR_MAC_MAX);
}
-static s8 rtw89_txpwr_tas_to_sar(const struct rtw89_sar_handler *sar_hdl,
- s8 cfg)
+static s32 rtw89_txpwr_tas_to_sar(const struct rtw89_sar_handler *sar_hdl,
+ s32 cfg)
{
const u8 fct = sar_hdl->txpwr_factor_sar;
@@ -128,8 +228,8 @@ static s8 rtw89_txpwr_tas_to_sar(const struct rtw89_sar_handler *sar_hdl,
return cfg >> (RTW89_TAS_FACTOR - fct);
}
-static s8 rtw89_txpwr_sar_to_tas(const struct rtw89_sar_handler *sar_hdl,
- s8 cfg)
+static s32 rtw89_txpwr_sar_to_tas(const struct rtw89_sar_handler *sar_hdl,
+ s32 cfg)
{
const u8 fct = sar_hdl->txpwr_factor_sar;
@@ -139,35 +239,67 @@ static s8 rtw89_txpwr_sar_to_tas(const struct rtw89_sar_handler *sar_hdl,
return cfg << (RTW89_TAS_FACTOR - fct);
}
-s8 rtw89_query_sar(struct rtw89_dev *rtwdev, u32 center_freq)
+static bool rtw89_tas_is_active(struct rtw89_dev *rtwdev)
+{
+ struct rtw89_tas_info *tas = &rtwdev->tas;
+ struct rtw89_vif *rtwvif;
+
+ if (!tas->enable)
+ return false;
+
+ rtw89_for_each_rtwvif(rtwdev, rtwvif) {
+ if (ieee80211_vif_is_mld(rtwvif_to_vif(rtwvif)))
+ return false;
+ }
+
+ return true;
+}
+
+static const char *rtw89_tas_state_str(enum rtw89_tas_state state)
+{
+ switch (state) {
+ case RTW89_TAS_STATE_DPR_OFF:
+ return "DPR OFF";
+ case RTW89_TAS_STATE_DPR_ON:
+ return "DPR ON";
+ case RTW89_TAS_STATE_STATIC_SAR:
+ return "STATIC SAR";
+ default:
+ return NULL;
+ }
+}
+
+s8 rtw89_query_sar(struct rtw89_dev *rtwdev, const struct rtw89_sar_parm *sar_parm)
{
const enum rtw89_sar_sources src = rtwdev->sar.src;
/* its members are protected by rtw89_sar_set_src() */
const struct rtw89_sar_handler *sar_hdl = &rtw89_sar_handlers[src];
struct rtw89_tas_info *tas = &rtwdev->tas;
- s8 delta;
+ s32 offset;
int ret;
s32 cfg;
u8 fct;
- lockdep_assert_held(&rtwdev->mutex);
+ lockdep_assert_wiphy(rtwdev->hw->wiphy);
if (src == RTW89_SAR_SOURCE_NONE)
return RTW89_SAR_TXPWR_MAC_MAX;
- ret = sar_hdl->query_sar_config(rtwdev, center_freq, &cfg);
+ ret = sar_hdl->query_sar_config(rtwdev, sar_parm, &cfg);
if (ret)
return RTW89_SAR_TXPWR_MAC_MAX;
- if (tas->enable) {
+ if (rtw89_tas_is_active(rtwdev)) {
switch (tas->state) {
case RTW89_TAS_STATE_DPR_OFF:
- return RTW89_SAR_TXPWR_MAC_MAX;
+ offset = rtw89_txpwr_tas_to_sar(sar_hdl, RTW89_TAS_DPR_OFF_OFFSET);
+ cfg += offset;
+ break;
case RTW89_TAS_STATE_DPR_ON:
- delta = rtw89_txpwr_tas_to_sar(sar_hdl, tas->delta);
- cfg -= delta;
+ offset = rtw89_txpwr_tas_to_sar(sar_hdl, RTW89_TAS_DPR_ON_OFFSET);
+ cfg -= offset;
break;
- case RTW89_TAS_STATE_DPR_FORBID:
+ case RTW89_TAS_STATE_STATIC_SAR:
default:
break;
}
@@ -177,73 +309,86 @@ s8 rtw89_query_sar(struct rtw89_dev *rtwdev, u32 center_freq)
return rtw89_txpwr_sar_to_mac(rtwdev, fct, cfg);
}
+EXPORT_SYMBOL(rtw89_query_sar);
-void rtw89_print_sar(struct seq_file *m, struct rtw89_dev *rtwdev, u32 center_freq)
+int rtw89_print_sar(struct rtw89_dev *rtwdev, char *buf, size_t bufsz,
+ const struct rtw89_sar_parm *sar_parm)
{
const enum rtw89_sar_sources src = rtwdev->sar.src;
/* its members are protected by rtw89_sar_set_src() */
const struct rtw89_sar_handler *sar_hdl = &rtw89_sar_handlers[src];
const u8 fct_mac = rtwdev->chip->txpwr_factor_mac;
+ char *p = buf, *end = buf + bufsz;
int ret;
s32 cfg;
u8 fct;
- lockdep_assert_held(&rtwdev->mutex);
+ lockdep_assert_wiphy(rtwdev->hw->wiphy);
if (src == RTW89_SAR_SOURCE_NONE) {
- seq_puts(m, "no SAR is applied\n");
- return;
+ p += scnprintf(p, end - p, "no SAR is applied\n");
+ goto out;
}
- seq_printf(m, "source: %d (%s)\n", src, sar_hdl->descr_sar_source);
+ p += scnprintf(p, end - p, "source: %d (%s)\n", src,
+ sar_hdl->descr_sar_source);
- ret = sar_hdl->query_sar_config(rtwdev, center_freq, &cfg);
+ ret = sar_hdl->query_sar_config(rtwdev, sar_parm, &cfg);
if (ret) {
- seq_printf(m, "config: return code: %d\n", ret);
- seq_printf(m, "assign: max setting: %d (unit: 1/%lu dBm)\n",
- RTW89_SAR_TXPWR_MAC_MAX, BIT(fct_mac));
- return;
+ p += scnprintf(p, end - p, "config: return code: %d\n", ret);
+ p += scnprintf(p, end - p,
+ "assign: max setting: %d (unit: 1/%lu dBm)\n",
+ RTW89_SAR_TXPWR_MAC_MAX, BIT(fct_mac));
+ goto out;
}
fct = sar_hdl->txpwr_factor_sar;
- seq_printf(m, "config: %d (unit: 1/%lu dBm)\n", cfg, BIT(fct));
+ p += scnprintf(p, end - p, "config: %d (unit: 1/%lu dBm)\n", cfg,
+ BIT(fct));
+
+ p += scnprintf(p, end - p, "support different configs by antenna: %s\n",
+ str_yes_no(rtwdev->chip->support_sar_by_ant));
+out:
+ return p - buf;
}
-void rtw89_print_tas(struct seq_file *m, struct rtw89_dev *rtwdev)
+int rtw89_print_tas(struct rtw89_dev *rtwdev, char *buf, size_t bufsz)
{
struct rtw89_tas_info *tas = &rtwdev->tas;
+ char *p = buf, *end = buf + bufsz;
- if (!tas->enable) {
- seq_puts(m, "no TAS is applied\n");
- return;
+ if (!rtw89_tas_is_active(rtwdev)) {
+ p += scnprintf(p, end - p, "no TAS is applied\n");
+ goto out;
}
- seq_printf(m, "DPR gap: %d\n", tas->dpr_gap);
- seq_printf(m, "TAS delta: %d\n", tas->delta);
+ p += scnprintf(p, end - p, "State: %s\n",
+ rtw89_tas_state_str(tas->state));
+ p += scnprintf(p, end - p, "Average time: %d\n",
+ tas->window_size * 2);
+ p += scnprintf(p, end - p, "SAR gap: %d dBm\n",
+ RTW89_TAS_SAR_GAP >> RTW89_TAS_FACTOR);
+ p += scnprintf(p, end - p, "DPR gap: %d dBm\n",
+ RTW89_TAS_DPR_GAP >> RTW89_TAS_FACTOR);
+ p += scnprintf(p, end - p, "DPR ON offset: %d dBm\n",
+ RTW89_TAS_DPR_ON_OFFSET >> RTW89_TAS_FACTOR);
+ p += scnprintf(p, end - p, "DPR OFF offset: %d dBm\n",
+ RTW89_TAS_DPR_OFF_OFFSET >> RTW89_TAS_FACTOR);
+
+out:
+ return p - buf;
}
static int rtw89_apply_sar_common(struct rtw89_dev *rtwdev,
const struct rtw89_sar_cfg_common *sar)
{
- enum rtw89_sar_sources src;
- int ret = 0;
-
- mutex_lock(&rtwdev->mutex);
-
- src = rtwdev->sar.src;
- if (src != RTW89_SAR_SOURCE_NONE && src != RTW89_SAR_SOURCE_COMMON) {
- rtw89_warn(rtwdev, "SAR source: %d is in use", src);
- ret = -EBUSY;
- goto exit;
- }
-
+ /* let common SAR have the highest priority; always apply it */
rtw89_sar_set_src(rtwdev, RTW89_SAR_SOURCE_COMMON, cfg_common, sar);
rtw89_core_set_chip_txpwr(rtwdev);
+ rtw89_tas_reset(rtwdev, false);
-exit:
- mutex_unlock(&rtwdev->mutex);
- return ret;
+ return 0;
}
static const struct cfg80211_sar_freq_ranges rtw89_common_sar_freq_ranges[] = {
@@ -279,6 +424,8 @@ int rtw89_ops_set_sar_specs(struct ieee80211_hw *hw,
s32 power;
u32 i, idx;
+ lockdep_assert_wiphy(rtwdev->hw->wiphy);
+
if (sar->type != NL80211_SAR_TYPE_POWER)
return -EINVAL;
@@ -304,64 +451,256 @@ int rtw89_ops_set_sar_specs(struct ieee80211_hw *hw,
return rtw89_apply_sar_common(rtwdev, &sar_common);
}
-static void rtw89_tas_state_update(struct rtw89_dev *rtwdev)
+static void rtw89_apply_sar_acpi(struct rtw89_dev *rtwdev,
+ const struct rtw89_sar_cfg_acpi *sar)
{
+ const struct rtw89_sar_table_from_acpi *tbl;
+ const struct rtw89_sar_entry_from_acpi *ent;
+ enum rtw89_sar_sources src;
+ unsigned int i, j, k;
+
+ src = rtwdev->sar.src;
+ if (src != RTW89_SAR_SOURCE_NONE) {
+ rtw89_warn(rtwdev, "SAR source: %d is in use", src);
+ return;
+ }
+
+ rtw89_debug(rtwdev, RTW89_DBG_SAR,
+ "SAR-ACPI downgrade 2TX: %u (unit: 1/%lu dBm)\n",
+ sar->downgrade_2tx, BIT(TXPWR_FACTOR_OF_RTW89_ACPI_SAR));
+
+ for (i = 0; i < sar->valid_num; i++) {
+ tbl = &sar->tables[i];
+
+ for (j = 0; j < RTW89_REGD_NUM; j++) {
+ ent = &tbl->entries[j];
+
+ rtw89_debug(rtwdev, RTW89_DBG_SAR,
+ "SAR-ACPI-[%u] REGD-%s (unit: 1/%lu dBm)\n",
+ i, rtw89_regd_get_string(j),
+ BIT(TXPWR_FACTOR_OF_RTW89_ACPI_SAR));
+
+ for (k = 0; k < NUM_OF_RTW89_ACPI_SAR_SUBBAND; k++)
+ rtw89_debug(rtwdev, RTW89_DBG_SAR,
+ "On subband %u, { %d, %d }\n", k,
+ ent->v[k][RF_PATH_A], ent->v[k][RF_PATH_B]);
+ }
+ }
+
+ rtw89_sar_set_src(rtwdev, RTW89_SAR_SOURCE_ACPI, cfg_acpi, sar);
+
+ /* SAR via ACPI is only configured in the early initial phase, so
+ * it does not seem necessary to reset txpwr related things here.
+ */
+}
+
+static void rtw89_set_sar_from_acpi(struct rtw89_dev *rtwdev)
+{
+ struct rtw89_sar_cfg_acpi *cfg;
+ int ret;
+
+ lockdep_assert_wiphy(rtwdev->hw->wiphy);
+
+ cfg = kzalloc(sizeof(*cfg), GFP_KERNEL);
+ if (!cfg)
+ return;
+
+ ret = rtw89_acpi_evaluate_sar(rtwdev, cfg);
+ if (ret) {
+ rtw89_debug(rtwdev, RTW89_DBG_SAR,
+ "evaluating ACPI SAR returns %d\n", ret);
+ goto out;
+ }
+
+ if (unlikely(!cfg->valid_num)) {
+ rtw89_debug(rtwdev, RTW89_DBG_SAR, "no valid SAR table from ACPI\n");
+ goto out;
+ }
+
+ rtw89_apply_sar_acpi(rtwdev, cfg);
+
+out:
+ kfree(cfg);
+}
+
+static bool rtw89_tas_query_sar_config(struct rtw89_dev *rtwdev, s32 *cfg)
+{
+ const struct rtw89_chan *chan = rtw89_chan_get(rtwdev, RTW89_CHANCTX_0);
const enum rtw89_sar_sources src = rtwdev->sar.src;
/* its members are protected by rtw89_sar_set_src() */
const struct rtw89_sar_handler *sar_hdl = &rtw89_sar_handlers[src];
- struct rtw89_tas_info *tas = &rtwdev->tas;
- s32 txpwr_avg = tas->total_txpwr / RTW89_TAS_MAX_WINDOW / PERCENT;
- s32 dpr_on_threshold, dpr_off_threshold, cfg;
- enum rtw89_tas_state state = tas->state;
- const struct rtw89_chan *chan;
+ struct rtw89_sar_parm sar_parm = {};
int ret;
- lockdep_assert_held(&rtwdev->mutex);
-
if (src == RTW89_SAR_SOURCE_NONE)
- return;
+ return false;
- chan = rtw89_chan_get(rtwdev, RTW89_CHANCTX_0);
- ret = sar_hdl->query_sar_config(rtwdev, chan->freq, &cfg);
+ sar_parm.center_freq = chan->freq;
+ ret = sar_hdl->query_sar_config(rtwdev, &sar_parm, cfg);
if (ret)
+ return false;
+
+ *cfg = rtw89_txpwr_sar_to_tas(sar_hdl, *cfg);
+
+ return true;
+}
+
+static bool __rtw89_tas_state_update(struct rtw89_dev *rtwdev,
+ enum rtw89_tas_state state)
+{
+ struct rtw89_tas_info *tas = &rtwdev->tas;
+
+ if (tas->state == state)
+ return false;
+
+ rtw89_debug(rtwdev, RTW89_DBG_SAR, "tas: switch state: %s -> %s\n",
+ rtw89_tas_state_str(tas->state), rtw89_tas_state_str(state));
+
+ tas->state = state;
+ return true;
+}
+
+static void rtw89_tas_state_update(struct rtw89_dev *rtwdev,
+ enum rtw89_tas_state state)
+{
+ if (!__rtw89_tas_state_update(rtwdev, state))
return;
- cfg = rtw89_txpwr_sar_to_tas(sar_hdl, cfg);
+ rtw89_core_set_chip_txpwr(rtwdev);
+}
+
+static u32 rtw89_tas_get_window_size(struct rtw89_dev *rtwdev)
+{
+ const struct rtw89_chan *chan = rtw89_chan_get(rtwdev, RTW89_CHANCTX_0);
+ u8 band = chan->band_type;
+ u8 regd = rtw89_regd_get(rtwdev, band);
- if (tas->delta >= cfg) {
+ switch (regd) {
+ default:
rtw89_debug(rtwdev, RTW89_DBG_SAR,
- "TAS delta exceed SAR limit\n");
- state = RTW89_TAS_STATE_DPR_FORBID;
- goto out;
+ "tas: regd: %u is unhandled\n", regd);
+ fallthrough;
+ case RTW89_IC:
+ case RTW89_KCC:
+ return 180;
+ case RTW89_FCC:
+ switch (band) {
+ case RTW89_BAND_2G:
+ return 50;
+ case RTW89_BAND_5G:
+ return 30;
+ case RTW89_BAND_6G:
+ default:
+ return 15;
+ }
+ break;
+ }
+}
+
+static void rtw89_tas_window_update(struct rtw89_dev *rtwdev)
+{
+ u32 window_size = rtw89_tas_get_window_size(rtwdev);
+ struct rtw89_tas_info *tas = &rtwdev->tas;
+ u64 total_txpwr = 0;
+ u8 head_idx;
+ u32 i, j;
+
+ WARN_ON_ONCE(tas->window_size > RTW89_TAS_TXPWR_WINDOW);
+
+ if (tas->window_size == window_size)
+ return;
+
+ rtw89_debug(rtwdev, RTW89_DBG_SAR, "tas: window update: %u -> %u\n",
+ tas->window_size, window_size);
+
+ head_idx = (tas->txpwr_tail_idx - window_size + 1 + RTW89_TAS_TXPWR_WINDOW) %
+ RTW89_TAS_TXPWR_WINDOW;
+ for (i = 0; i < window_size; i++) {
+ j = (head_idx + i) % RTW89_TAS_TXPWR_WINDOW;
+ total_txpwr += tas->txpwr_history[j];
+ }
+
+ tas->window_size = window_size;
+ tas->total_txpwr = total_txpwr;
+ tas->txpwr_head_idx = head_idx;
+}
+
+static void rtw89_tas_history_update(struct rtw89_dev *rtwdev)
+{
+ struct rtw89_bb_ctx *bb = rtw89_get_bb_ctx(rtwdev, RTW89_PHY_0);
+ struct rtw89_env_monitor_info *env = &bb->env_monitor;
+ struct rtw89_tas_info *tas = &rtwdev->tas;
+ u8 tx_ratio = env->ifs_clm_tx_ratio;
+ u64 instant_txpwr, txpwr;
+
+ /* txpwr in unit of linear(mW) multiply by percentage */
+ if (tx_ratio == 0) {
+ /* special case: idle tx power
+ * use -40 dBm * 100 tx ratio
+ */
+ instant_txpwr = rtw89_db_to_linear(-40);
+ txpwr = instant_txpwr * 100;
+ } else {
+ instant_txpwr = tas->instant_txpwr;
+ txpwr = instant_txpwr * tx_ratio;
}
- dpr_on_threshold = cfg;
- dpr_off_threshold = cfg - tas->dpr_gap;
+ tas->total_txpwr += txpwr - tas->txpwr_history[tas->txpwr_head_idx];
+ tas->total_tx_ratio += tx_ratio - tas->tx_ratio_history[tas->tx_ratio_idx];
+ tas->tx_ratio_history[tas->tx_ratio_idx] = tx_ratio;
+
+ tas->txpwr_head_idx = (tas->txpwr_head_idx + 1) % RTW89_TAS_TXPWR_WINDOW;
+ tas->txpwr_tail_idx = (tas->txpwr_tail_idx + 1) % RTW89_TAS_TXPWR_WINDOW;
+ tas->tx_ratio_idx = (tas->tx_ratio_idx + 1) % RTW89_TAS_TX_RATIO_WINDOW;
+ tas->txpwr_history[tas->txpwr_tail_idx] = txpwr;
+
+ rtw89_debug(rtwdev, RTW89_DBG_SAR,
+ "tas: instant_txpwr: %d, tx_ratio: %u, txpwr: %d\n",
+ rtw89_linear_to_db_quarter(instant_txpwr), tx_ratio,
+ rtw89_linear_to_db_quarter(div_u64(txpwr, PERCENT)));
+}
+
+static bool rtw89_tas_rolling_average(struct rtw89_dev *rtwdev)
+{
+ struct rtw89_tas_info *tas = &rtwdev->tas;
+ s32 dpr_on_threshold, dpr_off_threshold;
+ enum rtw89_tas_state state;
+ u16 tx_ratio_avg;
+ s32 txpwr_avg;
+ u64 linear;
+
+ linear = DIV_ROUND_DOWN_ULL(tas->total_txpwr, tas->window_size * PERCENT);
+ txpwr_avg = rtw89_linear_to_db_quarter(linear);
+ tx_ratio_avg = tas->total_tx_ratio / RTW89_TAS_TX_RATIO_WINDOW;
+ dpr_on_threshold = tas->dpr_on_threshold;
+ dpr_off_threshold = tas->dpr_off_threshold;
+
rtw89_debug(rtwdev, RTW89_DBG_SAR,
- "DPR_ON thold: %d, DPR_OFF thold: %d, txpwr_avg: %d\n",
- dpr_on_threshold, dpr_off_threshold, txpwr_avg);
+ "tas: DPR_ON: %d, DPR_OFF: %d, txpwr_avg: %d, tx_ratio_avg: %u\n",
+ dpr_on_threshold, dpr_off_threshold, txpwr_avg, tx_ratio_avg);
- if (txpwr_avg >= dpr_on_threshold)
+ if (tx_ratio_avg >= RTW89_TAS_TX_RATIO_THRESHOLD)
+ state = RTW89_TAS_STATE_STATIC_SAR;
+ else if (txpwr_avg >= dpr_on_threshold)
state = RTW89_TAS_STATE_DPR_ON;
else if (txpwr_avg < dpr_off_threshold)
state = RTW89_TAS_STATE_DPR_OFF;
+ else
+ return false;
-out:
- if (tas->state == state)
- return;
-
- rtw89_debug(rtwdev, RTW89_DBG_SAR,
- "TAS old state: %d, new state: %d\n", tas->state, state);
- tas->state = state;
- rtw89_core_set_chip_txpwr(rtwdev);
+ return __rtw89_tas_state_update(rtwdev, state);
}
-void rtw89_tas_init(struct rtw89_dev *rtwdev)
+static void rtw89_tas_init(struct rtw89_dev *rtwdev)
{
+ const struct rtw89_chip_info *chip = rtwdev->chip;
struct rtw89_tas_info *tas = &rtwdev->tas;
+ const struct rtw89_acpi_policy_tas *ptr;
struct rtw89_acpi_dsm_result res = {};
int ret;
- u8 val;
+
+ if (!chip->support_tas)
+ return;
ret = rtw89_acpi_evaluate_dsm(rtwdev, RTW89_ACPI_DSM_FUNC_TAS_EN, &res);
if (ret) {
@@ -370,8 +709,9 @@ void rtw89_tas_init(struct rtw89_dev *rtwdev)
return;
}
- val = res.u.value;
- switch (val) {
+ ptr = res.u.policy_tas;
+
+ switch (ptr->enable) {
case 0:
tas->enable = false;
break;
@@ -384,66 +724,170 @@ void rtw89_tas_init(struct rtw89_dev *rtwdev)
if (!tas->enable) {
rtw89_debug(rtwdev, RTW89_DBG_SAR, "TAS not enable\n");
- return;
+ goto out;
}
- tas->dpr_gap = RTW89_TAS_DPR_GAP;
- tas->delta = RTW89_TAS_DELTA;
+ tas->enabled_countries = ptr->enabled_countries;
+
+out:
+ kfree(ptr);
}
-void rtw89_tas_reset(struct rtw89_dev *rtwdev)
+void rtw89_tas_reset(struct rtw89_dev *rtwdev, bool force)
{
+ const struct rtw89_chan *chan = rtw89_chan_get(rtwdev, RTW89_CHANCTX_0);
struct rtw89_tas_info *tas = &rtwdev->tas;
+ u64 linear;
+ s32 cfg;
+ int i;
- if (!tas->enable)
+ if (!rtw89_tas_is_active(rtwdev))
+ return;
+
+ if (!rtw89_tas_query_sar_config(rtwdev, &cfg))
return;
- memset(&tas->txpwr_history, 0, sizeof(tas->txpwr_history));
- tas->total_txpwr = 0;
- tas->cur_idx = 0;
+ tas->dpr_on_threshold = cfg - RTW89_TAS_SAR_GAP;
+ tas->dpr_off_threshold = cfg - RTW89_TAS_SAR_GAP - RTW89_TAS_DPR_GAP;
+
+ /* avoid history reset after new SAR apply */
+ if (!force && tas->keep_history)
+ return;
+
+ linear = rtw89_db_quarter_to_linear(cfg) * RTW89_TAS_DFLT_TX_RATIO;
+ for (i = 0; i < RTW89_TAS_TXPWR_WINDOW; i++)
+ tas->txpwr_history[i] = linear;
+
+ for (i = 0; i < RTW89_TAS_TX_RATIO_WINDOW; i++)
+ tas->tx_ratio_history[i] = RTW89_TAS_DFLT_TX_RATIO;
+
+ tas->total_tx_ratio = RTW89_TAS_DFLT_TX_RATIO * RTW89_TAS_TX_RATIO_WINDOW;
+ tas->total_txpwr = linear * RTW89_TAS_TXPWR_WINDOW;
+ tas->window_size = RTW89_TAS_TXPWR_WINDOW;
+ tas->txpwr_head_idx = 0;
+ tas->txpwr_tail_idx = RTW89_TAS_TXPWR_WINDOW - 1;
+ tas->tx_ratio_idx = 0;
tas->state = RTW89_TAS_STATE_DPR_OFF;
+ tas->backup_state = RTW89_TAS_STATE_DPR_OFF;
+ tas->keep_history = true;
+
+ rtw89_debug(rtwdev, RTW89_DBG_SAR,
+ "tas: band: %u, freq: %u\n", chan->band_type, chan->freq);
}
-static const struct rtw89_reg_def txpwr_regs[] = {
- {R_PATH0_TXPWR, B_PATH0_TXPWR},
- {R_PATH1_TXPWR, B_PATH1_TXPWR},
-};
+static bool rtw89_tas_track(struct rtw89_dev *rtwdev)
+{
+ struct rtw89_tas_info *tas = &rtwdev->tas;
+ struct rtw89_hal *hal = &rtwdev->hal;
+ s32 cfg;
+
+ if (hal->disabled_dm_bitmap & BIT(RTW89_DM_TAS))
+ return false;
+
+ if (!rtw89_tas_is_active(rtwdev))
+ return false;
+
+ if (!rtw89_tas_query_sar_config(rtwdev, &cfg) || tas->block_regd)
+ return __rtw89_tas_state_update(rtwdev, RTW89_TAS_STATE_STATIC_SAR);
-void rtw89_tas_track(struct rtw89_dev *rtwdev)
+ if (tas->pause)
+ return false;
+
+ rtw89_tas_window_update(rtwdev);
+ rtw89_tas_history_update(rtwdev);
+
+ return rtw89_tas_rolling_average(rtwdev);
+}
+
+void rtw89_tas_scan(struct rtw89_dev *rtwdev, bool start)
{
- struct rtw89_env_monitor_info *env = &rtwdev->env_monitor;
- const enum rtw89_sar_sources src = rtwdev->sar.src;
- u8 max_nss_num = rtwdev->chip->rf_path_num;
struct rtw89_tas_info *tas = &rtwdev->tas;
- s16 tmp, txpwr, instant_txpwr = 0;
- u32 val;
- int i;
+ s32 cfg;
+
+ if (!rtw89_tas_is_active(rtwdev))
+ return;
+
+ if (!rtw89_tas_query_sar_config(rtwdev, &cfg))
+ return;
+
+ if (start) {
+ tas->backup_state = tas->state;
+ rtw89_tas_state_update(rtwdev, RTW89_TAS_STATE_STATIC_SAR);
+ } else {
+ rtw89_tas_state_update(rtwdev, tas->backup_state);
+ }
+}
- if (!tas->enable || src == RTW89_SAR_SOURCE_NONE)
+void rtw89_tas_chanctx_cb(struct rtw89_dev *rtwdev,
+ enum rtw89_chanctx_state state)
+{
+ struct rtw89_tas_info *tas = &rtwdev->tas;
+ s32 cfg;
+
+ if (!rtw89_tas_is_active(rtwdev))
return;
- if (env->ccx_watchdog_result != RTW89_PHY_ENV_MON_IFS_CLM)
+ if (!rtw89_tas_query_sar_config(rtwdev, &cfg))
return;
- for (i = 0; i < max_nss_num; i++) {
- val = rtw89_phy_read32_mask(rtwdev, txpwr_regs[i].addr,
- txpwr_regs[i].mask);
- tmp = sign_extend32(val, 8);
- if (tmp <= 0)
- return;
- instant_txpwr += tmp;
+ switch (state) {
+ case RTW89_CHANCTX_STATE_MCC_START:
+ tas->pause = true;
+ rtw89_tas_state_update(rtwdev, RTW89_TAS_STATE_STATIC_SAR);
+ break;
+ case RTW89_CHANCTX_STATE_MCC_STOP:
+ tas->pause = false;
+ break;
+ default:
+ break;
}
+}
+EXPORT_SYMBOL(rtw89_tas_chanctx_cb);
+
+void rtw89_sar_init(struct rtw89_dev *rtwdev)
+{
+ rtw89_set_sar_from_acpi(rtwdev);
+ rtw89_tas_init(rtwdev);
+}
+
+static bool rtw89_sar_track_acpi(struct rtw89_dev *rtwdev)
+{
+ struct rtw89_sar_cfg_acpi *cfg = &rtwdev->sar.cfg_acpi;
+ struct rtw89_sar_indicator_from_acpi *ind = &cfg->indicator;
+ const enum rtw89_sar_sources src = rtwdev->sar.src;
+ bool changed;
+ int ret;
+
+ lockdep_assert_wiphy(rtwdev->hw->wiphy);
+
+ if (src != RTW89_SAR_SOURCE_ACPI)
+ return false;
+
+ if (!ind->enable_sync)
+ return false;
+
+ ret = rtw89_acpi_evaluate_dynamic_sar_indicator(rtwdev, cfg, &changed);
+ if (likely(!ret))
+ return changed;
- instant_txpwr /= max_nss_num;
- /* in unit of 0.25 dBm multiply by percentage */
- txpwr = instant_txpwr * env->ifs_clm_tx_ratio;
- tas->total_txpwr += txpwr - tas->txpwr_history[tas->cur_idx];
- tas->txpwr_history[tas->cur_idx] = txpwr;
rtw89_debug(rtwdev, RTW89_DBG_SAR,
- "instant_txpwr: %d, tx_ratio: %d, txpwr: %d\n",
- instant_txpwr, env->ifs_clm_tx_ratio, txpwr);
+ "%s: failed to track indicator: %d; reset and disable\n",
+ __func__, ret);
- tas->cur_idx = (tas->cur_idx + 1) % RTW89_TAS_MAX_WINDOW;
+ memset(ind->tblsel, 0, sizeof(ind->tblsel));
+ ind->enable_sync = false;
+ return true;
+}
+
+void rtw89_sar_track(struct rtw89_dev *rtwdev)
+{
+ unsigned int changes = 0;
+
+ changes += rtw89_sar_track_acpi(rtwdev);
+ changes += rtw89_tas_track(rtwdev);
- rtw89_tas_state_update(rtwdev);
+ if (!changes)
+ return;
+
+ rtw89_core_set_chip_txpwr(rtwdev);
}
diff --git a/drivers/net/wireless/realtek/rtw89/sar.h b/drivers/net/wireless/realtek/rtw89/sar.h
index 4ae081d2d3b4..4b7f3d44f57b 100644
--- a/drivers/net/wireless/realtek/rtw89/sar.h
+++ b/drivers/net/wireless/realtek/rtw89/sar.h
@@ -10,21 +10,34 @@
#define RTW89_SAR_TXPWR_MAC_MAX 63
#define RTW89_SAR_TXPWR_MAC_MIN -64
+struct rtw89_sar_parm {
+ u32 center_freq;
+ enum rtw89_ntx ntx;
+
+ bool force_path;
+ enum rtw89_rf_path path;
+};
+
struct rtw89_sar_handler {
const char *descr_sar_source;
u8 txpwr_factor_sar;
- int (*query_sar_config)(struct rtw89_dev *rtwdev, u32 center_freq, s32 *cfg);
+ int (*query_sar_config)(struct rtw89_dev *rtwdev,
+ const struct rtw89_sar_parm *sar_parm, s32 *cfg);
};
extern const struct cfg80211_sar_capa rtw89_sar_capa;
-s8 rtw89_query_sar(struct rtw89_dev *rtwdev, u32 center_freq);
-void rtw89_print_sar(struct seq_file *m, struct rtw89_dev *rtwdev, u32 center_freq);
-void rtw89_print_tas(struct seq_file *m, struct rtw89_dev *rtwdev);
+s8 rtw89_query_sar(struct rtw89_dev *rtwdev, const struct rtw89_sar_parm *sar_parm);
+int rtw89_print_sar(struct rtw89_dev *rtwdev, char *buf, size_t bufsz,
+ const struct rtw89_sar_parm *sar_parm);
+int rtw89_print_tas(struct rtw89_dev *rtwdev, char *buf, size_t bufsz);
int rtw89_ops_set_sar_specs(struct ieee80211_hw *hw,
const struct cfg80211_sar_specs *sar);
-void rtw89_tas_init(struct rtw89_dev *rtwdev);
-void rtw89_tas_reset(struct rtw89_dev *rtwdev);
-void rtw89_tas_track(struct rtw89_dev *rtwdev);
+void rtw89_tas_reset(struct rtw89_dev *rtwdev, bool force);
+void rtw89_tas_scan(struct rtw89_dev *rtwdev, bool start);
+void rtw89_tas_chanctx_cb(struct rtw89_dev *rtwdev,
+ enum rtw89_chanctx_state state);
+void rtw89_sar_init(struct rtw89_dev *rtwdev);
+void rtw89_sar_track(struct rtw89_dev *rtwdev);
#endif
diff --git a/drivers/net/wireless/realtek/rtw89/ser.c b/drivers/net/wireless/realtek/rtw89/ser.c
index 26a944d3b672..811c91481441 100644
--- a/drivers/net/wireless/realtek/rtw89/ser.c
+++ b/drivers/net/wireless/realtek/rtw89/ser.c
@@ -156,9 +156,9 @@ static void ser_state_run(struct rtw89_ser *ser, u8 evt)
rtw89_debug(rtwdev, RTW89_DBG_SER, "ser: %s receive %s\n",
ser_st_name(ser), ser_ev_name(ser, evt));
- mutex_lock(&rtwdev->mutex);
+ wiphy_lock(rtwdev->hw->wiphy);
rtw89_leave_lps(rtwdev);
- mutex_unlock(&rtwdev->mutex);
+ wiphy_unlock(rtwdev->hw->wiphy);
ser->st_tbl[ser->state].st_func(ser, evt);
}
@@ -309,6 +309,9 @@ static void ser_reset_vif(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif)
rtw89_core_release_bit_map(rtwdev->hw_port, rtwvif_link->port);
rtwvif_link->net_type = RTW89_NET_TYPE_NO_LINK;
rtwvif_link->trigger = false;
+ rtwvif_link->rand_tsf_done = false;
+
+ rtw89_p2p_noa_once_deinit(rtwvif_link);
}
}
@@ -483,10 +486,13 @@ static void ser_l1_reset_pre_st_hdl(struct rtw89_ser *ser, u8 evt)
static void ser_reset_trx_st_hdl(struct rtw89_ser *ser, u8 evt)
{
struct rtw89_dev *rtwdev = container_of(ser, struct rtw89_dev, ser);
+ struct wiphy *wiphy = rtwdev->hw->wiphy;
switch (evt) {
case SER_EV_STATE_IN:
- cancel_delayed_work_sync(&rtwdev->track_work);
+ wiphy_lock(wiphy);
+ wiphy_delayed_work_cancel(wiphy, &rtwdev->track_work);
+ wiphy_unlock(wiphy);
drv_stop_tx(ser);
if (hal_stop_dma(ser)) {
@@ -517,8 +523,8 @@ static void ser_reset_trx_st_hdl(struct rtw89_ser *ser, u8 evt)
hal_enable_dma(ser);
drv_resume_rx(ser);
drv_resume_tx(ser);
- ieee80211_queue_delayed_work(rtwdev->hw, &rtwdev->track_work,
- RTW89_TRACK_WORK_PERIOD);
+ wiphy_delayed_work_queue(wiphy, &rtwdev->track_work,
+ RTW89_TRACK_WORK_PERIOD);
break;
default:
@@ -708,9 +714,9 @@ static void ser_l2_reset_st_hdl(struct rtw89_ser *ser, u8 evt)
switch (evt) {
case SER_EV_STATE_IN:
- mutex_lock(&rtwdev->mutex);
+ wiphy_lock(rtwdev->hw->wiphy);
ser_l2_reset_st_pre_hdl(ser);
- mutex_unlock(&rtwdev->mutex);
+ wiphy_unlock(rtwdev->hw->wiphy);
ieee80211_restart_hw(rtwdev->hw);
ser_set_alarm(ser, SER_RECFG_TIMEOUT, SER_EV_L2_RECFG_TIMEOUT);
diff --git a/drivers/net/wireless/realtek/rtw89/txrx.h b/drivers/net/wireless/realtek/rtw89/txrx.h
index 70fe7cebc9d5..94f27a9ee9f7 100644
--- a/drivers/net/wireless/realtek/rtw89/txrx.h
+++ b/drivers/net/wireless/realtek/rtw89/txrx.h
@@ -712,6 +712,25 @@ static inline u8 rtw89_core_get_qsel(struct rtw89_dev *rtwdev, u8 tid)
}
}
+static inline u8
+rtw89_core_get_qsel_mgmt(struct rtw89_dev *rtwdev, struct rtw89_core_tx_request *tx_req)
+{
+ struct rtw89_tx_desc_info *desc_info = &tx_req->desc_info;
+ struct rtw89_vif_link *rtwvif_link = tx_req->rtwvif_link;
+
+ if (desc_info->hiq) {
+ if (rtwvif_link->mac_idx == RTW89_MAC_1)
+ return RTW89_TX_QSEL_B1_HI;
+ else
+ return RTW89_TX_QSEL_B0_HI;
+ }
+
+ if (rtwvif_link->mac_idx == RTW89_MAC_1)
+ return RTW89_TX_QSEL_B1_MGMT;
+ else
+ return RTW89_TX_QSEL_B0_MGMT;
+}
+
static inline u8 rtw89_core_get_ch_dma(struct rtw89_dev *rtwdev, u8 qsel)
{
switch (qsel) {
@@ -719,12 +738,24 @@ static inline u8 rtw89_core_get_ch_dma(struct rtw89_dev *rtwdev, u8 qsel)
rtw89_warn(rtwdev, "Cannot map qsel to dma: %d\n", qsel);
fallthrough;
case RTW89_TX_QSEL_BE_0:
+ case RTW89_TX_QSEL_BE_1:
+ case RTW89_TX_QSEL_BE_2:
+ case RTW89_TX_QSEL_BE_3:
return RTW89_TXCH_ACH0;
case RTW89_TX_QSEL_BK_0:
+ case RTW89_TX_QSEL_BK_1:
+ case RTW89_TX_QSEL_BK_2:
+ case RTW89_TX_QSEL_BK_3:
return RTW89_TXCH_ACH1;
case RTW89_TX_QSEL_VI_0:
+ case RTW89_TX_QSEL_VI_1:
+ case RTW89_TX_QSEL_VI_2:
+ case RTW89_TX_QSEL_VI_3:
return RTW89_TXCH_ACH2;
case RTW89_TX_QSEL_VO_0:
+ case RTW89_TX_QSEL_VO_1:
+ case RTW89_TX_QSEL_VO_2:
+ case RTW89_TX_QSEL_VO_3:
return RTW89_TXCH_ACH3;
case RTW89_TX_QSEL_B0_MGMT:
return RTW89_TXCH_CH8;
diff --git a/drivers/net/wireless/realtek/rtw89/util.c b/drivers/net/wireless/realtek/rtw89/util.c
index e71956ce9853..073714db26f2 100644
--- a/drivers/net/wireless/realtek/rtw89/util.c
+++ b/drivers/net/wireless/realtek/rtw89/util.c
@@ -4,103 +4,159 @@
#include "util.h"
-#define FRAC_ROWS 3
-#define FRAC_ROW_MAX (FRAC_ROWS - 1)
-#define NORM_ROW_MIN FRAC_ROWS
-
-static const u32 db_invert_table[12][8] = {
- /* rows 0~2 in unit of U(32,3) */
- {10, 13, 16, 20, 25, 32, 40, 50},
- {64, 80, 101, 128, 160, 201, 256, 318},
- {401, 505, 635, 800, 1007, 1268, 1596, 2010},
- /* rows 3~11 in unit of U(32,0) */
- {316, 398, 501, 631, 794, 1000, 1259, 1585},
- {1995, 2512, 3162, 3981, 5012, 6310, 7943, 10000},
- {12589, 15849, 19953, 25119, 31623, 39811, 50119, 63098},
- {79433, 100000, 125893, 158489, 199526, 251189, 316228, 398107},
- {501187, 630957, 794328, 1000000, 1258925, 1584893, 1995262, 2511886},
- {3162278, 3981072, 5011872, 6309573, 7943282, 1000000, 12589254,
- 15848932},
- {19952623, 25118864, 31622777, 39810717, 50118723, 63095734, 79432823,
- 100000000},
- {125892541, 158489319, 199526232, 251188643, 316227766, 398107171,
- 501187234, 630957345},
- {794328235, 1000000000, 1258925412, 1584893192, 1995262315, 2511886432U,
- 3162277660U, 3981071706U},
+#define RTW89_DBM_QUARTER_FACTOR 2
+#define RTW89_MIN_DBM (-41.25 * (1 << RTW89_DBM_QUARTER_FACTOR))
+#define RTW89_MAX_DBM (96 * (1 << RTW89_DBM_QUARTER_FACTOR))
+#define RTW89_DB_INVERT_TABLE_OFFSET (-RTW89_MIN_DBM)
+
+static const u64 db_invert_table[] = {
+ /* in unit of 0.000001 */
+ 75, 79, 84, 89, 94, 100, 106, 112, 119, 126, 133, 141, 150, 158, 168, 178, 188,
+ 200, 211, 224, 237, 251, 266, 282, 299, 316, 335, 355, 376, 398, 422, 447, 473,
+ 501, 531, 562, 596, 631, 668, 708, 750, 794, 841, 891, 944, 1000, 1059, 1122, 1189,
+ 1259, 1334, 1413, 1496, 1585, 1679, 1778, 1884, 1995, 2113, 2239, 2371, 2512, 2661,
+ 2818, 2985, 3162, 3350, 3548, 3758, 3981, 4217, 4467, 4732, 5012, 5309, 5623, 5957,
+ 6310, 6683, 7079, 7499, 7943, 8414, 8913, 9441, 10000, 10593, 11220, 11885, 12589,
+ 13335, 14125, 14962, 15849, 16788, 17783, 18836, 19953, 21135, 22387, 23714, 25119,
+ 26607, 28184, 29854, 31623, 33497, 35481, 37584, 39811, 42170, 44668, 47315, 50119,
+ 53088, 56234, 59566, 63096, 66834, 70795, 74989, 79433, 84140, 89125, 94406, 100000,
+ 105925, 112202, 118850, 125893, 133352, 141254, 149624, 158489, 167880, 177828,
+ 188365, 199526, 211349, 223872, 237137, 251189, 266073, 281838, 298538, 316228,
+ 334965, 354813, 375837, 398107, 421697, 446684, 473151, 501187, 530884, 562341,
+ 595662, 630957, 668344, 707946, 749894, 794328, 841395, 891251, 944061, 1000000,
+ 1059254, 1122018, 1188502, 1258925, 1333521, 1412538, 1496236, 1584893, 1678804,
+ 1778279, 1883649, 1995262, 2113489, 2238721, 2371374, 2511886, 2660725, 2818383,
+ 2985383, 3162278, 3349654, 3548134, 3758374, 3981072, 4216965, 4466836, 4731513,
+ 5011872, 5308844, 5623413, 5956621, 6309573, 6683439, 7079458, 7498942, 7943282,
+ 8413951, 8912509, 9440609, 10000000, 10592537, 11220185, 11885022, 12589254,
+ 13335214, 14125375, 14962357, 15848932, 16788040, 17782794, 18836491, 19952623,
+ 21134890, 22387211, 23713737, 25118864, 26607251, 28183829, 29853826, 31622777,
+ 33496544, 35481339, 37583740, 39810717, 42169650, 44668359, 47315126, 50118723,
+ 53088444, 56234133, 59566214, 63095734, 66834392, 70794578, 74989421, 79432823,
+ 84139514, 89125094, 94406088, 100000000, 105925373, 112201845, 118850223, 125892541,
+ 133352143, 141253754, 149623566, 158489319, 167880402, 177827941, 188364909, 199526231,
+ 211348904, 223872114, 237137371, 251188643, 266072506, 281838293, 298538262, 316227766,
+ 334965439, 354813389, 375837404, 398107171, 421696503, 446683592, 473151259, 501187234,
+ 530884444, 562341325, 595662144, 630957344, 668343918, 707945784, 749894209, 794328235,
+ 841395142, 891250938, 944060876, 1000000000, 1059253725, 1122018454, 1188502227,
+ 1258925412, 1333521432, 1412537545, 1496235656, 1584893192, 1678804018, 1778279410,
+ 1883649089, 1995262315, 2113489040, 2238721139, 2371373706, 2511886432, 2660725060,
+ 2818382931, 2985382619, 3162277660, 3349654392, 3548133892, 3758374043, 3981071706,
+ 4216965034, 4466835922ULL, 4731512590ULL, 5011872336ULL, 5308844442ULL, 5623413252ULL,
+ 5956621435ULL, 6309573445ULL, 6683439176ULL, 7079457844ULL, 7498942093ULL,
+ 7943282347ULL, 8413951416ULL, 8912509381ULL, 9440608763ULL, 10000000000ULL,
+ 10592537252ULL, 11220184543ULL, 11885022274ULL, 12589254118ULL, 13335214322ULL,
+ 14125375446ULL, 14962356561ULL, 15848931925ULL, 16788040181ULL, 17782794100ULL,
+ 18836490895ULL, 19952623150ULL, 21134890398ULL, 22387211386ULL, 23713737057ULL,
+ 25118864315ULL, 26607250598ULL, 28183829313ULL, 29853826189ULL, 31622776602ULL,
+ 33496543916ULL, 35481338923ULL, 37583740429ULL, 39810717055ULL, 42169650343ULL,
+ 44668359215ULL, 47315125896ULL, 50118723363ULL, 53088444423ULL, 56234132519ULL,
+ 59566214353ULL, 63095734448ULL, 66834391757ULL, 70794578438ULL, 74989420933ULL,
+ 79432823472ULL, 84139514165ULL, 89125093813ULL, 94406087629ULL, 100000000000ULL,
+ 105925372518ULL, 112201845430ULL, 118850222744ULL, 125892541179ULL, 133352143216ULL,
+ 141253754462ULL, 149623565609ULL, 158489319246ULL, 167880401812ULL, 177827941004ULL,
+ 188364908949ULL, 199526231497ULL, 211348903984ULL, 223872113857ULL, 237137370566ULL,
+ 251188643151ULL, 266072505980ULL, 281838293126ULL, 298538261892ULL, 316227766017ULL,
+ 334965439158ULL, 354813389234ULL, 375837404288ULL, 398107170553ULL, 421696503429ULL,
+ 446683592151ULL, 473151258961ULL, 501187233627ULL, 530884444231ULL, 562341325190ULL,
+ 595662143529ULL, 630957344480ULL, 668343917569ULL, 707945784384ULL, 749894209332ULL,
+ 794328234724ULL, 841395141645ULL, 891250938134ULL, 944060876286ULL, 1000000000000ULL,
+ 1059253725177ULL, 1122018454302ULL, 1188502227437ULL, 1258925411794ULL,
+ 1333521432163ULL, 1412537544623ULL, 1496235656094ULL, 1584893192461ULL,
+ 1678804018123ULL, 1778279410039ULL, 1883649089490ULL, 1995262314969ULL,
+ 2113489039837ULL, 2238721138568ULL, 2371373705662ULL, 2511886431510ULL,
+ 2660725059799ULL, 2818382931264ULL, 2985382618918ULL, 3162277660168ULL,
+ 3349654391578ULL, 3548133892336ULL, 3758374042884ULL, 3981071705535ULL,
+ 4216965034286ULL, 4466835921510ULL, 4731512589615ULL, 5011872336273ULL,
+ 5308844442310ULL, 5623413251904ULL, 5956621435290ULL, 6309573444802ULL,
+ 6683439175686ULL, 7079457843841ULL, 7498942093325ULL, 7943282347243ULL,
+ 8413951416452ULL, 8912509381337ULL, 9440608762859ULL, 10000000000000ULL,
+ 10592537251773ULL, 11220184543020ULL, 11885022274370ULL, 12589254117942ULL,
+ 13335214321633ULL, 14125375446228ULL, 14962356560944ULL, 15848931924611ULL,
+ 16788040181226ULL, 17782794100389ULL, 18836490894898ULL, 19952623149689ULL,
+ 21134890398367ULL, 22387211385683ULL, 23713737056617ULL, 25118864315096ULL,
+ 26607250597988ULL, 28183829312645ULL, 29853826189180ULL, 31622776601684ULL,
+ 33496543915783ULL, 35481338923358ULL, 37583740428845ULL, 39810717055350ULL,
+ 42169650342858ULL, 44668359215096ULL, 47315125896148ULL, 50118723362727ULL,
+ 53088444423099ULL, 56234132519035ULL, 59566214352901ULL, 63095734448019ULL,
+ 66834391756862ULL, 70794578438414ULL, 74989420933246ULL, 79432823472428ULL,
+ 84139514164520ULL, 89125093813375ULL, 94406087628593ULL, 100000000000000ULL,
+ 105925372517729ULL, 112201845430197ULL, 118850222743702ULL, 125892541179417ULL,
+ 133352143216332ULL, 141253754462276ULL, 149623565609444ULL, 158489319246111ULL,
+ 167880401812256ULL, 177827941003893ULL, 188364908948981ULL, 199526231496888ULL,
+ 211348903983664ULL, 223872113856834ULL, 237137370566166ULL, 251188643150958ULL,
+ 266072505979882ULL, 281838293126446ULL, 298538261891796ULL, 316227766016838ULL,
+ 334965439157829ULL, 354813389233577ULL, 375837404288444ULL, 398107170553497ULL,
+ 421696503428583ULL, 446683592150964ULL, 473151258961482ULL, 501187233627272ULL,
+ 530884444230989ULL, 562341325190350ULL, 595662143529011ULL, 630957344480196ULL,
+ 668343917568615ULL, 707945784384138ULL, 749894209332456ULL, 794328234724284ULL,
+ 841395141645198ULL, 891250938133745ULL, 944060876285923ULL, 1000000000000000ULL,
+ 1059253725177290ULL, 1122018454301970ULL, 1188502227437020ULL, 1258925411794170ULL,
+ 1333521432163330ULL, 1412537544622760ULL, 1496235656094440ULL, 1584893192461110ULL,
+ 1678804018122560ULL, 1778279410038920ULL, 1883649089489810ULL, 1995262314968890ULL,
+ 2113489039836650ULL, 2238721138568340ULL, 2371373705661660ULL, 2511886431509590ULL,
+ 2660725059798820ULL, 2818382931264460ULL, 2985382618917960ULL, 3162277660168380ULL,
+ 3349654391578280ULL, 3548133892335770ULL, 3758374042884440ULL, 3981071705534970ULL
};
-u32 rtw89_linear_2_db(u64 val)
+s32 rtw89_linear_to_db_quarter(u64 val)
{
- u8 i, j;
- u32 dB;
-
- for (i = 0; i < 12; i++) {
- for (j = 0; j < 8; j++) {
- if (i <= FRAC_ROW_MAX &&
- (val << RTW89_LINEAR_FRAC_BITS) <= db_invert_table[i][j])
- goto cnt;
- else if (i > FRAC_ROW_MAX && val <= db_invert_table[i][j])
- goto cnt;
- }
- }
+ int r = ARRAY_SIZE(db_invert_table) - 1;
+ int l = 0;
+ int m;
- return 96; /* maximum 96 dB */
+ while (l <= r) {
+ m = l + (r - l) / 2;
-cnt:
- /* special cases */
- if (j == 0 && i == 0)
- goto end;
+ if (db_invert_table[m] == val)
+ return m - (s32)RTW89_DB_INVERT_TABLE_OFFSET;
- if (i == NORM_ROW_MIN && j == 0) {
- if (db_invert_table[NORM_ROW_MIN][0] - val >
- val - (db_invert_table[FRAC_ROW_MAX][7] >> RTW89_LINEAR_FRAC_BITS)) {
- i = FRAC_ROW_MAX;
- j = 7;
- }
- goto end;
+ if (db_invert_table[m] > val)
+ r = m - 1;
+ else
+ l = m + 1;
}
- if (i <= FRAC_ROW_MAX)
- val <<= RTW89_LINEAR_FRAC_BITS;
-
- /* compare difference to get precise dB */
- if (j == 0) {
- if (db_invert_table[i][j] - val >
- val - db_invert_table[i - 1][7]) {
- i--;
- j = 7;
- }
- } else {
- if (db_invert_table[i][j] - val >
- val - db_invert_table[i][j - 1]) {
- j--;
- }
- }
-end:
- dB = (i << 3) + j + 1;
+ if (l >= ARRAY_SIZE(db_invert_table))
+ return RTW89_MAX_DBM;
+ else if (r < 0)
+ return RTW89_MIN_DBM;
+ else if (val - db_invert_table[r] <= db_invert_table[l] - val)
+ return r - (s32)RTW89_DB_INVERT_TABLE_OFFSET;
+ else
+ return l - (s32)RTW89_DB_INVERT_TABLE_OFFSET;
+}
+EXPORT_SYMBOL(rtw89_linear_to_db_quarter);
- return dB;
+s32 rtw89_linear_to_db(u64 val)
+{
+ return rtw89_linear_to_db_quarter(val) >> RTW89_DBM_QUARTER_FACTOR;
}
-EXPORT_SYMBOL(rtw89_linear_2_db);
+EXPORT_SYMBOL(rtw89_linear_to_db);
-u64 rtw89_db_2_linear(u32 db)
+u64 rtw89_db_quarter_to_linear(s32 db)
{
- u64 linear;
- u8 i, j;
+ /* supported range -41.25 to 96 dBm, in unit of 0.25 dBm */
+ db = clamp_t(s32, db, RTW89_MIN_DBM, RTW89_MAX_DBM);
+ db += (s32)RTW89_DB_INVERT_TABLE_OFFSET;
- if (db > 96)
- db = 96;
- else if (db < 1)
- return 1;
+ return db_invert_table[db];
+}
+EXPORT_SYMBOL(rtw89_db_quarter_to_linear);
- i = (db - 1) >> 3;
- j = (db - 1) & 0x7;
+u64 rtw89_db_to_linear(s32 db)
+{
+ return rtw89_db_quarter_to_linear(db << RTW89_DBM_QUARTER_FACTOR);
+}
+EXPORT_SYMBOL(rtw89_db_to_linear);
- linear = db_invert_table[i][j];
+void rtw89_might_trailing_ellipsis(char *buf, size_t size, ssize_t used)
+{
+ static const char ellipsis[] = "...";
- if (i >= NORM_ROW_MIN)
- linear = linear << RTW89_LINEAR_FRAC_BITS;
+ /* length of null terminiator isn't included in 'used' */
+ if (used + 1 < size || size < sizeof(ellipsis))
+ return;
- return linear;
+ memcpy(buf + size - sizeof(ellipsis), ellipsis, sizeof(ellipsis));
}
-EXPORT_SYMBOL(rtw89_db_2_linear);
diff --git a/drivers/net/wireless/realtek/rtw89/util.h b/drivers/net/wireless/realtek/rtw89/util.h
index e669544cafd3..bd08495301e4 100644
--- a/drivers/net/wireless/realtek/rtw89/util.h
+++ b/drivers/net/wireless/realtek/rtw89/util.h
@@ -6,13 +6,11 @@
#include "core.h"
-#define RTW89_LINEAR_FRAC_BITS 3
-
#define rtw89_iterate_vifs_bh(rtwdev, iterator, data) \
ieee80211_iterate_active_interfaces_atomic((rtwdev)->hw, \
IEEE80211_IFACE_ITER_NORMAL, iterator, data)
-/* call this function with rtwdev->mutex is held */
+/* call this function with wiphy mutex is held */
#define rtw89_for_each_rtwvif(rtwdev, rtwvif) \
list_for_each_entry(rtwvif, &(rtwdev)->rtwvifs_list, list)
@@ -25,7 +23,7 @@ static inline bool rtw89_rtwvif_in_list(struct rtw89_dev *rtwdev,
{
struct rtw89_vif *rtwvif;
- lockdep_assert_held(&rtwdev->mutex);
+ lockdep_assert_wiphy(rtwdev->hw->wiphy);
rtw89_for_each_rtwvif(rtwdev, rtwvif)
if (rtwvif == new)
@@ -75,7 +73,10 @@ static inline void ether_addr_copy_mask(u8 *dst, const u8 *src, u8 mask)
}
}
-u32 rtw89_linear_2_db(u64 linear);
-u64 rtw89_db_2_linear(u32 db);
+s32 rtw89_linear_to_db_quarter(u64 val);
+s32 rtw89_linear_to_db(u64 val);
+u64 rtw89_db_quarter_to_linear(s32 db);
+u64 rtw89_db_to_linear(s32 db);
+void rtw89_might_trailing_ellipsis(char *buf, size_t size, ssize_t used);
#endif
diff --git a/drivers/net/wireless/realtek/rtw89/wow.c b/drivers/net/wireless/realtek/rtw89/wow.c
index 01754d031bb4..34a0ab49bd7a 100644
--- a/drivers/net/wireless/realtek/rtw89/wow.c
+++ b/drivers/net/wireless/realtek/rtw89/wow.c
@@ -604,6 +604,8 @@ static struct ieee80211_key_conf *rtw89_wow_gtk_rekey(struct rtw89_dev *rtwdev,
struct ieee80211_key_conf *key;
u8 sz;
+ lockdep_assert_wiphy(rtwdev->hw->wiphy);
+
cipher_info = rtw89_cipher_alg_recognize(cipher);
sz = struct_size(rekey_conf, key, cipher_info->len);
rekey_conf = kmalloc(sz, GFP_KERNEL);
@@ -616,15 +618,10 @@ static struct ieee80211_key_conf *rtw89_wow_gtk_rekey(struct rtw89_dev *rtwdev,
memcpy(rekey_conf->key, gtk,
flex_array_size(rekey_conf, key, cipher_info->len));
- /* ieee80211_gtk_rekey_add() will call set_key(), therefore we
- * need to unlock mutex
- */
- mutex_unlock(&rtwdev->mutex);
if (ieee80211_vif_is_mld(wow_vif))
key = ieee80211_gtk_rekey_add(wow_vif, rekey_conf, rtwvif_link->link_id);
else
key = ieee80211_gtk_rekey_add(wow_vif, rekey_conf, -1);
- mutex_lock(&rtwdev->mutex);
kfree(rekey_conf);
if (IS_ERR(key)) {
@@ -1089,8 +1086,7 @@ static int rtw89_wow_set_wakeups(struct rtw89_dev *rtwdev,
rtw89_wow_init_pno(rtwdev, wowlan->nd_config);
rtw89_for_each_rtwvif(rtwdev, rtwvif) {
- /* use the link on HW-0 to do wow flow */
- rtwvif_link = rtw89_vif_get_link_inst(rtwvif, 0);
+ rtwvif_link = rtw89_get_designated_link(rtwvif);
if (!rtwvif_link)
continue;
diff --git a/drivers/net/wireless/rsi/rsi_91x_hal.c b/drivers/net/wireless/rsi/rsi_91x_hal.c
index 2cebe562a1f4..53827657abb2 100644
--- a/drivers/net/wireless/rsi/rsi_91x_hal.c
+++ b/drivers/net/wireless/rsi/rsi_91x_hal.c
@@ -493,7 +493,7 @@ static void bl_cmd_timeout(struct timer_list *t)
struct rsi_hw *adapter = from_timer(adapter, t, bl_cmd_timer);
adapter->blcmd_timer_expired = true;
- del_timer(&adapter->bl_cmd_timer);
+ timer_delete(&adapter->bl_cmd_timer);
}
static int bl_start_cmd_timer(struct rsi_hw *adapter, u32 timeout)
@@ -511,7 +511,7 @@ static int bl_stop_cmd_timer(struct rsi_hw *adapter)
{
adapter->blcmd_timer_expired = false;
if (timer_pending(&adapter->bl_cmd_timer))
- del_timer(&adapter->bl_cmd_timer);
+ timer_delete(&adapter->bl_cmd_timer);
return 0;
}
diff --git a/drivers/net/wireless/rsi/rsi_91x_mac80211.c b/drivers/net/wireless/rsi/rsi_91x_mac80211.c
index 3425a473b9a1..9db08200f4fa 100644
--- a/drivers/net/wireless/rsi/rsi_91x_mac80211.c
+++ b/drivers/net/wireless/rsi/rsi_91x_mac80211.c
@@ -1754,7 +1754,7 @@ void rsi_roc_timeout(struct timer_list *t)
ieee80211_remain_on_channel_expired(common->priv->hw);
if (timer_pending(&common->roc_timer))
- del_timer(&common->roc_timer);
+ timer_delete(&common->roc_timer);
rsi_resume_conn_channel(common);
mutex_unlock(&common->mutex);
@@ -1776,7 +1776,7 @@ static int rsi_mac80211_roc(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
if (timer_pending(&common->roc_timer)) {
rsi_dbg(INFO_ZONE, "Stop on-going ROC\n");
- del_timer(&common->roc_timer);
+ timer_delete(&common->roc_timer);
}
common->roc_timer.expires = msecs_to_jiffies(duration) + jiffies;
add_timer(&common->roc_timer);
@@ -1820,7 +1820,7 @@ static int rsi_mac80211_cancel_roc(struct ieee80211_hw *hw,
return 0;
}
- del_timer(&common->roc_timer);
+ timer_delete(&common->roc_timer);
rsi_resume_conn_channel(common);
mutex_unlock(&common->mutex);
diff --git a/drivers/net/wireless/silabs/wfx/bus.h b/drivers/net/wireless/silabs/wfx/bus.h
index ccadfdd6873c..79edaef20881 100644
--- a/drivers/net/wireless/silabs/wfx/bus.h
+++ b/drivers/net/wireless/silabs/wfx/bus.h
@@ -28,6 +28,7 @@ struct wfx_hwbus_ops {
void (*lock)(void *bus_priv);
void (*unlock)(void *bus_priv);
size_t (*align_size)(void *bus_priv, size_t size);
+ void (*set_wakeup)(void *priv, bool enabled);
};
extern struct sdio_driver wfx_sdio_driver;
diff --git a/drivers/net/wireless/silabs/wfx/bus_sdio.c b/drivers/net/wireless/silabs/wfx/bus_sdio.c
index f290eecde773..ab0793b9908f 100644
--- a/drivers/net/wireless/silabs/wfx/bus_sdio.c
+++ b/drivers/net/wireless/silabs/wfx/bus_sdio.c
@@ -14,6 +14,7 @@
#include <linux/of_irq.h>
#include <linux/irq.h>
#include <linux/align.h>
+#include <linux/pm.h>
#include "bus.h"
#include "wfx.h"
@@ -172,6 +173,13 @@ static size_t wfx_sdio_align_size(void *priv, size_t size)
return sdio_align_size(bus->func, size);
}
+static void wfx_sdio_set_wakeup(void *priv, bool enabled)
+{
+ struct wfx_sdio_priv *bus = priv;
+
+ device_set_wakeup_enable(&bus->func->dev, enabled);
+}
+
static const struct wfx_hwbus_ops wfx_sdio_hwbus_ops = {
.copy_from_io = wfx_sdio_copy_from_io,
.copy_to_io = wfx_sdio_copy_to_io,
@@ -180,6 +188,7 @@ static const struct wfx_hwbus_ops wfx_sdio_hwbus_ops = {
.lock = wfx_sdio_lock,
.unlock = wfx_sdio_unlock,
.align_size = wfx_sdio_align_size,
+ .set_wakeup = wfx_sdio_set_wakeup,
};
static const struct of_device_id wfx_sdio_of_match[] = {
@@ -191,9 +200,48 @@ static const struct of_device_id wfx_sdio_of_match[] = {
};
MODULE_DEVICE_TABLE(of, wfx_sdio_of_match);
+static int wfx_sdio_suspend(struct device *dev)
+{
+ struct sdio_func *func = dev_to_sdio_func(dev);
+ struct wfx_sdio_priv *bus = sdio_get_drvdata(func);
+ int ret;
+
+ if (!device_may_wakeup(dev))
+ return 0;
+
+ flush_work(&bus->core->hif.bh);
+ /* Either "wakeup-source" attribute or out-of-band IRQ is required for
+ * WoWLAN
+ */
+ if (bus->of_irq) {
+ ret = enable_irq_wake(bus->of_irq);
+ if (ret)
+ return ret;
+ } else {
+ ret = sdio_set_host_pm_flags(func, MMC_PM_WAKE_SDIO_IRQ);
+ if (ret)
+ return ret;
+ }
+ return sdio_set_host_pm_flags(func, MMC_PM_KEEP_POWER);
+}
+
+static int wfx_sdio_resume(struct device *dev)
+{
+ struct sdio_func *func = dev_to_sdio_func(dev);
+ struct wfx_sdio_priv *bus = sdio_get_drvdata(func);
+
+ if (!device_may_wakeup(dev))
+ return 0;
+ if (bus->of_irq)
+ return disable_irq_wake(bus->of_irq);
+ else
+ return 0;
+}
+
static int wfx_sdio_probe(struct sdio_func *func, const struct sdio_device_id *id)
{
const struct wfx_platform_data *pdata = of_device_get_match_data(&func->dev);
+ mmc_pm_flag_t pm_flag = sdio_get_host_pm_caps(func);
struct device_node *np = func->dev.of_node;
struct wfx_sdio_priv *bus;
int ret;
@@ -235,6 +283,9 @@ static int wfx_sdio_probe(struct sdio_func *func, const struct sdio_device_id *i
if (ret)
goto sdio_release;
+ if (pm_flag & MMC_PM_KEEP_POWER)
+ device_set_wakeup_capable(&func->dev, true);
+
return 0;
sdio_release:
@@ -261,6 +312,8 @@ static const struct sdio_device_id wfx_sdio_ids[] = {
};
MODULE_DEVICE_TABLE(sdio, wfx_sdio_ids);
+static DEFINE_SIMPLE_DEV_PM_OPS(wfx_sdio_pm_ops, wfx_sdio_suspend, wfx_sdio_resume);
+
struct sdio_driver wfx_sdio_driver = {
.name = "wfx-sdio",
.id_table = wfx_sdio_ids,
@@ -268,5 +321,6 @@ struct sdio_driver wfx_sdio_driver = {
.remove = wfx_sdio_remove,
.drv = {
.of_match_table = wfx_sdio_of_match,
+ .pm = &wfx_sdio_pm_ops,
}
};
diff --git a/drivers/net/wireless/silabs/wfx/bus_spi.c b/drivers/net/wireless/silabs/wfx/bus_spi.c
index 160b90114aad..45ee19e1ecbf 100644
--- a/drivers/net/wireless/silabs/wfx/bus_spi.c
+++ b/drivers/net/wireless/silabs/wfx/bus_spi.c
@@ -13,6 +13,7 @@
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/of.h>
+#include <linux/pm.h>
#include "bus.h"
#include "wfx.h"
@@ -179,6 +180,13 @@ static size_t wfx_spi_align_size(void *priv, size_t size)
return ALIGN(size, 4);
}
+static void wfx_spi_set_wakeup(void *priv, bool enabled)
+{
+ struct wfx_spi_priv *bus = priv;
+
+ device_set_wakeup_enable(&bus->func->dev, enabled);
+}
+
static const struct wfx_hwbus_ops wfx_spi_hwbus_ops = {
.copy_from_io = wfx_spi_copy_from_io,
.copy_to_io = wfx_spi_copy_to_io,
@@ -187,8 +195,29 @@ static const struct wfx_hwbus_ops wfx_spi_hwbus_ops = {
.lock = wfx_spi_lock,
.unlock = wfx_spi_unlock,
.align_size = wfx_spi_align_size,
+ .set_wakeup = wfx_spi_set_wakeup,
};
+static int wfx_spi_suspend(struct device *dev)
+{
+ struct spi_device *func = to_spi_device(dev);
+ struct wfx_spi_priv *bus = spi_get_drvdata(func);
+
+ if (!device_may_wakeup(dev))
+ return 0;
+ flush_work(&bus->core->hif.bh);
+ return enable_irq_wake(func->irq);
+}
+
+static int wfx_spi_resume(struct device *dev)
+{
+ struct spi_device *func = to_spi_device(dev);
+
+ if (!device_may_wakeup(dev))
+ return 0;
+ return disable_irq_wake(func->irq);
+}
+
static int wfx_spi_probe(struct spi_device *func)
{
struct wfx_platform_data *pdata;
@@ -239,7 +268,12 @@ static int wfx_spi_probe(struct spi_device *func)
if (!bus->core)
return -EIO;
- return wfx_probe(bus->core);
+ ret = wfx_probe(bus->core);
+ if (ret)
+ return ret;
+
+ device_set_wakeup_capable(&func->dev, true);
+ return 0;
}
static void wfx_spi_remove(struct spi_device *func)
@@ -273,12 +307,15 @@ static const struct of_device_id wfx_spi_of_match[] = {
MODULE_DEVICE_TABLE(of, wfx_spi_of_match);
#endif
+static DEFINE_SIMPLE_DEV_PM_OPS(wfx_spi_pm_ops, wfx_spi_suspend, wfx_spi_resume);
+
struct spi_driver wfx_spi_driver = {
+ .id_table = wfx_spi_id,
+ .probe = wfx_spi_probe,
+ .remove = wfx_spi_remove,
.driver = {
.name = "wfx-spi",
.of_match_table = of_match_ptr(wfx_spi_of_match),
+ .pm = &wfx_spi_pm_ops,
},
- .id_table = wfx_spi_id,
- .probe = wfx_spi_probe,
- .remove = wfx_spi_remove,
};
diff --git a/drivers/net/wireless/silabs/wfx/main.c b/drivers/net/wireless/silabs/wfx/main.c
index 64441c8bc460..a61128debbad 100644
--- a/drivers/net/wireless/silabs/wfx/main.c
+++ b/drivers/net/wireless/silabs/wfx/main.c
@@ -121,6 +121,12 @@ static const struct ieee80211_iface_combination wfx_iface_combinations[] = {
}
};
+#ifdef CONFIG_PM
+static const struct wiphy_wowlan_support wfx_wowlan_support = {
+ .flags = WIPHY_WOWLAN_ANY | WIPHY_WOWLAN_DISCONNECT,
+};
+#endif
+
static const struct ieee80211_ops wfx_ops = {
.start = wfx_start,
.stop = wfx_stop,
@@ -153,6 +159,11 @@ static const struct ieee80211_ops wfx_ops = {
.unassign_vif_chanctx = wfx_unassign_vif_chanctx,
.remain_on_channel = wfx_remain_on_channel,
.cancel_remain_on_channel = wfx_cancel_remain_on_channel,
+#ifdef CONFIG_PM
+ .suspend = wfx_suspend,
+ .resume = wfx_resume,
+ .set_wakeup = wfx_set_wakeup,
+#endif
};
bool wfx_api_older_than(struct wfx_dev *wdev, int major, int minor)
@@ -289,6 +300,9 @@ struct wfx_dev *wfx_init_common(struct device *dev, const struct wfx_platform_da
NL80211_PROBE_RESP_OFFLOAD_SUPPORT_P2P |
NL80211_PROBE_RESP_OFFLOAD_SUPPORT_80211U;
hw->wiphy->features |= NL80211_FEATURE_AP_SCAN;
+#ifdef CONFIG_PM
+ hw->wiphy->wowlan = &wfx_wowlan_support;
+#endif
hw->wiphy->flags |= WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD;
hw->wiphy->flags |= WIPHY_FLAG_AP_UAPSD;
hw->wiphy->max_remain_on_channel_duration = 5000;
diff --git a/drivers/net/wireless/silabs/wfx/sta.c b/drivers/net/wireless/silabs/wfx/sta.c
index 7c04810dbf3d..e95b9ded17d9 100644
--- a/drivers/net/wireless/silabs/wfx/sta.c
+++ b/drivers/net/wireless/silabs/wfx/sta.c
@@ -10,6 +10,7 @@
#include "sta.h"
#include "wfx.h"
+#include "bus.h"
#include "fwio.h"
#include "bh.h"
#include "key.h"
@@ -803,6 +804,30 @@ void wfx_remove_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
}
}
+#ifdef CONFIG_PM
+int wfx_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan)
+{
+ /* FIXME: hardware also support WIPHY_WOWLAN_MAGIC_PKT and other filters */
+ if (!wowlan->any || !wowlan->disconnect)
+ return -EINVAL;
+ return 0;
+}
+
+int wfx_resume(struct ieee80211_hw *hw)
+{
+ return 0;
+}
+
+void wfx_set_wakeup(struct ieee80211_hw *hw, bool enabled)
+{
+ struct wfx_dev *wdev = hw->priv;
+
+ if (enabled)
+ dev_info(wdev->dev, "support for WoWLAN is experimental\n");
+ wdev->hwbus_ops->set_wakeup(wdev->hwbus_priv, enabled);
+}
+#endif
+
int wfx_start(struct ieee80211_hw *hw)
{
return 0;
diff --git a/drivers/net/wireless/silabs/wfx/sta.h b/drivers/net/wireless/silabs/wfx/sta.h
index 7817c7c6f3dd..8702eed5267f 100644
--- a/drivers/net/wireless/silabs/wfx/sta.h
+++ b/drivers/net/wireless/silabs/wfx/sta.h
@@ -56,6 +56,9 @@ int wfx_assign_vif_chanctx(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
void wfx_unassign_vif_chanctx(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
struct ieee80211_bss_conf *link_conf,
struct ieee80211_chanctx_conf *conf);
+int wfx_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan);
+int wfx_resume(struct ieee80211_hw *hw);
+void wfx_set_wakeup(struct ieee80211_hw *hw, bool enabled);
/* Hardware API Callbacks */
void wfx_cooling_timeout_work(struct work_struct *work);
diff --git a/drivers/net/wireless/st/cw1200/main.c b/drivers/net/wireless/st/cw1200/main.c
index a54a7b86864f..5d569eeb353f 100644
--- a/drivers/net/wireless/st/cw1200/main.c
+++ b/drivers/net/wireless/st/cw1200/main.c
@@ -458,7 +458,7 @@ static void cw1200_unregister_common(struct ieee80211_hw *dev)
ieee80211_unregister_hw(dev);
- del_timer_sync(&priv->mcast_timeout);
+ timer_delete_sync(&priv->mcast_timeout);
cw1200_unregister_bh(priv);
cw1200_debug_release(priv);
diff --git a/drivers/net/wireless/st/cw1200/pm.c b/drivers/net/wireless/st/cw1200/pm.c
index a20ab577a364..2002e3f9fe45 100644
--- a/drivers/net/wireless/st/cw1200/pm.c
+++ b/drivers/net/wireless/st/cw1200/pm.c
@@ -105,7 +105,7 @@ int cw1200_pm_init(struct cw1200_pm_state *pm,
void cw1200_pm_deinit(struct cw1200_pm_state *pm)
{
- del_timer_sync(&pm->stay_awake);
+ timer_delete_sync(&pm->stay_awake);
}
void cw1200_pm_stay_awake(struct cw1200_pm_state *pm,
diff --git a/drivers/net/wireless/st/cw1200/queue.c b/drivers/net/wireless/st/cw1200/queue.c
index 259739e53fc1..4fd76183c368 100644
--- a/drivers/net/wireless/st/cw1200/queue.c
+++ b/drivers/net/wireless/st/cw1200/queue.c
@@ -244,7 +244,7 @@ void cw1200_queue_stats_deinit(struct cw1200_queue_stats *stats)
void cw1200_queue_deinit(struct cw1200_queue *queue)
{
cw1200_queue_clear(queue);
- del_timer_sync(&queue->gc);
+ timer_delete_sync(&queue->gc);
INIT_LIST_HEAD(&queue->free_pool);
kfree(queue->pool);
kfree(queue->link_map_cache);
diff --git a/drivers/net/wireless/st/cw1200/sta.c b/drivers/net/wireless/st/cw1200/sta.c
index c259da8161e4..444272caf124 100644
--- a/drivers/net/wireless/st/cw1200/sta.c
+++ b/drivers/net/wireless/st/cw1200/sta.c
@@ -113,7 +113,7 @@ void cw1200_stop(struct ieee80211_hw *dev, bool suspend)
cancel_work_sync(&priv->unjoin_work);
cancel_delayed_work_sync(&priv->link_id_gc_work);
flush_workqueue(priv->workqueue);
- del_timer_sync(&priv->mcast_timeout);
+ timer_delete_sync(&priv->mcast_timeout);
mutex_lock(&priv->conf_mutex);
priv->mode = NL80211_IFTYPE_UNSPECIFIED;
priv->listening = false;
@@ -2102,7 +2102,7 @@ void cw1200_multicast_stop_work(struct work_struct *work)
container_of(work, struct cw1200_common, multicast_stop_work);
if (priv->aid0_bit_set) {
- del_timer_sync(&priv->mcast_timeout);
+ timer_delete_sync(&priv->mcast_timeout);
wsm_lock_tx(priv);
priv->aid0_bit_set = false;
cw1200_set_tim_impl(priv, false);
@@ -2170,7 +2170,7 @@ void cw1200_suspend_resume(struct cw1200_common *priv,
}
spin_unlock_bh(&priv->ps_state_lock);
if (cancel_tmo)
- del_timer_sync(&priv->mcast_timeout);
+ timer_delete_sync(&priv->mcast_timeout);
} else {
spin_lock_bh(&priv->ps_state_lock);
cw1200_ps_notify(priv, arg->link_id, arg->stop);
diff --git a/drivers/net/wireless/ti/wl1251/tx.c b/drivers/net/wireless/ti/wl1251/tx.c
index 474b603c121c..adb4840b0489 100644
--- a/drivers/net/wireless/ti/wl1251/tx.c
+++ b/drivers/net/wireless/ti/wl1251/tx.c
@@ -342,8 +342,10 @@ void wl1251_tx_work(struct work_struct *work)
while ((skb = skb_dequeue(&wl->tx_queue))) {
if (!woken_up) {
ret = wl1251_ps_elp_wakeup(wl);
- if (ret < 0)
+ if (ret < 0) {
+ skb_queue_head(&wl->tx_queue, skb);
goto out;
+ }
woken_up = true;
}
diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c
index 8fb58a5d911c..ea9bc4717a85 100644
--- a/drivers/net/wireless/ti/wlcore/main.c
+++ b/drivers/net/wireless/ti/wlcore/main.c
@@ -117,7 +117,7 @@ int wl1271_recalc_rx_streaming(struct wl1271 *wl, struct wl12xx_vif *wlvif)
else {
ret = wl1271_set_rx_streaming(wl, wlvif, false);
/* don't cancel_work_sync since we might deadlock */
- del_timer_sync(&wlvif->rx_streaming_timer);
+ timer_delete_sync(&wlvif->rx_streaming_timer);
}
out:
return ret;
@@ -2841,7 +2841,7 @@ deinit:
unlock:
mutex_unlock(&wl->mutex);
- del_timer_sync(&wlvif->rx_streaming_timer);
+ timer_delete_sync(&wlvif->rx_streaming_timer);
cancel_work_sync(&wlvif->rx_streaming_enable_work);
cancel_work_sync(&wlvif->rx_streaming_disable_work);
cancel_work_sync(&wlvif->rc_update_work);
diff --git a/drivers/net/wireless/virtual/mac80211_hwsim.c b/drivers/net/wireless/virtual/mac80211_hwsim.c
index cf6a331d4042..f6add19d1da1 100644
--- a/drivers/net/wireless/virtual/mac80211_hwsim.c
+++ b/drivers/net/wireless/virtual/mac80211_hwsim.c
@@ -4,7 +4,7 @@
* Copyright (c) 2008, Jouni Malinen <j@w1.fi>
* Copyright (c) 2011, Javier Lopez <jlopex@gmail.com>
* Copyright (c) 2016 - 2017 Intel Deutschland GmbH
- * Copyright (C) 2018 - 2024 Intel Corporation
+ * Copyright (C) 2018 - 2025 Intel Corporation
*/
/*
@@ -1229,6 +1229,11 @@ static void mac80211_hwsim_set_tsf(struct ieee80211_hw *hw,
/* MLD not supported here */
u32 bcn_int = data->link_data[0].beacon_int;
u64 delta = abs(tsf - now);
+ struct ieee80211_bss_conf *conf;
+
+ conf = link_conf_dereference_protected(vif, data->link_data[0].link_id);
+ if (conf && !conf->enable_beacon)
+ return;
/* adjust after beaconing with new timestamp at old TBTT */
if (tsf > now) {
@@ -1983,11 +1988,13 @@ static void mac80211_hwsim_tx(struct ieee80211_hw *hw,
return;
}
- if (sta && sta->mlo) {
- if (WARN_ON(!link_sta)) {
- ieee80211_free_txskb(hw, skb);
- return;
- }
+ /* Do address translations only between shared links. It is
+ * possible that while an non-AP MLD station and an AP MLD
+ * station have shared links, the frame is intended to be sent
+ * on a link which is not shared (for example when sending a
+ * probe response).
+ */
+ if (sta && sta->mlo && link_sta) {
/* address translation to link addresses on TX */
ether_addr_copy(hdr->addr1, link_sta->addr);
ether_addr_copy(hdr->addr2, bss_conf->addr);
@@ -2271,7 +2278,7 @@ static void mac80211_hwsim_beacon_tx(void *arg, u8 *mac,
{
struct mac80211_hwsim_link_data *link_data = arg;
u32 link_id = link_data->link_id;
- struct ieee80211_bss_conf *link_conf;
+ struct ieee80211_bss_conf *link_conf, *tx_bss_conf;
struct mac80211_hwsim_data *data =
container_of(link_data, struct mac80211_hwsim_data,
link_data[link_id]);
@@ -2290,10 +2297,11 @@ static void mac80211_hwsim_beacon_tx(void *arg, u8 *mac,
vif->type != NL80211_IFTYPE_OCB)
return;
- if (vif->mbssid_tx_vif && vif->mbssid_tx_vif != vif)
+ tx_bss_conf = rcu_access_pointer(link_conf->tx_bss_conf);
+ if (tx_bss_conf && tx_bss_conf != link_conf)
return;
- if (vif->bss_conf.ema_ap) {
+ if (link_conf->ema_ap) {
struct ieee80211_ema_beacons *ema;
u8 i = 0;
@@ -5345,6 +5353,7 @@ static int mac80211_hwsim_new_radio(struct genl_info *info,
ieee80211_hw_set(hw, REPORTS_TX_ACK_STATUS);
ieee80211_hw_set(hw, TDLS_WIDER_BW);
ieee80211_hw_set(hw, SUPPORTS_MULTI_BSSID);
+ ieee80211_hw_set(hw, STRICT);
if (param->mlo) {
hw->wiphy->flags |= WIPHY_FLAG_SUPPORTS_MLO;
@@ -5548,10 +5557,8 @@ static int mac80211_hwsim_new_radio(struct genl_info *info,
wiphy_ext_feature_set(hw->wiphy, NL80211_EXT_FEATURE_CQM_RSSI_LIST);
for (i = 0; i < ARRAY_SIZE(data->link_data); i++) {
- hrtimer_init(&data->link_data[i].beacon_timer, CLOCK_MONOTONIC,
- HRTIMER_MODE_ABS_SOFT);
- data->link_data[i].beacon_timer.function =
- mac80211_hwsim_beacon;
+ hrtimer_setup(&data->link_data[i].beacon_timer, mac80211_hwsim_beacon,
+ CLOCK_MONOTONIC, HRTIMER_MODE_ABS_SOFT);
data->link_data[i].link_id = i;
}
diff --git a/drivers/net/wireless/virtual/virt_wifi.c b/drivers/net/wireless/virtual/virt_wifi.c
index 4ee374080466..1fffeff2190c 100644
--- a/drivers/net/wireless/virtual/virt_wifi.c
+++ b/drivers/net/wireless/virtual/virt_wifi.c
@@ -146,7 +146,7 @@ static void virt_wifi_inform_bss(struct wiphy *wiphy)
static const struct {
u8 tag;
u8 len;
- u8 ssid[8];
+ u8 ssid[8] __nonstring;
} __packed ssid = {
.tag = WLAN_EID_SSID,
.len = VIRT_WIFI_SSID_LEN,
@@ -519,11 +519,13 @@ static rx_handler_result_t virt_wifi_rx_handler(struct sk_buff **pskb)
}
/* Called with rtnl lock held. */
-static int virt_wifi_newlink(struct net *src_net, struct net_device *dev,
- struct nlattr *tb[], struct nlattr *data[],
+static int virt_wifi_newlink(struct net_device *dev,
+ struct rtnl_newlink_params *params,
struct netlink_ext_ack *extack)
{
struct virt_wifi_netdev_priv *priv = netdev_priv(dev);
+ struct net *link_net = rtnl_newlink_link_net(params);
+ struct nlattr **tb = params->tb;
int err;
if (!tb[IFLA_LINK])
@@ -532,7 +534,7 @@ static int virt_wifi_newlink(struct net *src_net, struct net_device *dev,
netif_carrier_off(dev);
priv->upperdev = dev;
- priv->lowerdev = __dev_get_by_index(src_net,
+ priv->lowerdev = __dev_get_by_index(link_net,
nla_get_u32(tb[IFLA_LINK]));
if (!priv->lowerdev)
diff --git a/drivers/net/wireless/zydas/zd1211rw/zd_mac.c b/drivers/net/wireless/zydas/zd1211rw/zd_mac.c
index f90c33d19b39..9653dbaac3c0 100644
--- a/drivers/net/wireless/zydas/zd1211rw/zd_mac.c
+++ b/drivers/net/wireless/zydas/zd1211rw/zd_mac.c
@@ -21,7 +21,7 @@
struct zd_reg_alpha2_map {
u32 reg;
- char alpha2[2];
+ char alpha2[2] __nonstring;
};
static struct zd_reg_alpha2_map reg_alpha2_map[] = {